Copying OU permissions from an existing security group to a new security group

Currently we have a security group named: Limited_IT_Admins that has special permissions (restricted to only certain tasks they can do) on ~7 City OUs within a Country OU.

[Country] <- top level OU
  [City01]
  [City02]
  [City03]
  [City04]
  [City05]
  [City06]
  [City07]

However, now I have to split this single Security Group into three individual groups. Users from the Limited_IT_Admin group will be split up into the three separate new groups. The users will need the same access as the Limited_IT_Admins did, but only on their respective OUs.

Limited_IT_Admin_01 - User01
  City01, City02, City03

Limited_IT_Admin_02 - User02
  City04, City05

Limited_IT_Admin_03 - User03
   City06, City07

Instead of having to try to recreate all the special permissions that were set on the security group, is there any easier way to copy the permissions the Limited_IT_Admins had to the three new groups?


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:

  1. You need the Microsoft ActiveDirectory Powershell module. It's included with RSAT7.
  2. You need to update the following for your environment:
    1. $root - the PSDrive to your "root" OU. "Country" in your question.
    2. $sourceOU - the source OU (name, not DN) from which you will copy the ACEs.
    3. $sourceGroup - the group (name, not DN or domain) listed in the ACL which you will copy.
    4. $targetGroups - Hash of groups (name, not DN or domain) and OUs (name, not DN) for applying the ACEs.
  3. This will only copy explicit ACEs, not inherited. Maybe I should look at walking up the tree to grab the inherited ones?
  4. 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

        }

    }

}