I've created a Powershell function Copy-DsAcl
which should assist with performing this sort of Active Directory permissions copy. Using this function, the original answer (below the line) may be rewritten more cleanly as:
Import-Module ActiveDirectory
# Dot source the Copy-DsAcl function: https://github.com/jasonkeithscott/Copy-DsAcl
. .\Copy-DsAcl.ps1
# Reference objects
$sourceGroup = Get-ADGroup Limited_IT_Admins
$sourceObject = Get-ADOrganizationalUnit -Filter { Name -eq "City01" }
# Hash for the new groups and their assigned OUs
$targetGroups = @{}
$targetGroups.Add("Limited_IT_Admin_01", @("City01", "City02", "City03"))
$targetGroups.Add("Limited_IT_Admin_02", @("City04", "City05"))
$targetGroups.Add("Limited_IT_Admin_03", @("City06", "City07"))
# Walk each targetGroup in the hash
foreach ( $g in $targetGroups.GetEnumerator() ) {
$targetGroup = Get-ADGroup $g.Name
# Walk each $city OU and add the $targetGroup to the ACL
foreach ( $city in $g.Value ) {
Write-Host "Adding $($g.Name) to $city"
$targetObject = Get-ADOrganizationalUnit -Filter { Name -eq $city }
Copy-DsAcl $sourceGroup $sourceObject $targetGroup $targetObject
}
}
The Powershell below should do what you're asking. There are a few requirements:
- You need the Microsoft ActiveDirectory Powershell module. It's included with RSAT7.
- You need to update the following for your environment:
$root
- the PSDrive to your "root" OU. "Country" in your question.
$sourceOU
- the source OU (name, not DN) from which you will copy the ACEs.
$sourceGroup
- the group (name, not DN or domain) listed in the ACL which you will copy.
$targetGroups
- Hash of groups (name, not DN or domain) and OUs (name, not DN) for applying the ACEs.
- This will only copy explicit ACEs, not inherited. Maybe I should look at walking up the tree to grab the inherited ones?
- I had to run this as Domain Admin as I was getting "Access Denied" errors. My initial OU delegations were probably suspect though.
Reading all this over I think I should probably just write a more generic function, CopyOuAcl
, and update this when that's done. As it is written now, it is completely specific to your question and environment.
Import-Module ActiveDirectory
$root = "AD:\OU=Country,DC=example,DC=com"
$sourceOU = "City01"
$sourceACL = Get-Acl $root.Replace("AD:\", "AD:\OU=$sourceOU,")
$sourceGroup = "Limited_IT_Admins"
# Hash for the new groups and their OUs
$targetGroups = @{}
$targetGroups.Add("Limited_IT_Admin_01", @("City01", "City02", "City03"))
$targetGroups.Add("Limited_IT_Admin_02", @("City04", "City05"))
$targetGroups.Add("Limited_IT_Admin_03", @("City06", "City07"))
# Get the uniherited ACEs for the $sourceGroup from $sourceOU
$sourceACEs = $sourceACL |
Select-Object -ExpandProperty Access |
Where-Object { $_.IdentityReference -match "$($sourceGroup)$" -and $_.IsInherited -eq $False }
# Walk each targetGroup in the hash
foreach ( $g in $targetGroups.GetEnumerator() ) {
# Get the AD object for the targetGroup
Write-Output $g.Name
$group = Get-ADGroup $g.Name
$identity = New-Object System.Security.Principal.SecurityIdentifier $group.SID
# Could be multiple ACEs for the sourceGroup
foreach ( $a in $sourceACEs ) {
# From from the sourceACE for the ActiveDirectoryAccessRule constructor
$adRights = $a.ActiveDirectoryRights
$type = $a.AccessControlType
$objectType = New-Object Guid $a.ObjectType
$inheritanceType = $a.InheritanceType
$inheritedObjectType = New-Object Guid $a.InheritedObjectType
# Create the new "copy" of the ACE using the target group. http://msdn.microsoft.com/en-us/library/w72e8e69.aspx
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity, $adRights, $type, $objectType, $inheritanceType, $inheritedObjectType
# Walk each city OU of the target group
foreach ( $city in $g.Value ) {
Write-Output "`t$city"
# Set the $cityOU
$cityOU = $root.Replace("AD:\", "AD:\OU=$city,")
# Get the ACL for $cityOU
$cityACL = Get-ACL $cityOU
# Add it to the ACL
$cityACL.AddAccessRule($ace)
# Set the ACL back to the OU
Set-ACL -AclObject $cityACL $cityOU
}
}
}