I have a very large list of folders and I need to remove a single ACL from each of them. Rather than doing it manually, I'm trying to write a script to do it in a fraction of the time, but I'm running into a bit of trouble.
Here is what I have so far:
$filepath = "C:\ALCTEST"
$user = "domain\username"
$folders = @((get-item $filePath))
$folders += Get-ChildItem $filePath -Recurse |
where { $_.PSIsContainer -ne $false }
##Need to do this in order to remove item 0 from the array, otherwise
##item 0 is the parent folder
$newfolders = $folders[1,2 + 3..($folders.length - 1)]
foreach ($folder in $newfolders) {
$acl = Get-Acl -Path $folder.FullName
foreach ($access in $acl.access) {
foreach ($value in $access.IdentityReference.Value) {
if ($value -eq $user) {
$acl.RemoveAccessRule($access) | Out-Null
}
}
}
Set-Acl -Path $folder -AclObject $acl
}
From what I watched during the debug, I think everything works right up until I try to set the ACL back onto the folders. When it gets to that line, I get the error
Cannot find path 'C:\Windows\system32\01' because it does not exist.
Inside the parent folder of ACLTEST are seven folders named "01"..."07" with various ACLs for testing.
I'm not sure where to go from here. I was reading this Scripting Guy article which helped me through a lot, but his script seems to be focused on manually entering folder paths, which I definitely can't do for the several hundred folders that need to be changed.
Any help is appreciated. I'm new to PowerShell scripting, so if you see any mistakes other than what I'm referring to in the script above, I'd love to hear them.
The -Path
parameter of Set-Acl
expects a path string, not a DirectoryInfo
object. When passing the latter only its Name
property is expanded, so Set-Acl
is looking for an object with the given name in the current working directory (in your case apparently C:\Windows\system32
).
Change
Set-Acl -Path $folder -AclObject $acl
into
Set-Acl -Path $folder.FullName -AclObject $acl
and the problem will disappear.
With that said, you may want to make some other modifications as well.
$folders = @((get-item $filePath))
$folders += Get-ChildItem $filePath -Recurse |
where { $_.PSIsContainer -ne $false }
##Need to do this in order to remove item 0 from the array, otherwise
##item 0 is the parent folder
$newfolders = $folders[1,2 + 3..($folders.length - 1)]
If you don't want the parent folder in the array: don't put it in the array in the first place. And since you apparently have PowerShell v3, you could use the -Directory
parameter instead of a where
filter.
$newfolders = Get-ChildItem $filePath -Recurse -Directory
foreach ($access in $acl.access) {
foreach ($value in $access.IdentityReference.Value) {
if ($value -eq $user) {
$acl.RemoveAccessRule($access) | Out-Null
}
}
}
Each access rule has only a single identity reference, so the inner loop is pointless.
foreach ($access in $acl.access) {
if ($access.IdentityReference.Value -eq $user) {
$acl.RemoveAccessRule($access) | Out-Null
}
}
Your code could be streamlined to something like this:
$filepath = 'C:\ALCTEST'
$user = 'domain\username'
$folders = Get-ChildItem $filePath -Recurse -Directory
foreach ($folder in $folders) {
$acl = Get-Acl -Path $folder.FullName
foreach ($access in $acl.Access) {
if ($access.IdentityReference.Value -eq $user) {
$acl.RemoveAccessRule($access) | Out-Null
}
}
Set-Acl -Path $folder.FullName -AclObject $acl
}
or (using pipelines) like this:
$filepath = 'C:\ALCTEST'
$user = 'domain\username'
Get-ChildItem $filePath -Recurse -Directory | ForEach-Object {
$acl = Get-Acl -Path $_.FullName
$acl.Access | Where-Object {
$_.IdentityReference.Value -eq $user
} | ForEach-Object {
$acl.RemoveAccessRule($_) | Out-Null
}
Set-Acl -Path $_.FullName -AclObject $acl
}