<# .SYNOPSIS Removes delegated permissions from OU objects and containers. .DESCRIPTION This function is used to report and remove assigned delegated permissions from OU objects and containers. .PARAMETER DOMAINDNSNAME DNS domain name to search. If omitted, the default is the local domain. .PARAMETER IdentitySearchString The Identity to search for as all or part of the NT style delegation identity, example Dom\SamAccountName. You can also user a partial match here such as "SamName" You can search for more than one string using comma as the delimiter, ex: 'GRP1,GRP2" to search for group 1 or group 2 .PARAMETER SearchBase The path where you want the search to begin, example, OU=Users,DC=DC1,DC=constoso,DC=com .PARAMETER Scope BaseLevelOnly is the specified OU from Searchbase, OneLevel adds the OUs directly below, SubTree is selected OU and all OUs below .PARAMETER LogFile The file path to the Logfile. If omitted, the audit log file based on domain name is put on user's desktop. .PARAMETER OpenLog If present, the CSV log will open when the script completes .EXAMPLE Remove-DelegatedOUPermissions -IdentitySearchString 'CTSO\MyOldGroup' -logfile "$env:username\desktop\MyOldGroup.csv" -OpenLog Be prompted to remove all assigned delegated permissions to CTSO\MyOldGroup in the current domain, open log MyOldGroup.csv when done .EXAMPLE Remove-DelegatedOUPermissions -DomainDNSName 'D1.contoso.com' -WhatIf -IdentitySearchString 'GRP1,GRP2' -OpenLog -whatif Test remove all assigned delegated permissions where ACE matches GRP1 or GRP2. Open default log name when done .EXAMPLE Remove-DelegatedOUPermissions -DomainDNSName 'D1.contoso.com' -IdentitySearchString 'CTSO\MyUser' -SearchBase 'OU=Users,DC=Contoso,DC=com' -Scope BaseLevelOnly -OpenLog -Confirm:$false Remove with confirmation all assigned delegated permissions where ACE matches 'CTSO\MyUser'. Limit Search to Users OU. Open default log when done. .EXAMPLE #Remember that you can use Splatting for parameters, example: $SplatParams =@{ DomainDNSName= 'D1.contoso.com' IdentitySearchString = 'CTSO\MyUser' SearchBase = 'OU=Users,DC=Contoso,DC=com' Scope = BaseLevelOnly Confirm = $False OpenLog } Remove-DelegatedOUPermissions @splatParams .NOTES Alan dot Kaplan at va dot gov ver. 1.0 2-13-17 ver. 1.1 2-24-17 Added -SearchBase and -Scope, changed delimiter for multiple items in search to comma, changed regex Escape routine. Added count of changes ver. 1.2 2-25-17 Added dot source reminder. #> #Requires -module ActiveDirectory Function Remove-DelegatedOUPermissions { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] Param ( #DomainDNSName's default is the user's domain [Parameter(Mandatory=$false)] [string]$DomainDNSName = ((get-addomain -current localcomputer).dnsroot), #IdentitySearchString is the NTDom\SamAccounName of the user or group. List of full or partial matches separated by commas [Parameter(Mandatory=$True)] [string]$IdentitySearchString, #SearchBase is the CN of the OU to start the search [Parameter(Mandatory=$False)] [string]$SearchBase, #SearchBase is the CN of the OU to start the search [Parameter(Mandatory=$False)] [ValidateSet("BaseLevelOnly", "OneLevel", "SubTree")] [string]$Scope='BaseLevelOnly', #The default is a domain name based CSV created on the desktop with domain name [Parameter(Mandatory=$false)] [string]$LogFile, #OpenLog [Parameter(Mandatory=$false)] [switch]$OpenLog ) Begin{ $report = $null $Error.Clear() $si = 1 } Process{ if (!($LogFile)){ $LogFile = "$env:USERPROFILE\desktop\"+$domainDNSName.Replace(".","-")+'_AD_Permission_Changes.csv' } if ($pscmdlet.ShouldProcess($DomainDNSName)){ $bTest = $False}Else{$bTest = $true} if (!($SearchBase)){ #Ashley McGlone's way to get all OUs and containers http://aka.ms/GoateePFE Write "Getting a list of all OUs, plus the root containers (users, computers, etc.)." $OUs = @(Get-ADDomain -Server $DomainDNSName | Select-Object -ExpandProperty DistinguishedName) $OUs += Get-ADOrganizationalUnit -Server $DomainDNSName -Filter * | Select-Object -ExpandProperty DistinguishedName $OUs += Get-ADObject -Server $DomainDNSName -SearchBase (Get-ADDomain -server $domainDNSName).DistinguishedName -SearchScope OneLevel -LDAPFilter '(objectClass=container)' | Select-Object -ExpandProperty DistinguishedName }ELSE{ Write "Getting a OU(s) for $SearchBase $scope" $OUs = @(Get-ADOrganizationalUnit -server $DomainDNSName -Identity $searchbase) if ($Scope -match 'OneLevel|SubTree'){ $OUs += Get-ADOrganizationalUnit -Server $DomainDNSName -Filter * -SearchBase $searchbase -SearchScope $Scope } } $iChangeCount = 0 $OUCount = $OUs.count if ($OUCount -eq 1){ $msg= 'Found OU, processing ' }ELSE{ $msg= 'Found '+($OUcount).ToString()+' AD containers and OUs, processing ' } Write $msg if ($bTest) {Write "Test Mode. No changes are being written"} $EscapedSearchString = ($IdentitySearchString.split(",",[System.StringSplitOptions]::RemoveEmptyEntries) | foreach{[regex]::Escape($_.Trim())}) -join "|" # Loop through each of the OUs and retrieve the direct (not inherited) permissions # which match the group name $report = ForEach ($OU in $OUs) { $OUDomPath = $DomainDNSName+'/'+$OU Write-Progress -Activity "Searching for `"$IdentitySearchString`" in $OUDomPath" ` -Status "Processing $si of $OUcount OUs in $DomainDNSName" -PercentComplete (($si / $OUcount) * 100) $DE = [ADSI]"LDAP://$OUDomPath" $acl = $DE.psbase.ObjectSecurity if (($acl.Access).identityreference -match $EscapedSearchString) { $ACES = $acl.Access | where {($_.identityReference -match $EscapedSearchString) -and ($_.IsInherited -eq $False)} Foreach ($ace in $aces){ $msg = "Found " + $($ace).identityReference.Value.ToString() + " on $OUDomPath" Write-Progress $msg if ($btest -eq $true){ $resultMsg = "Test mode - would have deleted delegation" }ELSE{ Try{ $ACL.RemoveAccessRuleSpecific($ACE) #Setting ACL using SDDL $DE.psbase.ObjectSecurity.SetSecurityDescriptorSddlForm($acl.sddl) $DE.psbase.commitchanges() $resultMsg = "Success" }Catch{ $iChangeCount ++ $resultMsg = "Failed. " + $Error[0].exception.message.ToString() $Resultmsg = ($resultMsg.Replace( "`"","" )).Trim() } } [PSCustomObject]@{ Domain = $DomainDNSName OU = $OU IdentityReference= [string]$ace.identityReference AccessControlType= [string]$ace.AccessControlType ActiveDirectoryRights= [string]$ace.ActiveDirectoryRights InheritanceType= [string]$ace.InheritanceType Results = $resultMsg } } } #end if match $si ++ } } End{ #Don't ask to confirm creation or opening of log file $report|Export-Csv $LogFile -NoTypeInformation -whatif:$false -Confirm:$false Write "Done, with $iChangeCount changes. Logfile is $Logfile" Write-Progress "Done" -Completed if ($OpenLog ) {ii $LogFile -WhatIf:$false -Confirm:$false} } } ### Dot Source Reminder Begins ### $ScriptFullPath = $MyInvocation.MyCommand.Definition.ToString() $command = $MyInvocation.MyCommand.Name.Replace(".ps1","") if (($host.Name).Contains("ISE")){ Write-host "`n`nYou have loaded the advanced function `"$command`"" -ForegroundColor Green Write-Host "Use `"Get-Help`" for information about this function, Ex:`nPS C:\> Get-Help $command -detailed`n`n" }ELSE{ if ($MyInvocation.InvocationName -ne '.') { Write-host "`n`nThis is an advanced function which must be dot sourced in order to work correctly`n" -ForegroundColor Green write-host "Load example:`nPS C:\> . $ScriptFullPath`n" Write-Host "Use `"Get-Help`" for information about this function, Ex:`nPS C:\> Get-Help $command -detailed`n`n" #Pause if launched from shell with "Run with PowerShell" if (([Environment]::GetCommandLineArgs()) -match '&'){pause} } } ### Dot Source Reminder Ends