いまさら Ansible Vault (3): Vault パスワードを環境ごとの AWS アカウントの SSM パラメータストアで管理する

[前々回の記事](https://qiita.com/3244/items/52911e4c8448ad433eb5)で Ansible ソース上の機密情報を Vault で暗号化して、[前回の記事](https://qiita.com/3244/items/ebe6f71aedc333634f41)で開発・検証・本番環境の Vault パスワードを別々にしました。
しかしまだ Vault パスワードを管理するために、PC 内のファイルや環境変数を使用する必要があります。

そこで今回は以下の要件を実現します。

- Vault パスワードを暗号化してクラウドで管理する
- どのメンバーがどの環境の機密情報を取得できるかの許可や有効期限などを、クラウドの IAM で一元管理する

参考: [Ansible Vault — Ansible Documentation](https://docs.ansible.com/ansible/2.9_ja/user_guide/vault.html)

- 確認環境:
  - Ansible 2.10.7
  - Ubuntu 20.04 / Debian 10 (buster)

# AWS SSM パラメータストア

クラウドで Vault パスワードを暗号化して管理するためのストアとして、AWS の [SSM パラメータストア（Systems Manager Parameter Store）](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html)を使用します。

AWS のストアには、より機密情報の管理に特化した Secrets Manager もあるのですが、今回は無料で利用できて汎用的な SSM パラメータストアを選択しました。

参考:
[AWSのParameter StoreとSecrets Manager、結局どちらを使えばいいのか？比較 - Qiita](https://qiita.com/tomoya_oka/items/a3dd44879eea0d1e3ef5)
[AWS Systems Manager Parameter Store - AWS Systems Manager](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html)

# AWS アカウント

開発・検証・本番環境ごとに AWS アカウントを別々にして、計 3 つの AWS アカウントを使用します。

1 つの AWS アカウントで全環境を管理することもできますが、本記事の文脈では AWS アカウントを別々にすることで、個々のメンバーに対して環境ごとのアクセス権を与えるかどうかを簡単に管理できることが大きなメリットになります。

参考:
[AWS導入前に知っておきたかった「AWSアカウント設計」 - IST techブログ](https://www.is-tech.co.jp/ist-techblog/aws%E5%B0%8E%E5%85%A5%E5%89%8D%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%80%8Caws%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E8%A8%AD%E8%A8%88/)
[AWS アカウント作成の流れ | AWS](https://aws.amazon.com/jp/register-flow/)

# AWS IAM ユーザーと認証情報ファイル

各メンバーのために、アクセスを許可する環境の AWS アカウントごとに IAM ユーザーを作成して、アクセスキーが書かれた認証情報ファイル（`~/.aws/credentials`）を配布します。

参考:
[IAM ユーザー - AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_users.html)
[名前付きプロファイル - AWS Command Line Interface](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html)

各環境用のプロファイル名は、環境名および Vault ID と同じ「dev」「stg」「prd」とします。

以下は全環境の AWS アカウントにアクセスするための認証情報ファイルの例です。

```ini:~/.aws/credentials
[dev]
aws_access_key_id = AXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
region = ap-northeast-1
output = yaml

[stg]
aws_access_key_id = AYYYYYYYYYYYYYYYYYYY
aws_secret_access_key = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
region = ap-northeast-1
output = yaml

[prd]
aws_access_key_id = AZZZZZZZZZZZZZZZZZZZ
aws_secret_access_key = zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
region = ap-northeast-1
output = yaml
```

# AWS CLI のインストール

Vault パスワード取得用スクリプトが AWS SSM パラメータストアにアクセスできるように、各メンバーの PC やコンテナに AWS CLI をインストールします。

```shell:
curl -L "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" \
  -o "awscliv2.zip" \
  && unzip -oq "awscliv2.zip" \
  && sudo aws/install
```

参考: [AWS CLI バージョン 2 のインストール、更新、アンインストール - AWS Command Line Interface](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html)

# Vault パスワードを各 AWS アカウントのパラメータストアに保存する

パラメータストアに保存するパラメータ名は全環境で共通の `MY_VAULT_PASSWORD` とし、環境ごとの Vault パスワードは各ディレクトリ内のファイル「MY_VAULT_PASSWORD.secret」に書かれているとします。

```shell:
.
├── dev
│   └── MY_VAULT_PASSWORD.secret  # dev 環境用の Vault パスワードが書かれている
├── stg
│   └── MY_VAULT_PASSWORD.secret  # stg 環境用の Vault パスワードが書かれている
└── prd
    └── MY_VAULT_PASSWORD.secret  # prd 環境用の Vault パスワードが書かれている
```

:::note warn
*.secret ファイルをコミットしてしまわないように注意しましょう。
:::

以下は例では、各環境の Vault パスワードを *.secret ファイルから読み取って、それぞれの AWS アカウントのパラメータストアに保存します。

```shell:
for profile in $(ls .); do
  # 環境ごとのディレクトリ（dev|stg|prd）内の *.secret ファイルを対象とする
  for file in $(find ${profile} -name "*.secret" -print); do
    # パラメータ名はファイル名と同じ（拡張子除く）
    name="$(basename "${file}" .secret)"
    # AWS プロファイルは、環境名およびディレクトリ名と同じ（dev|stg|prd）
    aws ssm put-parameter \
      --type    SecureString \
      --name    "${name}" \
      --value   "$(cat "${file}")" \
      --profile "${profile}"
  done
done
```

　参考: [put-parameter — AWS CLI 2.2.45 Command Reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ssm/put-parameter.html)

# Vault パスワード取得スクリプトの改良

[前回の記事](https://qiita.com/3244/items/ebe6f71aedc333634f41#%E4%BB%8A%E5%9B%9E-vault-%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%8C%E8%A4%87%E6%95%B0%E3%81%AE%E5%A0%B4%E5%90%88)のとおり環境変数 [ANSIBLE_VAULT_IDENTITY_LIST](https://docs.ansible.com/ansible/2.9_ja/reference_appendices/config.html#envvar-ANSIBLE_VAULT_IDENTITY_LIST) に,
`{ID 1}@{スクリプト 1}, {ID 2}@{スクリプト 2}, ...` のように Vault ID と スクリプトのペアをいくつでも指定できます。

```shell:consts.env
ANSIBLE_VAULT_IDENTITY_LIST="dev@/workspace/vault_pass_dev.sh, stg@/workspace/vault_pass_stg.sh, prd@/workspace/vault_pass_prd.sh"
```

以下のスクリプトは、上記の `dev@/workspace/vault_pass_dev.sh` で「dev」に紐づけられているスクリプト「vault_pass_dev.sh」を改良する例です。

```shell:/workspace/vault_pass_dev.sh
#!/bin/bash
AWS_PROFILE="dev"  # dev 環境用
aws ssm get-parameter \
  --name    "MY_VAULT_PASSWORD" \
  --with-decryption \
  --output  json \
  --profile ${AWS_PROFILE} \
  | jq -r .Parameter.Value \
  | tr -d '\n'
```

パラメータ名 `MY_VAULT_PASSWORD` として保存された暗号文字列を復号化してパラメータストアから取得し、値だけを抽出して標準出力に出力します。

参考: [get-parameter — AWS CLI 2.2.45 Command Reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ssm/get-parameter.html)

「stg」「prd」環境用のスクリプトも同様に改良します。

以上で、`ansible-playbook` や `ansible-vault` などのコマンドを実行する時に `--profile` オプションで指定した環境用の Vault パスワードを、それぞれの AWS アカウント内の SSM パラメータストアから自動的に復号化して取得できるようになりました。

# 前の記事

- [いまさら Ansible Vault で変数とファイルを暗号化して、Vault パスワードをスクリプトで管理する - Qiita](https://qiita.com/3244/items/52911e4c8448ad433eb5)
- [いまさら Ansible Vault (2): せめて開発・検証・本番環境の Vault パスワードを別々に - Qiita](https://qiita.com/3244/items/ebe6f71aedc333634f41)