Fix-BrokenInheritance.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

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