I wrote this script to export the rules of business Units in a readable JSON format. It will create folders based on containers and business units.
Maybe someone else need this in future : )
# Function to clean folder
function Clear-FolderContent {
param(
[string]$Path
)
if (Test-Path $basePath) {
try {
Remove-Item -Path "$basePath\*" -Recurse -Force -ErrorAction Stop
Write-Log -Message "Folder cleaned successfully: $basePath"
}
catch {
Write-Log -Message "Error cleaning folder: $_" -Level "ERR"
}
} else {
Write-Log "Folder not found: $basePath"
}
}
# Function to ensure a directory exists
function Ensure-DirectoryExists {
param ([string]$path)
if (-not (Test-Path -Path $path -PathType Container)) {
try {
New-Item -Path $path -ItemType Directory -Force | Out-Null
Write-Log -Message "Created directory: $path"
return $true
} catch {
Write-Log -Level "ERROR" -Message "Failed to create directory: $path. Error: $($_.Exception.Message)"
return $false
}
}
return $true
}
# Logging function
function Write-Log {
param (
[string]$Level = "INFO",
[string]$Message
)
$entry = "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") [$Level] $Message"
# Console output for interactive sessions
switch ($Level.ToUpper()) {
"ERR" { Write-Host $entry -ForegroundColor Red }
"ERROR" { Write-Host $entry -ForegroundColor Red }
"WARN" { Write-Host $entry -ForegroundColor Yellow }
"WARNING" { Write-Host $entry -ForegroundColor Yellow }
default { Write-Host $entry -ForegroundColor Gray }
}
}
# Function to extract the common name from a distinguished name
function Get-CommonName {
param ([string]$dn)
# Extract the CN part and remove the "CN=" prefix
if ($dn -match "CN=([^,]+)") {
return $matches[1]
} else {
# If CN can't be extracted, use a sanitized version of the DN
return $dn -replace '[\\/:*?"<>|]', '_'
}
}
function ConvertTo-SafeFolderName {
param(
[Parameter(Mandatory=$true)][string]$Name,
[int]$MaxLen = 120
)
$n = $Name
# Unescape common artifacts like '\+'
$n = $n -replace '\\\+', '+'
# Prevent unintended subfolders
$n = $n -replace '[\\/]+', ' - '
# Windows-invalid filename chars: <>:"/\|?*
$n = $n -replace '[<>:"|?*]+', ' - '
# Control chars -> space
$n = -join ($n.ToCharArray() | ForEach-Object {
if ([int]$_ -lt 32) { ' ' } else { $_ }
})
$n = ($n -replace '\s+', ' ').Trim()
$n = ($n -replace '\s*-\s*', ' - ').Trim()
$n = $n.TrimEnd('.', ' ')
$n = $n.TrimStart('.', ' ')
if ([string]::IsNullOrWhiteSpace($n)) { $n = "_unnamed" }
$upper = $n.ToUpperInvariant()
$reserved = @("CON","PRN","AUX","NUL") + (1..9 | ForEach-Object { "COM$($_)" }) + (1..9 | ForEach-Object { "LPT$($_)" })
if ($reserved -contains $upper) { $n = "_$($n)" }
if ($n.Length -gt $MaxLen) {
$hash = [BitConverter]::ToString([Security.Cryptography.SHA1]::Create().ComputeHash([Text.Encoding]::UTF8.GetBytes($n))).Replace("-", "").Substring(0, 8)
$cut = $MaxLen - 10
if ($cut -lt 10) { $cut = 10 }
$n = "$($n.Substring(0, $cut))_$($hash)"
}
return $n
}
# Safe file write function with try/catch
function Write-SafeFile {
param (
[string]$FilePath,
[string]$Content,
[string]$Description = "file"
)
try {
$Content | Out-File -FilePath $FilePath -ErrorAction Stop
return $true
} catch {
Write-Log -Level "ERROR" -Message "Failed to write $Description to $FilePath. Error: $($_.Exception.Message)"
return $false
}
}
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi") | Out-Null
$basePath = "C:\temp\adx-business-units-rules"
Clear-FolderContent -Path $basePath
# Prompt for credentials.
if($null -eq $credential) {
$credential = Get-Credential -UserName "<domain>\<username>" -Message "Password"
}
# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")
$businessUnitContainerPath = $service.Backend.GetConfigurationContainerPath("BusinessUnits")
$businessUnitContainerPathObj = New-Object "Softerra.Adaxes.Adsi.AdsPath" $businessUnitContainerPath
$businessUnitContainer = $service.OpenObject($businessUnitContainerPathObj.ToString(), $null, $null, 0)
Ensure-DirectoryExists $basePath | Out-Null
foreach($container in $businessUnitContainer) {
$containerNameCN = $container.Name
# Skip internal(?) container
if($containerNameCN -like "*Queries*") {
continue
}
Write-Host "Found Container: $(Get-CommonName $containerNameCN)"
$containerPathFile = Join-Path -Path $basePath -ChildPath (ConvertTo-SafeFolderName (Get-CommonName $containerNameCN))
Ensure-DirectoryExists $containerPathFile | Out-Null
$businessUnitPath = $businessUnitContainerPathObj.CreateChildPath($containerNameCN)
$businessUnitPathObj = New-Object "Softerra.Adaxes.Adsi.AdsPath" $businessUnitPath
$businessUnit = $service.OpenObject($businessUnitPathObj.ToString(), $null, $null, 0)
foreach($item in $businessUnit) {
$itemNameCN = $item.Name
Write-Host "Processing Business Unit: $(Get-CommonName $containerNameCN)/$(Get-CommonName $itemNameCN)"
$businessUnitPathFile = Join-Path -Path $containerPathFile -ChildPath (ConvertTo-SafeFolderName (Get-CommonName $itemNameCN))
Ensure-DirectoryExists $businessUnitPathFile | Out-Null
$businessUnitItem = $service.OpenObject($item.ADsPath, $null, $null, 0)
$businessUnitItemDN = $businessUnitItem.Get("distinguishedName")
$rules = $businessUnitItem.GetMembershipRules()
Write-SafeFile -FilePath "$businessUnitPathFile\dn_info.txt" -Content $businessUnitItemDN -Description "DN info" | Out-Null
#Write-SafeFile -FilePath "$businessUnitPathFile\ads_info.txt" -Content "$($item.ADsPath)" -Description "ADS info" | Out-Null
$i=0
foreach ($rule in $rules) {
#Write-Log -Message "Rule $($i) - $($rule.Type)"
switch ($rule.Type) {
"ADM_BUSINESSUNITMEMBERSHIPTYPE_QUERY" {
Write-Log -Message "Rule $($i) - Query"
try {
$ruleJSON = $rule.GetCriteria().ToJson($NULL) | ConvertFrom-Json | ConvertTo-Json -Depth 10 -Compress:$false
Write-SafeFile -FilePath "$businessUnitPathFile\rule_$($i)_queryresults.json" -Content $ruleJSON -Description "query results" | Out-Null
Write-Log -Message "Rule $($i) - Query Results - Exported"
} catch {
Write-Log -Level "ERROR" -Message "Failed to export query rule $i. Error: $($_.Exception.Message)"
}
try {
$scope = $rule.BaseObjectPath
if($scope) {
try {
# Bind to the configuration object.
$configObject = $service.OpenObject($scope, $credential.UserName, `
$credential.GetNetworkCredential().Password, 0)
$class = $configObject.Class
$path = ($configObject.ADsPath).Replace("Adaxes://<domain.tld>/","")
$parent = ($configObject.Parent).Replace("Adaxes://<domain.tld>/","")
$jsonObject = @{
Class = $class
Path = $path
Parent = $parent
}
$scopeJSON = $jsonObject | ConvertTo-Json
Write-SafeFile -FilePath "$businessUnitPathFile\rule_$($i)_scope.json" -Content $scopeJSON -Description "scope results" | Out-Null
Write-Log -Message "Rule $($i) - Scope - Exported"
} catch {
Write-Log -Level "ERROR" -Message "Failed to fetch scope rule $i. Error: $($_.Exception.Message)"
}
}
} catch {
Write-Log -Level "ERROR" -Message "Failed to export scope rule $i. Error: $($_.Exception.Message)"
}
}
"ADM_BUSINESSUNITMEMBERSHIPTYPE_SPECIFIC" {
Write-Log -Message "Rule $($i) - Specific Objects"
try {
try {
$temp = $service.OpenObject($rule.ObjectPath, $null, $null, 0)
$tempDN = $temp.Get("distinguishedName")
} catch {
$tempDN = "not found"
}
$ruleJSON = [PSCustomObject]@{
ObjectPath = $rule.ObjectPath
DistinguishedName = $tempDN
Exclude = $rule.Exclude
Disabled = $rule.Disabled
} | ConvertTo-Json | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }
Write-SafeFile -FilePath "$businessUnitPathFile\rule_$($i)_specific.json" -Content $ruleJSON -Description "query results" | Out-Null
Write-Log -Message "Rule $($i) - Specific Results - Exported"
} catch {
Write-Log -Level "ERROR" -Message "Failed to export Specific rule $i. Error: $($_.Exception.Message)"
}
}
"ADM_BUSINESSUNITMEMBERSHIPTYPE_GROUP" {
Write-Log -Message "Rule $($i) - Group Members"
try {
try {
$temp = $service.OpenObject($rule.GroupPath, $null, $null, 0)
$tempDN = $temp.Get("distinguishedName")
} catch {
$tempDN = "not found"
}
$ruleJSON = [PSCustomObject]@{
GroupPath = $rule.GroupPath
DistinguishedName = $tempDN
IncludeDirectMembersOnly = $rule.IncludeDirectMembersOnly
Exclude = $rule.Exclude
Disabled = $rule.Disabled
} | ConvertTo-Json | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }
Write-SafeFile -FilePath "$businessUnitPathFile\rule_$($i)_groupmember.json" -Content $ruleJSON -Description "query results" | Out-Null
Write-Log -Message "Rule $($i) - Group Member Results - Exported"
} catch {
Write-Log -Level "ERROR" -Message "Failed to export Group Member rule $i. Error: $($_.Exception.Message)"
}
}
"ADM_BUSINESSUNITMEMBERSHIPTYPE_CONTAINER" {
Write-Log -Message "Rule $($i) - Objects located in OU or container"
try {
$ruleJSON = $rule | Select-Object BaseObjectPath, @{Name='Scope'; Expression={$_.Scope.ToString()}}, Exclude, Disabled | ConvertTo-Json
Write-SafeFile -FilePath "$businessUnitPathFile\rule_$($i)_container.json" -Content $ruleJSON -Description "query results" | Out-Null
Write-Log -Message "Rule $($i) - Container Results - Exported"
} catch {
Write-Log -Level "ERROR" -Message "Failed to export Container rule $i. Error: $($_.Exception.Message)"
}
}
default {
Write-Log -Message -Level "WARN" "Rule $($i) - Unsupported Type: $($rule.Type)"
}
}
$i++
}
}
}