functions/acl/Remove-AdsOrphanAce.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
function Remove-AdsOrphanAce { <# .SYNOPSIS Removes all access rules that have an unresolveable identity. .DESCRIPTION Removes all access rules that have an unresolveable identity. This is aimed at identifying and remediating orphaned SIDs in active directory. .PARAMETER Path The full distinguished name to the object to clean. .PARAMETER ExcludeDomainSID SIDs from the specified domain SIDs will be ignored. Use this to safely handle one-way trust where ID resolution is impossible for some IDs. .PARAMETER IncludeDomainSID If specified, only unresolved identities from the specified SIDs will be listed. Use this to safely target only rules from your owned domains in the targeted domain. .PARAMETER Server The server / domain to connect to. .PARAMETER Credential The credentials to use for AD operations. .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions. This is less user friendly, but allows catching exceptions in calling scripts. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .EXAMPLE PS C:\> Get-ADObject -LDAPFillter '(objectCategory=*)' | Remove-AdsOrphanAce Purges all objects in the current domain from orphaned access rules. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [Parameter(ValueFromPipeline = $true, Mandatory = $true)] [string[]] $Path, [string[]] $ExcludeDomainSID, [string[]] $IncludeDomainSID, [string] $Server, [System.Management.Automation.PSCredential] $Credential, [switch] $EnableException ) begin { $adParameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential Assert-ADConnection @adParameters -Cmdlet $PSCmdlet function Write-Result { [CmdletBinding()] param ( [string] $Path, [System.DirectoryServices.ActiveDirectoryAccessRule] $AccessRule, [ValidateSet('Deleted', 'Failed')] [string] $Action, [System.Management.Automation.ErrorRecord] $ErrorRecord ) [PSCustomObject]@{ PSTypeName = 'ADSec.AccessRule' Path = $Path Identity = $AccessRule.IdentityReference Action = $Action ADRights = $AccessRule.ActiveDirectoryRights Type = $AccessRule.AccessControlType ObjectType = $AccessRule.ObjectType InheritedOpectType = $AccessRule.InheritedObjectType Rule = $AccessRule Error = $ErrorRecord } } # Wrap as nested pipeline to avoid asserting connection each time $scriptCmd = { Get-AdsAcl @adParameters -EnableException:$EnableException } $getAdsAcl = $scriptCmd.GetSteppablePipeline() $getAdsAcl.Begin($true) } process { foreach ($pathItem in $Path) { Write-PSFMessage -Level Verbose -String 'Remove-AdsOrphanAce.Searching' -StringValues $pathItem try { $acl = $getAdsAcl.Process($pathItem) | Write-Output } catch { Stop-PSFFunction -String 'Remove-AdsOrphanAce.Read.Failed' -StringValues $pathItem -EnableException $EnableException -ErrorRecord $_ -Cmdlet $PSCmdlet -Continue } if (-not $acl) { Stop-PSFFunction -String 'Remove-AdsOrphanAce.Read.Failed' -StringValues $pathItem -EnableException $EnableException -Cmdlet $PSCmdlet -Continue } $rulesToPurge = foreach ($rule in $acl.Access) { if ($rule.IsInherited) { continue } if ($rule.IdentityReference -is [System.Security.Principal.NTAccount]) { continue } if ($rule.IdentityReference.AccountDomainSID.Value -in $ExcludeDomainSID) { continue } if ($IncludeDomainSID -and ($rule.IdentityReference.AccountDomainSID.Value -notin $IncludeDomainSID)) { continue } try { $null = $rule.IdentityReference.Translate([System.Security.Principal.NTAccount]) } catch { $null = $acl.RemoveAccessRule($rule) $rule } } if (-not $rulesToPurge) { Write-PSFMessage -Level Verbose -String 'Remove-AdsOrphanAce.NoOrphans' -StringValues $pathItem continue } Invoke-PSFProtectedCommand -ActionString 'Remove-AdsOrphanAce.Removing' -ActionStringValues ($rulesToPurge | Measure-Object).Count -Target $pathItem -ScriptBlock { try { Set-ADObject @adParameters -Identity $pathItem -Replace @{ ntSecurityDescriptor = $acl } -ErrorAction Stop foreach ($rule in $rulesToPurge) { Write-Result -Path $pathItem -AccessRule $rule -Action Deleted } } catch { foreach ($rule in $rulesToPurge) { Write-Result -Path $pathItem -AccessRule $rule -Action Failed -ErrorRecord $_ } throw } } -EnableException $EnableException.ToBool() -PSCmdlet $PSCmdlet -Continue } } end { $getAdsAcl.End() } } |