PowerShellでWindowsフィーチャを効率的に管理する方法という問いには、候補FeatureNameを完全一致で読み、依存と再起動時間を承認してからWhatIfを経て一機能だけ変更するという方法で答えます。Get-WindowsOptionalFeatureとEnable-WindowsOptionalFeatureはWindowsイメージの機能を扱う。FeatureNameは設定画面の表示名と一致しない場合があり、機能によって再起動やソースが必要になる。この記事ではFeatureNameを主キーにState、RestartRequired、Windowsビルド、Online対象を記録するを判断軸にし、実行前の確認、記事固有のコード、合否判定、戻し方を一続きで示します。
アプリのインストールではなく、Windowsイメージに含まれるオプション機能を扱う。完了は「対象FeatureNameのStateが期待値となり、RestartRequiredを処理し、関連アプリの動作確認が終わる」と定義します。対象が取れない場合は「FeatureNameがない場合はOS SKUとビルド、別のWindows CapabilityかServer Roleかを確認する」として切り分け、推測で成功扱いにしません。
機能名と表示名を区別
候補FeatureNameを完全一致で読み、依存と再起動時間を承認してからWhatIfを経て一機能だけ変更する。Windowsオプション機能の管理ではこの進め方により、操作したという事実ではなく、期待する状態へ到達したかでタイトルの問いへ答えられます。アプリのインストールではなく、Windowsイメージに含まれるオプション機能を扱う。
機能名と表示名を区別の合格条件は、対象FeatureNameのStateが期待値となり、RestartRequiredを処理し、関連アプリの動作確認が終わることです。作業時刻、実行ユーザー、端末名を添え、判断に使った値が後から追える形にします。
Onlineイメージの現在Stateを取得
Onlineイメージの現在Stateを取得では、Windowsオプション機能の管理の対象を「FeatureNameを主キーにState、RestartRequired、Windowsビルド、Online対象を記録する」という単位で扱います。Get-WindowsOptionalFeatureとEnable-WindowsOptionalFeatureはWindowsイメージの機能を扱う。FeatureNameは設定画面の表示名と一致しない場合があり、機能によって再起動やソースが必要になる。対象が複数なら表示名の部分一致で先頭を採らず、一意になる条件を追加します。
Windowsオプション機能の管理を始める前に、PowerShellの版、コマンドの提供元、必要権限、管理ポリシーの有無を確認します。権限不足と対象なしは意味が異なるため、例外を0件へ置き換えません。
依存関係と再起動要否を確認
依存関係と再起動要否を確認は変更前の基準点です。FeatureNameを主キーにState、RestartRequired、Windowsビルド、Online対象を記録するを出力に含め、取得時刻と一緒に保存します。値だけを切り取ると別対象との比較になるため、識別列を省きません。
$featureName = 'Microsoft-Hyper-V-All'
$beforeRows = @(Get-WindowsOptionalFeature -Online -FeatureName $featureName -ErrorAction Stop)
if ($beforeRows.Count -ne 1) { throw "FeatureNameを一意にできません: $featureName" }
$beforeFeature = $beforeRows[0]
$beforeState = [string]$beforeFeature.State
if ($beforeState -notin @('Disabled','Enabled')) { throw "pending/unknown開始Stateでは操作しません: $beforeState" }
$os = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop
$featureBaseline = [pscustomobject]@{
FeatureName=[string]$beforeFeature.FeatureName; State=$beforeState
RestartRequired=[bool]$beforeFeature.RestartRequired; OsBuild=[string]$os.BuildNumber
BaselineBootUtc=$os.LastBootUpTime.ToUniversalTime().ToString('o'); CapturedUtc=(Get-Date).ToUniversalTime().ToString('o')
}
$featureBaseline | Format-List
Get-WindowsOptionalFeatureとEnable-WindowsOptionalFeatureはWindowsイメージの機能を扱う。FeatureNameは設定画面の表示名と一致しない場合があり、機能によって再起動やソースが必要になる。出力が多い場合も最初から無理に一件へ絞らず、候補数と除外理由を残してから対象を決めます。
WhatIfで一機能だけをプレビュー
WhatIfで一機能だけをプレビューでは、候補FeatureNameを完全一致で読み、依存と再起動時間を承認してからWhatIfを経て一機能だけ変更する。Windowsオプション機能の管理の例中にある名前、パス、ID、時刻はサンプルなので、そのまま本番へ貼らず、直前の読み取り結果から承認値を入れます。
if($beforeState -eq 'Enabled'){[pscustomobject]@{FeatureName=$featureName;Result='AlreadyEnabled';Changed=$false};return}
$checkpointPath='C:\ApprovedBackup\ittrip-feature-operation.json';$operationId=[guid]::NewGuid().ToString('D');$operationUtc=(Get-Date).ToUniversalTime();$operationBootUtc=(Get-CimInstance Win32_OperatingSystem -ErrorAction Stop).LastBootUpTime.ToUniversalTime()
$checkpoint=[ordered]@{Schema=1;OperationId=$operationId;Phase='PreApply';FeatureName=$featureName;BeforeState=$beforeState;ExpectedApplyState='Enabled';OsBuild=$featureBaseline.OsBuild;OperationUtc=$operationUtc.ToString('o');OperationBootUtc=$operationBootUtc.ToString('o');ApplyRestartRequired=$null;ApplyNoRestartConfirmed=$false;RestoreOperationUtc=$null;RestoreOperationBootUtc=$null;RestoreRestartRequired=$null;RestoreNoRestartConfirmed=$false}
$checkpoint|ConvertTo-Json -Depth 6;Write-Host "上のJSONをメモ帳で $checkpointPath へ手動保存します。既存fileは上書きしません。";Read-Host '保存後Enterでpre-apply checkpointを再読します'
$pre=Get-Content -LiteralPath $checkpointPath -Raw -ErrorAction Stop|ConvertFrom-Json;if([string]$pre.OperationId-ne$operationId -or [string]$pre.Phase-ne'PreApply' -or [string]$pre.FeatureName-ne$featureName -or [string]$pre.BeforeState-ne$beforeState){throw 'pre-apply checkpoint identityが一致しません。'}
$token="OPEN-WINDOWS-FEATURES $featureName OP=$operationId FROM=$beforeState";if((Read-Host "GUI手順を表示する場合は $token を入力")-ne$token){throw '中止しました。'}
$rebind=@(Get-WindowsOptionalFeature -Online -FeatureName $featureName -ErrorAction Stop);if($rebind.Count-ne1 -or [string]$rebind[0].State-ne$beforeState){throw 'GUI操作直前のfeature identity/stateが変わりました。'}
Write-Host 'Windowsの機能画面で同じFeatureだけを一回checkし、確定します。再起動要求の有無を正確に記録します。';$restartAnswer=Read-Host 'GUIが再起動を要求した場合は RESTART、明示的に不要だった場合は NO-RESTART を入力';if($restartAnswer-notin@('RESTART','NO-RESTART')){throw 'restart dispositionを確定できません。'}
$checkpoint.Phase='ApplyVerify';$checkpoint.ApplyRestartRequired=$restartAnswer-eq'RESTART';$checkpoint.ApplyNoRestartConfirmed=$restartAnswer-eq'NO-RESTART';$checkpoint.ApplyResultCapturedUtc=(Get-Date).ToUniversalTime().ToString('o');$checkpoint|ConvertTo-Json -Depth 6
Write-Host '上の更新済みJSONで同じcheckpointをメモ帳から手動置換します。';Read-Host '置換保存後Enterでhashを取得します';$saved=Get-Content -LiteralPath $checkpointPath -Raw -ErrorAction Stop|ConvertFrom-Json;if([string]$saved.Phase-ne'ApplyVerify' -or [bool]$saved.ApplyRestartRequired-ne($restartAnswer-eq'RESTART') -or [bool]$saved.ApplyNoRestartConfirmed-ne($restartAnswer-eq'NO-RESTART')){throw 'apply checkpoint更新が一致しません。'};Get-FileHash -LiteralPath $checkpointPath -Algorithm SHA256
一覧をパイプして全機能を有効・無効にしない。Hyper-V等は仮想化製品や起動構成へ影響する。Windowsオプション機能の管理でプレビュー対応コマンドを使える場合はWhatIfを先に実行し、非対応の操作は対象一覧と引数を画面へ出して人が承認してから一度だけ実行します。
Enabled以外の中間状態を読む
Enabled以外の中間状態を読むでは同じ対象を別経路でもう一度読みます。判定したいのは「コマンドが終了したか」ではなく、対象FeatureNameのStateが期待値となり、RestartRequiredを処理し、関連アプリの動作確認が終わるかどうかです。
$checkpointPath='C:\ApprovedBackup\ittrip-feature-operation.json';$expectedCheckpointSha256='PASTE_CURRENT_CHECKPOINT_SHA256'
if($expectedCheckpointSha256-like'PASTE_*'){throw '保存したcheckpoint SHA256へ置換してください。'};if((Get-FileHash -LiteralPath $checkpointPath -Algorithm SHA256 -ErrorAction Stop).Hash-ne$expectedCheckpointSha256){throw 'checkpoint hashが保存時と一致しません。'}
$cp=Get-Content -LiteralPath $checkpointPath -Raw -ErrorAction Stop|ConvertFrom-Json;if([int]$cp.Schema-ne1 -or [string]$cp.Phase-notin@('ApplyVerify','RestoreVerify')){throw 'checkpoint schema/phaseが不正です。'}
$currentOs=Get-CimInstance Win32_OperatingSystem -ErrorAction Stop;if([string]$currentOs.BuildNumber-ne[string]$cp.OsBuild){throw 'OS buildがoperation checkpointと一致しません。'};$currentBootUtc=$currentOs.LastBootUpTime.ToUniversalTime();$rows=@(Get-WindowsOptionalFeature -Online -FeatureName ([string]$cp.FeatureName) -ErrorAction Stop);if($rows.Count-ne1 -or [string]$rows[0].FeatureName-ne[string]$cp.FeatureName){throw 'Feature identityを一意に再読できません。'};$state=[string]$rows[0].State
if([string]$cp.Phase-eq'RestoreVerify'){
if([bool]$cp.RestoreRestartRequired){if($currentBootUtc-le([datetime]$cp.RestoreOperationBootUtc).ToUniversalTime() -or $currentBootUtc-le([datetime]$cp.RestoreOperationUtc).ToUniversalTime()){throw 'restore要求後の別起動境界がありません。'}}elseif(-not[bool]$cp.RestoreNoRestartConfirmed){throw 'restore no-restart assertionがcheckpointにありません。'}
$exact=$state-eq[string]$cp.BeforeState;[pscustomobject]@{Phase='RestoreVerify';OperationId=$cp.OperationId;FeatureName=$cp.FeatureName;Expected=$cp.BeforeState;Actual=$state;CurrentBootUtc=$currentBootUtc;Exact=$exact};if(-not$exact){throw 'standalone post-rollback verifierで元Stateを確認できません。'};return
}
if([bool]$cp.ApplyRestartRequired){if($currentBootUtc-le([datetime]$cp.OperationBootUtc).ToUniversalTime() -or $currentBootUtc-le([datetime]$cp.OperationUtc).ToUniversalTime()){throw 'apply要求後の別起動境界がありません。'}}elseif(-not[bool]$cp.ApplyNoRestartConfirmed){throw 'apply no-restart assertionがcheckpointにありません。'}
$applyExact=$state-eq[string]$cp.ExpectedApplyState;[pscustomobject]@{Phase='ApplyVerify';OperationId=$cp.OperationId;FeatureName=$cp.FeatureName;Expected=$cp.ExpectedApplyState;Actual=$state;RestartRequired=[bool]$cp.ApplyRestartRequired;NoRestartConfirmed=[bool]$cp.ApplyNoRestartConfirmed;Exact=$applyExact};if(-not$applyExact){throw 'apply Stateが期待値に一致しません。'}
$restoreToken="OPEN-WINDOWS-FEATURE-RESTORE $($cp.FeatureName) OP=$($cp.OperationId) TO=$($cp.BeforeState)";if((Read-Host "手動復元手順を表示する場合だけ $restoreToken を入力。不要ならEnter")-eq$restoreToken){
$rebind=@(Get-WindowsOptionalFeature -Online -FeatureName ([string]$cp.FeatureName) -ErrorAction Stop);if($rebind.Count-ne1 -or [string]$rebind[0].State-ne[string]$cp.ExpectedApplyState){throw 'restore直前のfeature identity/applied stateが一致しません。'};$cp.Phase='RestoreVerify';$cp.RestoreOperationUtc=(Get-Date).ToUniversalTime().ToString('o');$cp.RestoreOperationBootUtc=$currentBootUtc.ToString('o')
Write-Host 'Windowsの機能画面で同じFeatureだけを一回uncheckし確定します。';$answer=Read-Host 'restoreで再起動要求なら RESTART、明示的に不要なら NO-RESTART を入力';if($answer-notin@('RESTART','NO-RESTART')){throw 'restore restart dispositionが不明です。'};$cp.RestoreRestartRequired=$answer-eq'RESTART';$cp.RestoreNoRestartConfirmed=$answer-eq'NO-RESTART';$cp|ConvertTo-Json -Depth 7;Write-Host '上のRestoreVerify JSONへcheckpointをメモ帳で手動置換します。';Read-Host '保存後Enterで新hashを取得します';$saved=Get-Content -LiteralPath $checkpointPath -Raw -ErrorAction Stop|ConvertFrom-Json;if([string]$saved.Phase-ne'RestoreVerify' -or [bool]$saved.RestoreRestartRequired-ne($answer-eq'RESTART')){throw 'restore checkpoint更新が一致しません。'};Get-FileHash -LiteralPath $checkpointPath -Algorithm SHA256;Write-Host '必要なら再起動後、新hashを貼り、このstandalone blockを再実行します。RestoreVerify branchはEnabledを先に要求せず元Stateを検証します。'
}
FeatureNameがない場合はOS SKUとビルド、別のWindows CapabilityかServer Roleかを確認する。Windowsオプション機能の管理の期待値と実測値が一致しないときは追加変更を重ねず、対象識別、権限、ポリシー、時間差の順で原因を分けます。
ソース不足や保留中で止める
一覧をパイプして全機能を有効・無効にしない。Hyper-V等は仮想化製品や起動構成へ影響する。ソース不足や保留中で止めるに該当したら、警告を消して継続するのではなく、どの条件で止まったかを記録します。
FeatureNameがない場合はOS SKUとビルド、別のWindows CapabilityかServer Roleかを確認する。Windowsオプション機能の管理ではエラー本文、FullyQualifiedErrorId、対象ID、直前に成功した段階を残すと、別担当者が安全な地点から調査できます。
変更前Stateへ戻す計画
変更チケットに機能名、依存、再起動、ソース、ロールバック期限を記載する。Windowsオプション機能の管理を繰り返す場合は、正常、対象なし、要承認、失敗を異なる終了状態として記録し、前回値との比較だけで異常を決めません。
| 変更前Stateへ戻す計画の識別軸 | FeatureNameを主キーにState、RestartRequired、Windowsビルド、Online対象を記録する |
| 採用する実測 | 対象FeatureNameのStateが期待値となり、RestartRequiredを処理し、関連アプリの動作確認が終わる |
| 0件時の扱い | FeatureNameがない場合はOS SKUとビルド、別のWindows CapabilityかServer Roleかを確認する |
| 保留にする兆候 | 一覧をパイプして全機能を有効・無効にしない。Hyper-V等は仮想化製品や起動構成へ影響する |
Windowsオプション機能の管理の実行記録には、開始前の対象候補、採用した識別値、実行したコード、終了後の実測、除外した候補と理由を同じ作業番号で残します。特に「FeatureNameを主キーにState、RestartRequired、Windowsビルド、Online対象を記録する」を省くと、後日の再確認で別対象の値を比較するおそれがあります。画面コピーだけでなく、日時と端末名を含む構造化した出力も保存します。
PowerShellでWindowsフィーチャを効率的に管理する方法を定期手順へ組み込む場合も、初回は対話的に候補を確認します。正常時は「対象FeatureNameのStateが期待値となり、RestartRequiredを処理し、関連アプリの動作確認が終わる」、判定不能時は「FeatureNameがない場合はOS SKUとビルド、別のWindows CapabilityかServer Roleかを確認する」、中止時は「一覧をパイプして全機能を有効・無効にしない。Hyper-V等は仮想化製品や起動構成へ影響する」をそれぞれ別の結果として扱います。これにより、0件や例外を都合よく成功へ丸めず、次の担当者が同じ対象と条件で追試できます。
修正後コードの合格条件:正式FeatureNameと変更前Stateを保存し、WhatIf後に同じFeatureNameを入力した場合だけ一機能を有効化します。戻り値のRestartNeededと再取得Stateを確認し、EnabledまたはEnablePendingでなければ変更前がDisabledだった対象を無効化して中止します。
再修正後はWindows機能の有効化と復旧をtry/catchで扱い、保留状態は成功にしません。永続checkpointと再起動境界を使う別コードでEnabledまたは復旧済みDisabledを再取得して完了させます。
安全版 r4a-takeover-20260719:DISM cmdletによる自動有効化・自動無効化を廃止しました。FeatureName/State/OS buildを直前確認し、Windowsの機能画面で一回だけ手動変更します。pending時は再起動を独立工程とし、次回起動後にStateを読み取り、復元も同じGUIで手動実施します。
独立監査追補 r4a-r6-20260719:manual checkpointへApplyVerify/RestoreVerify phase、restart requiredまたは明示no-restart assertion、operation/boot時刻を保存します。standalone verifierはphaseを先に分岐し、restore後はEnabledを要求せず開始前Stateと必要な別起動境界を検証します。
公式情報・参考資料
Windowsオプション機能の管理で使うコマンド名、引数、対応環境は次のMicrosoft一次資料で確認しました。記事の確認日は2026年7月17日です。OSやモジュール更新後は、実行端末のGet-Helpと併せて再確認してください。

コメント