PowershellとRLoginで大量サーバ管理

WindowsのターミナルソフトだとRLoginがダントツで便利だと思うのだけれど、周囲のほとんどの人がTeraTerm使いなのでRLoginを拡めるために色々と考えてみた。
※RLoginのバージョンは記事作成時点（2018/01/07）で最新の2.23.0を使っています。
※Powershellは5.0で書いてますが、3.0以上であれば動かせると思います（Out-GridViewのPassThruオプションが3.0から実装されたものらしいので）

スクリプトを実行すると、一つ目の画像のようなCSVファイルを読み込んで二つ目の画像のような画面が表示されて、OKを押すと選択していたサーバにRLoginで接続されます。

![csv.png](https://qiita-image-store.s3.amazonaws.com/0/116209/6d1053e3-c4f9-a081-1f3f-6011ee702914.png)

![gridview.png](https://qiita-image-store.s3.amazonaws.com/0/116209/bec4d4f5-0774-15cc-7096-f411d0448a6d.png)


# TeraTerm使いに対抗するためのRLoginの便利機能

RLoginを使ってくれないと元も子もないので、「これができるなら乗り換えるかも」と思われるような機能を書いてみます。
他の便利機能や利点などはググればいっぱい出てきます。タブとかSFTPとか。

- 接続時に接続情報（接続先、ユーザ、パスワード）を入力して繋ぐようにする

接続時に接続情報の入力を求めるダイアログが表示されます。
設定方法は、Entry編集画面の「サーバー・ユーザー・パスワード/フレーズの入力」にチェックを入れるだけです。
RLoginは語尾の長音を付ける派のようです。
あらかじめPasswordなどの欄を記入しておくとデフォルト値として設定された状態でダイアログが表示されるので共通の場合は便利です。
このEntryを一つ作っておくだけでTeraTermのような使い方ができるようになるので、TeraTerm使いが最も主張するRLoginの欠点を潰せます。とりあえず作っておきましょう。
ポートフォワーディングの設定をしたEntryなどをいくつか作っておくだけでも大量サーバの管理が楽になると思います。

- コマンドラインから引数付きで実行

RLoginも[コマンドラインから引数にIPなどを指定して実行](http://nanno.dip.jp/softlib/man/rlogin/#CMDLINE)することができます。
使用するEntryも指定でき、もともとEntryに設定されていた項目を引数で指定した場合は引数の方が優先されるため、割と使い勝手が良いです。
コマンド例：

```powershell
RLogin.exe /ip 192.168.1.2 /port 22 /user root /pass password /title hoge /entry fuga /inuse
```

- 通信ログの生成

RLoginでの操作ログをリアルタイムにファイルに出力するようになります。
設定方法は、Entry編集画面の「スクリーン->ヒストリ」画面の「通信ログを年月日を付けて自動生成する」にチェックを入れ、その下の空欄にログを生成するパスを記入しておくだけです。
[%Dなどの特殊文字](http://nanno.dip.jp/softlib/man/rlogin/#FILENAMEEXT)を使って接続時のサーバアドレスや時刻から出力するログのパスを自動生成することもできます。
RAWモードのままだと厳密すぎて見辛いので、出力されたログを見ながら適当に変更しておくとよいです。

- マクロ

Entry編集画面の「サーバー->スクリプト」画面のでかい空欄に書けば接続時に実行されます。スクリプトファイルを作っておいて実行もできます。
「接続後に特定のコマンド実行」程度だったらTeraTermマクロと同じようなノリで書けます。
[関数や変数も色々用意されているようなので](http://nanno.dip.jp/softlib/man/rlogin/script.html)、だいたいのことはできるんじゃないかと思います。
ただ、RLoginでマクロを使いたくなったことが無いのであんまりよくわかりません。
ログイン自動化はマクロ必要無いし、踏み台経由して接続なんかもポートフォワードの設定でできますし。

- その他

TeraTermではデフォルトになっている「左クリックの範囲指定だけでコピー」「右クリックでペースト」などの設定もできます。
Entry編集画面の「クリップボード」画面から設定できます。
ちなみにペーストするときは「改行を確認しながら送信する」に**チェックを入れておく**と、複数行ペーストしたときに前のコマンドがリターンされる前に後続の行もペーストされてTeraTermっぽいです。
チェックを入れていないと、前のコマンドの実行完了を待ってから次のコマンドがペーストされます。個人的にはこちらの方が良いと思いますが念のため。
何のことかわからない人は「sleep 1」コマンドを3行分くらいコピペすると違いがわかると思います。
（設定の言葉の意味と実際の挙動が逆な気がするけど気のせい？）

# 大量サーバ管理

ここからがメインだけど、実は上記に書いた機能を組み合わせてPowershellでGUI部分を作っただけです。

RLoginは基本的にサーバごとにEntryを作って管理するため、少数のサーバであれば個数分のEntryを作るかと思います。
ただ、管理するサーバが二桁三桁になってくるとEntryを作るだけでも大変で、接続ユーザやパスワードが変わったときの変更作業も膨大なものになってしまいます。
かと言って、「接続時に接続情報を入力して繋ぐようにする」の最後に書いた少数のEntryだけを作っておく方法を使うと、自動ログインができなくてちょっと面倒です。
よく使うサーバだけ別にEntry作っておくとかでもよさそうだけど、なんとなく無法地帯になって泥沼化しそうな危険な予感がします。
そこで何か良い方法ないかと考えていたらPowershellにOut-GridViewなんてものがあったのを思い出したので、
これとコマンドライン実行を組み合わせれば良い感じのサーバ管理ソフトっぽいものが作れるんじゃないかと思って実装してみました。

## Out-GridViewについて
 
オブジェクトをパイプで渡すとExcelみたいな表形式で表示してくれるコマンドレットです。
pecoを知っている人はpecoみたいなものだと思ってもらえればだいたいあっています。
適当なフォルダで``ls | Out-GridView``とか実行してみるとどんなものなのかわかりやすいです。

簡易的なビューワとしても見やすくて便利なんですが、更に便利な機能として、「PassThru」オプションを付けると選択したレコードのオブジェクトを返り値として取得することができます。
要するに、サーバ接続情報一覧みたいな表から接続したいサーバをソートやフィルタ使って探して選択してOKを押せばそのサーバにRLoginで接続するってことが割と簡単にできるわけです。

## 実装

おそらく「サーバ一覧.xlsx, パスワード一覧.xlsx」みたいなファイルが存在していると思うので、そこから適当に必要な情報を抽出して接続用のファイル（CSVが無難だと思う）を作っておきます。
RLoginの方では必要な分のEntryを作成しておきます。（ポートフォワーディングして踏み台経由で繋ぐ用とかtelnet用とかroot専用とか）
必要があればログやマクロの設定などもここで行っておきます。
後は、Powershellで接続用ファイルを読み込んで適当にRLogin実行用のコマンドを組み立てて[オブジェクト作って](https://qiita.com/S_SenSq/items/456260c39f5a3e816c4c)Out-GridViewに渡すコードを書けば完成です。
複雑なパスワードのサーバも、踏み台何個も経由しないと繋げられない複雑なネットワーク構成のサーバも数クリックで簡単に繋げられます。
パスワード平文で書くのどうなの？って言われそうだけど、ほとんどのサーバに接続できるサーバが不正アクセスされたらその時点で他のサーバも既にアウトな状態だと思うので気にしないことにします。

## Powershellサンプルコード

下記gist参照
[https://gist.github.com/sensq/62f529d3b86ce230d69a56a8795ed589](https://gist.github.com/sensq/62f529d3b86ce230d69a56a8795ed589)

※Title, Entry名はスペースを指定することも可能なので、コマンドを組み立てるときにクォートで括っておくと安心
※Powershell 2.0でも動くと思うけど未確認（Enum関連が怪しいかも）

ちなみに、上記コードの大半は最低限必要な情報からオブジェクトを作成するための処理なので、下記のような最初からコマンドを書いたCSVファイルを作ってしまえばImport-Csvを使って数行で書けたりします。（記事書いてる途中で気付いた）
こっちの方がこれ以上無いくらいシンプルで怪しまれないから好かれるかも。

```text:server_list.csv
Name,cmd
Ansible,.\RLogin.exe /ip 192.168.1.10 /port 22 /user root /pass 'password' /title 'Ansible[192.168.1.10]' /entry 'Default_SSH' /inuse
Webサーバ_01,.\RLogin.exe /ip 192.168.1.11 /port 22 /user root /pass 'password' /title 'Webサーバ_01[192.168.1.11]' /entry 'Default_SSH' /inuse
Webサーバ_02,.\RLogin.exe /ip 192.168.1.12 /port 22 /user root /pass 'password' /title 'Webサーバ_02[192.168.1.12]' /entry 'Default_SSH' /inuse
Appサーバ_01,.\RLogin.exe /ip 192.168.1.21 /port 22 /user root /pass 'password' /title 'Appサーバ_01[192.168.1.21]' /entry 'Default_SSH' /inuse
Appサーバ_02,.\RLogin.exe /ip 192.168.1.22 /port 22 /user root /pass 'password' /title 'Appサーバ_02[192.168.1.22]' /entry 'Default_SSH' /inuse
```

```powershell:connect.ps1
$objs = Import-Csv -Encoding Default "server_list.csv"

$objs | Out-GridView -PassThru -Title "接続先を選択して下さい" | %{
  Invoke-Expression $_.cmd
  echo $_.cmd
}
```