📝

今から始める CDK 入門 #4

2023/11/09に公開

今から始める CDK 入門シリーズの 4 回目です。

各回については以下のリンクからご覧ください。

  1. 今から始める CDK 入門 #1
  2. 今から始める CDK 入門 #2
  3. 今から始める CDK 入門 #3

前回は実践!AWS CDK #3 テスト | DevelopersIO を参考に以下の内容を実施しました。

  • Fine-grained assertions でのテスト

今回は実践!AWS CDK #4 Context | DevelopersIO を実施します。

Context とは

ブログによると、CloudFormation におけるパラメータに該当するのが CDK の Context とのことです。
ドキュメントにもキー/バリューのペアである旨の記載があります。
ランタイムコンテキスト - AWS Cloud Development Kit (AWS CDK) v2

コンテキスト値は、アプリケーション、スタック、またはコンストラクトに関連付けることのできるキー/バリューペアをいいます。

CloudFormation のパラメータはスタック作成時や更新時に都度指定可能で、テンプレート内から参照できる機能です。
パラメータ - AWS CloudFormation

パラメーターを使用すると、スタックを作成または更新するたびにテンプレートにカスタム値を入力できます。

Context やパラメータの詳細についてはブログおよびドキュメントをご参照ください。
ブログでは以下の 2 つ方法を試しています。

  • プロジェクトの cdk.json ファイル
    • デフォルト値の設定に利用
  • CDK コマンドの --context オプション
    • デフォルト値以外の設定に利用

ブログ内でも紹介されている通り、値の取得には construct.node.tryGetContext メソッドを使用します。
ドキュメントでは各プログラミング言語ごとでの値の取得方法も紹介されていますので、TypeScript 以外での実装については上述のドキュメントをご参照ください。

実装

まずは cdk.json ファイルを使用する方法です。
ブログと同様に cdk.json ファイルの context オブジェクト内末尾にキー/バリューを追記します。

cdk.json
{
  "app": "npx ts-node --prefer-ts-exts bin/devio.ts",
  "watch": {
    "include": [
      "**"
    ],
    "exclude": [
      "README.md",
      "cdk*.json",
      "**/*.d.ts",
      "**/*.js",
      "tsconfig.json",
      "package*.json",
      "yarn.lock",
      "node_modules",
      "test"
    ]
  },
  "context": {
    "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
    "@aws-cdk/core:checkSecretUsage": true,
    "@aws-cdk/core:target-partitions": [
      "aws",
      "aws-cn"
    ],
    "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
    "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
    "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
    "@aws-cdk/aws-iam:minimizePolicies": true,
    "@aws-cdk/core:validateSnapshotRemovalPolicy": true,
    "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
    "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
    "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
    "@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
    "@aws-cdk/core:enablePartitionLiterals": true,
    "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
    "@aws-cdk/aws-iam:standardizedServicePrincipals": true,
    "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
    "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
    "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
    "@aws-cdk/aws-route53-patters:useCertificate": true,
    "@aws-cdk/customresources:installLatestAwsSdkDefault": false,
    "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
    "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
    "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
    "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
    "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
    "@aws-cdk/aws-redshift:columnId": true,
    "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
    "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
    "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
    "@aws-cdk/aws-kms:aliasNameRef": true,
    "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
    "@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
    "@aws-cdk/aws-efs:denyAnonymousAccess": true,
    "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
    "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
    "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
    "systemName": "devio", 追記
    "envType": "stg" 追記
  }
}

続いて devio-stack.ts で上記のデフォルト値を取得します。
なお、コードは CDK V2 用のコードですのでブログのコードとは異なる部分もあります。前回までのブログとの相違点については冒頭のこれまでのブログをご参照ください。

devio-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { CfnVPC } from 'aws-cdk-lib/aws-ec2';

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

    const systemName = this.node.tryGetContext('systemName'); // 追記
    const envType = this.node.tryGetContext('envType'); // 追記

    new CfnVPC(this, 'Vpc', {
      cidrBlock: '10.0.0.0/16',
      tags: [{ key: 'Name', value: `${systemName}-${envType}-vpc` }] // 更新
    });
  }
}

前述した通り、値の取得には construct.node.tryGetContext メソッドを使用します。
construct については this となっているだけです。
systemNameenvType という変数に cdk.json から取得したデフォルト値を格納し、tags で結合しているだけです。

確認

cdk synth で確認します。

$ cdk synth

Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: devio-stg-vpc ← 想定通り

想定通り、タグの値に cdk.json から取得したデフォルト値を結合した値が指定されています。

デフォルト値以外の値を使用する場合は cdk synth--context オプションを付与します。
なお、--context オプションは -c と略しても機能するためブログ内では -c を使用しています。

$ cdk synth -c systemName=starwars -c envType=prd

Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: starwars-prd-vpc ← デフォルト値以外の値になっている

--context オプションを付与することでデフォルト値をオーバーライドしていることが確認できました。

テスト時の注意

ブログにも記載されている通り、テスト実行時には Context の値を取得できないため回避策も紹介されています。

  • undefined が来ることを想定してテストコードを記述する
  • cdk.App() のイニシャライザーに Context を指定する

試しに 'Value': 'undefined-undefined-vpc' に変更しない状態でテストを実行したところ、テストが失敗しました。

$ npm run build && npm test
 FAIL  test/devio.test.ts (10.078 s)
  ✕ Vpc (51 ms)

  ● Vpc

    Template has 1 resources with type AWS::EC2::VPC, but none match as expected.
    The 1 closest matches:
    Vpc :: {
      "Properties": {
        "CidrBlock": "10.0.0.0/16",
        "Tags": [
          {
            "Key": "Name",
    !!       Expected devio-stg-vpc but received undefined-undefined-vpc
            "Value": "undefined-undefined-vpc"
          }
        ]
      },
      "Type": "AWS::EC2::VPC"
    }

      11 |
      12 |   template.resourceCountIs('AWS::EC2::VPC', 1)
    > 13 |   template.hasResourceProperties('AWS::EC2::VPC', {
         |            ^
      14 |     CidrBlock: '10.0.0.0/16',
      15 |     Tags: [{ 'Key': 'Name', 'Value': 'devio-stg-vpc' }]
      16 |   });

      at Template.hasResourceProperties (node_modules/aws-cdk-lib/assertions/lib/template.js:1:2556)
      at Object.<anonymous> (test/devio.test.ts:13:12)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        10.285 s
Ran all test suites.

ブログに記載されている通り、テストでは Context の値が undefined になっていました。
ブログ内の回避策も試したところ、テストは成功したのでテスト時には一工夫必要なこともわかりました。

まとめ

今回の手順でブログと大きく異なる点はありませんでした。
ただし、テスト時には一工夫必要でした。

次回は実践!AWS CDK #5 サブネット | DevelopersIO を実施します。

参考資料

Discussion