Get ACL Folder & Subfolder + Users Using Powershell

时间:2022-12-31 00:27:09

Is it possible to get the permissions of a folder and its sub-folders then display the path, group, and users associated to that group? So, to look something like this. Or will it have to be one folder at a time.

是否可以获取文件夹及其子文件夹的权限,然后显示与该组关联的路径,组和用户?所以,看起来像这样。或者它必须一次只能是一个文件夹。

-Folder1

-Folder1

-Line separator
-Group
-Line separator
-List of users

-Folder2

-Folder2

 -Line separator
 -Group
 -Line separator
 -List of users

The script I've come up with so far be warned I have very little experience with powershell. (Don't worry my boss knows.)

到目前为止我提出的脚本会被警告我对powershell的经验很少。 (别担心我的老板知道。)

Param([string]$filePath)

$Version=$PSVersionTable.PSVersion
if ($Version.Major -lt 3) {Throw "Powershell version out of date. Please update powershell." }

Get-ChildItem $filePath -Recurse | Get-Acl | where { $_.Access | where { $_.IsInherited -eq $false } } | select -exp Access | select IdentityReference -Unique | Out-File .\Groups.txt

$Rspaces=(Get-Content .\Groups.txt) -replace 'JAC.*?\\|','' |
 Where-Object {$_ -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | ForEach-Object {$_.TrimEnd()}
$Rspaces | Out-File .\Groups.txt

$ErrorActionPreference= 'SilentlyContinue'
$Groups=Get-Content .\Groups.txt
ForEach ($Group in $Groups)
{Write-Host"";$Group;Write-Host --------------
;Get-ADGroupMember -Identity $Group -Recursive | Get-ADUser -Property DisplayName | Select Name}

This only shows the groups and users, but not the path.

这仅显示组和用户,但不显示路径。

1 个解决方案

#1


1  

Ok, let's take it from the top! Excellent, you actually declare a parameter. What you might want to consider is setting a default value for the parameter. What I would do is use the current directory, which conveniently has an automatic variable $PWD (I believe that's short for PowerShell Working Directory).

好的,我们从顶部开始吧!很好,你实际上声明了一个参数。您可能需要考虑的是为参数设置默认值。我要做的是使用当前目录,它有一个自动变量$ PWD(我相信这是PowerShell工作目录的简称)。

Param([string]$filePath = $PWD)

Now if a path is provided it will use that, but if no path is provided it just uses the current folder as a default value.

现在,如果提供了路径,它将使用它,但如果没有提供路径,它只使用当前文件夹作为默认值。

Version check is fine. I'm pretty sure there's more elegant ways to do it, but I honestly don't have never done any version checking.

版本检查没问题。我很确定有更优雅的方法可以做到这一点,但老实说,我从来没有做过任何版本检查。

Now you are querying AD for each group and user that is found (after some filtering, granted). I would propose that we keep track of groups and members so that we only have to query AD once for each one. It may not save a lot of time, but it'll save some if any group is used more than once. So for that purpose we're going to make an empty hashtable to track groups and their members.

现在,您要查找找到的每个组和用户的AD(经过一些过滤,授予后)。我建议我们跟踪组和成员,以便我们只需要为每个查询AD一次。它可能不会节省很多时间,但如果任何组被多次使用,它将节省一些时间。因此,为此目的,我们将制作一个空的哈希表来跟踪组及其成员。

$ADGroups = @{}

Now starts a bad trend... writing to files and then reading those files back in. Outputting to a file is fine, or saving configurations, or something that you'll need again outside of the current PowerShell session, but writing to a file just to read it back into the current session is just a waste. Instead you should either save the results to a variable, or work with them directly. So, rather than getting the folder listing, piping it directly into Get-Acl, and losing the paths we're going to do a ForEach loop on the folders. Mind you, I added the -Directory switch so it will only look at folders and ignore files. This happens at the provider level, so you will get much faster results from Get-ChildItem this way.

现在开始一个不好的趋势...写入文件,然后重新读取这些文件。输出到文件很好,或者保存配置,或者在当前PowerShell会话之外再次需要的东西,但写入文件只是把它读回当前的会话只是浪费。相反,您应该将结果保存到变量,或直接使用它们。因此,不是获取文件夹列表,而是将其直接管道输入Get-Acl,并且丢失路径,我们将在文件夹上执行ForEach循环。请注意,我添加了-Directory开关,因此它只会查看文件夹并忽略文件。这种情况发生在提供程序级别,因此您可以通过这种方式从Get-ChildItem获得更快的结果。

ForEach($Folder in (Get-ChildItem $filePath -Recurse -Directory)){

Now, you wanted to output the path of the folder, and a line. That's easy enough now that we aren't ditching the folder object:

现在,您想要输出文件夹的路径和一行。现在我们没有放弃文件夹对象,这很容易:

    $Folder.FullName
    '-'*$Folder.FullName.Length

Next we get the ACLs for the current folder:

接下来,我们获取当前文件夹的ACL:

    $ACLs = Get-Acl -Path $Folder.FullName

And here's where things get complicated. I'm getting the group names from the ACLs, but I've combined a couple of your Where statements, and also added a check to see if it is an Allow rule (because including Deny rules in this would just be confusing). I've used ? which is an alias for Where, as well as % which is an alias for ForEach-Object. You can have a natural line brake after a pipe, so I've done that for ease of reading. I included comments on each line for what I'm doing, but if any of it is confusing just let me know what you need clarification on.

事情变得复杂了。我从ACL中获取组名,但是我已经结合了几个Where语句,并且还添加了一个检查以查看它是否是允许规则(因为在此包含拒绝规则只会令人困惑)。我用过吗?这是Where的别名,以及%是ForEach-Object的别名。你可以在管道后进行自然线刹,所以我这样做是为了便于阅读。我在每一行都包含了对我正在做的事情的评论,但如果其中任何一个令人困惑,请告诉我你需要澄清的内容。

    $Groups = $ACLs.Access | #Expand the Access property
        ?{ $_.IsInherited -eq $false -and $_.AccessControlType -eq 'Allow' -and $_.IdentityReference -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | #Only instances that allow access, are not inherited, and aren't a local group or special case
        %{$_.IdentityReference -replace 'JAC.*?\\'} | #Expand the IdentityReference property, and replace anything that starts with JAC all the way to the first backslash (likely domain name trimming)
        Select -Unique #Select only unique values

Now we'll loop through the groups, starting off by outputting the group name and a line.

现在我们将循环浏览组,首先输出组名和一行。

    ForEach ($Group in $Groups){
        $Group
        '-'*$Group.Length

For each group I'll see if we already know who's in it by checking the list of keys on the hashtable. If we don't find the group there we'll query AD and add the group as a key, and the members as the associated value.

对于每个组,我将通过检查哈希表上的键列表来查看我们是否已经知道谁在其中。如果我们在那里找不到组,我们将查询AD并将组添加为键,并将成员添加为关联值。

        If($ADGroups.Keys -notcontains $Group){
            $Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name
            $ADGroups.Add($Group,$Members)
        }

Now that we're sure that we have the group members we will display them.

现在我们确定我们有小组成员,我们将展示它们。

        $ADGroups[$Group]

We can close the ForEach loop pertaining to groups, and since this is the end of the loop for the current folder we'll add a blank line to the output, and close that loop as well

我们可以关闭与组相关的ForEach循环,因为这是当前文件夹的循环结束,我们将在输出中添加一个空行,并关闭该循环

    }
    "`n"
}

So I wrote this up and then ran it against my C:\temp folder. It did tell me that I need to clean up that folder, but more importantly it showed me that most of the folders don't have any non-inherited permissions, so it would just give me the path with an underline, a blank line, and move to the next folder so I had a ton of things like:

所以我写了这个,然后在我的C:\ temp文件夹上运行它。它确实告诉我,我需要清理该文件夹,但更重要的是,它告诉我大多数文件夹没有任何非继承权限,所以它只会给我带下划线的路径,空行,并移动到下一个文件夹,所以我有很多东西,如:

C:\Temp\FolderA
---------------

C:\Temp\FolderB
---------------

C:\Temp\FolderC
---------------

That doesn't seem useful to me. If it is to you then use the lines above as I have them. Personally I chose to get the ACLs, check for groups, and then if there are no groups move to the next folder. The below is the product of that.

这似乎对我没用。如果是你,那么就像我拥有它们一样使用上面的行。我个人选择获取ACL,检查组,然后如果没有组移动到下一个文件夹。以下是其中的产物。

Param([string]$filePath = $PWD)

$Version=$PSVersionTable.PSVersion
if ($Version.Major -lt 3) {Throw "Powershell version out of date. Please update powershell." }

#Create an empty hashtable to track groups
$ADGroups = @{}

#Get a recursive list of folders and loop through them
ForEach($Folder in (Get-ChildItem $filePath -Recurse -Directory)){
    # Get ACLs for the folder
    $ACLs = Get-Acl -Path $Folder.FullName

    #Do a bunch of filtering to just get AD groups
    $Groups = $ACLs | 
        % Access | #Expand the Access property
        where { $_.IsInherited -eq $false -and $_.AccessControlType -eq 'Allow' -and $_.IdentityReference -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | #Only instances that allow access, are not inherited, and aren't a local group or special case
        %{$_.IdentityReference -replace 'JAC.*?\\'} | #Expand the IdentityReference property, and replace anything that starts with JAC all the way to the first backslash (likely domain name trimming)
        Select -Unique #Select only unique values

    #If there are no groups to display for this folder move to the next folder
    If($Groups.Count -eq 0){Continue}

    #Display Folder Path
    $Folder.FullName
    #Put a dashed line under the folder path (using the length of the folder path for the length of the line, just to look nice)
    '-'*$Folder.FullName.Length

    #Loop through each group and display its name and users
    ForEach ($Group in $Groups){
        #Display the group name
        $Group
        #Add a line under the group name
        '-'*$Group.Length
        #Check if we already have this group, and if not get the group from AD
        If($ADGroups.Keys -notcontains $Group){
            $Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name
            $ADGroups.Add($Group,$Members)
        }
        #Display the group members
        $ADGroups[$Group]
    }
    #output a blank line, for some seperation between folders
    "`n"
}

#1


1  

Ok, let's take it from the top! Excellent, you actually declare a parameter. What you might want to consider is setting a default value for the parameter. What I would do is use the current directory, which conveniently has an automatic variable $PWD (I believe that's short for PowerShell Working Directory).

好的,我们从顶部开始吧!很好,你实际上声明了一个参数。您可能需要考虑的是为参数设置默认值。我要做的是使用当前目录,它有一个自动变量$ PWD(我相信这是PowerShell工作目录的简称)。

Param([string]$filePath = $PWD)

Now if a path is provided it will use that, but if no path is provided it just uses the current folder as a default value.

现在,如果提供了路径,它将使用它,但如果没有提供路径,它只使用当前文件夹作为默认值。

Version check is fine. I'm pretty sure there's more elegant ways to do it, but I honestly don't have never done any version checking.

版本检查没问题。我很确定有更优雅的方法可以做到这一点,但老实说,我从来没有做过任何版本检查。

Now you are querying AD for each group and user that is found (after some filtering, granted). I would propose that we keep track of groups and members so that we only have to query AD once for each one. It may not save a lot of time, but it'll save some if any group is used more than once. So for that purpose we're going to make an empty hashtable to track groups and their members.

现在,您要查找找到的每个组和用户的AD(经过一些过滤,授予后)。我建议我们跟踪组和成员,以便我们只需要为每个查询AD一次。它可能不会节省很多时间,但如果任何组被多次使用,它将节省一些时间。因此,为此目的,我们将制作一个空的哈希表来跟踪组及其成员。

$ADGroups = @{}

Now starts a bad trend... writing to files and then reading those files back in. Outputting to a file is fine, or saving configurations, or something that you'll need again outside of the current PowerShell session, but writing to a file just to read it back into the current session is just a waste. Instead you should either save the results to a variable, or work with them directly. So, rather than getting the folder listing, piping it directly into Get-Acl, and losing the paths we're going to do a ForEach loop on the folders. Mind you, I added the -Directory switch so it will only look at folders and ignore files. This happens at the provider level, so you will get much faster results from Get-ChildItem this way.

现在开始一个不好的趋势...写入文件,然后重新读取这些文件。输出到文件很好,或者保存配置,或者在当前PowerShell会话之外再次需要的东西,但写入文件只是把它读回当前的会话只是浪费。相反,您应该将结果保存到变量,或直接使用它们。因此,不是获取文件夹列表,而是将其直接管道输入Get-Acl,并且丢失路径,我们将在文件夹上执行ForEach循环。请注意,我添加了-Directory开关,因此它只会查看文件夹并忽略文件。这种情况发生在提供程序级别,因此您可以通过这种方式从Get-ChildItem获得更快的结果。

ForEach($Folder in (Get-ChildItem $filePath -Recurse -Directory)){

Now, you wanted to output the path of the folder, and a line. That's easy enough now that we aren't ditching the folder object:

现在,您想要输出文件夹的路径和一行。现在我们没有放弃文件夹对象,这很容易:

    $Folder.FullName
    '-'*$Folder.FullName.Length

Next we get the ACLs for the current folder:

接下来,我们获取当前文件夹的ACL:

    $ACLs = Get-Acl -Path $Folder.FullName

And here's where things get complicated. I'm getting the group names from the ACLs, but I've combined a couple of your Where statements, and also added a check to see if it is an Allow rule (because including Deny rules in this would just be confusing). I've used ? which is an alias for Where, as well as % which is an alias for ForEach-Object. You can have a natural line brake after a pipe, so I've done that for ease of reading. I included comments on each line for what I'm doing, but if any of it is confusing just let me know what you need clarification on.

事情变得复杂了。我从ACL中获取组名,但是我已经结合了几个Where语句,并且还添加了一个检查以查看它是否是允许规则(因为在此包含拒绝规则只会令人困惑)。我用过吗?这是Where的别名,以及%是ForEach-Object的别名。你可以在管道后进行自然线刹,所以我这样做是为了便于阅读。我在每一行都包含了对我正在做的事情的评论,但如果其中任何一个令人困惑,请告诉我你需要澄清的内容。

    $Groups = $ACLs.Access | #Expand the Access property
        ?{ $_.IsInherited -eq $false -and $_.AccessControlType -eq 'Allow' -and $_.IdentityReference -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | #Only instances that allow access, are not inherited, and aren't a local group or special case
        %{$_.IdentityReference -replace 'JAC.*?\\'} | #Expand the IdentityReference property, and replace anything that starts with JAC all the way to the first backslash (likely domain name trimming)
        Select -Unique #Select only unique values

Now we'll loop through the groups, starting off by outputting the group name and a line.

现在我们将循环浏览组,首先输出组名和一行。

    ForEach ($Group in $Groups){
        $Group
        '-'*$Group.Length

For each group I'll see if we already know who's in it by checking the list of keys on the hashtable. If we don't find the group there we'll query AD and add the group as a key, and the members as the associated value.

对于每个组,我将通过检查哈希表上的键列表来查看我们是否已经知道谁在其中。如果我们在那里找不到组,我们将查询AD并将组添加为键,并将成员添加为关联值。

        If($ADGroups.Keys -notcontains $Group){
            $Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name
            $ADGroups.Add($Group,$Members)
        }

Now that we're sure that we have the group members we will display them.

现在我们确定我们有小组成员,我们将展示它们。

        $ADGroups[$Group]

We can close the ForEach loop pertaining to groups, and since this is the end of the loop for the current folder we'll add a blank line to the output, and close that loop as well

我们可以关闭与组相关的ForEach循环,因为这是当前文件夹的循环结束,我们将在输出中添加一个空行,并关闭该循环

    }
    "`n"
}

So I wrote this up and then ran it against my C:\temp folder. It did tell me that I need to clean up that folder, but more importantly it showed me that most of the folders don't have any non-inherited permissions, so it would just give me the path with an underline, a blank line, and move to the next folder so I had a ton of things like:

所以我写了这个,然后在我的C:\ temp文件夹上运行它。它确实告诉我,我需要清理该文件夹,但更重要的是,它告诉我大多数文件夹没有任何非继承权限,所以它只会给我带下划线的路径,空行,并移动到下一个文件夹,所以我有很多东西,如:

C:\Temp\FolderA
---------------

C:\Temp\FolderB
---------------

C:\Temp\FolderC
---------------

That doesn't seem useful to me. If it is to you then use the lines above as I have them. Personally I chose to get the ACLs, check for groups, and then if there are no groups move to the next folder. The below is the product of that.

这似乎对我没用。如果是你,那么就像我拥有它们一样使用上面的行。我个人选择获取ACL,检查组,然后如果没有组移动到下一个文件夹。以下是其中的产物。

Param([string]$filePath = $PWD)

$Version=$PSVersionTable.PSVersion
if ($Version.Major -lt 3) {Throw "Powershell version out of date. Please update powershell." }

#Create an empty hashtable to track groups
$ADGroups = @{}

#Get a recursive list of folders and loop through them
ForEach($Folder in (Get-ChildItem $filePath -Recurse -Directory)){
    # Get ACLs for the folder
    $ACLs = Get-Acl -Path $Folder.FullName

    #Do a bunch of filtering to just get AD groups
    $Groups = $ACLs | 
        % Access | #Expand the Access property
        where { $_.IsInherited -eq $false -and $_.AccessControlType -eq 'Allow' -and $_.IdentityReference -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | #Only instances that allow access, are not inherited, and aren't a local group or special case
        %{$_.IdentityReference -replace 'JAC.*?\\'} | #Expand the IdentityReference property, and replace anything that starts with JAC all the way to the first backslash (likely domain name trimming)
        Select -Unique #Select only unique values

    #If there are no groups to display for this folder move to the next folder
    If($Groups.Count -eq 0){Continue}

    #Display Folder Path
    $Folder.FullName
    #Put a dashed line under the folder path (using the length of the folder path for the length of the line, just to look nice)
    '-'*$Folder.FullName.Length

    #Loop through each group and display its name and users
    ForEach ($Group in $Groups){
        #Display the group name
        $Group
        #Add a line under the group name
        '-'*$Group.Length
        #Check if we already have this group, and if not get the group from AD
        If($ADGroups.Keys -notcontains $Group){
            $Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name
            $ADGroups.Add($Group,$Members)
        }
        #Display the group members
        $ADGroups[$Group]
    }
    #output a blank line, for some seperation between folders
    "`n"
}