private/Add-OUStructureFromTemplate.ps1
Function Add-OUStructureFromTemplate { [CmdletBinding(SupportsShouldProcess = $true)] Param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] [String]$Name, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] [String]$Description, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 2)] [String]$Path, [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true, Position = 3)] [System.Collections.Hashtable]$Template, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 4)] [String]$ParentOrg, [Switch]$ResetRoleMembership, [Switch]$ResetRightsMembership, [Microsoft.ActiveDirectory.Management.ADDirectoryServer]$Server = (get-addomainController -Writable -Discover) ) BEGIN { $shouldProcess = @{ Confirm = [bool]($ConfirmPreference -eq "low") Whatif = [bool]($WhatIfPreference.IsPresent) verbose = [bool]($VerbosePreference -ne "SilentlyContinue") } #$ShouldProcess.verbose = $True } PROCESS { write-verbose "Starting to Add structure from template (DC: $($Server.Hostname))" if ($PsItem.Name) { $Name = $_.Name } if ($PsItem.Description) { $Description = $_.Description } if ($PsItem.Path) { $Path = $_.Path } if ($PsItem.Template) { $Template = $_.Template } if ($PsItem.ParentOrg) { $ParentOrg = $_.ParentOrg } $DefaultRights = $Template.DefaultRights $DefaultRoles = $Template.DefaultRoles $resetRoleParam = @{ resetMembership = [bool]($ResetRoleMembership) } $resetRightsParam = @{ resetMembers = [bool]($ResetRightsMembership) } $structureString = "`r`n{0} OUs {0}{0}{0} Descriptions {0}{0}{0}" -f "#####" $structureString += "`r`n {0,-24} : {1}" -f $name, $description $structureString += $Template.OUs | ForEach-Object { "`r`n --> {0,-20} : {1}" -f $_.name, ($_.description[0..95] -join "") } $ChildPath = "OU=$name,$path" if (-not $shouldProcess.verbose) { Write-Host ("Creating OU Structure: '{0}'`r`n under: '{1}'`r`n{2}" -f $name, $path, $structureString) } if ($PSCmdlet.ShouldProcess(("`r`nCreating OU Structure: '{0}'`r`n under: '{1}'`r`n{2}" -f $name, $path, $structureString))) { $OUBase = CreateOrSetOU -Name $name -Description $Description -Path $path @shouldProcess $ChildPath = $OUBase.DistinguishedName $ChildOUs = $Template.OUs | foreach-object { $_.Path = $ChildPath write-verbose ("--- Template ChildOU: {0} at {1}" -f $_.name, $_.path) [pscustomObject]$_ } $ChildOUs | CreateOrSetOU @ShouldProcess | out-null } Write-Host "Finished Structure Creation`r`n" if ($ParentOrg) { # Write-verbose "There is a component" $GroupMidName = "$parentOrg-$Name" $RightsPath = "OU={0},{1}" -f $($Settings.Names.RightsOU), $ChildPath $RolesPath = "OU={0},{1}" -f $Settings.Names.RightsOU, $ChildPath $ParentPrincipalPrefix = "$parentOrg" $parentOrgObj = get-rbacOrg -org $parentOrg } else { # write-verbose "No Component" $GroupMidName = $name $RightsPath = "OU={0},{1}" -f $($Settings.Names.RightsOU), $ChildPath $RolesPath = "OU={0},{1}" -f $Settings.Names.RightsOU, $ChildPath $ParentPrincipalPrefix = $GlobalOrgName $parentOrgObj = get-rbacOrg -org global -includeGlobal } # write-verbose "Rights: $rightsPath; Roles: $RolesPath" if ($defaultRights) { Write-host ("{0,-48} @ {1}" -f "About to create default rights", $RightsPath) } $rightsDef = @{} foreach ($Group in $DefaultRights) { $def = [pscustomobject]@{ Name = "$RightsName-$GroupMidName-$($Group.nameSuffix)" Description = $Group.Description path = $RightsPath GroupScope = $RightScope Info = $Group.Description Members = @() memberOf = @() } if ($Group.AddParents -ne $false -and $ParentPrincipalPrefix -ne $name) { try { $parentGroupFilter = "{0} -eq '{1}{2}-{3}-{4}' -or {0} -eq '{2}-{3}-{4}' -and GroupScope -eq '{5}'" -f "name", $RightPrefix, $RightsName, $ParentPrincipalPrefix, $Group.nameSuffix,$RightScope $parentGroupName = (get-adgroup -filter $parentGroupFilter -searchBase $parentOrgObj.distinguishedName -server $Server ).name if ($ParentGroupName.count -gt 1) { write-warning "Multiple groups found, assuming group with rightPrefix" $parentGroupName = @($parentGroupName.where({$_ -like "$RightPrefix*"})) } if ($ParentGroupName.count -gt 1) { throw "Still too many groups! $parentGroupFilter" } write-Verbose "Adding the parent Org group $ParentGroupName as a member to $($def.name)" $def.members = $parentGroupName } catch { write-warning $_.exception.getType().fullname Write-Warning "Parent org group doesn't exist? skipping" } } if (-not $group.DoNotPrefixGroupName) { $def.name = "$RightPrefix$($def.name)" } Write-Host ("--->{0,-30}" -f $def.name, $def.path) $rightsDef.$($group.nameSuffix) = $def } $rightsDef.values | CreateOrSetGroup @ShouldProcess @resetRightsParam | out-null if ($defaultRoles) { Write-host ("`r`n{0,-48} @ {1}" -f "About to create Roles:", $RolesPath) } $rolesDef = @{} foreach ($Group in $DefaultRoles) { $def = [psCustomObject]@{ Name = "{0}-{1}-{2}{3}" -f $Settings.Names.RolesName, $GroupMidName, $Group.nameSuffix, $rolesuffix Description = $Group.Description path = $RolesPath GroupScope = $RoleScope Info = $Group.Description MemberOf = @() } if ($Group.AddParents -eq $true -and $name -ne "Global") { $parentGroupFilter = "{0} -eq '{2}-{3}-{4}{1}' -or {0} -eq '{2}-{3}-{4}' -and GroupScope -eq '$RoleScope'" -f "name", $RoleSuffix, $RightsName, $ParentPrincipalPrefix, $Group.nameSuffix, $RoleScope $parentGroupName = "{0}-{1}-{2}" -f $Settings.Names.RolesName, $ParentPrincipalPrefix, $Group.nameSuffix $parentGroupName = (get-adgroup -filter $parentGroupFilter -searchBase $parentOrgObj.distinguishedName -server $Server ).name if ($ParentGroupName.count -gt 1) { write-warning "Multiple groups found, assuming group with rightPrefix" $parentGroupName = @($parentGroupName.where({$_ -like "$RightPrefix*"})) } if ($ParentGroupName.count -gt 1) { throw "Still too many groups! $parentGroupFilter" } write-Verbose "Add parent group as member: $parentGroupName" $def.members = $ParentGroupName } Write-Host ("--->{0,-30}" -f $def.name, $def.path) $protectedRole = $false $def.memberOf = foreach ($right in $Group.rights) { $rightName = $rightsDef.$right.name if ($rightName -like "*$($protectedRight)*") { write-warning "âš¡âš¡âš¡$rightNameâš¡âš¡âš¡" $protectedRole = $true } $rightName } if ($group.auxiliaryGroups) { $def.memberof += $Group.auxiliaryGroups } if ($true -eq $Group.Protected) { $def.memberOf += "Protected Users" $protectedRole = $true } foreach ($g in $def.memberOf) { if ($null -ne $g) { write-Host (" |-->{0}" -f $g) } } if ($protectedRole) { $def.name = "$protectedRole$($def.name)" } $rolesDef.$($Group.nameSuffix) = $def } $rolesDef.values | CreateOrSetGroup @shouldProcess @resetRoleParam | out-null # Pre-create the Deny permissions as it's relatively slow to do. $ObjectGUIDs = get-ADObjectGUIDs $DefaultDenyRules = $DefaultDenyObjectTypes | foreach-object -parallel { [PSCustomObject]@{ ADRight = "CreateChild" Action = "Deny" TargetObject = $PSItem InheritanceType = "None" } [PSCustomObject]@{ ADRight = "CreateChild" Action = "Deny" TargetObject = $PSItem InheritanceType = "All" } } | new-OUPermission -principal "NT Authority\Everyone" # Build ParameterList foreach ($delegation in $Template.OUDelegations) { $ADPath = $null $ADPath = if ($delegation.ADPath) { $delegation.ADPath } elseif ($delegation.ADPathQuery) { $query = $delegation.ADPathQuery Write-host "--> Children of $($query.searchbase)" (get-adobject @query -server $server).DistinguishedName } elseif ($delegation.ADPathLeafOU) { write-Verbose "Deriving ADPath: $($ADPath)" join-string -inputObject @($delegation.ADPathLeafOU, $ChildPath) -Separator "," } else { $ChildPath } Write-host "--> $ADPath" $outputText = ("--->{0,-40}" -f $ADPath) $ACEList = @($delegation.ACLs | Foreach-Object -parallel { $objectGUIDsInner = $using:ObjectGUIDs $rightsList = $using:rightsdef #Action that will run in Parallel. Reference the current object via $PSItem and bring in outside variables with $USING:varname if ($PSItem.PrincipalSuffix -and -not $PSItem.Principal) { #$Principal = "{0}-{1}-{2}" -f $using:RightsName, $using:GroupMidName, $PSItem.PrincipalSuffix $principal = $rightsList.$($PSItem.PrincipalSuffix).name write-Verbose "Deriving Principal: $($Principal)" } elseif ($PSItem.Principal) { $Principal = $PSItem.principal write-verbose "Principal: $Principal" } else { throw "Missing principal or principal suffix" } for ($i = 0; $i -lt $using:SleepTimeout/$using:sleepLength; $i++) { try { $principalSID = [System.Security.Principal.NTAccount]::new($principal).translate([System.security.Principal.SecurityIdentifier]) $identity = [System.Security.Principal.IdentityReference] $principalSID continue } catch { write-warning $_.exception.getType().fullname write-warning "Could not find principal $principal; sleeping for $($using:sleepLength)" start-sleep -Seconds $using:sleepLength } } if (-not $identity) { write-error "Failed to resolve principal before timeout: $principal" write-warning "Skipping this set of ACLs." continue } $PSItem.ACEs | foreach-object -parallel { # Details on permissions: # # ObjectType: Can be either an object, or a "right" retrieved by the get-ADObjectGUIDs. If this is an extended right, it refers to the "right" type # # InheritedObjectType: "Applies to" in the GUI. This is an object GUID. # # ADRight: Generally CreateChild, DeleteChild, GenericAll, or something involving "ExtendedRight". # # #try { If ($PSItem.appliesTo) { $AppliesTo = $PSItem.appliesTo $inheritedObjectType = $using:ObjectGUIdsInner | where-object { $_.name -eq $appliesTo -and $_.type -eq "Object" } } else { write-verbose "IOT null" $inheritedObjectType = @{ name = "---Null---" GUID = [GUID]"00000000-0000-0000-0000-000000000000" } } [System.security.AccessControl.AccessControlType] $action = if ($PSItem.Action) { $PSItem.action } else { "Allow" } [System.DirectoryServices.ActiveDirectorySecurityInheritance] $InheritanceType = if ($PSItem.inheritanceType) { $PsItem.InheritanceType } else { "All" } If ($PSItem.ExtendedRight -and -not $PSItem.ADRight) { $ADRight = [System.directoryservices.ActiveDirectoryRights]"ExtendedRight" } else { [System.directoryservices.ActiveDirectoryRights] $ADRight = $psitem.adright } if ($PSItem.extendedRight) { $extendedRightName = $PSItem.extendedRight write-verbose "ObjectType as extended right ($extendedRightName)" $ObjectType = [GUID]($using:ObjectGUIdsInner | where-object { $_.type -eq "Right" -and $_.name -eq $extendedRightName }).GUID } elseif (-not $PSItem.targetObject) { write-verbose "no target object, Setting null objectType" $objectType = @{ name = "---Null---" GUID = [GUID]"00000000-0000-0000-0000-000000000000" } } else { $targetObjectName = $PSItem.targetObject $ObjectType = ($using:ObjectGUIdsInner | where-object { $_.name -eq $targetObjectName}) write-Verbose "Not filtering object type ($targetObjectName --> $($objectType.guid))" } write-verbose ("{0,6}{1,-48} on: {2,-36} IOT: {3,-36} {4}" -f "ACE:", $using:Principal, $ObjectType.name, $inheritedObjectType.name, $ADRight) if ($ADRight -eq "CreateChild" -and $objectType -eq [GUID]"00000000-0000-0000-0000-000000000000" -and $psitem.targetObject -eq "organizational-unit") { write-warning "SQUAWK" write-warning "TargetObjectName: $($targetObjectName); $($PSItem.targetObject); $objectType" } try { New-object System.DirectoryServices.ActiveDirectoryAccessRule($using:Identity, $ADRight, $Action, $objectType.GUID, $InheritanceType, $inheritedObjectType.GUID) } catch { write-warning $_.exception.getType().fullname write-warning "error creating ACE." write-warning ("{0,6}{1,-48} on: {2,-36} IOT: {3,-36} {4}" -f "ACE:", $using:Principal, $ObjectType.name, $inheritedObjectType.name, $ADRight) } # } # catch { # $_ | format-list * -force # Write-warning "WHOOPS" # } } }) if ($delegation.ApplyDefaultDeny -ne $false) { # Check the ACE list for 'createChild' rights, and add the associated objects to a list. This lets us affirmatively deny object creation for items not in the list $CreateChildACEs = $aceList.where({ $_.ActiveDirectoryRights -Like "*CreateChild*" }) $AllowCreationObjects = $CreateChildACEs | foreach-object { $GUIDList = @($_.objectType.guid, $_.InheritedObjectType.guid).where({ $_ -ne "00000000-0000-0000-0000-000000000000" }) $objectGUIDs.where({ $_.GUID -in $GUIDList }) } if ($AllowCreationObjects.count -gt 0) { write-Verbose "CreateChild found, only allowing the following child items: $($AllowCreationObjects.name -join "; ")" } elseif ($createChildAces.count -gt 0) { Write-error "Whoops, we have createChild ACEs but our filter failed???" } if (-not $delegation.DefaultDenyInheritance) { $denyInheritance = "None" } else { $denyInheritance = $delegation.DefaultDenyInheritance } # DefaultDenyRules should have an 'inheritance all' and an 'inheritance none' ace for each object, so we're just filtering down. $DenyRules = $DefaultDenyRules | where-object { $_.ObjectType -notin $AllowCreationObjects.GUID -and $_.inheritanceType -eq $denyInheritance} $ACEList += $DenyRules write-verbose "Adding $($denyRules.count) Deny rules." } write-verbose "ACEList size: $($AceList.count)" write-host $(get-OUACLs -ACLList $ACEList -ShowDefaults | format-table | out-string) if ($PScmdlet.ShouldProcess("Applying Delegations now....")) { Add-OUPermissions -path $ADPath -aceList $AceList @shouldProcess } } } <#if ($ACEList) { Write-Host "`r`nApplying AD PSItems" } $textIndent = "|-->" $ACLList | group-object path | foreach-object { Write-Host ("--->{0,-40}" -f $_.name) $_.group.acl | foreach-object { Write-verbose ("{0,6}{1,-48} on: {2,-36} IOT: {3,-36} {4}" -f $textIndent, $_.Principal, ($_.ExtendedRight + $_.TargetObject), $_.AppliesTo, $_.ADRight) #Add-OUPermission @_ } write-host "" }#> } |