ECSのコンテナ定義からパラメータストアが参照できるようになった！

* 2018/12/18 Fargate対応を追記
* 2019/1/23 Secrets ManagerのSecrets参照について補足を追記

## はじめに
ECSのコンテナ定義を眺めていたところ見慣れない文言が。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/d74f24d8-daca-1858-1b0e-f06d47d0b630.png)

>'valueFrom'フィールドを使用して、AWS Systems ManagerのパラメータストアキーまたはARNを指定することもできます。 ECSは実行時にコンテナに値を注入します。

やったぜ！

ドキュメントも更新されていました。

**Specifying Sensitive Data**
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html

というわけで、もしDBパスワードのようなセンシティブ情報を環境変数に
直接入れてしまっている場合は、パラメータストアから参照するようにしましょう。約束です。

~~注意点としては、2018/11/16時点でFargateには対応していません。~~
**2018/12/18 追記**
Platform Version 1.3.0 で Fargateにも対応しました。
ただしSecrets Managerの参照はまだ対応していません。
https://aws.amazon.com/jp/about-aws/whats-new/2018/12/aws-fargate-platform-version-1-3-adds-secrets-support/


## やってみる
### ECS コンテナエージェントのアップデート
コンテナエージェントのバージョンが1.22.0以降である必要があります。
既存のコンテナインスタンスで実行するにはエージェントの更新が必要です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/fa8a2a67-2482-60a9-8306-36b5a9173644.png)

現在のコンテナインスタンスが1.21.0を実行していましたので以下の手順を参考に更新を行います。
**Updating the Amazon ECS Container Agent**
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-agent-update.html

LinuxのECS最適化AMIであればコンソールまたはAWS CLIから更新が可能です。
コンソールの場合は、コンテナインスタンスの詳細ページからエージェントの更新を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/037ec4f1-915e-1b5a-0911-9c1def02b6f8.png)

AWS CLIの場合は以下のコマンドです。

```shell-session
$ aws ecs update-container-agent --cluster cluster_name --container-instance container_instance_id
```

### パラメータストアの作成
ECSから参照させるパラメータストアを作成します。
ここでは以下のように設定しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/64d2b9a8-d1a0-00b6-f283-1da98fd904be.png)

名前: ecs-db-password
タイプ: 安全な文字列
KMSの主要なソース: 現在のアカウント
KMSキーID: alias/aws/ssm
値: password

### タスク実行ロールへの権限追加
ECSがパラメータストアを参照できるようにするため、
タスク実行ロールに以下のような権限を追加します。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "secretsmanager:GetSecretValue", ★オプション
        "kms:Decrypt"　★オプション
      ],
      "Resource": [
        "arn:aws:ssm:<region>:<aws_account_id>:parameter/ecs-db-password",
        "arn:aws:secretsmanager:<region>:<aws_account_id>:secret:<secret_name>",　★オプション
        "arn:aws:kms:<region>:<aws_account_id>:key:<key_id>"　★オプション
      ]
    }
  ]
}
```

secretsmanager:GetSecretValue および kms:Decryptはオプションです。
secretsmanager:GetSecretValue: パラメータストアから更にSecrets Managerを参照させる場合のみ必要です。
kms:Decrypt: カスタムKMSキーを使用する場合のみ必要です。
今回はどちらも使用していません。

以下のようにインラインポリシーで追加しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/3c5b611f-ff90-3d97-19e9-056003de4841.png)

### コンテナ定義の変更
タスク定義で新しいリビジョンを作成し、コンテナ定義を編集します。
環境変数で任意のキーを入力し、ValueFromを選択します。値にはパラメータ名を入力します。
異なるリージョンのパラメータストアを参照する場合はARNでの入力が必須です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/1bff987c-72d6-b60a-a23b-1eec8a476f9c.png)

JSONで直接タスク定義を編集する場合には、containerDefinitionsに以下のように追加します。

```JSON
    "containerDefinitions": [
~~省略
            "secrets": [
                {
                    "name": "DB_PASSWORD",
                    "valueFrom": "ecs-db-password"
                }
            ],
~~以下略
```

### 動作確認
後は通常どおりにサービス、タスクを起動します。
タスク起動後、コンテナに定義されている環境変数を確認すると、
パラメータストアの値が設定されていることが確認できます。

```shell-session
# docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' 01dbaed2e060
AWS_EXECUTION_ENV=AWS_ECS_EC2
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/xxxxxxxxxxxxxxxxxxx
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/xxxxxxxxxxxxxxxxxxxxxxxxxx
DB_PASSWORD=password ★パラメータストアの値が設定されている
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
APACHE_RUN_USER=www-data
APACHE_RUN_GROUP=www-data
APACHE_LOG_DIR=/var/log/apache2
```

## Secrets Managerの値を参照させる場合
前述した通り、パラメータストア経由でSecrets Managerの値を参照させることができます。

### Secrets Manager シークレットの作成
ここでは以下の通りRDSデータベースの認証情報を作成しました。
確認用のため、自動ローテーションは無効にしています。
ユーザー名: rdsuser
パスワード: rdspassword
暗号化キーを選択: DefaultEncryptionKey
データベースを選択: 任意のDBインスタンス
シークレット名: dev/aurora/secret

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/ebb05182-798e-115e-cb20-f90e65f71b72.png)

### タスク実行ロールへの権限追加
secretsmanager:GetSecretValue を追加で設定します。

**Referencing AWS Secrets Manager Secrets from Parameter Store Parameters**
https://docs.aws.amazon.com/systems-manager/latest/userguide/integration-ps-secretsmanager.html
ここでは以下のように設定しています。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "secretsmanager:GetSecretValue"
      ],
      "Resource": [
        "arn:aws:ssm:<region>:<aws_account_id>:parameter:/aws/reference/secretsmanager/dev/arurora/secret",
        "arn:aws:secretsmanager:<region>:<aws_account_id>:secret:dev/aurora/secret",
      ]
    }
  ]
}
```

### タスク定義の作成
~~Secrets Managerのシークレットを参照する場合、パラメータ名は/aws/reference/secretsmanager/
で始まる必要があります。~~
**2019/1/23追記**
ECS側のアップデートにより上記のような名称にしなくとも、直接Secrets Managerを
参照できるようになったようです。

```json
"containerDefinitions": [
    {
        "secrets": [
            {
                "name": "environment_variable_name",
                "valueFrom": "arn:aws:ssm:region:aws_account_id:parameter/parameter_name"
            }
        ]
    }
]
```
**Specifying Sensitive Data**
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html

以上です。
参考になれば幸いです。