The bad news: I don’t think you can do exactly what you’re asking with pure PowerShell. When you check that box, I’m pretty sure the GUI is calling either the TreeResetNamedSecurityInfo or TreeSetNamedSecurityInfo Win32 API functions, and I’m not aware of any cmdlets or .NET classes that will call that. To do it in PowerShell (without using P/Invoke to implement that function), you’ll have to walk the tree and do work on each security descriptor.
More bad news: I was trying to mock up some PS code to do a simple walking of the folder structure and ensuring that SACL inheritance was enabled, and it seems that the underlying .NET classes have some sort of issue with this when the SACL is null, and it doesn’t seem to honor turning the ACL protection off. I’ll include an example of what the steps to fix it with pure PowerShell should look like, along with an example of how to recreate the problem (please work through it just to make sure I’m not imagining things).
The good news: you can still do this with PowerShell if you’re willing to use a third party module (you can probably still do it with pure PowerShell/.NET if you track down why an empty and/or null SACL makes it so .NET won’t let you enable SACL inheritance). I’ve got a module you can download from here. It’s on GitHub here (there are two branches–one for 3.0 and one for 4.0). The 3.0 version is a script module, so you can open it up and see the source anytime, and the 4.0 is a compile module, so you’ll need to look at the GitHub page to see its source.
If you get v4.0, this should work:
$PacSdOption = New-PacSDOption -SecurityDescriptorSections Audit -Recurse -BypassAclCheck
$Root = 'c:\path\to\root'
# See the problem areas (this is optional):
Get-PacSecurityDescriptor $Root -PacSDOption $PacSdOption |
where AreAuditRulesProtected -eq $true |
Tee-Object -Variable BadSacls |
select Path, AreAuditRulesProtected
# Fix them:
Get-PacSecurityDescriptor $Root -PacSDOption $PacSdOption |
select -Skip 1 | # You may want the $Root to have SACL inheritance disabled
where AreAuditRulesProtected -eq $true |
Enable-PacAclInheritance -SystemAcl -PacSDOption $PacSdOption -Apply #-Force
# NOTE: As the command is, you'll be prompted for each SACL before the change
# is made. Uncommenting the -Force should fix that
This is the pure PowerShell way that should work, but doesn’t seem to work if the SACL is empty:
# Set up a tmp folder structure:
$TempRoot = mkdir "$env:temp\sacl_test"
$Child = mkdir "${TempRoot}\child"
$ChildSd = Get-Acl $Child -Audit
$ChildSd.SetAuditRuleProtection($true, $false)
$ChildSd | Set-Acl
# This should fix it, but it doesn't:
Get-ChildItem $TempRoot -Recurse | ForEach-Object {
$CurrentItem = $_
$Result = try {
$SD = Get-Acl $CurrentItem.FullName -Audit -ErrorAction Stop
if ($SD.AreAuditRulesProtected) {
$SD.SetAuditRuleProtection($false, $false)
$SD | Set-Acl
#$CurrentItem.SetAccessControl($SD)
'SACL inheritance was disabled; enabled now'
}
else {
'SACL inheritance already enabled'
}
}
catch {
"Error: ${_}"
}
[PSCustomObject] @{
Path = $CurrentItem.FullName
Result = $Result
}
}
I think it’s a .NET instead of PowerShell issue, though, because the SetAccessControl() method that Get-Item would return won’t fix it, either. If I get a little more time, I may try to see what’s going on under the covers. I’m guessing it has to do with the SACL being null in the underlying CommonSecurityDescriptor instance, but it still ignores it if you create one using SetSecurityDescriptorSddlForm().