【bat】バッチファイルで複数PCに一括コマンドを送信する方法(psexec活用)

【bat】バッチファイルで複数PCに一括コマンドを送信する方法(psexec活用) bat

複数のWindowsマシンに対して同じコマンドを一括で実行したいとき、1台ずつリモートデスクトップで接続して作業するのは非効率です。10台、20台と台数が増えれば、その非効率さは倍増します。

PsExecを使えば、バッチファイルから複数PCへのリモートコマンド実行を自動化できます。ホスト一覧ファイルを読み込んで一括処理する、ファイルをリモートPCに転送して実行する、複数PCを並列で処理する——こうした操作をコマンドラインだけで実現できます。

この記事では、PsExecの基本から、エラーハンドリング・ログ設計・セキュリティ対策まで、実務で使える知識を体系的に解説します。パッチ適用、ソフトウェア配布、ログ収集の完全なバッチ例も掲載しています。

この記事で学べること

  • PsExecの仕組みと用途の整理
  • 環境準備とWMIファイアウォール設定
  • 主要オプション一覧(表形式)
  • ホスト一覧ファイルを使った一括同期実行
  • ファイルを配布して実行する -c オプションの活用
  • start /b と PowerShell Job による並列実行
  • System権限・認証情報の安全な扱い方
  • エラーハンドリングとログ設計のパターン
  • UAC・LocalAccountTokenFilterPolicy 対応
  • PowerShell Remoting との使い分け
  • パッチ適用・ソフト配布・ログ収集の完全バッチ例
スポンサーリンク
  1. PsExecとは?なぜバッチで使うのか
    1. PsExecが解決する課題
    2. 代表的な用途
  2. 前提と環境準備
    1. 必要なもの
    2. ファイアウォール設定(リモートPC側)
    3. PsExecの配置場所
  3. PsExecの主要オプション一覧
  4. 基本:単一PCへのリモートコマンド実行
    1. 最もシンプルな実行
    2. 戻り値でエラー判定
  5. ホスト一覧ファイルで一括実行
    1. ホスト一覧ファイルの作成
    2. @hostsfile オプションで一括実行
    3. for /f でホストを1台ずつ処理する(詳細版)
  6. ファイルを配布して実行(-c オプション)
    1. 基本的な -c の使い方
    2. ホスト一覧に -c で一括配布
  7. 並列実行の実装
    1. 方法1:start /b による並列実行
    2. 方法2:PowerShell Job による並列実行(推奨)
  8. System権限での実行(-s オプション)
  9. 認証情報の安全な扱い方
    1. 方法1:実行アカウントのパスワードを入力させる
    2. 方法2:ドメイン環境ではパスワードなしで実行
    3. 方法3:認証情報を環境変数ファイルから読み込む
  10. エラーハンドリングとログ設計
    1. ログ設計の基本パターン
  11. トラブルシューティング
  12. UAC・LocalAccountTokenFilterPolicy 対応
    1. LocalAccountTokenFilterPolicy の有効化
    2. ドメイン環境での推奨アプローチ
  13. PowerShell Remoting との使い分け
    1. PowerShell Remoting の基本例
  14. 実践:パッチ適用・ソフト配布・ログ収集の完全バッチ例
    1. 例1:Windowsパッチ適用スクリプト
    2. 例2:ソフトウェア一括配布・インストール
    3. 例3:ログ収集スクリプト
  15. まとめ

PsExecとは?なぜバッチで使うのか

PsExec は Microsoft Sysinternals Suite に含まれる軽量なリモート実行ツールです。インストール不要でSMBプロトコル(ポート445)を使い、リモートPCにコマンドを送信・実行できます。

PsExecが解決する課題

Windowsの標準機能でリモートコマンドを実行するには、PSRemoting(WS-Management)を有効化するか、タスクスケジューラのリモート設定が必要です。一方PsExecはエージェントのインストール不要で、SMBが通っていれば即座に使えます。

手段 事前準備 得意な用途 弱点
PsExec SMB有効・管理共有オン 単発コマンド・バッチ配布 出力取得が複雑
PowerShell Remoting WinRM有効化が必要 オブジェクト操作・並列処理 ポリシー設定が煩雑
タスクスケジューラ リモート管理許可が必要 定期実行・UI不要 即時実行に不向き
WMI/WMIC WMI有効・DCOM設定 情報収集・管理クエリ Win10以降では非推奨化

代表的な用途

  • セキュリティパッチの一括適用
  • 設定変更スクリプトの全端末配布
  • インベントリ情報(OS/IPアドレス/ソフト一覧)の収集
  • サービスの一括起動・停止・再起動
  • ログファイルの回収と集約

前提と環境準備

必要なもの

  • PsExec.exe(Sysinternals Suite からダウンロード)
  • 管理者権限のあるドメインアカウントまたはローカル管理者アカウント
  • リモートPC側でSMB(ポート445)が通っていること
  • 管理共有(ADMIN$)が有効であること

注意:PsExec は Sysinternals の公式サイト(https://learn.microsoft.com/ja-jp/sysinternals/downloads/psexec)またはwinget(winget install Microsoft.Sysinternals.PsExec)から取得してください。サードパーティサイトの配布物は改ざんリスクがあります。

ファイアウォール設定(リモートPC側)

firewall-setup.bat(管理者権限で実行)
REM SMB(ファイル共有)を許可
netsh advfirewall firewall set rule group="ファイルとプリンターの共有" new enable=yes

REM リモート管理を許可
netsh advfirewall firewall set rule group="Windowsの管理" new enable=yes

REM 管理共有(ADMIN$)が有効かを確認
net share ADMIN$ >nul 2>&1
if %ERRORLEVEL% neq 0 (
  echo ADMIN$ が無効です。レジストリで有効化してください。
  reg add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" ^
    /v AutoShareWks /t REG_DWORD /d 1 /f
)

PsExecの配置場所

PsExec.exe は PATH が通ったフォルダ(例:C:\Windows\System32\ または C:\Tools\)に配置するか、バッチファイルと同じディレクトリに置いて %~dp0PsExec.exe で参照するのが一般的です。

setup-path.bat
@echo off
set "PSEXEC=%~dp0PsExec.exe"

REM PsExec が存在するか確認
if not exist "%PSEXEC%" (
  echo エラー: PsExec.exe が見つかりません
  exit /b 1
)
echo PsExec: %PSEXEC%

PsExecの主要オプション一覧

オプション 説明 主な用途
\\コンピュータ名 接続先を指定 単一PC指定
\\* ドメイン内全PCに実行 ドメイン一括処理
@hostsfile ファイルからホスト一覧を読み込む 任意のPC群を対象に
-u ユーザー 接続ユーザー名を指定 別アカウントで実行
-p パスワード パスワードを指定(平文注意) 認証情報の指定
-s SYSTEM権限で実行 高権限処理、サービス操作
-c ローカルファイルをリモートへコピーして実行 スクリプト配布+実行
-f -c 時に既存ファイルを上書き 更新版スクリプトを再配布
-d プロセス終了を待たずに戻る 非同期・バックグラウンド実行
-i セッションID 指定セッションと対話(UI表示) ユーザーデスクトップへの表示
-n 秒数 接続タイムアウトを秒で指定 応答なしのPCをスキップ
-w 作業ディレクトリ リモートでの作業ディレクトリを指定 パスを明示して実行
-h 管理者昇格(UAC)を試みる Vista以降でのUAC回避
-accepteula ライセンスダイアログを非表示 自動化スクリプトで必須

ポイント:-accepteula は自動化スクリプトで必ず付けるべきオプションです。初回実行時にEULAダイアログが表示されると処理が止まります。

基本:単一PCへのリモートコマンド実行

最もシンプルな実行

単一PCにコマンドを送る
REM ipconfig を PC01 で実行し、結果を表示する
PsExec \\PC01 ipconfig /all

REM 別ユーザーで実行する
PsExec \\PC01 -u Administrator -p Pa$$w0rd ipconfig

REM タイムアウト付き(10秒以内に接続できなければエラー)
PsExec \\PC01 -n 10 -accepteula ipconfig

戻り値でエラー判定

PsExecは実行したコマンドの終了コードをそのままERRORLEVELとして返します。

single-remote.bat
@echo off
set "TARGET=PC01"

PsExec \\%TARGET% -accepteula cmd /c "ipconfig /all > C:\temp\ipconfig.txt"

if %ERRORLEVEL% neq 0 (
  echo エラー: %TARGET% への実行に失敗しました(コード: %ERRORLEVEL%)
  exit /b 1
)

echo %TARGET% の実行が完了しました
exit /b 0

注意:PsExec 自身の接続エラー(PCに到達できないなど)の終了コードは、実行したコマンドの終了コードと混在することがあります。PsExec固有のエラーコードは主に -1073741819(アクセス拒否)、1(接続失敗)などです。

ホスト一覧ファイルで一括実行

ホスト一覧ファイルの作成

@hostsfile オプションでホスト一覧ファイルを指定すると、ファイルに書かれたすべてのPCに対して順番にコマンドを実行します。

hosts.txt
PC01
PC02
PC03
192.168.1.101
192.168.1.102

ポイント:ホスト名またはIPアドレスを1行1エントリで記述します。# で始まる行はコメント扱いされません。コメント行を入れたい場合は後述のバッチ側でスキップ処理を追加してください。

@hostsfile オプションで一括実行

bulk-exec-simple.bat
@echo off
setlocal

set "HOSTS=%~dp0hosts.txt"
set "CMD=ipconfig /all"

if not exist "%HOSTS%" (
  echo エラー: %HOSTS% が見つかりません
  exit /b 1
)

echo 一括実行を開始します...
PsExec @"%HOSTS%" -accepteula %CMD%

echo 完了(終了コード: %ERRORLEVEL%)
endlocal

for /f でホストを1台ずつ処理する(詳細版)

@hostsfile では個別の成否を把握しにくいため、実務では for /f でホストを1つずつ処理してログを残す方式が使われます。

bulk-exec-detailed.bat
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "LOG=%~dp0result_%date:~0,4%%date:~5,2%%date:~8,2%.log"
set "PSEXEC=%~dp0PsExec.exe"
set OK_COUNT=0
set ERR_COUNT=0

echo [%date% %time%] 一括実行開始 >> "%LOG%"

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"
  echo [%time%] 処理中: !HOST!
  echo [%date% %time%] 開始: !HOST! >> "%LOG%"

  "%PSEXEC%" \\!HOST! -accepteula -n 10 ^
    cmd /c "ipconfig /all > C:\temp\result.txt 2>&1" >> "%LOG%" 2>&1

  if !ERRORLEVEL! neq 0 (
    echo [%date% %time%] 失敗: !HOST! (コード: !ERRORLEVEL!) >> "%LOG%"
    set /a ERR_COUNT+=1
  ) else (
    echo [%date% %time%] 成功: !HOST! >> "%LOG%"
    set /a OK_COUNT+=1
  )
)

echo [%date% %time%] 完了: 成功 !OK_COUNT! 件 / 失敗 !ERR_COUNT! 件 >> "%LOG%"
echo 完了: 成功 !OK_COUNT! 件 / 失敗 !ERR_COUNT! 件
endlocal
exit /b 0

ポイント:eol=# を指定すると # で始まる行をスキップできます。hosts.txt にコメントを書きたい場合は # このPCはテスト用 のように書いてください。

ファイルを配布して実行(-c オプション)

-c オプションを使うと、ローカルのバッチファイルやスクリプトをリモートPCにコピーしてから実行できます。リモートPC側に事前にファイルを置く必要がなく、配布と実行を1コマンドで行えます。

基本的な -c の使い方

deploy-and-run.bat
@echo off
setlocal

set "SCRIPT=%~dp0setup.bat"
set "TARGET=PC01"

REM setup.bat をリモートPCにコピーして実行
REM -f: 既存ファイルを上書き
PsExec \\%TARGET% -accepteula -c -f "%SCRIPT%"

if %ERRORLEVEL% neq 0 (
  echo エラー: %TARGET% でのスクリプト実行に失敗しました
  exit /b 1
)

echo %TARGET% へのデプロイが完了しました
endlocal

ホスト一覧に -c で一括配布

bulk-deploy.bat
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "SCRIPT=%~dp0patch-apply.bat"
set "LOG=%~dp0deploy.log"
set OK=0
set NG=0

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"
  echo 配布中: !HOST!

  PsExec \\!HOST! -accepteula -c -f -n 15 "%SCRIPT%" >> "%LOG%" 2>&1

  if !ERRORLEVEL! equ 0 (
    echo [OK] !HOST! >> "%LOG%"
    set /a OK+=1
  ) else (
    echo [NG] !HOST! >> "%LOG%"
    set /a NG+=1
  )
)

echo 配布結果: 成功 !OK! 件 / 失敗 !NG! 件
endlocal

注意:-c でコピーされたファイルはリモートPC上の一時フォルダ(通常 %WINDIR%\)に配置されます。実行後もファイルが残るため、機密情報を含むスクリプトは実行後に del で削除する後処理を追加することを推奨します。

並列実行の実装

デフォルトのPsExec一括実行は同期(直列)処理です。台数が多い場合は並列実行で大幅に時間を短縮できます。

方法1:start /b による並列実行

start /b でPsExecをバックグラウンド起動し、複数PCへの処理を同時進行させます。

parallel-start.bat
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "LOG_DIR=%~dp0logs"
set MAX_PARALLEL=5

if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"

set JOB_COUNT=0

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"
  echo 開始: !HOST!

  REM バックグラウンドで PsExec を起動、ログはホスト別ファイルへ
  start /b "" PsExec \\!HOST! -accepteula -n 15 ^
    cmd /c "ipconfig /all" > "%LOG_DIR%\!HOST!.log" 2>&1

  set /a JOB_COUNT+=1

  REM MAX_PARALLEL 件ごとに待機
  if !JOB_COUNT! geq %MAX_PARALLEL% (
    echo %MAX_PARALLEL% 件実行中 - 完了待ち...
    timeout /t 30 /nobreak >nul
    set JOB_COUNT=0
  )
)

echo すべてのジョブを発行しました。完了を待機中...
timeout /t 60 /nobreak >nul
echo 処理完了
endlocal

方法2:PowerShell Job による並列実行(推奨)

PowerShell の Start-JobWait-Job を使うと、ジョブの完了を確実に待機でき、各ジョブの成否も取得できます。

parallel-jobs.ps1
# PowerShell スクリプト: 並列 PsExec 実行
param(
  [string]$HostsFile = ".\hosts.txt",
  [int]$MaxJobs = 5
)

$hosts = Get-Content $HostsFile | Where-Object { $_ -notmatch '^#' -and $_.Trim() -ne ' }
$jobs = @()

foreach ($h in $hosts) {
  # 最大同時実行数に達したら待機
  while (($jobs | Where-Object { $_.State -eq 'Running' }).Count -ge $MaxJobs) {
    Start-Sleep -Seconds 2
  }

  $job = Start-Job -ScriptBlock {
    param($h)
    & ".\PsExec.exe" \\$h -accepteula -n 15 ipconfig /all
    return $LASTEXITCODE
  } -ArgumentList $h

  $jobs += [PSCustomObject]@{ Host = $h; Job = $job }
  Write-Host "ジョブ開始: $h"
}

# 全ジョブの完了を待機
$jobs.Job | Wait-Job | Out-Null

# 結果集計
foreach ($entry in $jobs) {
  $result = Receive-Job -Job $entry.Job
  $state = if ($entry.Job.ChildJobs[0].Output -ne $null) { "OK" } else { "NG" }
  Write-Host "[$state] $($entry.Host)"
  Remove-Job -Job $entry.Job
}
PowerShell スクリプトを呼ぶ bat
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0parallel-jobs.ps1" ^
  -HostsFile "%~dp0hosts.txt" -MaxJobs 5
exit /b %ERRORLEVEL%
方法 メリット デメリット 向いているシーン
start /b シンプル・純バッチで完結 完了待機が難しい 20台以下・監視不要
PowerShell Job 完了待機・結果収集が確実 PowerShell 知識が必要 台数多め・成否確認必要
@hostsfile コマンド1行で済む 逐次処理・個別ログなし 少数・動作確認用

System権限での実行(-s オプション)

-s オプションを指定すると、SYSTEM権限(最高権限)でコマンドを実行します。サービスの操作、レジストリ変更、ユーザー共通の設定変更などに使います。

system-run.bat
@echo off
setlocal

REM SYSTEM権限でサービスを再起動
PsExec \\PC01 -s -accepteula sc stop Spooler
PsExec \\PC01 -s -accepteula sc start Spooler

REM SYSTEM権限でレジストリを変更
PsExec \\PC01 -s -accepteula reg add ^
  "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" ^
  /v DisableWindowsUpdateAccess /t REG_DWORD /d 0 /f

endlocal

注意:SYSTEM権限はネットワーク資格情報を持ちません。ネットワーク先のリソース(\\server\share など)にはアクセスできません。ネットワークリソースを扱う場合は -u/-p で明示的にユーザーを指定してください。

認証情報の安全な扱い方

-p パスワード で平文パスワードをコマンドラインに書くのはセキュリティ上のリスクがあります。プロセスリストやログにパスワードが露出する可能性があります。

方法1:実行アカウントのパスワードを入力させる

interactive-auth.bat
@echo off
setlocal

REM パスワードを対話的に入力させる(ログに残らない)
set /p ADMIN_USER=管理者ユーザー名: 

REM PowerShell で安全にパスワード入力(エコーなし)
for /f %%P in ('powershell -command "$p=Read-Host -AsSecureString; [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($p))"') do (
  set "ADMIN_PASS=%%P"
)

PsExec \\PC01 -u %ADMIN_USER% -p "%ADMIN_PASS%" -accepteula ipconfig

REM 使用後にパスワード変数をクリア
set "ADMIN_PASS="
endlocal

方法2:ドメイン環境ではパスワードなしで実行

ドメイン参加PCで、実行するバッチ自体をドメイン管理者権限で起動すれば、-u/-p を省略できます。

domain-run.bat
@echo off
REM ドメイン管理者として実行している前提
REM -u/-p 不要。現在のログオンアカウントで認証される

PsExec \\PC01 -accepteula ipconfig

方法3:認証情報を環境変数ファイルから読み込む

auth-from-file.bat
@echo off
setlocal

REM 認証ファイル(ACL でアクセスを限定すること)
set "CRED_FILE=C:\secure\creds.env"

if not exist "%CRED_FILE%" (
  echo エラー: 認証ファイルが見つかりません
  exit /b 1
)

REM creds.env の内容: ADMIN_USER=domain\admin と ADMIN_PASS=xxxxx
for /f "usebackq eol=# tokens=1,* delims==" %%A in ("%CRED_FILE%") do (
  set "%%A=%%B"
)

PsExec \\PC01 -u "%ADMIN_USER%" -p "%ADMIN_PASS%" -accepteula ipconfig

REM 使用後に変数をクリア
set "ADMIN_PASS="
endlocal

注意:認証情報ファイルは NTFS ACL で管理者のみ読み取り可能に設定し、Gitなどのバージョン管理に含めないようにしてください。

エラーハンドリングとログ設計

ログ設計の基本パターン

実務運用では、実行日時・ホスト名・結果・終了コードを記録するログが不可欠です。

bulk-with-log.bat
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "LOGFILE=%~dp0exec_%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%.log"
set "LOGFILE=!LOGFILE: =0!"
set OK=0
set NG=0

REM ログヘッダー
(
  echo ================================================
  echo 実行日時: %date% %time%
  echo 実行者: %USERNAME%
  echo ================================================
) > "%LOGFILE%"

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"
  echo --- [%time%] 処理中: !HOST! --- >> "%LOGFILE%"

  PsExec \\!HOST! -accepteula -n 15 cmd /c "your-command.bat" >> "%LOGFILE%" 2>&1
  set RC=!ERRORLEVEL!

  if !RC! equ 0 (
    echo [PASS] !HOST! (RC=!RC!) >> "%LOGFILE%"
    set /a OK+=1
  ) else (
    echo [FAIL] !HOST! (RC=!RC!) >> "%LOGFILE%"
    set /a NG+=1
  )
)

REM サマリー
(
  echo ================================================
  echo 成功: !OK! 件 / 失敗: !NG! 件
  echo 終了日時: %date% %time%
  echo ================================================
) >> "%LOGFILE%"

echo 完了: 成功 !OK! / 失敗 !NG!  ログ: %LOGFILE%
if !NG! gtr 0 exit /b 1
exit /b 0
endlocal

トラブルシューティング

症状・エラー 原因 対処法
Access is denied (0x5) 権限不足 / UAC -h で昇格試行、または LocalAccountTokenFilterPolicy 設定(後述)
Could not connect to PC01 ファイアウォール / SMB無効 ポート445の疎通確認:Test-NetConnection PC01 -Port 445
The network path was not found ADMIN$ が無効 net share ADMIN$ で確認。AutoShareWks をレジストリで有効化
Logon failure パスワード誤り / ユーザー名誤り net use \\PC01\ADMIN$ /user:Administrator で接続テスト
EULAダイアログが出て止まる -accepteula 未指定 -accepteula を必ず付ける
コマンドの出力が文字化けする コードページの差異 cmd /c "chcp 65001 & your-cmd" でUTF-8に統一
終了コードが常に0になる cmd /c の外側でパイプを使っている パイプの最後のコマンドのERRORLEVELのみ返るため設計を見直す
PsExec がウイルスとして検出される セキュリティソフトの誤検知 Sysinternals 公式版を使い、ハッシュ値を確認。除外設定を追加

UAC・LocalAccountTokenFilterPolicy 対応

Windows Vista 以降、ローカルアカウントで管理共有(ADMIN$)にアクセスするとUACにより権限が制限されます(ネットワーク経由のローカル管理者は管理者権限が得られない仕様)。

LocalAccountTokenFilterPolicy の有効化

enable-uac-policy.bat(管理者権限で実行)
REM ローカル管理者がリモート管理共有に完全な権限でアクセス可能にする
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" ^
  /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f

echo LocalAccountTokenFilterPolicy を有効化しました
echo 変更を反映するにはPCの再起動が必要な場合があります

注意:LocalAccountTokenFilterPolicy を有効にするとセキュリティが低下します。ドメイン環境では代わりにドメイン管理者アカウントを使うことを強く推奨します。スタンドアロン環境での一時的な管理作業にのみ使用してください。

ドメイン環境での推奨アプローチ

環境 推奨する認証方法 理由
ドメイン参加PC ドメイン管理者アカウントで実行 UACの制限を受けない
スタンドアロンPC(同一PW) LocalAccountTokenFilterPolicy + -u/-p ローカル管理者でも管理共有にアクセス可能
ワークグループ -u Administrator -p パスワード 明示的に認証情報を渡す

PowerShell Remoting との使い分け

PsExec と PowerShell Remoting はどちらも複数PC管理に使えますが、得意分野が異なります。

観点 PsExec PowerShell Remoting
事前準備 SMB/ADMIN$ のみ WinRM 有効化 + ファイアウォール設定
並列実行 start /b や PS Job が必要 Invoke-Command -ComputerName で標準対応
出力形式 テキスト(文字列) .NETオブジェクト(構造化データ)
SYSTEM権限 -s で簡単に取得 追加の構成が必要
ファイル転送 -c で実行ファイルのみ Copy-Item で任意ファイルを転送可能
得意なシーン レガシー環境・既存バッチとの連携 モダン環境・構造化データ操作

ポイント:WinRMが使える環境なら Invoke-Command -ComputerName (Get-Content hosts.txt) -ScriptBlock { ... } -ThrottleLimit 5 で並列実行まで1行で書けます。新規構築する管理スクリプトなら PowerShell Remoting を優先検討してください。

PowerShell Remoting の基本例

invoke-command-bulk.ps1
# WinRM を使った並列リモート実行
$hosts = Get-Content ".\hosts.txt" | Where-Object { $_ -notmatch '^#' }

$results = Invoke-Command -ComputerName $hosts -ThrottleLimit 5 -ScriptBlock {
  # リモートで実行したい処理
  [PSCustomObject]@{
    Host = $env:COMPUTERNAME
    IP   = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -ne 'Loopback' }).IPAddress
    OS   = (Get-WmiObject Win32_OperatingSystem).Caption
  }
}

$results | Format-Table Host, IP, OS -AutoSize

実践:パッチ適用・ソフト配布・ログ収集の完全バッチ例

例1:Windowsパッチ適用スクリプト

patch-apply.bat(リモートPC上で実行されるスクリプト)
@echo off
setlocal

set "PATCH=\\fileserver\patches\security-2026-03.msu"
set "LOG=C:\temp\patch.log"

echo [%date% %time%] パッチ適用開始: %COMPUTERNAME% >> "%LOG%"

wusa "%PATCH%" /quiet /norestart >> "%LOG%" 2>&1
set RC=%ERRORLEVEL%

if %RC% equ 0 (
  echo [%date% %time%] 適用成功 >> "%LOG%"
) else if %RC% equ 2359302 (
  REM 0x240006: パッチが既に適用済み
  echo [%date% %time%] 適用済みのためスキップ >> "%LOG%"
  set RC=0
) else (
  echo [%date% %time%] 失敗 RC=%RC% >> "%LOG%"
)

endlocal
exit /b %RC%
run-patch-all.bat(管理PCから実行する親スクリプト)
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "SCRIPT=%~dp0patch-apply.bat"
set "LOG=%~dp0patch-result-%date:~0,4%%date:~5,2%%date:~8,2%.log"
set OK=0 & set NG=0 & set SKIP=0

echo パッチ一括適用を開始します
echo [%date% %time%] パッチ配布開始 > "%LOG%"

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"
  echo 処理中: !HOST!

  PsExec \\!HOST! -accepteula -c -f -s -n 30 "%SCRIPT%" >> "%LOG%" 2>&1
  set RC=!ERRORLEVEL!

  if !RC! equ 0 (
    echo [PASS] !HOST! >> "%LOG%"
    set /a OK+=1
  ) else (
    echo [FAIL] !HOST! RC=!RC! >> "%LOG%"
    set /a NG+=1
  )
)

echo [%date% %time%] 完了: OK=!OK! / NG=!NG! >> "%LOG%"
echo 完了 OK=!OK! NG=!NG! ログ: %LOG%
endlocal
exit /b !NG!

例2:ソフトウェア一括配布・インストール

deploy-software.bat
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "INSTALLER=\\fileserver\deploy\myapp-3.0.0.msi"
set "LOG=%~dp0deploy.log"

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"

  REM msiexec をサイレントインストール・再起動なし
  PsExec \\!HOST! -s -accepteula -n 60 ^
    msiexec /i "%INSTALLER%" /quiet /norestart ^
    /l*v "C:\temp\install-myapp.log" >> "%LOG%" 2>&1

  if !ERRORLEVEL! equ 0 (
    echo [OK] !HOST! >> "%LOG%"
  ) else (
    echo [NG] !HOST! RC=!ERRORLEVEL! >> "%LOG%"
  )
)
endlocal

例3:ログ収集スクリプト

collect-logs.bat
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "COLLECT_DIR=%~dp0collected\%date:~0,4%%date:~5,2%%date:~8,2%"

if not exist "%COLLECT_DIR%" mkdir "%COLLECT_DIR%"

for /f "usebackq eol=# tokens=*" %%H in ("%HOSTS%") do (
  set "HOST=%%H"
  echo ログ収集: !HOST!

  REM リモートのアプリログを一時フォルダにコピー
  PsExec \\!HOST! -accepteula -n 20 cmd /c ^
    "copy C:\app\logs\app.log C:\temp\!HOST!-app.log" >nul 2>&1

  REM UNCパス経由で収集用サーバーにコピー
  copy \\!HOST!\c$\temp\!HOST!-app.log "%COLLECT_DIR%\" >nul 2>&1

  if !ERRORLEVEL! equ 0 (
    echo [取得済] !HOST!
    REM 収集後に一時ファイルを削除
    PsExec \\!HOST! -accepteula del "C:\temp\!HOST!-app.log" >nul 2>&1
  ) else (
    echo [失敗] !HOST!
  )
)

echo 収集完了: %COLLECT_DIR%
endlocal

まとめ

PsExecを使ったバッチによる複数PCへの一括コマンド実行について、基本から実践まで解説しました。

用途 推奨パターン
少数PC・動作確認 PsExec @hosts.txt -accepteula コマンド
個別成否ログが必要 for /f + PsExec + ログ出力
スクリプト配布+実行 -c -f オプション
多台数を高速に処理 PowerShell Job 並列実行
SYSTEM権限が必要な処理 -s オプション
モダン環境・構造化データ PowerShell Remoting + Invoke-Command

PsExecの最大の強みはエージェント不要・SMBのみで動く手軽さにあります。一方で認証情報の管理や並列制御はバッチ側で工夫が必要です。レガシー環境との混在が多い現場ではPsExecが有効な選択肢です。WinRMが使える環境では、PowerShell Remotingへの移行も視野に入れて検討してください。