Hi team,

I hope you are doing well.

Do you happen to have a ready-to-use script or solution to export business units and their criteria?

I need to find business units where a specific OU is configured to include members of that OU or its sub-OUs.

Thanks

by (2.5k points)

1 Answer

by (308k points)
0 votes

Hello,

Unfortunately, we do not have any scripts of the kind.

by (2.5k points)
0

Morning,

ok.

I am trying to find it out myself currently, can you guide me in the right direction?

I want to run this via Powershell, outside of Adaxes and already have the following

[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi") | Out-Null

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$businessUnitsPath = $service.Backend.GetConfigurationContainerPath("BusinessUnits")
$businessUnitsPathObj = New-Object "Softerra.Adaxes.Adsi.AdsPath" $businessUnitsPath
$businessUnits = $service.OpenObject($businessUnitsPathObj.ToString(), $null, $null, 0)

If I now run this command

$businessUnits | ft parent, name, unitname

I get the full list of my business units (133 in total) image.png However, If I do a count, I get only 9?

$businessUnits | Measure-Object

I have a total of 8 containers image.png

How do I get the container names of my business units to then loop through each of it and get relevant member busniess units?

I will then fetch each business unit and their rule and export it.

by (308k points)
0

Hello,

As we understand, you have 9 containers with business units and want to search under them. We do not have the exact example for business units, but the following article should be helpful: https://adaxes.com/sdk/WritingAdsiScripts.

by (2.5k points)
0

//update checking something

by (2.5k points)
0

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++
        }
    }
}

Related questions

Hello, We have some Create Business Rules with ifs and other actions. Now these have to be controlled by a third person who has no rights to the console. How can I export the ... to a .txt file? The TXT must contain the condition and the actions. Thanks a lot

asked Aug 10, 2023 by DRiVSSi (360 points)
0 votes
1 answer

Is it possible to export Business Rules and/or Custom Command, from the Admin Console, so that we can have a backup of the current setup? I especially want to have a document of the Custom Commands and their IDs, which I'm using in PowerShell scripts.

asked Feb 26, 2014 by sdavidson (730 points)
0 votes
1 answer

Hi, I'm trying to create a business rule that basically says if a user is a member of group a, they can't be a member of group b. Optionally would also like to ... removed from group a. Can i get some guidance on how I could accomplish this in adaxes?

asked Mar 14, 2022 by blaiseb (120 points)
0 votes
0 answers

Hi, we would like to combine different OUs into business units for differnt local helpdesk teams. We started to add OUs with this logic for Business Units This give us a ... messy as every item is shown at once and not structered anymore. Any solution here?

asked Jul 4, 2023 by wintec01 (2.5k points)
0 votes
1 answer

We have a large number of members that we would like to show as part of a Business Unit. I have looked in the config file and found a value that says 500, but wasn't able ... IIS Services to recall the web config and I get the same issue. Version: 3.7.13122.0

asked Jul 1, 2016 by charlesk (20 points)
0 votes
0 answers