實用 PowerShell 小工具 - 產生資料夾權限清單
| | 37 | | ![]() |
跟同事聊到一個需求,異動 Windows 資料夾權限前希望能保留整個資料夾(含子目錄)的原有權限設定,作為異動前後對照或萬一操作失誤的還原參考。
基本上就是記錄檔案總管資料夾內容的安全性頁籤,將各群組或使用者名稱及其對映的完全控制、修改、讀取和執行... 等權限儲存下來,但要用程式完成:
按慣例,又是 PowerShell 展現威力的時刻。研究後發現,對 PowerShell 是件簡單任務,先 Get-ChildItem -Recurse -Directory 列出所有子資料夾,再 Get-ACL 取得權限設定就行了。有個眉角是子資料夾權限多半來自繼承,可以用推算的,逐一表列的意義不大,我們關心的重點在額外設定的權限,排除來自繼承的權限才不會模糊焦點。
故清單的輸出原則為:最上層目錄列舉所有權限,底下的子目錄若完全繼承上層權限就不顯示,權限與上層不同者也只會顯示多出的權限。
工具程式 List-Acl.ps1 的輸入參數有三個,$path 為查詢對象、$report 是開關參數,用來切換輸出權限物件集合或是 HTML 報表、$showInheritedPerms 也是開關,控制要不要印出繼承權限。
若目錄包含多層或大量子資料夾,遞迴查完得花上好一段時間,為避免使用者看不到進度陷入焦慮,我新學會用 Write-Progress 顯示目前處理的目錄名稱,能大幅提升使用者體驗。
List-Acl 的使用方式有兩種,第一種是輸出權限設定物件再轉換,像是接 ConvertTo-Json 轉成 JSON:
.\List-Acl.ps1 C:\WWW | ConvertTo-Json
第二種是加上 -report 參數輸出成 HTML 報表,所有權限一目瞭然,十分實用:(灰色為繼承權限,藍色為額外設定權限)
.\List-Acl.ps1 C:\WWW -report > D:\Report.html
完整程式碼如下:
param (
[Parameter(Mandatory = $true)]
[string]$path,
[switch]$report,
[switch]$showInheritedPerms
)
function FileSysRightText($sysRights) {
$t = $sysRights.ToString()
if ([System.Text.RegularExpressions.Regex]::IsMatch($t, "^[-0-9]")) {
return "Special"
}
return $t
}
function Bool2YN($bool) {
if ($bool) { return "Y" }
return "N"
}
function GetAclRecord([string]$path, [bool]$incInheritedPerms) {
Write-Progress -Activity "$path"
$acl = Get-Acl $path
$access = $acl.Access
if (!$incInheritedPerms) {
$access = $access | Where-Object { !$_.IsInherited }
}
if ($access.Count -gt 0) {
[PSCustomObject]@{
Path = $path;
Access = ($access | ForEach-Object {
"$($_.IdentityReference)`t$($_.AccessControlType.ToString()[0])`t$(FileSysRightText $_.FileSystemRights)`t$(Bool2YN $_.IsInherited)"
})
}
}
}
Clear-Host
$list = New-Object System.Collections.ArrayList
$list.Add((GetAclRecord $path $true)) | Out-Null
Get-ChildItem -Path $path -Directory -Recurse | ForEach-Object {
$list.Add((GetAclRecord $_.FullName $showInheritedPerms)) | Out-Null
}
Clear-Host
if ($report) {
@"
<!DOCTYPE html>
<html><head><title>Permission Checklist for $path</title>
<style>
html,body,table { font-size: 10pt; font-family: 微軟正黑體; }
table { border-collapse: collapse; }
thead td { text-align: center; background-color: royalblue; color: white; }
td { padding: 3px; border: 1px solid #bbb; }
td[rowspan] { vertical-align: top; }
td.Y { color: gray; }
td.N { color: blue; }
</style>
</head><body><h3>$path Permission Checklist</h3>
<table><thead><tr><td>Path</td><td>User</td><td>Rights</td></tr></thead>
<tbody>
"@
$list | Where-Object { $_.Path } | ForEach-Object {
$acl = $_
$first = $true
$acl.Access | ForEach-Object {
"<tr>"
if ($first) {
"<td rowspan=$($acl.Access.Count)>$($acl.Path)</td>"
$first = $false
}
$p = $_.Split("`t")
"<td class='$($p[1]) $($p[3])'>$($p[0])</td><td class='$($p[1]) $($p[3])'>$($p[2])</td>"
"</tr>"
}
}
"</tbody></table></body></html>"
}
else {
Write-Output $list
}
不到 100 行寫完這個實用小工具,自己也覺得滿意,是我近期的得意作品之一,分享給大家。
A handy tool to list permissions of directories with PowerShell.
Comments
# by James Chang
拍拍手...真的很不錯...共用資料夾開太多就很煩惱....
# by Andrew Hsieh
謝謝高手的分享... 目前執行script後Report HTML只有第一層目錄權限,但沒有子目錄權限,請問是否有其他要注意的地方? (我是跑在Win10 專業版版本 1803 上) 非常感謝!
# by Jeffrey
to Andrew Hsieh,清單輸出原則為:最上層目錄列舉所有權限,底下的子目錄若完全繼承上層權限就不顯示,權限與上層不同者也只會顯示多出的權限。如果要輸出所有繼承權限,可加上 -showInheritedPerms 參數。
# by Andrew Hsieh
Hi Jeffrey, 感謝您的快速回覆! 先說聲抱歉,可能我之前的表達有一點不清楚,實際上我執行script後產生的 HTML 檔案是只有第一層目錄,以您的範例就是只有 C:\WWW 而已,並沒有出現第二層目錄 AppNet and Darkblog,所以我才想是否是有遺漏甚麼.非常感謝!
# by Jeffrey
to Andrew Hsieh, 試著在 C:\WWW 建個 C:\WWW\Test 資料夾,改一下 Test 的 NTFS 權限(例如:賦與 Everyone 完全控制),再跑一次 PS Script,此時應該會看到 C:\WWW\Test。
# by Andrew Hsieh
Hi Jeffrey, 完美,已經OK. ps. 經確認只有一層係因為我的測試目錄都是"繼承權限",沒有"額外設定權限",所以才沒有第二層目錄. 感謝喔!
# by 聶風
抱歉,這個功能要怎麼使用呢?程式碼要存成psl嗎?
# by Jeffrey
to 聶風,存成 .ps1 (123 的 1,不是 l),如果之前沒用過 PowerShell,這裡有篇教學 https://officeguide.cc/powershell-beginner-introduction/
# by kith
這是一個很棒的Powershell,已經用了約半年左右,都可以解決很多報表產出!!! 不過近期我遇到一個問題,PowerShell get-acl List folder contents vs ReadAndExecute 網址:https://stackoverflow.com/questions/34414438/powershell-get-acl-list-folder-contents-vs-readandexecute 請問是否有機會解決目錄權限在判斷上的落差,謝謝
# by kith
自問自答,調整ps1內容: function FileSysRightText($sysRights) { $t = $sysRights.ToString() if ([System.Text.RegularExpressions.Regex]::IsMatch($t, "^[-0-9]")) { return "Special" } if ([System.Text.RegularExpressions.Regex]::IsMatch($t, "ReadAndExecute, Synchronize")) { return "ListDirectory" } return $t }
# by kith
調整如此後~~ReadAndExecute 的權限,會有問題,繼續調整
# by jason11111
請問報表產生的user,可以是顯示名稱嗎?
# by Jeffrey
to jason11111, 使用 net user (CMD) 或 Get-ADUser (PowerShell) 可由帳號查詢使用者名稱,應能滿足你的需求。
# by Chuck
想請問我用Powershell 控制台跑了這個程式 有顯示結果,但都沒有產出html檔案,是哪裡還要修正嗎? 感謝
# by Chuck
抱歉 不用理我 上面已經寫出怎麼產report...感謝
# by GG
幫了我一個大忙!!
# by speed
# 2021-02-17 04:58 PM by Jeffrey to Andrew Hsieh,清單輸出原則為:最上層目錄列舉所有權限,底下的子目錄若完全繼承上層權限就不顯示,權限與上層不同者也只會顯示多出的權限。如果要輸出所有繼承權限,可加上 -showInheritedPerms 參數。 請問 -showInheritedPerms 參數,要放在哪裡?
# by speed
沒事,我看懂了!!! 非常強大!!
# by Terence
最近在稽核共用資料夾權限, 看到這個覺得的救了!!!! ((不用一個一個截圖)) 不過...我們資料夾超多層的,有辦法限制只scan到第n層就不掃描嗎? 謝謝!
# by Terence
沒事,已解決,是我不懂 PowerShell 加一個 -Depth n 即可。
# by Erik
感謝大哥提供如此好用的程式。 順便告知一下 bug: .\List-Acl.ps1 C:\WWW -report > D:\Resport.html 這樣最後的英文多了一個 "s"
# by Jeffrey
to Erik, 感謝,不是 Bug,是個人特色。哈!
# by KEVIN
請問版大,如果權限在產出的時候,之前變成中問,可以用甚麼樣的方式?
# by Jeffrey
to Kevin, 沒看懂「之前變成中(間?)」的意思
# by KEVIN
不好意思沒有說清楚,例如:程式產出報表權限時顯示(FullControl),直接變成設定的中文文字(完全控制),大概是這個意思。
# by Jeffrey
to KEVIN, 比較快的做法是自己翻譯,MS Learn 有權限列舉的完整清單 https://learn.microsoft.com/zh-tw/dotnet/api/system.security.accesscontrol.filesystemrights?view=netframework-4.8 但我沒查到官方的中文對照表
# by QCC
請問可以指定掃到第幾層嗎?
# by Jeffrey
to QCC,在 Get-ChildItem 加上 Depth 參數可控制掃到第幾層 https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem
# by frozenblue
感謝,非常實用,解決了不少問題。
# by CL
非常實用,感謝大感謝~~
# by Kai
大大~ 如果只想要列出某指定帳號 ABC 所擁有權限的資料夾目錄及檔案樹狀圖 要如何修改調整呢
# by Jeffrey
to Kai, 判斷是否擁有權限有點複雜,例如目錄授權A群組,帳號屬於B群組,B群組是A群組成員;X帳號同時是A、B群組成員,A群組唯讀、B群組可讀寫,結果為可讀寫... 我沒想到好方法可以快速判斷。
# by Quit
請問各位前輩,上述留言關於 [僅搜尋到第n層] 的 -Depth n 參數,要怎麼下? 不知道要加在哪裡 假如只想要蒐到第3層目錄就好 .\List-Acl.ps1 C:\WWW -report -Depth 3 -showInheritedPerms > D:\Report.html 這樣會錯誤[找不到符合參數名稱 'Depth 3' 的參數。]
# by Jeffrey
to Quit, 要改程式,加在 Get-ChildItem https://learn.microsoft.com/zh-tw/powershell/module/microsoft.powershell.management/get-childitem
# by Quit
To Jeffrey, 我試著在U底下建立了test、test\test1、test\test1\test2\... 持續到test5 然後在程式改成了 Get-ChildItem -Path $path -Depth 3 -Directory -Recurse | ForEach-Object { 得到的結果如下: U:\test\test1\test2\test3 第1層的test,是不會計算在內嗎?因為看起來是搜尋到第4層
# by Jeffrey
to Quit, 文件是寫「-Depth 2 包含 Path 參數的目錄、子目錄的第一層,以及子目錄的第二層。」你看看是否與你實測結果一致。
# by jian
請問有辦法加入欄位 用ADGroupMember 列出該群組下方的全部使用者嗎