CDKのAwsCustomResourceでAWSのAPIを呼ぶ

aws

CDKでリソース作成後にしか取得できない値を渡す必要があるとき Describe API を呼びたいことがある。通常、このような CloudFormation の管理外のことを行うには自前のLambdaを用意して CustomResource を作成することになるが、AWS の API を呼ぶだけだったら AwsCustomResource を用いることができる。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as eks from 'aws-cdk-lib/aws-eks';
import { KubectlV27Layer } from '@aws-cdk/lambda-layer-kubectl-v27';

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

    const vpc = new ec2.Vpc(this, 'vpc', {
      vpcName: 'cdk-call-aws-api-test',
      ipAddresses: ec2.IpAddresses.cidr('10.18.0.0/18'),
      maxAzs: 2,
      ipProtocol: ec2.IpProtocol.DUAL_STACK,
    })

    const cluster = new eks.Cluster(this, 'cluster', {
      vpc,
      clusterName: 'cdk-call-aws-api-test',
      version: eks.KubernetesVersion.V1_27,
      kubectlLayer: new KubectlV27Layer(this, 'KubectlV27Layer'),
      defaultCapacity: 1,
      defaultCapacityInstance: new ec2.InstanceType('m5.large'),
      ipFamily: eks.IpFamily.IP_V6,
    })

    const customResourceParams = ({
      onCreate: {
        service: 'eks',
        // https://docs.aws.amazon.com/eks/latest/APIReference/API_DescribeCluster.html
        action: 'describeCluster',
        parameters: {
          name: cluster.clusterName,
        },
        physicalResourceId: cdk.custom_resources.PhysicalResourceId.fromResponse("cluster.arn") // = PhysicalResourceId.of(cluster.clusterArn)
      },
      onUpdate: {
        service: 'sts',
        action: 'getCallerIdentity',
				// parameters: {},
      },
      policy: cdk.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
        resources: cdk.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
      }),
      timeout: cdk.Duration.minutes(5),
    })

    const clusterInfo = new cdk.custom_resources.AwsCustomResource(this, 'EksDescribeCluster', customResourceParams);
    new cdk.custom_resources.AwsCustomResource(this, 'EksDescribeCluster2', customResourceParams);
  
    new cdk.CfnOutput(this, 'ServiceIpv6CidrOutput', {
      value: clusterInfo.getResponseField("cluster.kubernetesNetworkConfig.serviceIpv6Cidr"),
    })
  }
}

これをデプロイすると次のような Role が作成され onCreate の eks.describeCluster の値が Output に出力される。

カスタムリソースの置き換え判定に用いられる physicalResourceId は重複してもエラーにならなかった。

その後 timeout などの値を変えても同じ出力になったが、Lambda に渡される parameters の値を変えてデプロイすると onUpdate の sts.getCallerIndentity が呼ばれ Output の getResponseField() で対象フィールドがなく失敗するようになった。また onUpdate を消して再度デプロイしても同様のエラーが発生した。

onCreate のデフォルト値は onUpdate なので Describe のようにリソースを作成しない API の場合は onUpdate だけ渡し、リソースを作成する場合は後片付けのために onDelete も渡すと良いように思う。