【bat】複数PCに一括コマンドを送る完全ガイド|PsExec・WinRM・WMI・OpenSSHの使い分けと実装パターン徹底解説

【bat】複数PCに一括コマンドを送る完全ガイド|PsExec・WinRM・WMI・OpenSSHの使い分けと実装パターン徹底解説 bat

社内の10台のPCにソフトウェア更新を配布したい、全サーバーのサービス状態を確認したい、特定のレジストリ値を一括変更したい——こうした作業を1台ずつRDPで接続して実行するのは時間がかかり、漏れも発生します。

バッチファイルとWindowsが標準で持つリモート実行の仕組みを組み合わせると、ホストリストを用意するだけで数十台に同じコマンドを一括送信できます。方法は1つではなく、環境やセキュリティ要件に応じて使い分けが必要です。

この記事では、PsExec・WinRM(PowerShell Remoting)・WMI・OpenSSH・schtasks方式の5つを比較し、それぞれのセットアップ・バッチ実装・エラー処理・ログ記録まで体系的に解説します。PsExecの詳細はバッチファイルで複数PCに一括コマンドを送信する方法(PsExec活用)で深く解説しているため、合わせて参照してください。

この記事で学べること
5つのリモート実行方式の比較と用途別の使い分け基準、ホストリストからfor /fでループして一括実行するパターン、PsExecで複数PCにコマンドを一括送信する基本と応用、WinRM(winrm quickconfig)のセットアップとバッチからの呼び出し方、WMI(wmic /node)でリモートPCの情報を取得・コマンドを実行する方法、OpenSSH(Windows標準機能)でSSH越しにコマンドを送る方法、エラー時の通知・ログ記録・セキュリティ対策の実装方法
スポンサーリンク

5つのリモート実行方式の比較

まず各方式の特徴を比較して、自環境に合った方法を選びましょう。

方式 必要なセットアップ 認証 向いている用途 注意点
PsExec PsExec.exeを配置するだけ Windowsユーザー/パスワード 手軽な一括実行・ファイルコピー+実行 セキュリティソフトが誤検知することがある
WinRM
(PowerShell Remoting)
winrm quickconfig
ファイアウォール設定
Windowsユーザー/パスワード PowerShellコマンドの一括実行・収集 初期設定が必要。ドメイン環境で使いやすい
WMI
(wmic /node)
WMI有効化(多くはデフォルト) Windowsユーザー/パスワード リモートPCの情報収集・プロセス実行 戻り値が取りにくい・文字コード注意
OpenSSH サーバー側にOpenSSH Serverをインストール SSH鍵またはパスワード Linux混在環境・スクリプト実行 Windows 10 1809 / Server 2019以降が必要
schtasks /s 対象PCへのファイアウォール許可 Windowsユーザー/パスワード タスク登録してバッチをリモート起動 スケジューラ経由のため即時実行ではない
選び方のポイント
最速で始めたい→ PsExec(配置だけで動く)
ドメイン環境・PowerShell多用→ WinRM
情報収集メイン→ WMI(wmic /node)
Linux混在・SSH標準化→ OpenSSH
管理者権限で非同期実行→ schtasks /s

共通基盤:ホストリストから一括実行するパターン

どの方式を使う場合も、対象PCのリストを外部ファイルに持ち、for /f でループして1台ずつコマンドを送る基本構造は共通です。

hosts.txt(対象ホストリスト)
:: 先頭が :: の行はコメント(スキップ)
:: ホスト名またはIPアドレスを1行に1つ記載
PC-CLIENT-01
PC-CLIENT-02
192.168.1.103
FILESERVER-01
:: PC-TEST-01  ← コメントアウトで一時除外
ホストリストをループして一括実行する基本骨格
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "HOSTS=%BASE%hosts.txt"
set "LOGDIR=%BASE%logs"
set "LOG=%LOGDIR%
emote_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"
if not exist "%HOSTS%" (
    echo [ERROR] ホストリストが見つかりません: %HOSTS%
    exit /b 1
)

set /a OK=0
set /a FAIL=0
set /a SKIP=0

echo ============================================ >> "%LOG%"
echo  開始: %DATE% %TIME% >> "%LOG%"
echo ============================================ >> "%LOG%"

for /f "usebackq eol=: delims=" %%H in ("%HOSTS%") do (
    if not "%%H"=="" (
        echo [TARGET] %%H
        echo [TARGET] %%H >> "%LOG%"

        :: ── ここに各方式のリモート実行コマンドを入れる ──
        call :remote_exec "%%H"
        set /a RC=!ERRORLEVEL!
        :: ──────────────────────────────────────────────

        if !RC! EQU 0 (
            set /a OK+=1
            echo [OK]   %%H >> "%LOG%"
        ) else (
            set /a FAIL+=1
            echo [FAIL] %%H ERRORLEVEL=!RC! >> "%LOG%"
            echo [FAIL] %%H - エラーコード: !RC!
        )
    )
)

echo ============================================ >> "%LOG%"
echo  完了: OK=%OK% FAIL=%FAIL% SKIP=%SKIP% >> "%LOG%"
echo ============================================ >> "%LOG%"
echo.
echo 実行結果: 成功 %OK% / 失敗 %FAIL% / スキップ %SKIP%
if !FAIL! GTR 0 exit /b 1
exit /b 0

:: ── リモート実行サブルーチン(方式によって中身を変える) ──
:remote_exec
setlocal
set "HOST=%~1"
:: ここに実行コマンドを書く(後続のセクションを参照)
echo   実行中: !HOST!
endlocal
exit /b 0
eol=: でコメント行をスキップ
for /f "eol=: delims="eol=: で、:: で始まる行(コメント)を自動的にスキップします。一時的に除外したいホストはコメントアウトするだけでOKです。

方式1:PsExec(最もシンプルな一括実行)

PsExec は Microsoft Sysinternals が提供するツールで、インストール不要でリモートPCのコマンドを実行できます。バッチファイルで複数PCに一括コマンドを送信する方法(PsExec活用)でコマンドオプションや実践パターンを詳しく解説しているため、ここでは基本構造と一括実行の骨格のみ示します。

事前準備

確認項目 設定方法
PsExec.exeの入手 https://learn.microsoft.com/ja-jp/sysinternals/downloads/psexec からダウンロード
ターゲットPCのファイアウォール 「ファイルとプリンターの共有」を有効化(ポート445)
管理者共有(ADMIN$) デフォルトで有効。無効化している場合は再有効化が必要
実行ユーザー 対象PCのローカル管理者または同一ドメインの管理者アカウント
PsExecで一括コマンド送信
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "PSEXEC=%BASE%PsExec.exe"
set "HOSTS=%BASE%hosts.txt"
set "LOGDIR=%BASE%logs"
set "LOG=%LOGDIR%\psexec_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
set "REMOTE_USER=domaindmin"
:: パスワードは環境変数や資格情報マネージャーから取得することを推奨
set /p REMOTE_PASS=パスワードを入力してください:

if not exist "%LOGDIR%" mkdir "%LOGDIR%"

set /a OK=0
set /a FAIL=0

for /f "usebackq eol=: delims=" %%H in ("%HOSTS%") do (
    if not "%%H"=="" (
        echo [TARGET] %%H
        echo [%DATE% %TIME%] [TARGET] %%H >> "%LOG%"

        "%PSEXEC%" \%%H -u "%REMOTE_USER%" -p "%REMOTE_PASS%" ^
            -h -accepteula ^
            cmd /c "ipconfig /all" ^
            >> "%LOG%" 2>&1

        set /a RC=!ERRORLEVEL!
        if !RC! EQU 0 (
            set /a OK+=1
            echo [OK]   %%H >> "%LOG%"
        ) else (
            set /a FAIL+=1
            echo [FAIL] %%H ERR=!RC! >> "%LOG%"
            echo [FAIL] %%H - エラーコード: !RC!
        )
    )
)

echo 完了: 成功 %OK% / 失敗 %FAIL%
パスワードをコードに直書きしない
バッチ内に set "PASS=password" のようにパスワードを直書きすることはセキュリティリスクがあります。代替案として、①set /p でその都度入力、②Windowsの資格情報マネージャーを利用(PsExec は -n オプションで資格情報マネージャー参照)、③実行アカウントをタスクスケジューラで設定してパスワード不要にする、の3つを検討してください。

複数PCにファイルをコピーして実行する

ファイルのコピー+リモート実行パターン
@echo off
setlocal enabledelayedexpansion

set "PSEXEC=PsExec.exe"
set "SCRIPT=C:	ools\deploy.bat"
set "REMOTE_USER=domaindmin"
set /p REMOTE_PASS=パスワード:

for /f "usebackq eol=: delims=" %%H in ("hosts.txt") do (
    if not "%%H"=="" (
        echo [%%H] ファイルコピー中...
        :: ファイルをリモートPCにコピー
        copy /y "%SCRIPT%" "\%%Hdmin$	emp\deploy.bat" > nul 2>&1
        if !ERRORLEVEL! NEQ 0 (
            echo [FAIL] %%H - ファイルコピー失敗
        ) else (
            echo [%%H] コマンド実行中...
            "%PSEXEC%" \%%H -u "%REMOTE_USER%" -p "%REMOTE_PASS%" -h -accepteula ^
                cmd /c "C:\Windows\Temp\deploy.bat"
            echo [%%H] 完了: ERRORLEVEL=!ERRORLEVEL!
        )
    )
)

方式2:WinRM(PowerShell Remoting)

WinRM(Windows Remote Management)はWindowsに標準搭載されたリモート管理プロトコルです。PowerShell Remoting の基盤となっており、コマンド1つでセットアップできます。ドメイン環境では特に相性がよく、Kerberos認証でパスワードを直書きせずに実行できます。

WinRMのセットアップ(対象PC側)

WinRM有効化(管理者PowerShellで実行)
# 対象PC側で実行(管理者権限のPowerShellで)
Enable-PSRemoting -Force

# ファイアウォールルール確認
Get-NetFirewallRule -DisplayName "*Windows Remote Management*" | Select Name, Enabled

# 特定のホストからの接続のみ許可(セキュリティ強化)
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.1.*" -Force

# 確認
winrm enumerate winrm/config/listener
WinRM疎通テスト(管理PC側)
:: 接続テスト(コマンドプロンプトから)
winrm id -r:http://TARGET-PC:5985

:: PowerShellからテスト
powershell -Command "Test-WSMan -ComputerName TARGET-PC"

バッチからPowerShell Remotingを呼び出す

WinRMで複数PCに一括コマンド送信
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "LOGDIR=%~dp0logs"
set "LOG=%LOGDIR%\winrm_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
set "REMOTE_USER=domaindmin"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"

set /a OK=0
set /a FAIL=0

for /f "usebackq eol=: delims=" %%H in ("%HOSTS%") do (
    if not "%%H"=="" (
        echo [TARGET] %%H
        echo [%DATE% %TIME%] [START] %%H >> "%LOG%"

        :: PowerShell Remotingで実行(ドメイン環境はパスワード不要)
        powershell -NoProfile -Command ^
            "Invoke-Command -ComputerName '%%H' -ScriptBlock { ^
                $hostname = $env:COMPUTERNAME; ^
                $os = (Get-CimInstance Win32_OperatingSystem).Caption; ^
                "$hostname: $os" ^
            }" >> "%LOG%" 2>&1

        set /a RC=!ERRORLEVEL!
        if !RC! EQU 0 (
            set /a OK+=1
            echo [OK]   %%H >> "%LOG%"
        ) else (
            set /a FAIL+=1
            echo [FAIL] %%H ERR=!RC! >> "%LOG%"
            echo [FAIL] %%H
        )
    )
)

echo 完了: 成功 %OK% / 失敗 %FAIL%
echo 完了: 成功 %OK% / 失敗 %FAIL% >> "%LOG%"
if !FAIL! GTR 0 exit /b 1
exit /b 0
クレデンシャル付きWinRM(ワークグループ環境)
@echo off
setlocal enabledelayedexpansion

set "REMOTE_USER=localadmin"
set /p REMOTE_PASS=パスワード:

for /f "usebackq eol=: delims=" %%H in ("hosts.txt") do (
    if not "%%H"=="" (
        echo [%%H] 実行中...

        :: クレデンシャルを指定してInvoke-Command
        powershell -NoProfile -Command ^
            "$pw = ConvertTo-SecureString '%REMOTE_PASS%' -AsPlainText -Force; ^
             $cred = New-Object PSCredential('%REMOTE_USER%', $pw); ^
             Invoke-Command -ComputerName '%%H' -Credential $cred -ScriptBlock { ^
                 Get-Service -Name 'Spooler' ^
             }" 2>&1

        if !ERRORLEVEL! EQU 0 (echo [OK] %%H) else (echo [FAIL] %%H)
    )
)
WinRMでファイルをコピーして実行する
Copy-Item -ToSessionInvoke-Command を組み合わせると、PowerShell Remotingセッション経由でスクリプトファイルをコピーして実行できます。共有フォルダなしでファイル転送できるため、ネットワーク構成がシンプルになります。

方式3:WMI(wmic /node)でリモート情報収集・実行

WMI(Windows Management Instrumentation)はWindowsの管理情報にアクセスする標準インターフェースです。wmic /node でリモートPCに接続し、情報取得やプロセス起動ができます。セットアップ不要(WMIはデフォルトで有効)で使えるのが利点ですが、Windows 11 / Server 2025 以降では非推奨となり、PowerShell の Get-CimInstance が推奨されます。

wmicでリモートPC情報を収集
@echo off
setlocal enabledelayedexpansion

set "LOGDIR=%~dp0logs"
set "LOG=%LOGDIR%\wmi_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.csv"
set "REMOTE_USER=domaindmin"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"

:: CSVヘッダー
echo HostName,OSCaption,TotalMemoryGB,FreeMemoryGB >> "%LOG%"

for /f "usebackq eol=: delims=" %%H in ("hosts.txt") do (
    if not "%%H"=="" (
        echo [INFO] %%H 情報収集中...

        :: OS情報取得
        for /f "skip=1 tokens=*" %%O in (
            'wmic /node:%%H /user:"%REMOTE_USER%" os get Caption /format:csv 2^>nul'
        ) do (
            if not "%%O"=="" set "OS_CAP=%%O"
        )

        :: メモリ取得(MB → GB 変換)
        for /f "skip=1 tokens=*" %%M in (
            'wmic /node:%%H /user:"%REMOTE_USER%" computersystem get TotalPhysicalMemory /format:csv 2^>nul'
        ) do (
            if not "%%M"=="" (
                set /a TOTAL_GB=%%M / 1073741824
            )
        )

        echo %%H,!OS_CAP!,!TOTAL_GB! >> "%LOG%"
    )
)

echo 完了: %LOG% に保存しました。
wmicでリモートプロセスを起動
@echo off
setlocal enabledelayedexpansion

set "REMOTE_USER=domaindmin"
set /p REMOTE_PASS=パスワード:

for /f "usebackq eol=: delims=" %%H in ("hosts.txt") do (
    if not "%%H"=="" (
        echo [%%H] プロセス起動...

        :: wmic process call create でリモートプロセス起動
        wmic /node:%%H /user:"%REMOTE_USER%" /password:"%REMOTE_PASS%" ^
            process call create "cmd /c C:\tools\update.bat > C:\logs\update.log 2>&1"

        if !ERRORLEVEL! EQU 0 (
            echo [OK]   %%H - プロセス起動成功
        ) else (
            echo [FAIL] %%H - プロセス起動失敗
        )
    )
)
wmicの戻り値と文字コードに注意
wmic process call create はプロセスの起動成功を返すだけで、起動したプロセスの終了コードは取得できません。非同期実行として扱ってください。また、wmic の出力は UTF-16 LE のため、for /f で取得した値が文字化けする場合があります。その場合は powershell -Command "Get-CimInstance ..." への置き換えを検討してください。

方式4:OpenSSH(Windows標準のSSH機能)

Windows 10 バージョン 1809 / Windows Server 2019 以降は、OpenSSH Server が標準機能として搭載されています。Linux・Mac・他のWindowsどこからでもSSH経由でコマンドを実行できるため、異種OS混在環境や将来的な互換性を重視する場合に最適です。

OpenSSH Serverのセットアップ(対象PC側)

OpenSSH Serverのインストールと起動(PowerShell)
# インストール(管理者PowerShell)
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

# サービス起動 + 自動起動設定
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic

# ファイアウォール確認(自動で設定されるが念のため確認)
Get-NetFirewallRule -Name *ssh*

# SSH鍵認証を設定する場合(推奨)
# authorized_keysファイルを作成
# C:\ProgramData\sshdministrators_authorized_keys に管理者用公開鍵を追加

バッチからSSH一括実行

OpenSSHで複数PCに一括コマンド送信
@echo off
setlocal enabledelayedexpansion

set "HOSTS=%~dp0hosts.txt"
set "SSH_KEY=%USERPROFILE%\.ssh\id_rsa"
set "REMOTE_USER=admin"
set "LOGDIR=%~dp0logs"
set "LOG=%LOGDIR%\ssh_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"

set /a OK=0
set /a FAIL=0

for /f "usebackq eol=: delims=" %%H in ("%HOSTS%") do (
    if not "%%H"=="" (
        echo [TARGET] %%H
        echo [%DATE% %TIME%] [TARGET] %%H >> "%LOG%"

        :: SSH鍵認証でリモートコマンドを実行
        ssh -i "%SSH_KEY%" ^
            -o StrictHostKeyChecking=no ^
            -o ConnectTimeout=10 ^
            -o BatchMode=yes ^
            %REMOTE_USER%@%%H ^
            "ipconfig /all && echo EXIT_CODE=0" >> "%LOG%" 2>&1

        set /a RC=!ERRORLEVEL!
        if !RC! EQU 0 (
            set /a OK+=1
            echo [OK]   %%H >> "%LOG%"
            echo [OK]   %%H
        ) else (
            set /a FAIL+=1
            echo [FAIL] %%H ERR=!RC! >> "%LOG%"
            echo [FAIL] %%H
        )
    )
)

echo 完了: 成功 %OK% / 失敗 %FAIL%
echo 完了: 成功 %OK% / 失敗 %FAIL% >> "%LOG%"
if !FAIL! GTR 0 exit /b 1
exit /b 0
リモートにスクリプトファイルをコピーして実行
@echo off
setlocal enabledelayedexpansion

set "SSH_KEY=%USERPROFILE%\.ssh\id_rsa"
set "REMOTE_USER=admin"
set "LOCAL_SCRIPT=C:	ools\deploy.sh"

for /f "usebackq eol=: delims=" %%H in ("hosts.txt") do (
    if not "%%H"=="" (
        echo [%%H] スクリプトをコピー中...

        :: scpでスクリプトをコピー
        scp -i "%SSH_KEY%" -o BatchMode=yes "%LOCAL_SCRIPT%" ^
            %REMOTE_USER%@%%H:/tmp/deploy.sh

        if !ERRORLEVEL! EQU 0 (
            echo [%%H] 実行中...
            :: コピー後に実行(PowerShellスクリプトを実行)
            ssh -i "%SSH_KEY%" -o BatchMode=yes ^
                %REMOTE_USER%@%%H "powershell -ExecutionPolicy Bypass -File C:\tools\deploy.ps1"
            echo [%%H] 結果: !ERRORLEVEL!
        ) else (
            echo [FAIL] %%H - コピー失敗
        )
    )
)
SSH鍵認証の設定方法
管理者用の公開鍵は C:\ProgramData\ssh\administrators_authorized_keys に配置します(通常ユーザーは %USERPROFILE%\.ssh\authorized_keys)。アクセス許可は特定のアカウントのみに制限し、グループやEveryoneには与えないでください。詳細は Microsoft の公式ドキュメント「OpenSSH key management」を参照してください。

方式5:schtasks /s でリモートにタスクを登録して実行

schtasks コマンドの /s オプションで、リモートPCのタスクスケジューラにタスクを登録・即時実行できます。schtasksコマンドでタスクスケジューラを完全制御する完全ガイドでschtasksの詳細な使い方を解説しています。この方式はバッチファイルやスクリプトをリモートPCで実行させたいが、対象PCに既にファイルが存在する場合に特に有効です。

schtasks /s でリモートタスクを登録・即時実行
@echo off
setlocal enabledelayedexpansion

set "REMOTE_USER=domaindmin"
set /p REMOTE_PASS=パスワード:
set "TASK_NAME=RemoteDeploy_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%"
set "REMOTE_BAT=C:	ools\deploy.bat"
set "LOGDIR=%~dp0logs"
set "LOG=%LOGDIR%\schtask_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"

for /f "usebackq eol=: delims=" %%H in ("hosts.txt") do (
    if not "%%H"=="" (
        echo [%%H] タスク登録中...

        :: リモートPCにタスクを登録
        schtasks /create ^
            /s %%H ^
            /u "%REMOTE_USER%" /p "%REMOTE_PASS%" ^
            /tn "%TASK_NAME%" ^
            /tr ""%REMOTE_BAT%"" ^
            /sc once /st 00:00 ^
            /ru SYSTEM ^
            /f > nul 2>&1

        if !ERRORLEVEL! NEQ 0 (
            echo [FAIL] %%H - タスク登録失敗
        ) else (
            :: 即時実行
            schtasks /run /s %%H /u "%REMOTE_USER%" /p "%REMOTE_PASS%" /tn "%TASK_NAME%" > nul 2>&1
            echo [OK]   %%H - タスク起動 >> "%LOG%"
            echo [OK]   %%H - タスク起動

            :: 後で削除(任意)
            :: schtasks /delete /s %%H /u "%REMOTE_USER%" /p "%REMOTE_PASS%" /tn "%TASK_NAME%" /f
        )
    )
)

echo 完了。各PCでタスクが実行されています。
SYSTEM権限で実行できるのがメリット
schtasks /s 方式では、リモートPC上で SYSTEM 権限(または任意のサービスアカウント)でバッチを実行できます。ネットワーク越しの共有フォルダへのアクセスはSYSTEM権限では制限される場合があるため、管理者権限で自動実行するバッチファイルの作り方も合わせて確認してください。

実践:全台を並列で高速実行する

対象が10台以上になると、順次実行では時間がかかります。バッチファイルで並列処理を実行する方法のパターンを応用し、複数台に同時にコマンドを送ることで大幅な時間短縮ができます。

並列リモート実行(start /b + ロックファイル完了待ち)
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "HOSTS=%BASE%hosts.txt"
set "LOCKDIR=%TEMP%
locks_%RANDOM%"
set "LOGDIR=%BASE%logs"
set "LOG=%LOGDIR%\parallel_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

mkdir "%LOCKDIR%"
if not exist "%LOGDIR%" mkdir "%LOGDIR%"

set /a TOTAL=0

:: ── 全ホストを並列起動 ────────────────────────────────
for /f "usebackq eol=: delims=" %%H in ("%HOSTS%") do (
    if not "%%H"=="" (
        set /a TOTAL+=1
        :: ロックファイルを事前に作成
        copy nul "%LOCKDIR%\%%H.lock" > nul

        :: バックグラウンドでPsExecを実行(完了後にロックファイルを削除)
        start /b cmd /c "psexec \%%H -accepteula -h ipconfig /all >> "%LOG%" 2>&1 && del "%LOCKDIR%\%%H.lock" 2>nul || del "%LOCKDIR%\%%H.lock" 2>nul"
        echo [LAUNCH] %%H
    )
)

echo %TOTAL% 台に同時送信しました。完了を待機中...

:: ── 全ロックファイルが消えるまで待機(最大 120 秒)────
set /a WAIT=0
:wait_loop
timeout /t 3 /nobreak > nul
set /a WAIT+=3
set "REMAIN="
for %%F in ("%LOCKDIR%\*.lock") do set "REMAIN=1"
if defined REMAIN (
    if !WAIT! LSS 120 goto :wait_loop
    echo [TIMEOUT] 一部のホストが時間内に完了しませんでした。
)

echo 全処理完了(待機時間: %WAIT%秒)
rmdir /s /q "%LOCKDIR%" 2>nul
echo 全処理完了 >> "%LOG%"

エラー処理・ログ記録・通知の実装

大量のPCに送信すると一部で失敗が出るのは避けられません。失敗したホストをログに記録し、リトライリストを生成する仕組みを作っておくと運用が楽になります。バッチファイルでログを出力する方法完全ガイドのログ出力パターンとバッチファイルでエラー通知メールを自動送信する完全ガイドの通知を組み合わせた完成版を示します。

失敗リスト生成+通知付き完成版スクリプト
@echo off
setlocal enabledelayedexpansion

set "BASE=%~dp0"
set "HOSTS=%BASE%hosts.txt"
set "FAIL_LIST=%BASE%hosts_failed_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt"
set "LOGDIR=%BASE%logs"
set "LOG=%LOGDIR%
emote_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"

if not exist "%LOGDIR%" mkdir "%LOGDIR%"
if exist "%FAIL_LIST%" del "%FAIL_LIST%"

set /a OK=0
set /a FAIL=0
set /a TOTAL=0

echo ============================================ >> "%LOG%"
echo  開始: %DATE% %TIME% >> "%LOG%"
echo ============================================ >> "%LOG%"

for /f "usebackq eol=: delims=" %%H in ("%HOSTS%") do (
    if not "%%H"=="" (
        set /a TOTAL+=1

        :: 接続前にpingで生死確認(タイムアウトを短くして高速化)
        ping -n 1 -w 1000 %%H > nul 2>&1
        if !ERRORLEVEL! NEQ 0 (
            echo [SKIP] %%H - pingタイムアウト(オフラインの可能性)
            echo [SKIP] %%H - ping timeout >> "%LOG%"
            echo %%H >> "%FAIL_LIST%"
            set /a FAIL+=1
        ) else (
            echo [TARGET] %%H
            :: ── メインコマンドを実行 ──
            psexec \%%H -accepteula -h cmd /c "ipconfig /all" >> "%LOG%" 2>&1
            set /a RC=!ERRORLEVEL!

            if !RC! EQU 0 (
                set /a OK+=1
                echo [OK]   %%H >> "%LOG%"
                echo [OK]   %%H
            ) else (
                set /a FAIL+=1
                echo [FAIL] %%H ERR=!RC! >> "%LOG%"
                echo [FAIL] %%H - エラーコード: !RC!
                echo %%H >> "%FAIL_LIST%"
            )
        )
    )
)

echo ============================================ >> "%LOG%"
echo  完了: 全%TOTAL% OK=%OK% FAIL=%FAIL% >> "%LOG%"
echo ============================================ >> "%LOG%"

echo.
echo ────────────────────────────────────────
echo  実行結果: 全 %TOTAL% 台 / 成功 %OK% / 失敗 %FAIL%
if !FAIL! GTR 0 (
    echo  失敗リスト: %FAIL_LIST%
    echo  ヒント: 失敗リストを hosts.txt として再実行できます
)
echo  ログ: %LOG%
echo ────────────────────────────────────────

:: 失敗があればメール通知(PowerShell Send-MailMessage)
if !FAIL! GTR 0 (
    powershell -NoProfile -Command ^
        "Send-MailMessage -From 'batch@example.com' -To 'admin@example.com' ^
         -Subject '[ALERT] リモート実行で %FAIL% 台が失敗 (%DATE%)' ^
         -Body (Get-Content '%FAIL_LIST%' -Raw) ^
         -SmtpServer 'smtp.example.com'"
    echo [通知] 失敗リストをメール送信しました。
)

if !FAIL! GTR 0 exit /b 1
exit /b 0
失敗ホストへのリトライ
上のスクリプトが生成する hosts_failed_YYYYMMDD.txthosts.txt にリネームして同じスクリプトを再実行するだけで、失敗したホストだけをリトライできます。複数バッチファイルを一括管理・実行するマスタースクリプト完全ガイドの再開機能と組み合わせると、さらに洗練されたリトライ管理が実現できます。

セキュリティ上の注意点と対策

リモート実行は便利な反面、適切な制御がなければセキュリティリスクになります。運用前に以下の点を確認してください。

リスク 対策
パスワードのコード直書き 資格情報マネージャー利用、SSH鍵認証、タスクスケジューラのサービスアカウント実行
不要なポートの開放 実行方式に必要なポートのみ開放(PsExec:445、WinRM:5985/5986、SSH:22)
ADMIN$共有の悪用 リモート実行に専用の限定権限アカウントを作成し、管理者権限を最小化
コマンド実行ログの欠如 実行コマンドと対象PC・実行時刻を必ずログに記録
ターゲット以外のPCへの誤送 hosts.txtをdry-runモードで確認してから本番実行(echo のみで確認)
ドライランモードで対象を確認してから実行する
本番実行前に --dry-run 的な確認ステップを入れることを強く推奨します。例えば、実際のコマンドを送る前に echo [DRY RUN] %%H にコマンドを送信します だけを表示するモードを用意し、対象ホストの一覧を目視確認してから本番実行してください。誤ったホストに変更系コマンドを送ると取り消しが困難です。

よくある質問(FAQ)

QPsExec・WinRM・OpenSSHのどれを使えばいいですか?
APsExec: 既存のActive Directory環境で手軽に始めたい場合。ただしセキュリティソフトの誤検知に注意。・WinRM: ドメイン環境でPowerShellを多用する場合。Kerberos認証でパスワード不要。・OpenSSH: 新規構築・Linux混在・長期的な標準化を目指す場合。SSH鍵認証でセキュリティも高い。個人的にはOpenSSHへの移行を推奨しますが、既存の環境に合わせて選択してください。
Qドメインに参加していないワークグループ環境でも使えますか?
APsExec・WinRM・OpenSSHとも、ローカルアカウントのユーザー名・パスワードを指定することでワークグループ環境でも使えます。ただし、WinRMのワークグループ環境では TrustedHosts に対象PCを追加する必要があります。OpenSSHはSSH鍵認証を使えばパスワード不要で安全に使えるため、ワークグループ環境でも特におすすめです。
Qリモートコマンドの実行結果(stdout)をローカルのログに取得できますか?
APsExec・ssh・WinRMはいずれも標準出力をローカルにリダイレクトできます。psexec \HOST cmd /c "ipconfig" >> local_log.txt 2>&1 の形で問題ありません。ただし、wmicはCSV形式でのみ出力取得が安定しています。また、ssh は -o BatchMode=yes を付けると対話入力を無効化してログに向けやすくなります。
Q対象が100台以上になると並列実行でどのくらい速くなりますか?
A順次実行(1台ずつ)で各台10秒かかる処理なら100台で約1000秒(17分)かかります。並列実行で同時10本走らせると約100秒(2分)になります。ただし並列数を上げすぎるとネットワーク・対象サーバーへの負荷が増えます。バッチファイルで並列処理を実行する方法のスロット制御パターン(同時N本まで制限)を使うとバランスが取れます。
QセキュリティソフトがPsExecをブロックします。どうすればいいですか?
APsExecはマルウェアにも悪用されるため、一部のセキュリティソフトが警告・ブロックする場合があります。対処法として①セキュリティソフトの例外リストにPsExecを追加する、②WinRMやOpenSSHなど代替方式に移行する、③PsExec以外のSysinternalsツールやWindows標準のリモート管理機能(WSMAN)を使う、の3つがあります。長期的にはOpenSSHへの移行が最もセキュアで持続可能な選択肢です。

まとめ

方式 主な用途 前提条件 特徴
PsExec 手軽な一括実行・ファイルコピー PsExec.exe配置・ADMIN$有効 設定最小・セキュリティソフト誤検知に注意
WinRM PowerShellコマンドの一括実行 Enable-PSRemoting + FW設定 ドメイン環境に最適・Kerberos認証対応
WMI 情報収集・プロセス起動 WMI有効(デフォルト) 戻り値が非同期・Win11以降は非推奨
OpenSSH SSH越しのコマンド実行・ファイル転送 OpenSSH Server インストール 鍵認証・Linux混在環境・将来標準
schtasks /s 管理者権限でのバッチ遠隔起動 ファイアウォール許可 SYSTEM権限実行可能・既存スクリプト活用
関連記事
バッチファイルで複数PCに一括コマンドを送信する方法(PsExec活用):PsExecのオプション詳細と実践パターン。
ネットワーク接続の有無を判定して処理を分岐する方法:送信前のping疎通確認の実装方法。
バッチファイルで並列処理を実行する方法:多数のホストへの並列送信パターン。
schtasksコマンドでタスクスケジューラを完全制御する完全ガイド:schtasksコマンドの詳細な使い方。
バッチファイルでエラー通知メールを自動送信する完全ガイド:失敗時のメール・Slack自動通知の実装。