Fix-BrokenInheritance.ps1
<#PSScriptInfo .VERSION 2.0 .GUID 5f08a00a-f000-4bec-8b24-a72281f57dde .AUTHOR Aaron Guilmette .COMPANYNAME Microsoft .COPYRIGHT 2021 .TAGS active directory, object inheritance .LICENSEURI .PROJECTURI https://www.undocumented-features.com/2021/08/27/find-and-fix-broken-ad-object-inheritance/ .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .DESCRIPTION This script will search Active Directory for objects with permissions inheritance disabled. .PRIVATEDATA #> <# .SYNOPSIS Find objects without permissions inheritance enabled and optionally update. .EXAMPLE .\Fix-BrokenInheritance.ps1 -LogFile output.txt Find objects with disabled inheritance and output to logfile output.txt. .EXAMPLE .\Fix-BrokenInheritance.ps1 -Logfile output.txt -Confirm Find objects with disabled inheritance, update them, and log changes to output.txt. .PARAMETER Logfile Specify logfile for operations. .PARAMETER SearchBase Set the BaseDN for the search query. Defaults to the DN of the current domain. .PARAMETER Confirm Confirm changes to Active Directory objects. .LINK .NOTES All envrionments perform differently. Please test this code before using it in production. THIS CODE AND ANY ASSOCIATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK OF USE, INABILITY TO USE, OR RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. Author: Aaron Guilmette aaron.guilmette@microsoft.com #> Param( [Parameter(Mandatory=$false,HelpMessage="Active Directory Base DN")] [string]$SearchBase = (Get-ADDomain).DistinguishedName, [Parameter(Mandatory=$false,HelpMessage="Log File")] [string]$LogFile, [Parameter(Mandatory=$false,HelpMessage="Confirm")] [switch]$Confirm ) If (!(Get-Module ActiveDirectory)) { Import-Module ActiveDirectory } $DomainAccountSID = (Get-ADDomain).DomainSID.ToString() $DomainSID = $DomainAccountSID.Split("-")[4] + "-" + $DomainAccountSID.Split("-")[5] + "-" + $DomainAccountSID.Split("-")[6] $ProtectedUsersData = @" "@ <# New-Object System.Management.Automation.PSObject $ProtectedUsers | Add-Member -TypeName NoteProperty -Name User $ProtectedUsers | Add-Member -TypeName NoteProperty -Name SID $ProtectedUsers | Add-Member -Value "Administrator" -SecondValue "S-1-5-21-" + $DomainSID + "-500" $ProtectedUsers | Add-Member -Value "Guest" -SecondValue "S-1-5-21-" + $DomainSID + "-501" $ProtectedUsers = @{ "administrator" = "S-1-5-21-" + $DomainSID + "-500" "guest" = "S-1-5-21-" + $DomainSID + "-501" "krbtgt" = "S-1-5-21-" + $DomainSID + "-502" } #> $ProtectedGroups = @{ "Account Operators" = "S-1-5-32-548" "Administrators" = "S-1-5-32-544" "Backup Operators" = "S-1-5-32-551" "Cert Publishers" = "S-1-5-21-" + $DomainSID + "-517" "Domain Admins" = "S-1-5-21-" + $DomainSID + "-512" "Domain Controllers" = "S-1-5-21-" + $DomainSID + "-516" "Enterprise Admins" = "S-1-5-21-" + $DomainSID + "-519" "Enterprise Read-Only Domain Controllers" = "S-1-5-21-" + $DomainSID + "-498" "Print Operators" = "S-1-5-32-550" "Read-Only Domain Controllers" = "S-1-5-21-" + $DomainSID + "-521" "Replicator" = "S-1-5-32-552" "Schema Admins" = "S-1-5-21-" + $DomainSID + "-518" "Server Operators" = "S-1-5-32-549" } function CheckProtectedAccount($user) { } function CheckIfInProtectedGroup($user) { } # Start Logfile If ($LogFile) { $head = """" + "DistinguishedName" + """" + "," + """" + "UPN" + """" + "," + """" + "InheritanceDisabled-Before" + """" + "," + """" + "InheritanceDisabled-After" + """" + "," + """" + "adminSDHolderProtected" + """" $head | Out-File $LogFile } # Instantiate Directory Searcher $DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$SearchBase","(&(objectcategory=user)(objectclass=user))") # Find All Users $Users = $DirectorySearcher.FindAll() Foreach ($obj in $users) { # Set 'objBefore' to the current object so we can track any changes $objBefore = $obj.GetDirectoryEntry() # Check to see if user has Inheritance Disabled; $True is inheritance disabled, $False is inheritance enabled If ($objBefore.psBase.ObjectSecurity.AreAccessRulesProtected -eq $True) { Write-Host "User: $($objBefore.sAMAccountName) Inheritance is disabled: $($objBefore.psBase.ObjectSecurity.AreAccessRulesProtected) ; adminSDHolder: $($objBefore.Properties.AdminCount)" $objBeforeACL = $($objBefore.psBase.ObjectSecurity.AreAccessRulesProtected) #$user.psBase.ObjectSecurity | GM "*get*access*" # If Confirm switch was enabled to make changes If ($Confirm) { Write-Host -ForegroundColor Green "Updating $($objBefore.sAMAccountName)." $objBefore.psbase.ObjectSecurity.SetAccessRuleProtection($false,$true) $objBefore.psbase.CommitChanges() } # Set 'objAfter' so we can see the updated change $objAfter = $obj.GetDirectoryEntry() $objAfterACL = $($objAfter.psBase.ObjectSecurity.AreAccessRulesProtected) # If logging is enabled, write a log file If ($LogFile) { $LogData = """" + $objBefore.DistinguishedName + """" + "," + """" + $objBefore.UserPrincipalName + """" + "," + """" + $objBeforeACL + """" + "," + """" + $objAfterACL + """" + "," + """" + $objBefore.Properties.AdminCount + """" $LogData | Out-File $LogFile -Append } } Else { # User has inheritance enabled, so do nothing } } |