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
        }
    }