自分用のTeratermのマクロです

# はじめに

サーバにログインする機会が増えてきたので、自分用の[Teratermマクロ](https://teratermproject.github.io/manual/5/ja/macro/howtorun.html)を整理しました。

**参考資料🙏**
[Tera Term ヘルプ 目次](https://teratermproject.github.io/manual/5/ja/)
[TTL コマンドリファレンス](https://teratermproject.github.io/manual/5/ja/macro/command/index.html)

## テラタームマクロでログイン

- 色々とベタ書き & 作業ログ不要なら6行くらい。
- チャレンジレスポンス認証の場合..
  - /ssh /auth=Challenge にする（/2 を消す）
  - SSH ver2 はサポート外 → [SSH接続](https://teratermproject.github.io/manual/5/ja/usage/ssh.html)

``` login.ttl
host = '**ip or hostname**'
user = '**user**'
pw = '**pw**'

; SSH接続 22番ポート宛 SSH ver2 password認証
sprintf2 cmd_ssh '%s:22 /ssh /2 /auth=Password /user=%s /passwd=%s' host user pw
connect cmd_ssh
end
```

- マクロ名でセット & 作業ログ取得なら👇な感じ
  - [`strcopy`](https://teratermproject.github.io/manual/5/ja/macro/command/strcopy.html)とかでもいいかも
    - `strcopy param1 0 3 prefix  ; 0-3文字目をprefixにセット`

``` login_host_withlog.ttl
call SetLoginParams
; SSH接続 22番ポート宛 SSH ver2 password認証
sprintf2 cmd_ssh '%s:22 /ssh /2 /auth=Password /user=%s /passwd=%s' host user pw
connect cmd_ssh

call MakeLogDir
call BuildLogPath
logopen log_path 0 1
end

;*****************************************
; login_**host**_hoge.ttl みたいなマクロ名にする
:SetLoginParams

strsplit param1 '_' 3 ; 分割数は1から->最大値9
host = groupmatchstr2
user = '**user**'
pw = '**pw**'

return

;*****************************************
; マクロのあるフォルダに log フォルダをつくる
:MakeLogDir

getdir ttl_dir
sprintf2 log_dir '%s\log' ttl_dir

foldersearch log_dir
if result = 0 then
  foldercreate log_dir
endif

return

;*****************************************
; log_dirはグローバル。\log\log_**マクロ名**_YYYYmmDDHHMMSS.txt
:BuildLogPath

getdate now_date '%Y%m%d'
gettime now_time '%H%M%S'
ttl_name = param1
strreplace ttl_name 1 '\.ttl' ''

sprintf2 log_path '%s\log_%s_%s%s.txt' log_dir ttl_name now_date now_time

return
```

- べた書き配列でセット
  - ファイル名だけでセットするのもシンドイので
  - 別途キー列とかをつくるのもいいかも
  - バージョンによってはifにthenが必須とのこと -> [if, then, elseif, else, endif](https://teratermproject.github.io/manual/5/ja/macro/command/ifthenelseif.html)

```
hosts_count = 2 ; 要素数は事前にセット
strdim hosts hosts_count

hosts[0] = 'host1,opeuser1,pass1,next_host1'
hosts[1] = 'host1,opeuser2,pass2,next_host2'

i = 0 
while i < hosts_count
  strsplit hosts[i] ',' 4
  host = groupmatchstr1
  user = groupmatchstr2
  pw = groupmatchstr3
  next = groupmatchstr4

  strcompare host **input**
  if result = 0 break

  i = i + 1
endwhile

if i = hosts_count exit
```

- 小ネタ: ログアウト時にターミナルを閉じる
  - TERATERM.INIでAutoWinClose=off

- 小ネタ: ポップ（known hostsリストに追加する）の抑止
  - /nosecuritywarning を足す。<- [TTSSH command line](https://teratermproject.github.io/manual/4/ja/commandline/ttssh.html)
  - 接続先サーバが初見であることに気づけなくなるのでセキュリティ的にはNG。クローズドな環境向け。

## そのほか

- コマンド一覧(.txt)を読みこんでコマンド発行（空白行をいれない）
  - 日本語が含まれるファイル名だとバグる? 
  - 経験上べた書きで大体事足りる。ファイル分かれるとメンテだるい。
  - ファイルを閉じないと実行時にロックエラー..
  - 素のコマンドが残るので、ドキュメント用にはいいかも

```
; コマンド一覧(.txt)はマクロと同じフォルダにある前提
getdir ToolDir

sprintf2 cmdlistPath '%s\%s' ToolDir "cmd_list.txt"
fileopen cmdlist cmdlistPath 0

:loopstart
  filereadln cmdlist cmd
  if result <> 0 then ;読み込めなかった場合
    goto loopend
  endif

  wait '$'
  sendln cmd
  goto loopstart
:loopend
```

- ターミナルの標準出力を受け取る
```
/*
recvln を続ける。（1つ目には [プロンプト]$コマンド が入るので捨てる）
recvln の中身は inputstr にセットされる。

＜作りたいコマンド～grep & substring＞
 ⇒ grep USER test.conf | awk '{print substr($0,index($0,"=")+1)}'
  1セットで括らず、出力に含めない方で挟んで連結すればOK（"～"'～''～'）※文字列定数
*/

wait '$'
sendln 'grep USER test.conf | awk'" '{print substr($0,index($0,"'"=")+1)}'"'"
recvln
recvln
USER=inputstr

messagebox USER "title"
```

- SSHポートフォワーディング
```
; ssh -L 9990:10.1.1.10:443 -L 9991:10.1.1.11:443 user@host -p 22 と同義
host_name = '**ip or hostname**'
user_name = '**user**'
password = '**pw**'
lport_dest_list = '9990:10.1.1.10:https,9991:10.1.1.11:https' ;カンマ区切り

sprintf2 cmd_ssh '%s:22 /ssh /2 /auth=Password /user=%s /passwd=%s /ssh-L%s' host_name user_name password lport_dest_list
connect cmd_ssh
```
<details><summary>補足説明</summary>
GPT3.5曰く

SSHポートフォワーディングは、RFC 4254やRFC 4253など、SSHプロトコルに関連するRFCでその概念や手法が定義されています。

SSHポートフォワーディングは、安全な通信を確立したSSHトンネルを通じて、ネットワーク上のリモートリソースに安全にアクセスするための仕組みです。主にローカルマシンとリモートサーバー間でのTCPポートの転送を可能にするためのもので、ローカルマシンからリモートサーバーへの接続を暗号化し、セキュリティを確保します。

[teraterm マクロで複数のSSH転送設定を組み込む](https://qiita.com/Kiyoshi-T/items/1031bd1bdb91df0915f8)
[TTSSHによるSSHの設計と実装](https://teratermproject.github.io/manual/5/ja/reference/sourcecode.html#ttssh)
</details>

## PowerShellから呼んでみる

PowerShellから呼ぶメリット？
1. パターンマッチ(*)で複数のマクロ（.ttl）をまとめて実行
2. 作業ログに後処理ができる or 作業ログをネタに次のマクロ起動とか..

→ 1.は作業の工夫で大体なんとかなるので、2.の需要があるかがポイント。
　はじめからPowerShellで呼ぼうとせず、別途でログを（で）何かするネタあるか考える..特に思いつかなければやらなくてOK

- 呼ばれる側のTeratermマクロ
  - `strlen` とかでケアしないとPowerShell起動専用になってしまう
    - `strlen param2`
      `if result = 0 then ..`

``` login_fromPS.ttl
host = param2
user = param3
pw   = param4

; SSH接続 22番ポート宛 SSH ver2 password認証
sprintf2 cmd_ssh '%s:22 /ssh /2 /auth=Password /user=%s /passwd=%s' host user pw
connect cmd_ssh
end
```

- 呼ぶ側のPowerShellスクリプト その1
  - 引数（`host = param2` など）あるとExeのパスと-Argumentlist が要る
  - ハッシュじゃなくて配列で十分かも
  - ログのファイルパスorフォルダをPowerShellで指定するのもアリ
  - ログをいじりたければ大体 Start-Processに-Waitが要るはず
``` sample1.ps1
$params = @"
192.168.1.1,user,password
192.168.1.2,user,password
"@

$logins = @()
$params.split("`n") | foreach {
  $logins += @{
    'host' = $_.split(",")[0]
    'user' = $_.split(",")[1]
    'pw'   = $_.split(",")[2]
  }
}

$ttls   = Get-ChildItem -Path $PSScriptRoot -Filter "login_fromPS.ttl"
$ttlExe = Join-path 'C:\Program Files (x86)\teraterm' 'ttpmacro.exe'

$ttls | foreach {
  $ttl = $_
  $logins | foreach {
    Start-Process -FilePath $ttlExe -Argumentlist $ttl, $_.host, $_.user, $_.pw -wait
  }
}
```

- 呼ぶ側のPowerShellスクリプト その2
  - 前述のようなマクロ(.ttl)を用意。ファイル名をいじってコピーして実行×N 
  - 雛形を置くフォルダ、コピーマクロを展開するフォルダは分けた方が無難かと.. 

```sample2.ps1
$renameMap = @"
getconf_host1_logname1,getconf_host1_logname1.ttl
getconf_host1_logname1,getconf_host2_logname2.ttl
"@

$orgDir = "**org Directory**"
$workDir = "**work Directory**"

Remove-Item -Path (Join-Path $workDir "/*") -Confirm:$false # ワークフォルダ直下を一掃

$renameMap.split("`n") | foreach {
  Copy-Item (Join-Path $orgDir $_.split(",")[0]) (Join-Path $workDir $_.split(",")[1])
}

$ttls = Get-ChildItem -Path $workDir -Filter "*.ttl"
$ttls | foreach { Start-Process $_ -Wait}

$logDir = "**log Directory**" # Join-Path $workDir "log"
# ログの後処理とか、ログからパラメータつくって何かやる
```

- 小ネタ: CLIから実行すると`$PSScriptRoot`はnull..
  - [about_Automatic_Variables](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-5.1#psscriptroot)
  - [Powershellスクリプトを「ファイル名を指定して実行」で即座に実行する](https://zenn.dev/takeaship/articles/execute_psscript_immediately)

- 小ネタ: Sleep
  - ttl: pause 5、PS: Start-Sleep -Seconds 5

## まとめ雑感？

マクロ自体のファイル名で挙動を制御するのは邪道だと思うので、自分用に留めておくのが無難です。