跟同事聊到一個需求,異動 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:\Resport.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 的權限,會有問題,繼續調整

Post a comment


66 - 0 =