Windowsファイルサーバのアクセス権調査

ファイルサーバを移行する場合ではAD環境でしたら比較的アクセス権の移行は容易ですが、ワークグループ環境やNASから新規サーバにデータ移行すると同時にアクセス権の移行を行う場合、フォルダ2階層以下は同じアクセス権を付与するなど明確なポリシーがドキュメントに残っていないと割と面倒な作業になります。
現在のWindowsファイルサーバのフォルダアクセス権を調査して、階層のアクセス権を判断しやすいスクリプトを作成しました。

●フォルダアクセス権の調査
以下のPowerShellスクリプトを実行すると、デスクトップにFolderAclList.csvが成果品として出力されます。
FolderAclList.logは作業ログ、FolderAclList_ErrorFolders.logはエラーログを出力します。
対象はWindowsのGドライブになっていますので、ドライブレターが異なる場合は【 $targetPath = "G:\" 】の部分を修正してご利用ください。プログラムは対象ドライブのカウント後に処理を実施します(1000行単位のバッファで進行)。
出力は以下の形式のサンプルCSVファイルです。
FolderPath,IdentityReference,FileSystemRights,Category,AccessControlType
G:\deep\001\002\003\004\005\006\007,BUILTIN\Users,AppendData,その他,Allow
G:\deep\001\002\003\004\005\006\007,BUILTIN\Users,CreateFiles,その他,Allow
G:\deep\001\002\003\004\005\006\007\008,BUILTIN\Administrators,FullControl,フル,Allow
G:\deep\001\002\003\004\005\006\007\008,BUILTIN\Administrators,FullControl,フル,Allow
G:\deep\001\002\003\004\005\006\007\008,NT AUTHORITY\SYSTEM,FullControl,フル,Allow
G:\deep\001\002\003\004\005\006\007\008,CREATOR OWNER,GENERIC_ALL (FullControl),フル,Allow
G:\deep\001\002\003\004\005\006\007\008,BUILTIN\Users,ReadAndExecute, Synchronize,読み取り,Allow
G:\deep\001\002\003\004\005\006\007\008,BUILTIN\Users,AppendData,その他,Allow
1.フォルダアクセス権の調査用スクリプト
注意!:フォルダ名に,(カンマ)が使われている場合、当該の行は正常に表示されません!!

# ================================
# ACL調査スクリプト
# ================================

$targetPath = "G:\"
$csvPath = "$env:USERPROFILE\Desktop\FolderAclList.csv"
$logPath = "$env:USERPROFILE\Desktop\FolderAclList.log"
$errorLog = "$env:USERPROFILE\Desktop\FolderAclList_ErrorFolders.log"

# ---- ログの定義 ----
function Write-Log {
param([string]$message)
$timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$line = "$timestamp $message"
Write-Host $line
Add-Content -Path $logPath -Value $line
}

# ---- エラーログの定義 ----
function Write-ErrorLog {
param([string]$folder, [string]$errorMessage)
$timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$line = "$timestamp [ERROR] $folder $errorMessage"
Add-Content -Path $errorLog -Value $line
}

Write-Log "=== スクリプト開始 ==="
Write-Log "ターゲット: $targetPath"
Write-Log "CSV出力先: $csvPath"
Write-Log "ログ出力先: $logPath"
Write-Log "エラーフォルダ出力先: $errorLog"

# ---- CSVヘッダ(初期化)----
"FolderPath,IdentityReference,FileSystemRights,Category,AccessControlType" | Out-File -FilePath $csvPath -Encoding UTF8
"" | Out-File -FilePath $errorLog -Encoding UTF8

# ---- 権限変換関数 ----
function Convert-FileSystemRights {
param([System.Security.AccessControl.FileSystemRights]$rights)
$raw = $rights.value__
$genericMap = @{
268435456 = "GENERIC_ALL (FullControl)"
536870912 = "GENERIC_WRITE"
1073741824 = "GENERIC_READ"
2147483648 = "GENERIC_EXECUTE"
}
if ($genericMap.ContainsKey($raw)) { return $genericMap[$raw] }
try { return ([System.Security.AccessControl.FileSystemRights]$raw).ToString() }
catch { return $raw }
}

function Classify-Rights {
param([string]$rights)
$r = $rights.ToLower()
if ($r.Contains("fullcontrol") -or $r.Contains("generic_all")) { "フル" }
elseif ($r.Contains("modify") -or ($r.Contains("write") -and $r.Contains("delete"))) { "変更" }
elseif ($r.Contains("read") -or $r.Contains("listdirectory") -or $r.Contains("execute")) { "読み取り" }
else { "その他" }
}

# ---- .NET フォルダ列挙 ----
function Get-FoldersRecursive {
param([string]$path, [ref]$count)

try {
foreach ($dir in [System.IO.Directory]::EnumerateDirectories($path)) {

$count.Value++

if ($count.Value % 1000 -eq 0) {
Write-Log "フォルダ列挙中: $($count.Value) 個"
}

$dir
Get-FoldersRecursive -path $dir -count $count
}
}
catch {
Write-ErrorLog -folder $path -errorMessage "フォルダ列挙エラー: $($_.Exception.Message)"
}
}

# ---- フォルダ列挙 ----
Write-Log "フォルダ列挙開始..."
$folderCount = 0
$foldersEnum = Get-FoldersRecursive -path $targetPath -count ([ref]$folderCount)
$folderList = @($targetPath) + @($foldersEnum)

$total = $folderList.Count
Write-Log "フォルダ列挙完了: 合計 $total 個"

# ---- ACL解析処理開始 ----
Write-Log "ACL 取得開始..."
$counter = 0
$startTime = Get-Date

# ---- ETA 移動平均バッファモード ----
$etaSamples = New-Object System.Collections.Generic.List[double]
$etaSampleSize = 10

# ---- CSV書込みバッファ ----
$buffer = New-Object System.Collections.Generic.List[string]
$bufferSize = 1000

foreach ($folder in $folderList) {

$counter++

# ================================
# 除外判定(フォルダ名の basename を比較)
# 末尾スラッシュやパス長に依存せずに除外
# ================================
try {
$baseName = Split-Path -Path $folder -Leaf
}
catch {
$baseName = $folder
}

# 大文字小文字を無視して比較(-ieq: case-insensitive equals)
if ($baseName -ieq 'System Volume Information' -or $baseName -ieq '$RECYCLE.BIN') {
# 除外ログ(任意で有効化する場合はコメント解除)
# Write-Log "除外: $folder"
continue
}

# ---- 1000フォルダごとにETAと進捗率を表示 ----
if ($counter % 1000 -eq 0) {

$elapsed = (Get-Date) - $startTime
$rate = $elapsed.TotalSeconds / $counter

# 移動平均に追加
$etaSamples.Add($rate)
if ($etaSamples.Count -gt $etaSampleSize) {
$etaSamples.RemoveAt(0)
}

# 平均処理速度
$avgRate = ($etaSamples | Measure-Object -Average).Average

# 残り時間(+-30%誤差程度)
$remaining = ($total - $counter) * $avgRate
$eta = [TimeSpan]::FromSeconds($remaining)

$percent = [math]::Round(($counter / $total) * 100, 2)
Write-Log "$counter / $total ACL処理 (${percent}%) 残り時間(目安): $($eta.ToString("hh\:mm\:ss"))"
}

# ---- ACL取得 ----
try {
$acl = Get-Acl -Path $folder
}
catch {
Write-ErrorLog -folder $folder -errorMessage "ACL取得失敗: $($_.Exception.Message)"
continue
}

foreach ($access in $acl.Access) {

$rightsReadable = Convert-FileSystemRights $access.FileSystemRights
$rightsCategory = Classify-Rights $rightsReadable

$buffer.Add("$folder,$($access.IdentityReference),$rightsReadable,$rightsCategory,$($access.AccessControlType)")

if ($buffer.Count -ge $bufferSize) {
$buffer | Add-Content -Path $csvPath -Encoding UTF8
$buffer.Clear()
}
}
}

# ---- バッファ残りを出力 ----
if ($buffer.Count -gt 0) {
$buffer | Add-Content -Path $csvPath -Encoding UTF8
$buffer.Clear()
}

Write-Log "=== スクリプト終了 ==="
●ログの整理
出力されたFolderAclList.csvは見やすいものではないのでコマンドでACLをバッチ適用するには使えると思いますが、下記のようなフォーマットに変換して認識し易くしています。
G:\deep\001\002\003\004\005\006\007\008
├─ BUILTIN\Administrators | FullControl | フル | Allow
├─ BUILTIN\Administrators | FullControl | フル | Allow
├─ NT AUTHORITY\SYSTEM | FullControl | フル | Allow
├─ CREATOR OWNER | GENERIC_ALL (FullControl) | フル | Allow
├─ BUILTIN\Users | ReadAndExecute | Synchronize | 読み取り,Allow
├─ BUILTIN\Users | AppendData | その他 | Allow
├─ BUILTIN\Users | CreateFiles | その他 | Allow
FolderAclList.csvを同じフォルダにおいて以下のコマンドラインで実行します。
PowerShellのウィンドウで以下のcdコマンドでAclTree.ps1のあるフォルダに移動します。FolderAclList.csvAclTree.ps1同じフォルダにある必要があります。PowerShellのウィンドウで以下のコマンドを実行します。
powershell -ExecutionPolicy Bypass -File .\AclTree.ps1 ".\FolderAclList.csv" ".\AclTree.txt"
終了するとAclTree.txtが出力されます。

2.ACLログ整理用スクリプト
<#
AclTree.ps1
CSV を逐次処理しつつTree構造を出力
実行書式 powershell -ExecutionPolicy Bypass -File .\AclTree.ps1 ".\FolderAclList.csv" ".\AclTree.txt"
#>

# 引数/デフォルト(← デフォルト名を FolderAclList.csv に変更)
$CsvPath = if ($args.Count -ge 1) { $args[0] } else { ".\FolderAclList.csv" }
$OutPath = if ($args.Count -ge 2) { $args[1] } else { ".\AclTree.txt" }

if (-not (Test-Path $CsvPath)) {
Write-Error "入力ファイルが見つかりません: $CsvPath"
exit 1
}

# 出力ファイルを上書き(既存削除)
try {
if (Test-Path $OutPath) { Remove-Item -LiteralPath $OutPath -Force }
} catch {
Write-Warning "出力ファイルを削除できませんでした: $_"
}

# ===== 進捗用:CSV の総行数を取得 =====
$totalLines = (Get-Content -Path $CsvPath -ReadCount 0).Count
if ($totalLines -le 1) {
Write-Error "CSV が空です: $CsvPath"
exit 1
}
$dataLines = $totalLines - 1 # ヘッダ除く
Write-Host "処理対象行数: $dataLines 行"

# ===== 分割関数の定義 =====
function Split-FirstNCommas {
param(
[string]$line,
[int]$maxParts = 5
)
$parts = @()
$start = 0
for ($i = 1; $i -lt $maxParts; $i++) {
$idx = $line.IndexOf(',', $start)
if ($idx -lt 0) { break }
$parts += $line.Substring($start, $idx - $start)
$start = $idx + 1
}
$parts += $line.Substring($start)
return ,$parts
}

# ===== Streamを作成 =====
$fsOut = $null
$sw = $null
$sr = $null

try {
$fsOut = [System.IO.FileStream]::new(
$OutPath,
[System.IO.FileMode]::CreateNew,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::Read,
65536,
$false
)
$sw = [System.IO.StreamWriter]::new($fsOut, [System.Text.Encoding]::UTF8)
$sw.AutoFlush = $false

$sr = [System.IO.StreamReader]::new($CsvPath, [System.Text.Encoding]::UTF8)

# ヘッダ行を読み捨てする
$header = $sr.ReadLine()

$previousFolder = $null
$count = 0 # 進捗カウンタ
$progressStep = 1000 # 1000行ごとに進捗表示

Write-Host "処理を開始します..."

while (($line = $sr.ReadLine()) -ne $null) {

if ($line.Trim().Length -eq 0) { continue }

$count++

# ==== 進捗表示 ====
if (($count % $progressStep) -eq 0) {
$percent = [math]::Round(($count / $dataLines) * 100, 2)
Write-Host ("進捗: {0}/{1} 行 ({2} %)" -f $count, $dataLines, $percent)
}

# 分割
$cols = Split-FirstNCommas -line $line -maxParts 5
if ($cols.Count -lt 5) { continue }

$folder = $cols[0].Trim()
$idref = $cols[1].Trim()
$rights = $cols[2].Trim()
$cate = $cols[3].Trim()
$type = $cols[4].Trim()

# 階層レベル計算
$relative = $folder
if ($relative.Length -ge 3 -and $relative[1] -eq ':' -and $relative[2] -eq '\') {
$relative = $relative.Substring(3)
} elseif ($relative.StartsWith("\\\")) {
$relative = $relative.TrimStart("\")
}

if ($relative -eq "") {
$levels = 0
} else {
$levels = ($relative.Split('\').Count)
}

$indent = (" " * 4) * $levels

# フォルダが変わったら見出し出力
if ($folder -ne $previousFolder) {
if ($previousFolder -ne $null) { $sw.WriteLine() }
$sw.WriteLine("$indent$folder")
$previousFolder = $folder
}

# ACL 出力
$sw.WriteLine("$indent ├─ $idref | $rights | $cate | $type")
}

$sw.Flush()
Write-Host "処理完了: $OutPath"
}
catch {
Write-Error "処理中にエラー発生: $_"
}
finally {
if ($sw) { $sw.Dispose() }
if ($sr) { $sr.Dispose() }
if ($fsOut) { $fsOut.Dispose() }
}
●整理したログからアクセス権の差異を調べる
第2層からTree構造を把握し、上のフォルダとのアクセス権の差異を抽出する(AclDiffResult.txtとして出力)
こんな感じです。
------------------------------------------------------------
権限の不一致を検出
対象フォルダ: G:\deep\001\002\003\004\005
親フォルダ : G:\deep\001\002\003\004

[追加/変更された権限]:
+ BUILTIN\Users | AppendData | その他 | Allow
[親にあって子にない権限]:
- BUILTIN\Users | AppendData | その他 | Deny
------------------------------------------------------------

3.アクセス権の差分抽出スクリプト

# ==========================================================
# 50万行対応・第二階層基準ACL 差分チェック
# ==========================================================

$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

# スクリプトと同じ場所にある AclTree.txt
$ScriptPath = $PSScriptRoot
if (-not $ScriptPath) { $ScriptPath = "." }
$InputFile = Join-Path $ScriptPath "AclTree.txt"
$OutputTxt = Join-Path $ScriptPath "AclDiffResult.txt"

if (Test-Path $OutputTxt) { Remove-Item $OutputTxt -Force }

if (-not (Test-Path $InputFile)) {
Write-Error "AclTree.txt が見つかりません。"
exit
}

# ---------------------------------------------------------
# 1. データロード
# ---------------------------------------------------------
Write-Host "ファイルを読み込み中..." -ForegroundColor Cyan
$Lines = [System.IO.File]::ReadAllLines($InputFile)
$LineCount = $Lines.Count

# ---------------------------------------------------------
# 2. フォルダごとにACLを構築
# ---------------------------------------------------------
Write-Host "ACLを解析中..." -ForegroundColor Cyan

$FolderPerms = [System.Collections.Generic.Dictionary[string, System.Collections.Generic.List[string]]]::new()
$CurrentPath = $null
$TreePattern = "^[ \t├─└│]+"

for ($i = 0; $i -lt $LineCount; $i++) {

$Line = $Lines[$i].Trim()
if ($Line.Length -eq 0) { continue }

# フォルダ行
if ($Line -like '*:\*' -and $Line -notlike '*|*') {
$CurrentPath = $Line
$FolderPerms[$CurrentPath] = [System.Collections.Generic.List[string]]::new()
continue
}

# ACL行
if ($CurrentPath -and $Line -like '*|*') {
$Clean = [regex]::Replace($Line, $TreePattern, "")
$FolderPerms[$CurrentPath].Add($Clean)
}
}

# ---------------------------------------------------------
# 3. 第二階層を抽出
# ---------------------------------------------------------
Write-Host "第二階層の抽出中..." -ForegroundColor Cyan

$SecondLevel = @()

foreach ($Path in $FolderPerms.Keys) {
$parts = $Path -split '\\'

# 例: ["G:", "deep", "001", ...]
if ($parts.Count -ge 2) {
$Second = "$($parts[0])\$($parts[1])"
if ($SecondLevel -notcontains $Second) {
$SecondLevel += $Second
}
}
}

# ---------------------------------------------------------
# 4. 差分チェック
# ---------------------------------------------------------
Write-Host "差分計算中..." -ForegroundColor Cyan

$Keys = $FolderPerms.Keys
$Total = $Keys.Count
$Index = 0

$OutputBuffer = New-Object System.Collections.Generic.List[string]

foreach ($Path in $Keys) {

$Index++

# 進捗表示(200回に1回更新)
if ($Index % 200 -eq 0) {
$Percent = [math]::Round(($Index / $Total) * 100, 1)
Write-Progress -Activity "ACL差分チェック中" `
-Status "$Percent% 完了($Index / $Total)" `
-PercentComplete $Percent
}

# 親フォルダ
$Parent = Split-Path $Path -Parent

# 第一階層は比較に含めない
if ($SecondLevel -notcontains $Parent -and -not $FolderPerms.ContainsKey($Parent)) {
continue
}

# 親フォルダが存在しない場合は比較しない
if (-not $FolderPerms.ContainsKey($Parent)) {
continue
}

# ハッシュセット比較
$ParentHash = New-Object System.Collections.Generic.HashSet[string]
$CurrentHash = New-Object System.Collections.Generic.HashSet[string]

foreach ($p in $FolderPerms[$Parent]) { $ParentHash.Add($p) | Out-Null }
foreach ($c in $FolderPerms[$Path]) { $CurrentHash.Add($c) | Out-Null }

$Added = @()
$Removed = @()

foreach ($c in $CurrentHash) {
if (-not $ParentHash.Contains($c)) { $Added += $c }
}

foreach ($p in $ParentHash) {
if (-not $CurrentHash.Contains($p)) { $Removed += $p }
}

if ($Added.Count -gt 0 -or $Removed.Count -gt 0) {

$OutputBuffer.Add("------------------------------------------------------------")
$OutputBuffer.Add("権限の不一致を検出")
$OutputBuffer.Add("対象フォルダ: $Path")
$OutputBuffer.Add("親フォルダ : $Parent")
$OutputBuffer.Add("")

if ($Added.Count -gt 0) {
$OutputBuffer.Add("[追加/変更された権限]:")
foreach ($x in $Added) { $OutputBuffer.Add(" + $x") }
}

if ($Removed.Count -gt 0) {
$OutputBuffer.Add("[親にあって子にない権限]:")
foreach ($x in $Removed) { $OutputBuffer.Add(" - $x") }
}

$OutputBuffer.Add("")
}
}

Write-Progress -Activity "ACL差分チェック中" -Completed

# ---------------------------------------------------------
# 5. まとめて保存
# ---------------------------------------------------------
[System.IO.File]::WriteAllLines($OutputTxt, $OutputBuffer)

$Stopwatch.Stop()

Write-Host ""
Write-Host "処理完了: $($Stopwatch.Elapsed.ToString())" -ForegroundColor Yellow
Write-Host "結果ファイル: $OutputTxt" -ForegroundColor Cyan
参考.1
ファイルから特手の文字列の数をカウントするPowershellスクリプト
※"権限の不一致を検出"をカウントすると親フォルダと子フォルダでアクセス権に差異がある対象個数がわかる

# ファイルパスと検索文字列を設定
$FilePath = "C:\path\file.txt"
$SearchString = "特定の文字列"

# Select-Stringで検索し、Countで件数を取得
(Select-String -Path $FilePath -Pattern $SearchString).Count
参考.2
フォルダの第3階層のみテキスト出力するPowershell
※d:\data\***のフォーマットをリストする

#inputFile:調査ファイル
#outputFile:出力ファイル
$inputFile = "C:\path\AclTree.txt"
$outputFile = "C:\path\output.txt"

#ドライブ直下(1階層)の調査したいフォルダを指定
Select-String -Path $inputFile -Pattern '\bG:\\data\\[^\\]+\\?$' |
ForEach-Object { $_.Line } |
Set-Content $outputFile
#階層を増やしたい場合には2行目を以下のように変更(1階層増)
#Select-String -Path $inputFile -Pattern '\bG:\\data\\[^\\]+\\[^\\]+\\?$' |

参考.3
ファイル内ですべてのアクセス権をリストする(重複排除)
想定外のアクセス権をみつけるヒントに

# ================== 設定 ==================
$InputFile = "C:\path\AclTree.txt"
$OutputFile = "C:\path\Acl_Permissions_Unique.txt"
$LogFile = "C:\path\Acl_Skip.log"

$FlushSize = 1000

# ---- 出力ファイルを必ず作る ----
"" | Set-Content -Encoding UTF8 $OutputFile
"" | Set-Content -Encoding UTF8 $LogFile

# ユニークなアクセス権セットを格納するためのハッシュセット
$globalSet = [System.Collections.Generic.HashSet[string]]::new()
$bufferSet = [System.Collections.Generic.HashSet[string]]::new()
$LineCount = 0

# 権限カテゴリを判定する関数
function Get-RightCategory($rights, $japaneseRights) {
# 2列目のアクセス権の英語表記を基に判定
if ($rights -match "FullControl|GENERIC_ALL") {
return "フル"
}
# 3列目の日本語表記を基に判定
if ($japaneseRights -match "読み取り") {
return "読み取り"
}
return "その他"
}

# ================== 入力ファイル処理 ==================

Write-Host "処理開始: $InputFile からアクセス権情報を抽出..." -ForegroundColor Cyan

# 入力ファイルを1行ずつ読み込む
Get-Content $InputFile | ForEach-Object {
$LineCount++
# 行頭・行末の空白をトリム
$line = $_.Trim()

# パス行(G:\deepなど)はスキップ
# パス行は通常、パイプ文字を含まない
if ($line -match '^[A-Z]:\\') {
return
}

# 正規表現のカスタマイズ
# 行頭の装飾文字(├─, 空白)を無視し、パイプ '|' で区切られたデータパターンに一致させる
# (.*?) は非マッチで、パイプ '|' または行末までキャプチャ
# 最初の `\s*` は、装飾文字後の任意の空白に対応させる
$pattern = '^\s*[├└]?\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*$'

if ($line -match $pattern) {

# 正規表現のマッチグループから値を取得
$identityReference = $Matches[1].Trim() # ユーザー/グループ名 (例: BUILTIN\Administrators)
$fileSystemRights = $Matches[2].Trim() # アクセス権の英語表記 (例: FullControl)
$japaneseRights = $Matches[3].Trim() # アクセス権の日本語表記 (例: フル, Synchronize)
# 4列目のデータ(例: Allow, 読み取り,Allow, deny, Allowny)から許可/拒否を抽出
$permissionType = $Matches[4].Trim()

# 最後の要素が "Allow" または "Deny" であることを確認
# 末尾が ",Allow" や ",deny" になっているケースに対応
if ($permissionType -match '(Allow|deny|Deny)$') {
$permissionType = $Matches[1].Trim()
} else {
# 4列目の値が "Allow" や "deny" 以外の場合(例: 単なる "その他" などの場合)
# 4列目が Allow/Deny に対応しない行はスキップ
Add-Content $LogFile "SKIP PERMISSION: $LineCount : $line"
return
}

# 権限カテゴリの決定
# NT HORITY\SYSTEM の誤字を修正
if ($identityReference -eq 'NT HORITY\SYSTEM') {
$identityReference = 'NT AUTHORITY\SYSTEM'
}

$rightCategory = Get-RightCategory $fileSystemRights $japaneseRights

# ユニークキーの生成
$key = "{0} | {1} | {2} | {3}" -f $identityReference, $fileSystemRights, $rightCategory, $permissionType

# ユニークセットの追加
if ($globalSet.Add($key)) {
$bufferSet.Add($key) | Out-Null
}

# バッファの書き出し (FlushSizeに達した場合)
if ($bufferSet.Count -ge $FlushSize) {
Write-Host "バッファ書き出し: $($bufferSet.Count) 件" -ForegroundColor Yellow
$bufferSet | Sort-Object | Add-Content -Encoding UTF8 $OutputFile
$bufferSet.Clear()
}
}
# else {
# # アクセス権の行と見なせない行をログに出力
# Add-Content $LogFile "SKIP LINE (NO MATCH): $LineCount : $line"
# }
}

# ---- 残りを書き出し ----
if ($bufferSet.Count -gt 0) {
Write-Host "最終書き出し: $($bufferSet.Count) 件" -ForegroundColor Yellow
$bufferSet | Sort-Object | Add-Content -Encoding UTF8 $OutputFile
}

Write-Host "処理終了" -ForegroundColor Green
Write-Host "総ユニーク ACL 数: $($globalSet.Count)" -ForegroundColor Green
Write-Host "ログ: $LogFile" -ForegroundColor Gray

ご参考まで

関連記事

TOP