[AWS] CDKでSSM パラメータストアに値をセットする

## パラメータストアとは
- AWS SSM パラメータストアは、AWSのサービスの設定値やシークレット値、アプリケーションで使用する環境変数等を安全に保存するためのサービスである。
- 階層型ストレージである。

## CDKでパラメータストアに値をセットする
早速、CDKでパラメータストアに値をセットしてみる。
```typescript:environments-stack.ts

import { App, Stack, StackProps } from 'aws-cdk-lib'
import * as ssm from 'aws-cdk-lib/aws-ssm';

export class EnvironmentStack extends Stack {
  constructor(scope: App, id: string, props: StackProps) {
    super(scope, id, props);
    new ssm.StringParameter(this, 'parameter', {
      parameterName: '/example/EXAMPLE_KEY',
      stringValue: `dummy`,
    });
  }
}

```
`aws-cdk-lib/aws-ssm`から`StringParameter`をimportしてきて、`parameterName`（キー）、`stringValue`（値）を設定することでパラメータストアに値がセットされる。
![スクリーンショット 2024-01-05 14.44.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655825/904b940a-4fd1-7c1e-702c-8c995f008a68.png)
（デフォルトでデータ型はtextのStringになります）

### .envファイルから読み込む
これはCDKの話ではない気がするが、`.env`ファイルで環境変数を管理しており、その値をパラメータにセットする場合は以下になる。
```typescript:environments-stack.ts
import { App, Stack, StackProps } from 'aws-cdk-lib'
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as dotenv from 'dotenv'

export class EnvironmentStack extends Stack {
  constructor(scope: App, id: string, props: StackProps) {
    super(scope, id, props);
    dotenv.config();
    new ssm.StringParameter(this, 'parameter', {
      parameterName: '/example/EXAMPLE_KEY',
      stringValue: `${process.env.EXAMPLE_KEY}`,
    });
  }
}
```
```.env
EXAMPLE_KEY="dummy"
```

### シークレットマネージャーから値を取得してセットする
DBの接続情報など、SecretManagerにある値をパラメータストアにセットする場合は、まずシークレットマネージャーから値を取得してくる必要がある。
```typescript:environments-stack.ts
import { App, Stack, StackProps } from 'aws-cdk-lib'
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as dotenv from 'dotenv'
import * as sm from 'aws-cdk-lib/aws-secretsmanager';

export class EnvironmentStack extends Stack {
  constructor(scope: App, id: string, props: StackProps) {
    super(scope, id, props);
    dotenv.config();
    //シークレットマネージャーから値を取得
    const secretJson = sm.Secret.fromSecretAttributes(this, 'SecretStrings', {
      secretCompleteArn: "arn:aws:secretsmanager:ap-northeast-1:xxxxxxx:secret:xxxxxx-xxxx-xxxxx"
    });
    const secret_DB_NAME = secretJson.secretValueFromJson('dbname').unsafeUnwrap();
    const secret_DB_PASSWORD = secretJson.secretValueFromJson('password').unsafeUnwrap();
    const secret_DB_USERNAME = secretJson.secretValueFromJson('username').unsafeUnwrap();
    const secret_DB_HOST = secretJson.secretValueFromJson('host').unsafeUnwrap();
    
    new ssm.StringParameter(this, 'parameter', {
      parameterName: '/example/EXAMPLE_KEY',
      stringValue: secret_DB_NAME,
    });
  }
}
```

`secretCompleteArn`はシークレットマネージャーのマネコンで確認できます。

## 注意点
### スタックについて
今回はパラメータストアにセットするためだけのスタック`environments-stack.ts`を作成している。理由は以下の2つ。
- 他のデプロイスタックに干渉しないようにするため。
- ローカルの`.env`ファイルにある環境変数をセットしているため、他のメンバーやCIによるデプロイでは値参照できずエラーとなってしまうため。

パラメータストアの値をデプロイするたびに毎回更新しても良いが、そもそも設定情報なので、変更がそこまでないのを考慮してスタックを分離してる。ただしデメリットとして、パラメータストアに変更が必要な場合にインフラ管理者に頼んで設定してもらう必要がある。（マネコンで操作できればあまり関係ないと思います）

### SecretManagerの取得方法について
上述した"スタックについて"でも述べたように、スタックを分けている & 変更が少ない点からSecretManagerから値を取得してくる時に、ARNを直接指定している。
（実際に今回の事例ではRDSの接続情報のみだったので一度取得してセットできればほぼ問題がないため）
`fromSecretNameV2`を使用することでシークレット名から値を取得できると思ったが、「a dash and six random alphanumeric characters at the end of the secret name」は含まれない。
```typescript
const secret = secretsmanager.Secret.fromSecretNameV2(
    this,
    "Secret",
    "MySuperSecret"
);
const secretArn = secret.secretArn;
```
↑で取得した`secretArn`は **末尾のランダムな文字列を含まない** ためエラーになる。
公式↓
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_secretsmanager.Secret.html#secretarn

なので今回はマネコンからFullARNを見つけてそれを元に値を取得した。