別端末(Windows)のプログラムを標準機能でリモート起動する方法まとめ

# はじめに

Windowsにて別端末のプログラムを起動する方法を調べたのでまとめておく。

* 方法１：リモートデスクトップ
* 方法２：Telnet
* 方法３：at (タスクスケジューラ)
* 方法４：schtasks (タスクスケジューラ)
* 方法５：PowerShell (WinRM)
* 方法６：WMI
* 方法７：PsExec

## 条件

いろんな方法があるので一応条件として

* Windowsの標準機能で実現
* スクリプト等は作成せずとにかく簡単に

## 環境

呼び出す側をローカル、呼び出される側をリモートとしておく。

端末     | OS        | IP
-------- | --------- | ---------------
ローカル | Windows 7 | 192.168.100.1
リモート | Windows 7 | 192.168.100.101

ここでは、
例としてリモート端末の単純なバッチ`C:\Remote\run_batch.bat`を起動する状況を考える。

```bat:run_batch.bat
echo Complete > C:\Remote\run.log
```

## 記事への補足

* `<user>`はリモート端末側にて有効なユーザ名
* `<pass>`は`<user>`のパスワード

# 方法１：リモートデスクトップ

* リモート端末のデスクトップにログオンしてプログラムを実行する方法
* プログラムを実行するだけなので、今回の用途には不適切

# 方法２：Telnet

* 簡単に言えばリモートデスクトップのCUI版
* リモートデスクトップ同様、プログラムの実行だけに利用するのは不適切
* 機能を有効化しないと利用できない

## リモート端末での準備

* Telnetサーバ の有効化(後述)
* 22番ポートの開放
* `telnet` サービスを開始する
* 接続ユーザを`TelnetClients`グループに所属させる

22番ポートはTelnetサーバインストール時に開放されていると思うので、
あとは以下コマンドでOKなはず。

```bat:管理者権限にて
> net start telnet
> net localgroup TelnetClients <user> /add
```

#### Telnetサーバ の有効化手順

1. [コントロールパネル] の [プログラム] - [プログラムと機能] から [Windowsの機能の有効化または無効化] をクリック
2. [Telnet サーバ] をチェック

## クライアント端末での準備

* Telnetクライアント の有効化

#### Telnetクライアント の有効化手順

1. [コントロールパネル] の [プログラム] - [プログラムと機能] から [Windowsの機能の有効化または無効化] をクリック
2. [Telnet クライアント] をチェック

## 実行例

telnetでリモート端末にログオンし、直接対象のプログラムを実行するだけ。

```cmd
> telnet 192.168.100.101
Microsoft Telnet クライアントへようこそ

エスケープ文字は 'CTRL+]' です


パスワード情報を インターネット ゾーンのリモート コンピューターに送信しようとし
ていますが、この操作は安全でない可能性があります。送信しますか? (Y/N):

Welcome to Microsoft Telnet Service

login: <user>
password:

*===============================================================
Microsoft Telnet Server.
*===============================================================
C:\Users\user>C:\Remote\run_batch.bat
C:\Users\user>type C:\Remote\run.log
Complete
```

ただしちゃんと警告してくれているようにそもそも安全性に難があり。

# 方法３：at (タスクスケジューラ)

* `at`コマンドを利用して、タスクスケジューラからプログラムを起動する
* 開始時間を指定して実行できる(逆に言えば時間を指定する必要がある)

## リモート端末での準備

* ファイアウォールにて "ファイルとプリンターの共有" グループを有効化
  * 英語版ならgroup名は "File and Printer Sharing"

```bat:管理者権限にて
> netsh advfirewall firewall set rule group="ファイルとプリンターの共有" new enable=yes
```

## クライアント端末での準備

利用ユーザによっては不要かと思われるが、
実行時にアクセスエラーが出る場合はIPCリソースへ接続できるようにしておく。

```bat
REM (必要なら) 事前にIPCリソースへ接続
> net use \\192.168.100.101\IPC$ /user:<user>
```

## 実行例

`at`コマンドではリモート端末側の開始時刻を指定するため、
リモート端末での時刻を`net time`で取得して1分後に指定してみる。

```bat
REM サーバの時間
> net time \\192.168.100.101
\\192.168.100.101 の現在の時刻は 2016/12/05 10:10:10 です

REM 1分後に実行
> at \\192.168.100.101 10:11 C:\Remote\run_batch.bat
新しいジョブをジョブ ID = 1 で追加しました。

> at \\192.168.100.101
状態 ID     日付                    時刻          コマンド ライン
-------------------------------------------------------------------------------
        1   今日                    10:11          C:\Remote\run_batch.bat

REM 実行ジョブが無くなっていれば実行完了(成功したかどうかは別）
> at \\192.168.100.101
一覧にエントリが存在しません。
```

ただし`at`コマンドでは存在していないが、リモート端末のタスクスケジューラには残る。
よって消さない限り増えていく。

```bat
> at \\192.168.100.101 <ジョブID> /delete
```

# 方法４：schtasks (タスクスケジューラ)

* `schtasks`コマンドを利用してタスクスケジューラからプログラムを起動する
* `at`コマンドより高機能なので、任意タイミングで実行することもできる

## リモート端末での準備

* ファイアウォールにて "スケジュールされたリモート タスク管理" グループを有効化
  * 英語版ならgroup名は "Remote Scheduled Tasks Management"

```bat:管理者権限にて
> netsh advfirewall firewall set rule group="スケジュールされたリモート タスク管理" new enable=yes
```

* 実行したいタスクスケジューラをあらかじめ作成しておいてもよい

## クライアント端末での準備

特に不要だった。

## 実行例

リモート端末に、開始時間を過去日付に指定したタスクを作成しておき、
そのタスクを任意のタイミングで呼び出すことでプログラムを実行する。

```bat
REM タスクスケジューラにタスクを作成
> schtasks /Create /S 192.168.100.101 /U <user> /P <pass> /TN sample /SC ONCE /SD 1900/01/01 /ST 00:00 /TR C:\Remote\run_batch.bat
警告: /ST が現時刻よりも早いため、タスクは実行されない可能性があります。
成功: スケジュール タスク "sample" は正しく作成されました。

REM 実行
> schtasks /Run /S 192.168.100.101 /U <user> /P <pass> /TN sample
成功: スケジュール タスク "sample" の実行が試行されました。
```

実行ユーザの変更など、それなりに細かい制御ができる。

# 方法５：PowerShell (WinRM)

* PowerShellからWinRMを利用する

## リモート端末での準備

* ファイアウォールにて "Windows リモート管理 (HTTP 受信)" を有効化
  * 英語版なら規則名は "Windows Remote Management (HTTP-In)"

```cmd:管理者権限にて
> netsh advfirewall firewall set rule name="Windows リモート管理 (HTTP 受信)" new enable=yes
```

## クライアント端末での準備

* WinRMサービスの起動
* 接続先のリモート端末を信頼する

作業としてはPowerShellにて以下を実行する

```bat:管理者権限にて
PS> Set-Item wsman:\localhost\Client\TrustedHosts "192.168.100.101" -Concatenate

WinRM サービスの起動
WinRM サービスは現在起動していません。このコマンドを実行すると、WinRM サービスが起動します。

続行しますか?
[Y] はい(Y)  [N] いいえ(N)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):

WinRM セキュリティの構成。
このコマンドは WinRM クライアントの TrustedHosts の一覧を変更します。TrustedHosts
の一覧内にあるコンピューターは認証されない可能性があります。クライアントはこれらのコンピューターに資格情報を送信する可
能性があります。この一覧を変更しますか?
[Y] はい(Y)  [N] いいえ(N)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):
```

## 実行例

PowerShellにて以下を実行するだけ。

```bat
PS> Invoke-Command -ComputerName 192.168.100.101 -Credential <user> {C:\Remote\run_batch.bat}

C:\Users\user\Documents>echo Complete  1>C:\Remote\run.log
```

# 方法６：WMI

* `WMIC`コマンドを利用して、WMIが管理する情報を利用する
* `WMIC`はWindows XP以降標準で利用可能なコマンドラインツール

## リモート端末での準備

* ファイアウォールにて "Windows Management Instrumentation (WMI)" グループを有効化

```bat:管理者権限にて
> netsh advfirewall firewall set rule group="Windows Management Instrumentation (WMI)" new enable=yes
```

## クライアント端末での準備

特に不要だった。

## 実行例

```bat
> WMIC /NODE:192.168.100.101 /user:<user> /password:<pass> process call create "C:\Remote\run_batch.bat"
```

`WMIC`自体はさまざまなWindows の状態を取得出来るツールなので、
もっと別の目的で活用してあげたい。

# 方法７：PsExec

* Stack Overflow の回答でも一番多い回答がこのPsExecを利用する方法
* 標準で入っていないのでタイトルと合わないが、Windows純正のソフトでダウンロードするだけで利用できる
* ローカルファイルをコピーすると同時にリモート実行など、他に無いちょっとクールな使い方もできる

## リモート端末での準備

ファイアウォールにて以下を開放しておく必要がある。

* 135/tcp（RPCエンドポイント・マッパー）
* 445/tcp（ダイレクト・ホスティングSMB）
* 1025～65535/tcp（RPC動的ポート）

コマンドでやるならこんな感じ

```bat:管理者権限にて
> netsh advfirewall firewall add rule name=PsExec dir=in action=allow localport=rpc-epmap protocol=tcp program= %windir%\system32\svchost.exe
> netsh advfirewall firewall add rule name=PsExec dir=in action=allow localport=445 protocol=tcp
> netsh advfirewall firewall add rule name=PsExec dir=in action=allow localport=rpc protocol=tcp program=%SystemRoot%\system32\services.exe
```

他でやっているように既存の規則を有効化してやっても良いが、新規にルールを追加すれば後から無効化するのが楽。
`netsh advfirewall firewall set rule name=PsExec new enable=no`みたいな感じで。

## クライアント端末での準備

以下よりPsExecをダウンロードし、適当な場所に展開する。
https://technet.microsoft.com/ja-jp/sysinternals/pxexec.aspx

## 実行例

一番今回の目的にあっていると思われる方法。

```bat
> PsExec.exe -u <user> -p <pass> \\192.168.100.101 C:\Remote\run_batch.bat

PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

C:\Windows\system32>echo Complete  1>C:\Remote\run.log
C:\Remote\run_batch.bat exited on 192.168.100.101 with error code 0.
```

またv2.1以降であれば、通信自体も暗号化されているとのこと。

# 参考

* [PsExec v2.1 – All Network Communication Is Now Encrypted](https://batchpatch.com/psexec-v2-1-all-network-communication-is-now-encrypted)
* [Tech TIPS：PsExecを使ってリモートのWindows PC上のプログラムを実行する - ＠IT](http://www.atmarkit.co.jp/ait/articles/1205/11/news147.html)
* [How to execute a batch file on a Remote pc using a batch file on local PC](http://stackoverflow.com/questions/32482483/how-to-execute-a-batch-file-on-a-remote-pc-using-a-batch-file-on-local-pc)