PowerShellでSystem.IO.FileSystemInfoのisInheritanceBlockedが表示されない原因と対処法|ScriptPropertyと型拡張

PowerShellでフォルダ一覧を取得すると、同僚は「isInheritanceBlocked」プロパティが使えるのに自分では見えない――この差は権限ではなく、モジュールや型拡張(ScriptProperty)の読み込み差で起きることがほとんどです。原因の特定と揃え方を手順付きで解説します。

目次

起きている現象を正しく言語化する

質問で出てくるコードは、次のようなものです。

$folders = Get-ChildItem -Directory -Path "C:\Folders"
$folders[0]

ここで得られる $folders[0] は通常 System.IO.DirectoryInfoSystem.IO.FileSystemInfo 派生)です。問題は「同僚のPowerShellでは $folders[0].isInheritanceBlocked が補完され、値も取れるのに、自分のセッションではメンバーとして存在しない」ことです。

まず重要なのは、これはWindowsの管理者権限(ローカル管理者)とは別の層で起きることが多い、という点です。PowerShellの表示やメンバーは、実体の.NETプロパティに加えて、PowerShellの拡張(Extended Type System: ETS)で“後付け”されるからです。

結論:isInheritanceBlockedは標準プロパティではなく、型拡張で追加されたScriptProperty

System.IO.FileSystemInfoSystem.IO.DirectoryInfo 自体には、標準で isInheritanceBlocked という.NETプロパティはありません。多くのケースでこれは、次のどれかによってScriptProperty(スクリプトプロパティ)として追加されています。

  • PowerShellモジュールの Types.ps1xml(TypesToProcess)
  • Update-TypeData によるセッション内の型データ更新
  • ユーザープロファイル($PROFILE)での自動読み込み

そのため、同じPCでも「同僚のユーザーだけ」特定モジュールを入れていたり、プロファイルで型拡張を読み込んでいたりすると、同僚にだけ isInheritanceBlocked が見えるという差が生まれます。特に、NTFS権限を扱うモジュール(例:NTFS権限管理系のサードパーティモジュール)が、DirectoryInfo に「継承がブロックされているか」を返すScriptPropertyを足しているパターンがよくあります。

最初の確認:本当に“存在しない”のか、“表示されていないだけ”なのか

PowerShellはオブジェクトをそのまま出力しても、表示(フォーマット)上は一部の既定プロパティしか見せません。「画面に出ない」だけで、メンバー自体は存在することもあります。まずは Get-Member で事実を確定させます。

確認したいことコマンド例分かること
isInheritanceBlockedがメンバーとして存在するか$folders[0] | Get-Member -Name isInheritanceBlocked存在すれば MemberType が ScriptProperty 等で表示される
隠しメンバー含めて確認する$folders[0] | Get-Member -Force | ? Name -eq 'isInheritanceBlocked'通常見えないメンバーまで含めた一覧で確認できる
表示定義だけが拡張されていないか$folders[0] | Format-List *表示上の列が増えただけか、実体のメンバーがあるかの切り分けに使える
プロパティ候補の補完が効くか$folders[0].<TAB>存在すれば候補に出る(環境差の把握に便利)

ここでGet-Memberでも出てこないなら、あなたのセッションにはその型拡張が入っていません。以降は「なぜ入っていないか」「どう揃えるか」を詰めます。逆に Get-Member で出るのに画面表示に出ないだけなら、Select-Object で明示して使えばOKです(表示定義の差に過ぎません)。

環境差チェック:同じPowerShellを見ているか

同じPCでも、同僚は Windows PowerShell 5.1、自分は PowerShell 7(pwsh)ということは珍しくありません。モジュールが片方にしか対応していないと、型拡張も片方にしか入らず、結果としてプロパティが見えません。まずは両者で次を比較します。

$PSVersionTable | Format-List PSVersion, PSEdition, OS, Platform
$Host.Name
[Environment]::Is64BitProcess
差が出やすいポイント影響
PowerShellの世代Windows PowerShell 5.1 / PowerShell 7.x読み込めるモジュール、プロファイル、モジュール配置先が変わる
32/64bitSysWOW64側で起動しているPSModulePathが違い、モジュールが見つからないことがある
プロファイルの実行有無-NoProfileで起動しているプロファイルでImportしている型拡張が入らない
実行ポリシーAllSigned / RemoteSigned などプロファイルやスクリプトが途中で失敗し、結果としてImportが走らないことがある

同僚が「通常のWindows PowerShell」を開いており、あなたが「pwsh」を開いている場合は、まず同僚と同じホストで再現するか、両方に同じモジュールを入れる方針を取るのが近道です。

原因の本命:同僚だけがモジュールをCurrentUserスコープで入れている

PowerShellモジュールは、インストール先(スコープ)によって「そのユーザーにだけ見える」状態になり得ます。典型的には次の配置です。

PowerShellスコープ代表的なパス説明
Windows PowerShell 5.1AllUsersC:\Program Files\WindowsPowerShell\Modules全ユーザーから参照可能
Windows PowerShell 5.1CurrentUserC:\Users\(ユーザー)\Documents\WindowsPowerShell\Modulesそのユーザーだけが参照可能
PowerShell 7AllUsersC:\Program Files\PowerShell\Modules全ユーザーから参照可能
PowerShell 7CurrentUserC:\Users\(ユーザー)\Documents\PowerShell\Modulesそのユーザーだけが参照可能

同僚が Install-Module -Scope CurrentUser で入れていれば、あなたのアカウントの PSModulePath にはその場所が存在しないため、同じPCでもモジュールが見つからず、結果として型拡張が読み込まれません。「同じマシンで両者ともローカル管理者」でも、ユーザーのドキュメント配下に置かれたモジュールは他ユーザーからは参照できないのがポイントです。

同僚の環境で「どのモジュールが追加しているか」を特定する

isInheritanceBlockedを提供している“元”が分からないと、揃え方が決まりません。特定の近道は「型データ(TypeData)を辿る」「モジュールのTypes.ps1xmlを探す」の二段構えです。

Get-TypeDataで型拡張の有無を確認する

同僚のセッションで、対象型に型拡張が入っているか確認します。DirectoryInfoFileSystemInfo の両方を見てください(どちらに定義されているかは実装次第です)。

Get-TypeData -TypeName System.IO.DirectoryInfo
Get-TypeData -TypeName System.IO.FileSystemInfo

さらに、メンバー一覧から目的のプロパティを絞り込みます。

(Get-TypeData -TypeName System.IO.DirectoryInfo).Members.GetEnumerator() |
  Where-Object Key -eq 'isInheritanceBlocked' |
  ForEach-Object { $_.Value }

ここでScriptPropertyとして定義が出れば、「型拡張として追加されている」ことが確定します。逆に何も出ないなら、Format.ps1xml(表示定義)側の拡張で、見た目だけ追加されている可能性もあります。

モジュール内のTypes.ps1xmlを“文字列検索”で突き止める

現場で一番強いのは、シンプルに「isInheritanceBlocked」という文字列をモジュール配下から検索する方法です。同僚のアカウントで次を実行します(管理者でなくても可)。

$pattern = 'isInheritanceBlocked'
$paths = $env:PSModulePath -split ';' | Where-Object { Test-Path $_ }

Get-ChildItem -Path $paths -Recurse -File -ErrorAction SilentlyContinue |
  Where-Object { $_.Extension -in '.ps1', '.psm1', '.psd1', '.ps1xml' } |
  Select-String -Pattern $pattern -SimpleMatch -List |
  Select-Object Path

結果に Types.ps1xml やモジュールのソースが出てきたら、その親フォルダ名がモジュール名のヒントになります。モジュールが分かったら、次で場所とバージョンも押さえます。

# 例:候補モジュールが分かったら
Get-Module -ListAvailable -Name YourModuleName | Select-Object Name, Version, Path

自分のセッションで“何が足りないか”を比較する

原因がモジュール差なら、比較が早いです。両者で次を取り、差分を確認します。

読み込まれているモジュール(セッション内)を比較する

Get-Module | Sort-Object Name | Select-Object -ExpandProperty Name

同僚にだけ存在するモジュール名が見つかったら、そのモジュールが型拡張を入れている可能性が高いです。ただし、型拡張が「プロファイルでUpdate-TypeData」されている場合は、モジュール一覧だけでは見つからないこともあります。

利用可能なモジュール(インストール済み)を比較する

Get-Module -ListAvailable | Sort-Object Name | Select-Object -ExpandProperty Name

この一覧に同僚のモジュールが出ず、自分には当然ないなら、同僚のCurrentUser領域にだけ入っていると判断できます。

差分を自動で出す(Compare-Object)

口頭でリストを見比べるより、機械的に差分を出す方が確実です。例えば、両者がそれぞれ自分のアカウントで以下を実行し、結果をファイルに保存して比較します。

# それぞれのユーザーで実行して保存
Get-Module -ListAvailable |
  Sort-Object Name |
  Select-Object -ExpandProperty Name |
  Set-Content -Encoding UTF8 "$env:TEMP\modules.txt"

次に、同僚側の modules.txt を受け取り、自分のファイルと比較します。

$mine   = Get-Content "$env:TEMP\modules_mine.txt"
$other  = Get-Content "$env:TEMP\modules_other.txt"
Compare-Object -ReferenceObject $mine -DifferenceObject $other

=> が付く行が「同僚にはあるが自分にはない」候補です。ここから型拡張を入れているモジュールを絞り込みます。

PSModulePathを比較する

モジュールが見える/見えないの根本は検索パスです。次で比較します。

$env:PSModulePath -split ';'

同僚のパスに「同僚のドキュメント配下」が含まれていても、あなたのパスには含まれません(ユーザーが違うので当然です)。したがって、同僚がCurrentUserに入れたモジュールはあなたのセッションから見えません。

解決策:モジュールをAllUsersで入れる(推奨)

チーム内で同じ挙動に揃えたいなら、型拡張を提供しているモジュールをAllUsersに配置するのがシンプルです。PSGallery等から入れるケースを例にします。

管理者PowerShellでAllUsersにインストールする

# 管理者として実行したPowerShellで
Install-Module -Name YourModuleName -Scope AllUsers

既に同僚がCurrentUserに入れている場合でも、AllUsersに入れ直せば他ユーザーも参照できるようになります。インストール後、自分のアカウントで確認します。

Import-Module -Name YourModuleName
$folders = Get-ChildItem -Directory -Path "C:\Folders"
$folders[0] | Get-Member -Name isInheritanceBlocked

ここで MemberTypeScriptProperty として出れば成功です。もしモジュールが自動で読み込まれる設計なら、Import-Module なしでも、該当コマンド実行をきっかけに読み込まれる場合もあります。ただし型拡張だけを期待しているなら、明示Importしておく方が事故が減ります。

インストール前後で確認しておくと安全なこと

観点コマンド目的
リポジトリの確認Get-PSRepositoryどこからモジュールを取得する設定になっているか把握する
インストール済みバージョン確認Get-InstalledModule -Name YourModuleName端末ごとのバージョン差を避ける(同僚と揃える)
読み込みエラー確認Import-Module YourModuleName -Verbose依存関係不足や互換性問題を早期発見する

AllUsersに入れられない場合の現実的な代替

  • あなたのアカウントにもCurrentUserで同じモジュールを入れる
  • 同僚のモジュールフォルダを配布して、あなたのCurrentUser配下に配置する(オフライン環境向け)
  • 社内リポジトリ(PSRepository)を用意し、承認済みモジュールだけを配布する

「PCは同じだがユーザーが違う」ケースでは、AllUsers配置にしておくと、将来の引き継ぎやRDP先でも再現性が上がります。

もう一つの原因:プロファイル($PROFILE)で同僚だけが読み込んでいる

同僚が毎回モジュールをインポートしている覚えがないのにプロパティが見えるなら、プロファイルに仕込みがある可能性があります。PowerShellのプロファイルは複数種類あり、どれが適用されるかはホストとEditionで変わります。

種類参照用途
CurrentUserCurrentHost$PROFILE.CurrentUserCurrentHostそのユーザー+そのホスト限定
CurrentUserAllHosts$PROFILE.CurrentUserAllHostsそのユーザー+全ホスト共通
AllUsersCurrentHost$PROFILE.AllUsersCurrentHost全ユーザー+そのホスト限定
AllUsersAllHosts$PROFILE.AllUsersAllHosts全ユーザー+全ホスト共通(チーム標準化に向く)

同僚のプロファイルに、次のような記述があると、同僚だけ型拡張が入ります。

Import-Module YourModuleName
# または
Update-TypeData -TypeName System.IO.DirectoryInfo -MemberType ScriptProperty -MemberName isInheritanceBlocked -Value { ... }

プロファイルの場所は環境によって違うため、まずは「どこがプロファイルとして扱われているか」を両者で表示して一致/不一致を確認します。

$PROFILE | Format-List *

また、プロファイルが存在して実行されているかは次で確認できます。

Test-Path $PROFILE.CurrentUserAllHosts
Get-ExecutionPolicy -List

あなた側でも同じ挙動にしたいなら、社内運用として AllUsersAllHosts 側のプロファイルで共通化する、あるいはチーム配布のスクリプトを用意して毎回読み込む、という選択肢があります。

isInheritanceBlockedの実体を理解する:NTFS継承ブロックの判定はAreAccessRulesProtectedで代替できる

このプロパティ名から推測できる通り、対象は多くの場合「NTFSアクセス制御の継承がブロックされているか」です。WindowsのACLでは、継承の保護状態が AreAccessRulesProtected として取得できます。モジュールが何をしているか分からない時でも、次のように素のPowerShellだけで同等の判定ができます。

$dir = Get-Item "C:\Folders\Example"
$acl = Get-Acl -Path $dir.FullName
$acl.AreAccessRulesProtected   # True なら継承が保護(=継承ブロック)されていることが多い

つまり、今すぐに「同僚と同じプロパティ名が必要」という事情がなければ、Select-Object の計算プロパティで代替するのも現実的です。

Get-ChildItem -Directory -Path "C:\Folders" |
  Select-Object FullName,
    @{ Name = 'IsInheritanceBlocked'; Expression = { (Get-Acl $_.FullName).AreAccessRulesProtected } }

大量フォルダに対して何度も Get-Acl を呼ぶと遅くなるため、パフォーマンスが気になる場合は対象を絞る、または必要な時だけ評価する運用にしておくと安全です(ScriptPropertyはアクセスした瞬間に評価されるため、知らないうちに重い処理を踏むことがあります)。

どうしてもプロパティ名を揃えたい場合:自前でScriptPropertyを追加する

「既存スクリプトが .isInheritanceBlocked を前提にしている」「同僚の手元のレポートと同じ列名で揃えたい」といった事情があるなら、あなたの環境に同等のScriptPropertyを追加できます。最短は Update-TypeData です。

Update-TypeData -TypeName System.IO.DirectoryInfo `
  -MemberType ScriptProperty `
  -MemberName isInheritanceBlocked `
  -Value { (Get-Acl $this.FullName).AreAccessRulesProtected } `
  -Force

これで、そのセッション内では次が動くようになります。

$folders = Get-ChildItem -Directory -Path "C:\Folders"
$folders[0].isInheritanceBlocked

ただし、これはセッションを閉じると消えます。恒久化したい場合は、プロファイルに同じ Update-TypeData を書くか、Types.ps1xml を作って読み込む運用にします。

プロファイルに追記して恒久化する例

# 例:CurrentUserAllHosts のプロファイルに追記
# PowerShell起動時に毎回同じ型拡張を適用する
Update-TypeData -TypeName System.IO.DirectoryInfo `
  -MemberType ScriptProperty `
  -MemberName isInheritanceBlocked `
  -Value { (Get-Acl $this.FullName).AreAccessRulesProtected } `
  -Force

なお、環境によっては「プロファイルはあるが実行されていない」こともあります。例えば、ショートカットやタスクスケジューラで -NoProfile を付けて起動している場合です。チームで運用するなら、どの起動方法でも同じ結果になるように、スクリプト側で必要な型拡張を適用する設計が安全です。

トラブルシューティング集:うまく見えない時のチェックポイント

症状チェック対処
同僚と同じコマンドでもプロパティが出ない$PSVersionTable の差、$env:PSModulePath の差同じEditionで揃える/モジュールを両方に入れる
Import-Moduleしても出ないモジュールがTypesToProcessを持つか、読み込みエラーがないかImport-Module -Verboseで確認、モジュール更新
-NoProfileで起動すると出ないプロファイル依存の型拡張かAllUsersプロファイルに移す/運用を明文化する
タブ補完に出るが値取得で遅いScriptPropertyがGet-Aclを毎回実行している可能性必要な時だけ評価、計算プロパティで一括取得
GUIでは継承OFFなのにFalseになる対象が共有/再解析ポイント、権限不足、例外処理不足対象パスを絞る、例外処理を入れる、管理者実行で検証

チームでの再発防止:環境を揃える運用設計

「同じスクリプトが人によって動いたり動かなかったり」を防ぐには、次のどれかを決めておくと安定します。

  • 依存モジュールを明記し、AllUsersに入れる(端末標準化)
  • スクリプト冒頭で Import-Module を明示し、無ければエラーにする(再現性優先)
  • 社内リポジトリで承認済みモジュールだけ配布し、バージョンを固定する(監査対応)
  • プロファイルに依存しない(-NoProfileでも動く)設計にする

特に「ScriptPropertyがある前提」でレポートや監視を作る場合、プロファイル依存にしてしまうと、別端末・別ユーザー・タスクスケジューラ実行で破綻しやすいです。スクリプト側で必要なモジュールを読み込むか、代替ロジック(Get-Acl)を持たせると、運用事故が減ります。

まとめ

isInheritanceBlocked が自分にだけ表示されないのは、ローカル管理者かどうかではなく、PowerShellの型拡張(ETS)を提供しているモジュールやプロファイルが同僚のユーザー環境にだけ入っている可能性が高いからです。まず Get-Member で存在を確認し、Get-TypeData と文字列検索で提供元を突き止め、モジュールをAllUsersに入れるか、必要なら自前で Update-TypeData を適用して揃える――この流れで解決できます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次