CodeBuild から EC2 に SSH して Jenkins の息の根を止める

# 背景

SSHして何かやる定形/不定形業務を廃止するのは、なかなか困難が伴います。

* SSH接続を許可するか許可しないか、くらいしかできない
    * いやまあ、Linux側でがんばってユーザー/グループを組めばいいんですけど
* 極少数のメンバーしか、保安上の理由から、許可できない
* 極少数のメンバーは、能力高しで多忙なのに、たいしたことないSSH作業に忙殺される

以前は、SSH定形業務をJenkinsジョブとして形式知化していましたが、

* JenkinsのEC2インスタンスが
    * SPOFとなる。単純に台数を増やせばいいものでもない。
    * 闇のダンジョンとなりがち
* だからといって、今からJenkinsfileとかすごく頑張るのは、あまりに低い山の頂上を目指しているのではないか
* Jenkinsおじさんの雇用が生まれてしまう

など問題がありました。このうち一部の問題について、CodeBuildジョブにすることで上手く扱えそうな感触が得られたので、まとめます。

ちなみに、ユニットテストやデプロイの類は、CircleCIやCodeBuildに移植済みで、Jenkinsには本当に「ポチるとSSHして何かするジョブ」だけが残っています。

# 作る

CodeBuildからEC2にSSHで何かさせてみます。

## EC2 SSH Keypair、EC2インスタンスを作り、SSHできることを確認する

これは何も躓くところ無いですね。Keypair名は「hoge」で作ります。hoge.pemがダウンロードできてるものとして記述します。お手元から接続できることまで確認してください。
 
## EC2 SSH KeypairのSSH秘密鍵をSSMパラメータストアに保管する

先程のSSH秘密鍵をSSMパラメータストアに暗号化保管します。

https://console.aws.amazon.com/systems-manager/parameters でパラメータの作成して、

<img width="848" alt="スクリーンショット 2019-06-01 12.32.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22947/cbef7c2b-f350-f3d2-dbc4-45e6a207e6fa.png">

先程のSSH秘密鍵をまるっとコピーして、

```bash
cat hoge.pem | pbcopy
```

AWS System Managerのパラメータストアの「値」にベタッとして、暗号化保管するので「安全な文字列」で、パラメータの作成。

<img width="823" alt="スクリーンショット 2019-06-01 12.40.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22947/fa3ef2fe-fb0f-4902-70e0-c64d9de73f71.png">

AWSのEC2 SSH Keypairで作成した鍵なら1700文字くらいなので大丈夫ですが、手元のsshコマンドで生成した場合は、SSH秘密鍵の文字数に注意してください。4096文字までです。

## CodeBuildジョブを作る

ジョブの組み立ては以下を参考に。ところかまわずSSHできるのはマズいでしょうから、VPC内でCodeBuildが起動するよう組むのが望ましいかと思います。

* [いますぐ使うCodeBuild - Qiita](https://qiita.com/sasasin/items/6e5f7001cba350851d49)

ポイントは以下。

* CodeBuildの環境変数で、プレーンテキストではなくパラメータとして、SSMパラメータストアから読むようにする
    * 作例では、CodeBuildの環境変数「SSH_PRIV_KEY」に、SSMパラメータストアの「ssh_private_key」を読み込ませています
* CodeBuildジョブに付けるIAMロールに、SSMパラメータストアを読む権限を付けてあげる
* sshコマンドをインストール済みのコンテナを使うと面倒が少ないです。たとえば https://hub.docker.com/r/ansible/ansible-runner とか。

buildspec.yaml は以下のように。sshコマンドで、初回アクセス時の(yes/no?)に答えようがないので抑止したりしてます。

```yaml
version: 0.2
phases:
  pre_build:
    commands:
      - mkdir ~/.ssh
      - echo "${SSH_PRIV_KEY}" > ~/.ssh/id_rsa
      - chmod 0700 ~/.ssh
      - chmod 0600 ~/.ssh/id_rsa
  build:
    commands:
      - ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ec2-user@172.16.0.6 -p 22 "pwd;ls -alF;hostname"
```

見てのとおりで、buildspec.yaml でシェル芸を極めたコマンドラインをsshに流し込むのはまあまあ大変です。

接続先のEC2内にシェルスクリプトの類を設置して、CodeBuildはそれを呼ぶだけにするのが良いんじゃないかと思っています。

そのシェルスクリプトは、AnsibleのGitHubリポジトリか、アプリケーションコードのGitHubリポジトリのどちらかで管理して。など。

# CodeBuildの権限制御をどうするか

SSHに比べると、いくらか制御しやすいんじゃないでしょうか。

Jenkinsなら [Jenkinsでユーザーに権限を付与してアクションを制限する - Qiita](https://qiita.com/west-hiroaki/items/410a76c96ad57c8cb547) の手法で権限制御できました。

AWS なら、

* IAMで権限制御する
    * IAMグループを作成し、どのIAMグループは、CodeBuildジョブ定義を作成/編集/削除できる、実行/中断できる、などを構成する
* 誰が、いつ、どのジョブに、何をしたか、の
    * 雑なトラッキングは、CodeBuildのジョブのログで見る
    * 詳細なトラッキングは、AWS CloudTrailでやる

# SSHの不定形業務はどうすんの

不定形だと思ってる業務も、Excel手順書(w)に書き出して、何度かやれば、これが定形業務で、ここは人間による条件分岐判断があって、その先は定形業務で、などと見えてくるはずなので、定形業務の部分をCodeBuildに切り出していくといいんじゃないですかね。

# SSMセッションマネージャーもあるじゃん

これとかですね。非常に良いと思っていて、技術指針に従って、どちらでも使ったらいいのではないかと。

* https://dev.classmethod.jp/cloud/aws/201902_jawsdays2019-e-session-manager/