PowerShell のスクリプトレットでエラーを無視して処理したい

 

継承されていないアクセス権があるフォルダを一覧にするスクリプトレット

いきなり結論

Get-ChildItem -path ¥¥foo.bar.com¥baz -Force -Recurse -Depth 6 | Where {$_.mode -match "d"} | %{Get-Acl -LiteralPath $_.FullName | Where {$_.Access.IsInherited -like "False"} | Select-Object @{Label="Path"; Expression={Convert-Path $_.Path}}, AccessToString | Export-CSV acl.csv -Append -Encording Default}

部のシステム管理者という存在

私の会社では全社のシステムはシステム管理部門が管理してくれるのですが、部内で使っているファイルサーバの運用は部の担当者に任されています。素人が雑用として管理するので、質は推して知るべしです。

引き継いだ部内ファイルサーバの中のフォルダのアクセス権がめちゃくちゃでした

部内ファイルサーバの管理が私に回ってきました。このファイルサーバは部全員がフルアクセス権限を持っています、というか特に気にしていなかったのです。それでちょっと知っている人が各フォルダのアクセス権を適当に変更していたので、「xx限定フォルダ」「外部署との共有フォルダ」が乱立していました。代謝した人や、無効なセキュリティグループのアクセス権も大量に残っていました。なにはともあれ各フォルダのアクセス権の一覧表が必要ですが、私もITは本業ではないので、手抜きで済ませたいところです。

「フォルダ アクセス権 一覧」でググります

「フォルダ−アクセス権マップツール」でフォルダのアクセス権一覧を取得する − @IT

トップに出たのは2007年の記事です。マイクロソフトが「フォルダーアクセス権マップツール」を無償提供しているとありますがリンク切れです。他の検索結果を見たら今はPowerShellを使うようだと気が付きました

Windows Serverでフォルダのアクセス権一覧を出力する方法 | LifeKeyNotes

共有フォルダのNTFSアクセス権一覧をPowerShellで取得 - 雑記+備忘録

【PowerShell】フォルダアクセス権一覧を出力する方法

しかしこれらの記事にあったスクリプトレットは自分にアクセス権がないフォルダが一つでもあるとGet-Aclのエラーで止まってしまい、結果が全く出力されません。どのフォルダでエラーになっているのかもコンソールに表示されません。

エラー 回避 無視 継続 PowerShell Get-Acl error ignore skip continue handling access Error-Action ...

など単語を変えてググっても解決策が見つかりません。スクリプトを組んで try ... catch することも考えました。しかしスクリプトだと管理をさらに別の人に押し付けるときに実行ポリシーの設定などで苦労しそうです。

PowerShellスクリプトレットの途中でエラーが出ても処理を続ける

パイプの途中で ForEach-Object を使うことで解決しました。

Get-ChildItem | Get-Acl | Export-CSV

一つでもアクセスできないフォルダがあると全体が止まる。どのフォルダがアクセスできなかったのかはわからない。

Get-ChildItem ... | %{Get-Acl -LiteralPath | Export-CSV -Append}

アクセスできないフォルダがあっても処理は止まらない。コンソールにどのフォルダがアクセスできなかったのか、エラーが表示される。

アクセスできなかったフォルダがわかれば下記でなんとかできそうです。

PowerShell で ACE (ファイルの所有者) がないファイルを操作する - tech.guitarrapc.cóm

やっぱりアクセス権がないと所有者を変えられませんね。当たり前でしたが。

最後にスクリプトレットのパイプを一つずつ解説しておきます(備忘録)

Get-ChildItem -path ¥¥foo.bar.com¥baz -Force -Recurse -Depth 6

-path ¥¥foo.bar.com¥baz 以下にあるファイル・フォルダを取得する

-Force 隠しファイルを含む

-Recurse サブフォルダを含む

-Depth 6 6階層に制限する

| Where {$_.mode -match "d"}

$_ パイプラインに渡されたオブジェクトを表す変数。

modeプロパティを使ってフォルダだけを選択する。

| %{Get-Acl -LiteralPath $_.FullName

% ForEach-Objectのエイリアス。以降のパイプ全体を {} で囲んでおく

-LiteralPath Get-ChildItemをGet-Aclにパイプするとき、ForEach-Objectを使う場合は引数にパスを渡す必要がある。PowerShellを正規表現として使える反面、ファイル名にが入っているときに問題を生じるので、LiteralPathを使ってワイルドカードを無効にしておく

| Where {$_.Access.IsInherited -like "False"}

継承されていないアクセス権を持つACLAccess Control List)を選ぶ

| Select-Object @{Label="Path"; Expression={Convert-Path $_.Path}}, AccessToString

Select-Object CSVに書き出すオブジェクトを選択する 

@{ 連想配列

Convert-Path $_.Pathを相対パスから絶対パスに変換する

AccessToString ACLからアクセス情報を取得するスクリプトプロパティ。

| Export-CSV acl.csv -Append -Encording Default}

-Append ForEach-Object を使うので、一行ずつ追加するようにする