🕊️

【AWS CDK】物理名が必須なリソースのL2コンストラクトで物理名をオプショナルにする

2022/03/27に公開

既存のL2コンストラクトに物理名を設定したい方はリソースのpropsにxxxNameというプロパティがあるので設定してください。
AWS::S3::Bucketだと次のようになります。なお、AWS CDKでリソースに物理名を設定することは非推奨ですのでご注意ください。

new s3.Bucket(this, 'Bucket', {
  bucketName: '付けたい物理名'
})

この記事の対象者

  • L2コンストラクトを実装する初心者CDKコントリビュータ
  • オリジナルコンストラクトをより良い設計にしたいCDKユーザ

何故必要なのか

CloudFormationリソースには物理名を明示的に指定しなければならないリソースがあります。次のリソースはその一例です。

AWS CDKのコンストラクト設計において、ユーザがこれらのリソースに物理名を指定しない場合はCDK側で値を設定する必要があります。この課題を解決するため、AWS CDKにはコンストラクト内でユニークなIDを生成する仕組みが組み込まれているので紹介します。

結論

コンストラクトをこのように実装します。

import { Lazy, Names, Resource } from '@aws-cdk/core';

export class Example extends Resource {
  constructor(scope: Construct, id: string, props: ExampleProps) {
    super(scope, id, {
      physicalName: props.exampleName ?? 
        Lazy.string({ produce: () => Names.uniqueId(this) }),
    });
  }
}

説明

@aws-cdk/coreパッケージのNamesクラスにはコンストラクト内でユニークなIDを生成する静的メソッドuniqueIdがあります。こちらを利用することで任意のIDを生成することができます。

names.ts
/**
 * Functions for devising unique names for constructs. For example, those can be
 * used to allocate unique physical names for resources.
 */
export class Names {
  /**
   * Returns a CloudFormation-compatible unique identifier for a construct based
   * on its path. The identifier includes a human readable portion rendered
   * from the path components and a hash suffix.
   *
   * @param construct The construct
   * @returns a unique id based on the construct path
   */
  public static uniqueId(construct: Construct): string {
    const node = Node.of(construct);
    const components = node.scopes.slice(1).map(c => Node.of(c).id);
    return components.length > 0 ? makeUniqueId(components) : '';
  }
}

なお、物理名はResourceクラスのコンストラクタに渡す必要がありますが、子クラスからsuper呼び出しを完了するまでthisの値は使用できません。そのため、Lazyクラスを使用して評価タイミングを遅延させることが必要です。

Lazy.string({ produce: () => Names.uniqueId(this) })

経緯

AWS CDKでAmazon CloudWatch RUMの実装をしていた際、AWS::RUM::AppMonitorは物理名を明示的に設定する必要があったため、RFC時にappMonitorNameをユーザーから受け取るpropsの必須プロパティにしていました。RFCをレビューしていただいたところ、レビュアーから

物理名はオプショナル型とし、CDK側で物理名を設定してください。

とコメントを頂き、これに対処したので記事にしました。
https://github.com/aws/aws-cdk-rfcs/pull/408#discussion_r832566719

AWS CDKのコントリビュートを始める方の手伝いになれば幸いです。

Discussion