AWS CDK(TypeScript)のL2 Constructで設定できないプロパティを設定する方法

2022.08.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

AWS CDKでは、CloudFromation Resourceの実装を直接表現したLow Level ConstructL1 Construct)と、実装のインターフェース(プロパティ)が抽象化されたHigh Level ConstructL2 Construct)があります。

全ての種類のAWS Resourceに対してL2 Constructが提供されているわけではありませんが、一般的にはL2 Constructを使った方がより少ないコードの記述で実装が可能となります。(今回は触れませんが、さらに上位のL3 Constructなんてのもあります。)

しかしこのL2 Constructは抽象化されているゆえに、L1で設定可能なプロパティがL2では設定できないパターンがあります。

今回は、AWS CDK(TypeScript)のL2 Constructで設定できないプロパティを設定する方法を確認してみました。

確認してみた

次のAWS CDK(TypeScript)の実装があります。

lib/aws-app-stack.ts

import { Construct } from 'constructs';
import {
  aws_s3,
  aws_glue,
  Stack,
  StackProps,
  RemovalPolicy,
} from 'aws-cdk-lib';
import * as glue_alpha from '@aws-cdk/aws-glue-alpha';

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

    // データ格納バケット
    const dataBucket = new aws_s3.Bucket(this, 'dataBucket', {
      bucketName: `data-${this.account}-${this.region}`,
      removalPolicy: RemovalPolicy.DESTROY,
    });

    // データカタログ
    const dataCatalog = new glue_alpha.Database(this, 'dataCatalog', {
      databaseName: 'data_catalog',
    });

    // データカタログテーブル
    const dataGlueTable = new glue_alpha.Table(this, 'dataGlueTable', {
      tableName: 'data_glue_table',
      database: dataCatalog,
      bucket: dataBucket,
      s3Prefix: 'data/',
      partitionKeys: [
        {
          name: 'device_id',
          type: glue_alpha.Schema.STRING,
        },
      ],
      dataFormat: glue_alpha.DataFormat.JSON,
      columns: [
        {
          name: 'userId',
          type: glue_alpha.Schema.STRING,
        },
        {
          name: 'count',
          type: glue_alpha.Schema.FLOAT,
        },
      ],
    });
  }
}

AWS GlueのTableを@aws-cdk/aws-glue-alphaのL2 Constructで実装しています。このConstructに対してPartition Projectionの設定をしたいのですが、このL2 Constructではそのためのプロパティへはアクセスできません。

そこで、次の2つの方法により設定を行ってみます。

方法その1:addPropertyOverrideで設定

まず、addPropertyOverrideを使う方法です。大まかに言うと、L2で実装したConstructのL1 Constructを取得し、設定したいプロパティをオーバーライドしています。

lib/aws-app-stack.ts

    // データカタログテーブルへのPartition Projectionの設定
    const cfnTable = dataGlueTable.node.defaultChild as aws_glue.CfnTable;
    cfnTable.addPropertyOverride('TableInput.Parameters', {
      'projection.enabled': true,
      'projection.device_id.type': 'injected',
      'storage.location.template':
        `s3://${dataBucket.bucketName}/data/` + '${device_id}',
    });

詳細を説明します。

始めにdataGlueTable.node.defaultChildによりL2 ConstructのdataGlueTableからDefault ChildとなるNodeを取得しています。L2 Constructでは複数のConstruct nodeが作成される場合があるため、デフォルトの子要素(ここではGlue Table)がこれにより取得できます。

そして取得したConstruct nodeをas aws_glue.CfnTableと型アサーションし、L1 Constructとして扱えるようにしています。これによりこのConstructでaddPropertyOverrideメソッドが利用可能となります。

addPropertyOverrideは、すべてのL1 Constructのクラスで利用可能なクラスです。設定したいプロパティのパスと値を引数として指定します。

public addPropertyOverride(propertyPath: string, value: any): void

方法その2:直接設定

次に直接Parameterを設定する方法です。

lib/aws-app-stack.ts

    // データカタログテーブルへのPartition Projectionの設定
    (dataGlueTable.node.defaultChild as any).tableInput.parameters = {
      'projection.enabled': true,
      'projection.device_id.type': 'injected',
      'storage.location.template':
        `s3://${dataBucket.bucketName}/data/` + '${device_id}',
    };

dataGlueTable.node.defaultChildによりL2 Constructのデフォルトの子要素のConstruct nodeを取得するところまでは方法1と同じです。異なるのは、取得したConstruct nodeに対してnode.<parameter path> = <value>のように設定したいプロパティのパスと値を引数として直接指定しているところです。これにより方法1のaddPropertyOverrideによるものと同様の設定が可能です。

しかしanyによる型付けをしているため、ESLintなどを使用している環境ではビルドエラーになる場合があるので注意が必要です。特段の理由がなければaddPropertyOverrideを使う方法1で良いかと思います。

参考

以上