🔍

AWS CDK v2.20.0 でプレビューになった cdk import を試す

2022/04/12に公開

tl; dr

AWS CDK 以外の方法で作成した AWS リソースを AWS CDK の管理下に取り込むことが出来るようになった。
IaC のリファクタリングが捗るんじゃー^^

やっていく

ユースケース

実際に役に立ったユースケース。

Amazon Kinesis Data Streams を使用して Amazon DynamoDB への変更をキャプチャする、という機能があるのだが、
AWS CDK の L2 コンストラクタの制約で、fromTableArn で取り込んだテーブルに対してこれを設定することができない。
そこで、cdk import してから設定してしまえばいいんじゃね、という発想。

DynamoDB テーブルを手動作成する

AWS CLI でもマネジメントコンソールでも良いので、AWS CDK 管理外の方法で DynamoDB テーブルを作成しておく。
このテーブルが cdk import の対象となる。

$ aws dynamodb create-table --table-name sample-table \
                            --attribute-definitions AttributeName=id,AttributeType=S \
                            --key-schema AttributeName=id,KeyType=HASH \
                            --billing-mode PAY_PER_REQUEST

再度 cdk bootstrap をする (既存プロジェクトのみ)

AWS CDK 2.20.0 以前で cdk bootstrap を行った既存プロジェクトで cdk import を行う場合、下記のように権限不足でエラーになってしまう。

$ npx cdk import
The 'cdk import' feature is currently in preview.
SampleStack

User: arn:aws:sts::123456789012:assumed-role/cdk-xxxxxxxxx-deploy-role-123456789012-ap-northeast-1/aws-cdk-user is not authorized to perform: cloudformation:GetTemplateSummary because no identity-based policy allows the cloudformation:GetTemplateSummary action

AWS CDK を 2.20.0 以降にバージョンアップした後、改めて cdk bootstrap をすることで改善できるので忘れないように。

$ npm update
$ npx cdk bootstrap
❯ npx cdk bootstrap
⏳  Bootstrapping environment aws://123456789012/ap-northeast-1...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Execution policies: arn:aws:iam::aws:policy/AdministratorAccess
CDKToolkit: creating CloudFormation changeset...
✅  Environment aws://123456789012/ap-northeast-1 bootstrapped.

空のスタックで cdk deploy する (新規プロジェクトのみ)

新規プロジェクトとして AWS CDK を書いている場合、一旦空のスタックを cdk deploy しておく。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class SampleStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
  }
}

既存プロジェクトであれば、現状のコードとデプロイされているものが一致していれば問題ない。

インポートしたい AWS リソースのコードを追記して cdk import をする

CDK のコード上にインポートしたい AWS リソースのコードを追加する。
今回は DynamoDB テーブルなので、リソース作成時に設定が必須で後からの変更が難しいパーティションキーは合わせておく。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

import {
  aws_dynamodb as dynamodb,
} from 'aws-cdk-lib';

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

    // インポート対象の AWS リソース
    const table = new dynamodb.Table(this, 'Table', {
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
    });
  }
}

このままいつものように cdk deploy すると新しい AWS リソースが作成されるわけだが、そこを cdk import とする。

$ npx cdk import
The 'cdk import' feature is currently in preview.
SampleStack
SampleStack/Table/Resource (AWS::DynamoDB::Table): enter TableName to import (empty to skip): sample-table
SampleStack: importing resources into stack...
SampleStack: creating CloudFormation changeset...

 ✅  SampleStack
Import operation complete. We recommend you run a drift detection operation to confirm your CDK app resource definitions are up-to-date. Read more here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/detect-drift-stack.html

メッセージが出ているとおり、AWS CloudFormation のドリフト検出で差分をチェックし、必要であれば AWS CDK 側のコードを修正する。

今回の例でいうと、AWS CLI で作成した DynamoDB テーブルはオンデマンドモードで作成している。
一方で AWS CDK は特に指定しない場合、プロビジョニングモードで DynamoDB テーブルを作るので、その部分がドリフトとなってしまう。
CloudFormation のマネジメント コンソールで確認すると、下記のような差分が発生すると思う。

ドリフト検出の結果

このまま cdk deploy をすると AWS CDK のソースコードを正としてドリフトが修正されてしまうので、AWS CDK 側でもオンデマンドモードになるようにコードを修正する必要がある。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

import {
  aws_dynamodb as dynamodb,
} from 'aws-cdk-lib';

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

    const table = new dynamodb.Table(this, 'Table', {
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      // ドリフトを修正
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });
  }
}

修正したコードがドリフトを起こさないかどうか(=インポートしたリソースに悪影響を与えないか)を確認した上で cdk deploy したいところだが、良い方法がないように思える。
cdk deploy --no-execute で変更セットを作ってみたが、変更セット上では DynamoDB テーブルは Modify となってしまった。
ドリフトが起きないことを機械的に確認する手段が欲しいところ。

まあ今回は良い感じに修正できたものとして、cdk deploy する。

$ npx cdk deploy

✨  Synthesis time: 4.98s

SampleStack: deploying...
[0%] start: Publishing ffa23024e31addb6319561a6d27e6d2cdf5a6180653443b40e421bsamplehash:current_account-current_region
[100%] success: Published ffa23024e31addb6319561a6d27e6d2cdf5a6180653443b40e421bfsamplehash:current_account-current_region
SampleStack: creating CloudFormation changeset...

 ✅  SampleStack

✨  Deployment time: 27.3s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:123456789012:stack/SampleStack/858107c0-ba3d-11ec-b152-069473sample

✨  Total time: 32.27s

これで無事に cdk import 完了。

おまけ: Amazon Kinesis Data Streams を使用して Amazon DynamoDB への変更をキャプチャする

本題というか、元々やりたかった Amazon Kinesis Data Streams を使用して Amazon DynamoDB への変更をキャプチャする設定を追加する。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

import {
  aws_kinesis as kinesis,
  aws_dynamodb as dynamodb,
} from 'aws-cdk-lib';

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

    // キャプチャ先のストリームを作成
    const kinesisStream = new kinesis.Stream(this, 'Stream');

    const table = new dynamodb.Table(this, 'Table', {
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      // KDS によるキャプチャ設定
      kinesisStream: kinesisStream,
    });
  }
}

こうして AWS CDK 外で作成した DynamoDB テーブルに対しても Amazon Kinesis Data Streams を使用して変更をキャプチャする設定ができたのであった。

Discussion