Forum Discussion
How to sperate each group/user in AccessToString when using Get-ACL folder.
There's a number of issues with this script but I have to ask: are you sure you want to remove the groups listed in $TestMandatoryGroups?
This is what you appear to be trying to do in your ForEach loop starting on line 24.
I ask because the variable name, $TestMandatorySecurityGroups, somewhat implies that those are the groups you want to keep, not remove.
Cheers,
Lain
Yes the groups in $TestMandatoryGroups are ones I do want to keep.
I know their are issues, I'm slowly working my way thought them.
Thank you
- LainRobertsonJun 03, 2022Silver Contributor
Yep, no problem. I just wanted to be sure we were on the same page so you didn't end up removing the ACEs you actually meant to keep.
So, here's a brief rundown of the issues in the original script:
- Exporting the full object on line 7 results in a messy CSV that is hard to work with - particularly if you needed to use it for restoring permissions;
- The ForEach loop was written to remove the mandatory groups instead of retain them;
- One line 11, $Folder_ACL is being referenced yet it hasn't actually been defined beforehand, meaning it's a $null reference;
- AccessToString is not your friend. Even if you do split it, you'll run into additional parsing issues if the IdentityReference value contains spaces;
- In the second ForEach loop (lines 24 to 35), you don't need to call Get-Acl again as everything can be done in the one pass;
- The third ForEach loop (lines 37 to 48) isn't necessary.
Here's a basic example script you can alter to suit your needs.
I've changed the folders names to suit my test environment, so you will likely need to change those. You could also get away from having them hard-coded in the script and provide them via a parameter but I didn't bother going this far.
Reset-Acl.ps1
[cmdletbinding()] Param( [parameter()][switch]$Commit, [parameter()][string]$LogFile = ".\GDrive_ACL_stats.csv" ) $Folder_name = @( "D:\Data\Temp\Bogus1", "D:\Data\Temp\Bogus2" ); $TestMandatorySecurityGroups = @( "NT AUTHORITY\SYSTEM", "BUILTIN\Administrators", "BUILTIN\Users" ); # Remove the CSV file to avoid conflicts from repeated runs. Remove-Item -Path $LogFile -Force -ErrorAction:SilentlyContinue; foreach($Name in $Folder_name) { # Fetch the ACL. $Acl = Get-Acl -Path $Name; $FilePath = ($Acl.Path -split "::")[1].ToLowerInvariant(); # Enumerate the owner before moving onto the Access Control Entries (ACEs.) [PSCustomObject] @{ Path = $FilePath; AccessControlType = "Allow"; # Strictly speaking, there is no Allow or Deny for the Owner, but it's useful in filtering scenarios, so in it goes. IdentityReference = $Acl.Owner; FileSystemRights = "Owner"; # From here down, nothing else applies to the Owner, so set them as $null and head out to the ACEs. IsInherited = $null; InheritanceFlags = $null; PropagationFlags = $null; } | Export-csv -NoTypeInformation -Path $LogFile -Append; # Enumerate the ACEs, being sure to exclude those that are inherited. After all, you can't remove an inherited ACE. foreach ($Ace in $Acl.Access) { if (-not $Ace.IsInherited) { # Log the entry first. [PSCustomObject] @{ Path = $FilePath; AccessControlType = $Ace.AccessControlType; # Strictly speaking, there is no Allow or Deny for the Owner, but it's useful in filtering scenarios, so in it goes. IdentityReference = $Ace.IdentityReference; FileSystemRights = $Ace.FileSystemRights; # From here down, nothing else applies to the Owner, so set them as $null and head out to the ACEs. IsInherited = $Ace.IsInherited; InheritanceFlags = $Ace.InheritanceFlags; PropagationFlags = $Ace.PropagationFlags; } | Export-csv -NoTypeInformation -Path $LogFile -Append; # Then see if we need to remove it. if ($TestMandatorySecurityGroups -notcontains $Ace.IdentityReference) { if ($Commit.IsPresent) { Write-Warning -Message "Removing ""$($Ace.IdentityReference)"" from $Name"; } else { Write-Warning -Message """$($Ace.IdentityReference)"" would be removed from $Name"; } $null = $Acl.RemoveAccessRule($Ace); } } } # Commit the ACL changes. Not bothering to wrap in a try..catch block since it's the final statement and there's value in letting any exceptions (such as "access denied") surface. if ($Commit.IsPresent) { Set-Acl -Path $FilePath -AclObject $Acl; } }Here's a sample of the output from calling Reset-Acl.ps1 three times:
- First, to show what would happen but without committing any changes;
- Second, to commit the changes;
- Third, purely as confirmation that the changes have been committed.
Lastly, this is what you get in the CSV file.
Cheers,
Lain
- Hjb118Jun 04, 2022Copper Contributor
Good Morning Lain,
Thank you for your response this helps a lot.
What would be the best way to turn this in to a function? As it will ran as option admin script.
1. we don't really need commit it as this function makes sure the Mandatory security groups applies.
2. Then the function will apply 1 Security group depending on the folder name.
The folders that we are trying to change are created in another function and inheritances in then disabled on creation.- LainRobertsonJun 04, 2022Silver Contributor
Simply wrap the code in a function declaration, like so:
function Reset-Acl { # Blah # Blah }For example:
function Reset-Acl { [cmdletbinding()] Param( [parameter()][switch]$Commit, [parameter()][string]$LogFile = ".\GDrive_ACL_stats.csv" ) $Folder_name = @( "D:\Data\Temp\Bogus1", "D:\Data\Temp\Bogus2" ); $TestMandatorySecurityGroups = @( "NT AUTHORITY\SYSTEM", "BUILTIN\Administrators", "BUILTIN\Users" ); # Remove the CSV file to avoid conflicts from repeated runs. Remove-Item -Path $LogFile -Force -ErrorAction:SilentlyContinue; foreach($Name in $Folder_name) { # Fetch the ACL. $Acl = Get-Acl -Path $Name; $FilePath = ($Acl.Path -split "::")[1].ToLowerInvariant(); # Enumerate the owner before moving onto the Access Control Entries (ACEs.) [PSCustomObject] @{ Path = $FilePath; AccessControlType = "Allow"; # Strictly speaking, there is no Allow or Deny for the Owner, but it's useful in filtering scenarios, so in it goes. IdentityReference = $Acl.Owner; FileSystemRights = "Owner"; # From here down, nothing else applies to the Owner, so set them as $null and head out to the ACEs. IsInherited = $null; InheritanceFlags = $null; PropagationFlags = $null; } | Export-csv -NoTypeInformation -Path $LogFile -Append; # Enumerate the ACEs, being sure to exclude those that are inherited. After all, you can't remove an inherited ACE. foreach ($Ace in $Acl.Access) { if (-not $Ace.IsInherited) { # Log the entry first. [PSCustomObject] @{ Path = $FilePath; AccessControlType = $Ace.AccessControlType; # Strictly speaking, there is no Allow or Deny for the Owner, but it's useful in filtering scenarios, so in it goes. IdentityReference = $Ace.IdentityReference; FileSystemRights = $Ace.FileSystemRights; # From here down, nothing else applies to the Owner, so set them as $null and head out to the ACEs. IsInherited = $Ace.IsInherited; InheritanceFlags = $Ace.InheritanceFlags; PropagationFlags = $Ace.PropagationFlags; } | Export-csv -NoTypeInformation -Path $LogFile -Append; # Then see if we need to remove it. if ($TestMandatorySecurityGroups -notcontains $Ace.IdentityReference) { if ($Commit.IsPresent) { Write-Warning -Message "Removing ""$($Ace.IdentityReference)"" from $Name"; } else { Write-Warning -Message """$($Ace.IdentityReference)"" would be removed from $Name"; } $null = $Acl.RemoveAccessRule($Ace); } } } # Commit the ACL changes. Not bothering to wrap in a try..catch block since it's the final statement and there's value in letting any exceptions (such as "access denied") surface. if ($Commit.IsPresent) { Set-Acl -Path $FilePath -AclObject $Acl; } } }Cheers,
Lain