How can I remove a single user ACL from a large se

2019-06-25 12:35发布

问题:

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.

回答1:

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
}