How to Clone Active Directory OU Permissions for a Specific Group Using PowerShell

Ilya Fedotov

This script is a PowerShell script designed to copy permissions from a specified group in a source Organizational Unit (OU) within Active Directory to multiple target OUs. Here’s an overview of its functionality:

  1. Define Source and Target OUs: It sets the source OU from which permissions will be copied, specifies the group name whose permissions are to be copied, and lists the target OUs where these permissions will be applied.
  2. Logging Setup: The script creates a log file named with the current date to record the actions performed. A function Write-Log is defined for logging purposes.
  3. Get ACL from Source OU: Access Control List (ACL) of the source OU is retrieved, filtering Access Control Entries (ACEs) specific to the specified group.
  4. Process Each Target OU: For each target OU, the script performs the following steps:
    • Backup Current ACL: Backs up the current ACL of the target OU to an XML file.
    • Get and Modify ACL: Retrieves the current ACL of the target OU, adds each ACE from the source OU’s group, and applies the modified ACL back to the target OU.
    • Logging: Each step (backup creation, ACE addition, ACL update) is logged.
    • Optional Verification: Optionally, the script can verify if the ACL has been applied correctly by checking the updated ACL of the target OU.
  5. Final Log Entry: Marks the completion of the script execution.

This script is useful for administrators who need to replicate specific group permissions across multiple OUs in Active Directory, ensuring consistency and saving time compared to manually setting permissions for each OU. The use of logging and backup ensures traceability and safety, allowing for rollback in case of errors.

# Script: Copy-ADGroupPermissions.ps1
# Description: Copies permissions from a specified group in a source OU to multiple target OUs
# Author: Ilya Fedotov
# Date: April 7, 2025

# Import the ActiveDirectory module
Import-Module ActiveDirectory

# Define parameters
$sourceOU = "OU=Source,DC=contoso,DC=com"              # Source OU path
$groupName = "AD-Group-Permissions"                    # Group name whose permissions will be copied
$targetOUs = @(                                        # Target OUs where permissions will be applied
    "OU=Target1,DC=contoso,DC=com",
    "OU=Target2,DC=contoso,DC=com",
    "OU=Target3,DC=contoso,DC=com"
)
$backupFolder = "C:\Temp\ACL_Backups"                  # Folder for ACL backups
$verifyChanges = $true                                 # Set to $true to verify changes after applying

# Create backup folder if it doesn't exist
if (-not (Test-Path -Path $backupFolder)) {
    New-Item -Path $backupFolder -ItemType Directory | Out-Null
}

# Set up logging
$logDate = Get-Date -Format "yyyyMMdd-HHmmss"
$logFile = "C:\Temp\Copy-ADPermissions-$logDate.log"

# Function to write to log file
function Write-Log {
    param (
        [Parameter(Mandatory=$true)]
        [string]$Message,
        
        [Parameter(Mandatory=$false)]
        [ValidateSet("Info", "Warning", "Error")]
        [string]$Level = "Info"
    )
    
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "[$timestamp] [$Level] $Message"
    
    # Write to log file
    Add-Content -Path $logFile -Value $logEntry
    
    # Also output to console
    switch ($Level) {
        "Info"    { Write-Host $logEntry }
        "Warning" { Write-Host $logEntry -ForegroundColor Yellow }
        "Error"   { Write-Host $logEntry -ForegroundColor Red }
    }
}

Write-Log "Starting permission copy operation from group '$groupName' in OU '$sourceOU'"
Write-Log "Log file created at: $logFile"

try {
    # Get the source OU's ACL
    Write-Log "Retrieving ACL from source OU: $sourceOU"
    $sourceOUObject = [ADSI]"LDAP://$sourceOU"
    $sourceACL = $sourceOUObject.psbase.ObjectSecurity.GetAccessRules($true, $true, [System.Security.Principal.NTAccount])
    
    # Filter ACEs for the specified group
    $groupIdentity = New-Object System.Security.Principal.NTAccount($groupName)
    $groupACEs = $sourceACL | Where-Object { $_.IdentityReference -eq $groupIdentity }
    
    if ($groupACEs.Count -eq 0) {
        Write-Log "No permissions found for group '$groupName' in the source OU. Exiting script." -Level "Error"
        exit
    }
    
    Write-Log "Found $($groupACEs.Count) permission entries for group '$groupName' in the source OU"
    
    # Process each target OU
    foreach ($targetOU in $targetOUs) {
        Write-Log "Processing target OU: $targetOU"
        
        try {
            # Get target OU object
            $targetOUObject = [ADSI]"LDAP://$targetOU"
            
            # Backup current ACL to XML file
            $backupFileName = "$backupFolder\$($targetOU.Replace(',','_').Replace('=','_'))-$logDate.xml"
            Write-Log "Creating backup of current ACL to: $backupFileName"
            
            $currentACL = $targetOUObject.psbase.ObjectSecurity
            $currentACL | Export-Clixml -Path $backupFileName
            Write-Log "Backup created successfully"
            
            # Get current ACL
            $targetACL = $targetOUObject.psbase.ObjectSecurity
            
            # Add each ACE from the source OU's group to the target OU
            Write-Log "Adding permissions from source OU to target OU"
            foreach ($ace in $groupACEs) {
                Write-Log "  Adding ACE: $($ace.IdentityReference) - $($ace.ActiveDirectoryRights) - $($ace.AccessControlType)"
                
                # Create and add the new ACE
                $targetACL.AddAccessRule($ace)
            }
            
            # Apply the modified ACL to the target OU
            Write-Log "Applying modified ACL to target OU"
            $targetOUObject.psbase.ObjectSecurity = $targetACL
            $targetOUObject.psbase.CommitChanges()
            Write-Log "ACL successfully updated for target OU: $targetOU"
            
            # Verify changes if enabled
            if ($verifyChanges) {
                Write-Log "Verifying applied permissions"
                
                # Get updated ACL
                $updatedOUObject = [ADSI]"LDAP://$targetOU"
                $updatedACL = $updatedOUObject.psbase.ObjectSecurity.GetAccessRules($true, $true, [System.Security.Principal.NTAccount])
                
                # Check if all ACEs were applied
                $allApplied = $true
                foreach ($ace in $groupACEs) {
                    $matchingACE = $updatedACL | Where-Object { 
                        $_.IdentityReference -eq $ace.IdentityReference -and 
                        $_.ActiveDirectoryRights -eq $ace.ActiveDirectoryRights -and 
                        $_.AccessControlType -eq $ace.AccessControlType -and
                        $_.ObjectType -eq $ace.ObjectType -and
                        $_.InheritanceType -eq $ace.InheritanceType
                    }
                    
                    if (-not $matchingACE) {
                        $allApplied = $false
                        Write-Log "  Failed to find matching ACE for: $($ace.IdentityReference) - $($ace.ActiveDirectoryRights)" -Level "Warning"
                    }
                }
                
                if ($allApplied) {
                    Write-Log "  Verification successful: All permissions were applied correctly"
                } else {
                    Write-Log "  Verification failed: Some permissions may not have been applied correctly" -Level "Warning"
                }
            }
            
        } catch {
            Write-Log "Error processing target OU '$targetOU': $_" -Level "Error"
        }
    }
    
    Write-Log "Permission copy operation completed"
    
} catch {
    Write-Log "An error occurred: $_" -Level "Error"
}

Leave a Comment

Scroll to Top