🔥

AWS CDK でパラメータを指定し環境依存を改善した話

に公開

はじめに

前回 AWS CDK を使って、CloudFront + S3 環境を構築しましたが、バケット名などが固定値であったため、検証/本番などそれぞれの環境用に TypeScript を作成する必要がありました。
そこでパラメータを利用し、そういった環境依存を解決する方法を調べました。

対象読者

  • AWS CDK の導入を検討している方
  • AWS CDK の運用に悩んでいる方

どのような方法があるか?

この記事が参考になりました。
https://speakerdeck.com/tmokmss/answering-cdk-faqs?slide=23

抜粋

方法 概要 実行方法など
1. Context variable cdk.json や –c オプションで指定 cdk deploy –c env=dev
2. 環境変数 CDK コマンド実行時に環境変数を指定 ENV=dev cdk deploy
3. 各言語のオブジェクト CDK の言語でパラメータをハードコードする 例えば、TypeScript の object で値を指定する
4. AWS Systems Manager Parameter Store CDK 外でパラメータを作成 deploy 時に CFn が値を読込 AWS Systems Manager Parameter Store 経由で値を渡す
5. CfnParameter CloudFormation の Parameter 機能を使う cdk deploy --parameters hoge=fuga

試してみました

実行環境は前回のとおりです。

下記 Stack をサンプルとし、それぞれの方法で実行してみます。
( CDK は TypeScript です。)

my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    console.log("dev");
  }
}

実行結果は以下の通りです。
(プロンプトに dev が表示されれば OK です。)

my-cdk-app> cdk deploy
dev

1. Context variable

CDK 実行時、cdk.json に指定したパラメータを読み込む方法

my-cdk-app/cdk.json
{
  "context": {
    "dev": {
      "envValue": "hoge"
    },
    "prod": {
      "envValue": "fuga"
    }
  }
}
my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const env = this.node.tryGetContext("env");
    const conf = this.node.tryGetContext(env);

    console.log(conf.envValue);
  }
}

実行結果は以下の通りです。
(env=dev 指定時は hoge、env=prod 指定時は fuga が表示されれば OK です。)

my-cdk-app> cdk deploy -c env=dev
hoge
my-cdk-app> cdk destroy -c env=dev

my-cdk-app> cdk deploy -c env=prod
fuga
my-cdk-app> cdk destroy -c env=prod

2. 環境変数

CDK 実行前に環境変数を指定する方法

my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const envValue = process.env.ENV_VALUE ?? "hoge";

    console.log(envValue);
  }
}

実行結果は以下の通りです。

my-cdk-app> $env:ENV_VALUE = "hoge"; cdk deploy
hoge
my-cdk-app> cdk destroy

my-cdk-app> $env:ENV_VALUE = "fuga"; cdk deploy
fuga
my-cdk-app> cdk destroy

3. 各言語のオブジェクト

CDK プロジェクト内で TypeScript 用の config.ts を作り、環境設定を書く方法

my-cdk-app/lib/config.ts
export const config = {
  dev: {
    envValue: "hoge",
  },
  prod: {
    envValue: "fuga",
  },
} as const;

export type EnvName = keyof typeof config;
my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { config, type EnvName } from "./config";

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const envName = (this.node.tryGetContext("env") ?? "dev") as EnvName;

    const conf = config[envName];
    console.log(conf.envValue);
  }
}

実行結果は以下の通りです。
(env=dev 指定時は hoge、env=prod 指定時は fuga が表示されれば OK です。)

my-cdk-app> cdk deploy -c env=dev
hoge
my-cdk-app> cdk destroy

my-cdk-app> cdk deploy -c env=prod
fuga
my-cdk-app> cdk destroy

4. AWS Systems Manager Parameter Store

パラメータを AWS Systems Manager Parameter Store で設定する方式

my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ssm from 'aws-cdk-lib/aws-ssm';

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // SSM に値をセット
    const ssmParameter = new ssm.StringParameter(this, 'set_parameter', {
      parameterName: 'envValue',
      stringValue: 'hoge',
      description: 'Environment Value',
    });

    new cdk.CfnOutput(this, 'EnvValueOutput', {
      value: ssmParameter.stringValue,
      description: 'SSM Parameter Value',
      exportName: 'EnvValue',
    });
  }
}

実行結果は以下の通りです。
(Outputs で hoge が表示されれば OK です。)

my-cdk-app> cdk deploy
Outputs:
MyCdkAppStack.EnvValueOutput = hoge
my-cdk-app> cdk destroy

5. CfnParameter

CDK コマンド実行時に Parameter を指定する方法

my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const param = new cdk.CfnParameter(this, "EnvValue", {
      type: "String",
      default: "hoge",
    });

    new cdk.CfnOutput(this, 'EnvValueOutput', {
      value: param.valueAsString,
    });
  }
}

実行結果は以下の通りです。
(未指定時に hoge、EnvValue=fuga 指定時 fuga が表示されれば OK です。)

my-cdk-app> cdk deploy
Outputs:
MyCdkAppStack.EnvValueOutput = hoge
my-cdk-app> cdk destroy

my-cdk-app> cdk deploy --parameters EnvValue=fuga
Outputs:
MyCdkAppStack.EnvValueOutput = fuga
my-cdk-app> cdk destroy

おまけ:.env ファイルで指定

.env ファイル内に環境依存のパラメータを指定する方法
(個人的にはこれが一番しっくりくる)

  • 実行前に dotenvをインストール
my-cdk-app> npm install dotenv --save
my-cdk-app/.env
ENV1_VALUE=hoge
ENV2_VALUE=fuga
ENV3_VALUE=piyo
my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dotenv from 'dotenv';

export class MyCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    dotenv.config();

    console.log(process.env.ENV1_VALUE);
    console.log(process.env.ENV2_VALUE);
    console.log(process.env.ENV3_VALUE);
  }
}

実行結果は以下の通りです。

my-cdk-app> cdk deploy
hoge
fuga
piyo
my-cdk-app> cdk destroy

やってみた結果(感想)

参考サイトでは、下記の順番でおすすめでした。

私的には4番以外はプログラム変更が必要だったり、CDK実行時にコマンドでパラメータを指定しなきゃいけなかったりで、ちょっと管理や運用が大変だと感じました。
(4 はパラメータ値を秘匿できるのが良い点ですよね!)

個人的な「なれ」もありますが、「おまけ:.env ファイルで指定」が扱いやすいんじゃないかと!

まとめ

今回は CDK の環境依存を解消したくいろいろとやってみました。
CLI(Command Line Interface:コマンドラインインターフェイス)でパラメータを指定する時にタイポしてひどい目に遭ったことがあり、パラメータ入力が苦手 (キライ) なんです。😓
なので、できる限り自分で入力しなくて済む方法が、私的には安心できるので採用しちゃいますね!
この記事が、誰かのお役に立てば幸いです。

参考サイト

株式会社グローバルネットコア 有志コミュニティ(β)

Discussion