Skip to content

MS_PowerShellRemoting

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

PowerShellをリモートコンピューターで実行

概要

リモートコンピューターで PowerShell のコマンドを実行する方法についてまとめる。

invoke-command マシン名やSessionを指定 -scriptblock { ・・・コード・・・ }
invoke-command マシン名やSessionを指定 -filepath ファイルパス

※ Session を指定すれば処理中のジョブ等に再接続することも可能。

移行メモ(体裁): 移行元では上記の注記が # で始まっており、
PukiWiki 上は本文として扱われていた。
Markdown では見出し記法と衝突するため「※」に置き換えた。

補足(PowerShell リモート処理の全体像): 本ページは
Workgroup(非ドメイン)環境という最も面倒なケースを扱っている。
前提を整理しておくと、以降の手順の意味が分かりやすい。

【PSRemoting の基盤】★
   PowerShell Remoting
     └ WS-Management(WS-Man)… 標準プロトコル
         └ WinRM サービス       … Windows の実装
             └ HTTP:5985 / HTTPS:5986 ★
                (旧 80/443 から変更された)

【認証方式と難易度】★
   ・【ドメイン参加】環境
       → Kerberos で相互認証できる
       → 【設定はほぼ Enable-PSRemoting だけ】★
   ・【Workgroup】環境(本ページ)
       → Kerberos が使えず NTLM になる
       → サーバの真正性を確認できない
       → だから【TrustedHosts に手で登録する】必要がある
       → 本ページの手順が長いのはこのため

【もう一つの選択肢】
   ・【HTTPS(5986)+ 証明書】で構成する
     → TrustedHosts が不要になる ★
     → 証明書の配布・更新は必要
     → Workgroup で恒久運用するならこちらが本筋

詳細

Workgroup編

Invoke-Command を使って、リモートでコマンド実行ができます。

  • ただし、事前に構成を変更しておく必要があります。
  • また、実行する要件によっては追加の構成が必要です。

リモート先でリモートコマンドを呼ばれる準備 (PowerShellを 管理者として実行)

  1. PowerShell リモート処理を有効にする
Enable-PSRemoting

 Enable-PSRemoting は、PowerShell を使ってリモートからコマンド実行できるように、
以下の様な構成変更をまとめて行います。

  • WinRM サービスを起動します。
  • WinRM サービスのスタートアップの種類を [自動] に設定します。
  • 任意の IP アドレスで要求を受け入れるためのリスナーを作成します。
  • WS-Management 通信のファイアウォール例外を有効にします。
  • "Microsoft.PowerShell" セッション構成がまだ登録されていない場合は登録します。
  • "Microsoft.PowerShell32" セッション構成が 64 ビット コンピューターで
    まだ登録されていない場合は登録します。
  • 登録済みのすべてのセッション構成のセキュリティ記述子から
    "Deny Everyone" 設定を削除します。
  • WinRM サービスを再起動して、変更を有効にします。

解放しすぎる場合は(任意の IP アドレスで..など)、範囲を調整する必要があるか、検討ください。
リモートから実行の可否を制御するには、ユーザーと権限(後述)、または、
ファイアウォール例外("Windows リモート管理 (HTTP 受信)")のスコープ設定で行います。

移行メモ(体裁・誤字): 上記引用の 1 行目は移行元で
PukiWiki の打ち消し記法(%%...%%)が使われていたため、
GitHub の打ち消し記法(~~...~~)に変換した。
また「ファイルウォール例外」を「ファイアウォール例外」に修正した。

  1. PowerShell リモート処理可能なユーザーと権限の設定
Set-PSSessionConfiguration Microsoft.Powershell -ShowSecurityDescriptorUI

 実行すると権限を設定する画面が表示されるので、リモートから指定するユーザーと
権限を追加。
 (Administrators メンバー以外のユーザーを使用する場合に必要)

補足(Enable-PSRemoting は何を開けるのか): 上の一覧のうち、
セキュリティ上いちばん効くのはファイアウォール規則の範囲である。

【ファイアウォール規則のスコープ】★
   ・Enable-PSRemoting は
     「Windows リモート管理 (HTTP 受信)」を有効化する
   ・ネットワーク プロファイルが
     【パブリック】の場合、
     既定では【同一サブネットのみ】に制限される
     → Enable-PSRemoting -SkipNetworkProfileCheck で
       強制的に有効化できるが、
       【範囲が広がるので慎重に】★

   【範囲を絞る】
     Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP" `
       -RemoteAddress 10.0.0.0/24

【セッション構成(Endpoint)で絞る方が強力】★★
   ・【JEA(Just Enough Administration)】
     → 「このユーザーはこのコマンドだけ実行できる」
       という制限付きエンドポイントを作れる
     → 管理者権限を渡さずに
       特定の運用作業だけ委譲できる ★
     → Workgroup / ドメインどちらでも使える

       New-PSSessionConfigurationFile -Path .\ops.pssc `
         -SessionType RestrictedRemoteServer `
         -VisibleCmdlets 'Get-Service','Restart-Service'
       Register-PSSessionConfiguration -Name Ops -Path .\ops.pssc

   → 本ページの「権限を設定する画面」より
     【はるかに細かく制御できる】

リモートを呼び出す側の準備 (PowerShellを 管理者として実行)

  1. WinRM サービスを開始
Start-Service WinRM

 WinRM サービスを開始していなければ実行します。

  1. UAC のリモート設定
Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name LocalAccountTokenFilterPolicy -Value 1 -Type DWord

 管理者特権を維持するように設定変更します。
 これをしないと、下の wsman:localhost\client\trustedhosts の設定が出来ません。

  1. 対象のコンピュータを 信頼されたホストの一覧に追加
set-item wsman:localhost\client\trustedhosts -value リモートコンピュータ名

 リモートコンピュータ名 が複数ある場合は、
"リモートコンピュータ名,リモートコンピュータ名" の様に 文字列にして , で繋ぎます。
 既存の設定値に追加する場合は、get-item で値を確認し、 , で連結して設定します。

移行メモ(体裁・誤字): 移行元では上記 2 のコマンドが
「(上は、行を折り返しています、1行のコマンドです。)」という注記付きで
3 行に分けて記載されていたため、1 行のコマンドとして整形した
(本ページ内 2 箇所)。
また「依存の設定値に追加する場合」を「既存の設定値に追加する場合」に修正した。

※ LocalAccountTokenFilterPolicy と wsman:localhost\client\trustedhosts の関係について。

wsman:localhost\client\trustedhosts を設定するためには、以下の条件があります。

  • 管理者実行のコマンドプロンプト(PowerShell) で行う場合は、
    LocalAccountTokenFilterPolicy を 1 にする
  • ビルトイン Administrator で行う場合は、 LocalAccountTokenFilterPolicy の変更は不要
  • wsman:localhost\client\trustedhosts を設定した後は、
    LocalAccountTokenFilterPolicy を 0 に戻しても構いません。
    (LocalAccountTokenFilterPolicy を 0 に戻してもすぐには設定不可能に戻りません。
    その場合は WinRM サービスを再起動します。)

補足(LocalAccountTokenFilterPolicy の意味と危険性): このレジストリは
UAC のリモート制限を解除するもので、副作用が大きい

【既定の動作(UAC リモート制限)】★
   ・リモートからローカル アカウントで接続すると、
     Administrators のメンバーであっても
     【フィルタされた(標準ユーザー相当の)トークン】が
     与えられる
     → 管理者操作ができない
   ・例外は【ビルトイン Administrator(RID 500)】のみ
     → 原文の 2 番目の条件はこれを指している ★

【1 にすると何が起きるか】
   ・リモート接続で【完全な管理者トークン】が渡る
   ・つまり
     → 管理共有(\\host\C$)にアクセスできる
     → リモートでレジストリを操作できる
     → 【Pass-the-Hash 攻撃の横展開が容易になる】★★

【推奨】
   ・原文が書くとおり、
     【設定後は 0 に戻す】★
   ・恒久的に必要なら
     ドメイン参加 or HTTPS + 証明書を検討する
   ・そもそも
     【TrustedHosts に "*" を入れない】
     → 中間者攻撃を無条件に許すことになる ★

リモートでコマンドを実行

  1. Invoke-Command を実行
Invoke-Command -ComputerName リモートコンピュータ名 -Credential (get-credential) -ScriptBlock { リモートコマンド リモートコマンド引数... }

 (get-credential) によって、ユーザー名を入力する画面が開きます。
 -Credential (get-credential) を指定しないと、Invoke-Command を実行している
ユーザーで、リモートコンピュータへ接続し、コマンド実行されます。
 ユーザー名を入力したくない場合は、 get-credential を先に行って、変数に入れておくか、
-Credential を指定しないようにします。

注意事項ほか

  • PowerShell でリモート実行するコマンドから、
    ファイル共有などのネットワークリソースへアクセスする際は、匿名ユーザーが使われます。
    リモートのコマンド実行の中からネットワークリソースへユーザー指定でアクセスする場合は、
    後述のダブルホップ編を参照するか、
    sysinternals の psexec ツール を検討ください。
    ( psexec は サービス・タスク系のいろいろ も参照ください。)

移行メモ(誤字): 移行元の「ネットワーソース」を
「ネットワークリソース」に修正した。

  • Invoke-Command は リモートとの通信用に http や https も使用できます。
    それらの構成方法、オプションなどは、各コマンドを get-help で確認ください。

    • また、リモートとの通信に proxy を使用することもできます。
    • 必要であれば netsh コマンドなどで winhttp の proxy を設定する、
      通信できない場合に winhttp proxy が原因になっていないか確認ください。
  • Credential を指定しない場合は、PowerShell を実行しているアカウントが使われます。

    • Get-Credential はパスワード入力のためにポップアップ画面を表示します。
      バックグラウンド処理など、画面表示をしたくない場合には、
      パスワードを事前に暗号化してファイルに保存しておくこともできます。
    • パスワードをファイルに保存する方法、
      ファイルから取り出して Credential オブジェクトを作成する方法は、
      下のページを参考ください。

補足(パスワードのファイル保存は「同一ユーザー・同一マシン」限定):
この手法は便利だが、制約を知らずに使うと運用時に破綻する

【仕組み】★
   ConvertFrom-SecureString は
   既定で【DPAPI】(Data Protection API)を使う
     → 【現在のユーザー + 現在のマシン】に紐付いて暗号化

   # 保存
   (Get-Credential).Password |
       ConvertFrom-SecureString | Set-Content cred.txt
   # 復元
   $sec = Get-Content cred.txt | ConvertTo-SecureString
   $cred = New-Object PSCredential('user', $sec)

【破綻する場面】★★
   ・【別のユーザー】で実行すると復号できない
     → タスク スケジューラで
       別アカウントを指定した瞬間に失敗する ★
   ・【別のマシン】にコピーしても復号できない
   ・ユーザー プロファイルを作り直すと失われる

【-Key を使えば移植できるが】
   ConvertFrom-SecureString -Key $bytes
     → 鍵を渡せば他マシンでも復号できる
     → 【その鍵をどこに置くのか】という問題が残る
     → 本質的な解決にならない ★

【現在の推奨】★
   ・【SecretManagement / SecretStore】モジュール
       Install-Module Microsoft.PowerShell.SecretManagement
       Get-Secret -Name MyCred
     → Azure Key Vault / KeePass 等とも連携できる
   ・【gMSA(グループ管理サービス アカウント)】
     → パスワードを持たずに認証する(ドメイン環境)★
   ・証明書ベースの認証
   ・.NET Core 以降の PowerShell 7 では
     【DPAPI が非 Windows で使えない】点にも注意
  • Invoke-Command -ComputerName Server1,Server2,... の様に複数のサーバーを記述すると、
    それらのサーバーに対して同時実行します。
    実行結果は、PowerShell のオブジェクトとして、1つの配列に混合されて返ります。
    (cmd.exe など PowerShell でないコマンドを実行した場合は String(文字列) の配列になる。)
    オブジェクトには PSComputerName プロパティが付加されるので、
    サーバー別にソートしたり区別したいときは PSComputerName で判断できます。
    同時ではなく、順に実行したい場合は、-ComputerName に1つずつ指定します。

移行メモ(誤字): 移行元の「-ComputerName に1つつず指定します」を
「1つずつ指定します」に修正した。

補足(並列実行の上限とオブジェクトの制約): 複数サーバーへの同時実行は
強力だが、2 つの注意点がある。

【① 同時実行数の上限】★
   ・既定の【ThrottleLimit は 32】
   ・それを超える台数を指定すると
     順次キューイングされる
     → -ThrottleLimit で調整する
     → 上げすぎると呼び出し側が詰まる

【② 返るオブジェクトは「デシリアライズ済み」】★★
   ・リモートからの戻り値は
     XML でシリアライズされて運ばれる
     → 型名が【Deserialized.System.XXX】になる
     → 【メソッドが呼べない】(プロパティのみ)★

     $s = Invoke-Command -ComputerName X `
            -ScriptBlock { Get-Service W32Time }
     $s.Stop()      # ← 【失敗する】

   【対処】
     ・操作は【リモート側のスクリプト ブロック内で完結】させる
     ・戻すのは「結果の値」だけにする ★
【PowerShell 7 での改善】
   ・【ForEach-Object -Parallel】が追加された
     → ローカルの並列処理
   ・SSH ベースのリモーティングが使える ★
     Invoke-Command -HostName host -UserName user -SSHTransport
     → WinRM / TrustedHosts の設定が不要
     → Linux とも相互に接続できる
     → 【Workgroup 環境での有力な代替】★

Workgroup + ダブルホップ編

(ダブルホップについては ベース クライアント セキュリティ モデル を参照)

上の Workgroup 編では、リモートコマンドの中から、他サーバーのリソースにアクセスすると、
匿名ユーザーが使用されますが、
下の手順の様に、ダブルホップを使用することで、
-credential で指定するものと同じアカウントで 他サーバーのリソースに
アクセスすることができます。

補足(ダブルホップ問題とは): 本節の前提を明示しておく。

【ダブルホップ(二重ホップ)問題】★

   あなたのPC ──①認証──→ サーバA ──②?──→ サーバB
               (成功)           (失敗する)

   ・① で使った資格情報は
     サーバ A に【委任されない】
   ・サーバ A から B へ行こうとすると
     【匿名(NULL セッション)】になる
     → 「アクセスが拒否されました」

【なぜ既定で禁止されているのか】★
   ・資格情報を渡すと、
     サーバ A が【あなたになりすませる】
   ・A が侵害されていた場合、
     被害が B 以降へ無制限に広がる
   → だから【明示的に有効化する仕組み】になっている

【解決手段の比較】★
   ・【CredSSP】(本ページの手法)
       → 資格情報を【平文相当で A に渡す】
       → 実装が簡単だが【最も危険】★
       → Microsoft も「他に手段がない場合のみ」と注意喚起
   ・【Kerberos の制約付き委任】
       → ドメイン環境でのみ可能
       → 【委任先を限定できる】ので安全 ★
       → 可能ならこちらを選ぶ
   ・【リソース ベースの制約付き委任(RBCD)】
       → さらに新しい方式。委任される側が許可する
   ・【そもそも二重ホップしない設計にする】★★
       → B の処理を
         「A から呼ぶ」のではなく
         「手元から直接 B に対して行う」
       → 二段階のスクリプトに分ける
       → 最も安全で、最も見落とされがちな選択肢

準備 (上の Workgroup編と共通)

  • リモート先でリモートコマンドを呼ばれる準備 (PowerShell を 管理者として実行)
# 1.
Enable-PSRemoting

# 2.
Set-PSSessionConfiguration Microsoft.Powershell -ShowSecurityDescriptorUI
  • リモートを呼び出す側の準備 (PowerShell を 管理者として実行)
# 1.
Start-Service WinRM

# 2.
Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name LocalAccountTokenFilterPolicy -Value 1 -Type DWord

# 3.
set-item wsman:localhost\client\trustedhosts -value リモートコンピュータ名

ダブルホップのための 追加の準備

  • リモート先で資格情報を受け取る準備 (PowerShell を 管理者として実行)
# 1.
Enable-WSManCredSSP -Role Server
  • リモートを呼び出す側で、資格情報を送信する準備 (PowerShell を 管理者として実行)
# 2.
Enable-WSManCredSSP -Role Client -DelegateComputer リモートコンピュータ名
  • リモートを呼び出す側で、資格情報を NTLM 認証で行う様にグループポリシーを設定
# 3. gpedit.msc コマンド
コンピューターの構成 >> 管理用テンプレート >> システム >> 資格情報の委任
   NTLMのみのサーバー認証で新しい資格情報の委任を許可する
     (x) 有効
     サーバーを一覧に追加: [表示]
       wsman/リモートコンピュータ名

ダブルホップを使用した、リモートコマンドの実行

リモートでコマンドを実行

  1. Invoke-Command に -Authentication CredSSP を付けて実行
Invoke-Command -ComputerName リモートコンピュータ名 -Credential (get-credential) -ScriptBlock { cmd.exe /c "dir \\リソースサーバー\ファイル共有" } -Authentication CredSSP

(-ScriptBlock { ... } の中は例です。)

移行メモ(誤字・体裁): 移行元の「-Authentication CredSSP を付けて実行」の
重複した助詞を整理した。
また折り返し注記付きで複数行に分かれていたコマンドを 1 行に整形した。

補足(CredSSP の既知の脆弱性): この手法を使う前に、
必ず知っておくべき事故がある。

【CVE-2018-0886(CredSSP の暗号化オラクル脆弱性)】★★
   ・2018年3月に公開された【リモートコード実行】の脆弱性
   ・中間者が CredSSP セッションに介入し、
     委任された資格情報を悪用できた
   ・修正後、Microsoft は
     【既定のポリシーを「緩和済みクライアントのみ許可」に変更】
     → 【パッチ適用状況が食い違うと接続できなくなる】★
     → 「昨日まで動いていた RDP / CredSSP が
       突然エラーになる」の原因として有名

   ・回避のためにレジストリで
     AllowEncryptionOracle=2(脆弱)に戻す手順が
     ネット上に多数あるが、
     【絶対に行ってはならない】★★
     → 双方にパッチを当てるのが正解

【CredSSP を使う場合の最低限の防御】
   ・【HTTPS(5986)で使う】★
     → 平文の HTTP で CredSSP は避ける
   ・-DelegateComputer に【ワイルドカード(*)を使わない】
   ・使い終わったら
       Disable-WSManCredSSP -Role Client
       Disable-WSManCredSSP -Role Server
     で【必ず無効化する】★
   ・委任するアカウントの権限を最小にする
     → 原文の注意事項(Administrators でなくてよい)は
       まさにこの趣旨であり、適切な助言である ★

注意事項ほか

'-credential で指定する ユーザー について

-credential で指定する ユーザーおよびパスワード は、
リモートコンピュータ名 と リソースサーバー の両方に共通なものを作成しておきます。
上の例で \\リソースサーバー\ファイル共有 の共有フォルダは、
指定する ユーザー で参照権限を設定しておきます。
リモートコンピュータ上のユーザーは、Administrators でなくてもかまいません。
リモートコマンドの内容に応じて 権限を付加してください。

移行メモ(リンク切れ): 本ページの technet.microsoft.com
2018〜2019 年に Microsoft Learn へ統合されており、到達しない。
記録として残す。


Tags: 移行, シェル, インフラストラクチャ, Windows

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally