フォルダやディレクトリ、ファイル名には”_”と”-“以外の記号は使用しないのが常識的かと思いますが、おそらく非常識なようなので対応版を作成してみました。日本語・”、”・”$”などの大文字記号が含まれていても大丈夫なはずです。
●フォルダアクセス権の調査
# ================================
# 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 "=== スクリプト開始 ==="
# ---- 初期化 ----
Remove-Item $csvPath -ErrorAction SilentlyContinue
Remove-Item $errorLog -ErrorAction SilentlyContinue
# ---- 権限変換 ----
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 $path "フォルダ列挙エラー: $($_.Exception.Message)"
}
}
# ---- フォルダ列挙 ----
Write-Log "フォルダ列挙開始..."
$folderCount = 0
$folders = @($targetPath) + @(Get-FoldersRecursive -path $targetPath -count ([ref]$folderCount))
$total = $folders.Count
Write-Log "フォルダ列挙完了: $total 個"
# ---- CSV 出力用バッファ ----
$buffer = New-Object System.Collections.Generic.List[object]
$bufferSize = 1000
# ---- ACL 処理 ----
Write-Log "ACL取得開始..."
$counter = 0
$startTime = Get-Date
foreach ($folder in $folders) {
$counter++
# 除外フォルダ
try { $baseName = Split-Path $folder -Leaf } catch { $baseName = $folder }
if ($baseName -ieq 'System Volume Information' -or $baseName -ieq '$RECYCLE.BIN') {
continue
}
if ($counter % 1000 -eq 0) {
$elapsed = (Get-Date) - $startTime
$percent = [math]::Round(($counter / $total) * 100, 2)
Write-Log "$counter / $total (${percent}%) 経過: $($elapsed.ToString("hh\:mm\:ss"))"
}
try {
$acl = Get-Acl -Path $folder
}
catch {
Write-ErrorLog $folder "ACL取得失敗: $($_.Exception.Message)"
continue
}
foreach ($access in $acl.Access) {
$rightsReadable = Convert-FileSystemRights $access.FileSystemRights
$rightsCategory = Classify-Rights $rightsReadable
$buffer.Add([PSCustomObject]@{
FolderPath = $folder
IdentityReference = $access.IdentityReference.ToString()
FileSystemRights = $rightsReadable
Category = $rightsCategory
AccessControlType = $access.AccessControlType.ToString()
})
if ($buffer.Count -ge $bufferSize) {
$buffer | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8 -Append
$buffer.Clear()
}
}
}
# ---- 残り出力 ----
if ($buffer.Count -gt 0) {
$buffer | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8 -Append
}
Write-Log "=== スクリプト終了 ==="
●ログの整理(Tree形式)
#実行コマンドファイル名は適宜変更
#powershell -ExecutionPolicy Bypass -File .\AclTree.ps1 ".\FolderAclList.csv" ".\AclTree.txt"
<#
AclTree.ps1
CSVをTextFieldParser でストリーム処理し、
ACLをTree 形式でテキスト出力
#>
param(
[string]$CsvPath = ".\FolderAclList.csv",
[string]$OutPath = ".\AclTree.txt"
)
# ===== 入力チェック =====
if (-not (Test-Path $CsvPath)) {
Write-Error "入力CSVが見つかりません: $CsvPath"
exit 1
}
# ===== 出力ファイル初期化 =====
if (Test-Path $OutPath) {
Remove-Item -LiteralPath $OutPath -Force
}
# ===== 行数カウント =====
Write-Host "CSV 行数をカウント中..."
$totalLines = 0
Get-Content -Path $CsvPath -ReadCount 100000 | ForEach-Object {
$totalLines += $_.Count
}
$dataLines = [math]::Max($totalLines - 1, 0)
Write-Host "処理対象行数: $dataLines 行"
# ===== TextFieldParser準備 =====
Add-Type -AssemblyName Microsoft.VisualBasic
$parser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser($CsvPath)
$parser.TextFieldType = [Microsoft.VisualBasic.FileIO.FieldType]::Delimited
$parser.SetDelimiters(',')
$parser.HasFieldsEnclosedInQuotes = $true
# ヘッダ読み捨て
$null = $parser.ReadFields()
$previousFolder = $null
$count = 0
$progressStep = 100000
$sw = [System.IO.StreamWriter]::new($OutPath, $false, [System.Text.Encoding]::UTF8)
try {
Write-Host "処理を開始します..."
while (-not $parser.EndOfData) {
$fields = $parser.ReadFields()
if (-not $fields -or $fields.Count -lt 5) { continue }
$count++
# ==== 進捗表示(0 除算防止)====
if (($count % $progressStep) -eq 0) {
if ($dataLines -gt 0) {
$percent = [math]::Round(($count / $dataLines) * 100, 2)
Write-Host "進捗: $count / $dataLines 行 ($percent %)"
} else {
Write-Host "進捗: $count 行"
}
}
# CSV ヘッダ順に対応
$folder = $fields[0]
$idref = $fields[1]
$rights = $fields[2]
$cate = $fields[3]
$type = $fields[4]
if ([string]::IsNullOrWhiteSpace($folder)) { continue }
# ===== 階層計算 =====
$relative = $folder
if ($relative -match '^[A-Za-z]:\\') {
$relative = $relative.Substring(3)
}
elseif ($relative.StartsWith('\')) {
$relative = $relative.TrimStart('\')
}
$levels = if ($relative) { $relative.Split('\').Count } else { 0 }
$indent = (" " * 4) * $levels
# ===== フォルダ見出し =====
if ($folder -ne $previousFolder) {
if ($previousFolder) { $sw.WriteLine() }
$sw.WriteLine("$indent$folder")
$previousFolder = $folder
}
# ===== ACL 行 =====
$sw.WriteLine("$indent ├─ $idref | $rights | $cate | $type")
}
Write-Host "処理完了: $OutPath"
}
catch {
Write-Error "処理中にエラー発生: $_"
}
finally {
$sw.Dispose()
$parser.Close()
}