バージョン管理された人

subversionで管理されてます

CDK カスタムリソースで tagging

AWS のリソースではタグをつけられる。 CloudFormation はそれにならってタグをつける仕組みが存在し、 CDK もそれにならう形でタグをつけられるようになっている。 タグをつけることで作成者等の情報をリソースに入れ込むことができ、誰がどのような目的でどれくらいの期限までリソースを管理しているのかといった情報をリソースにメタ情報として載せることができる。 CDK でもプリミティブなリソースに対してはタグをつけることが可能となっているが、カスタムリソースを利用する場合、このタグをつける機能を自前で提供しなければならない。 今回はこの自前でタグをつけるシステムについて解説する。

ITaggable

CDK にはタグ付けを支援してくれる ITaggable というインターフェースが提供されている。 この ITaggable は tags というプロパティを持つことを強制するが、この tags で ITaggable をインプリメントしたコンポーネントをタグ管理することができるようになる。 ただし、これだけでは明示的に tags へタグへの追加/削除を行うメソッドを生やすなりする必要があり、 aws-cdk-lib.Tags.of(component).add 等を利用してカスタムリソースへタグを追加/削除することができない。 なぜならば、タグの追加タイミングはわからないが、カスタムリソースへタグを追加する場合は付けられたタグの情報を上手いことをカスタムリソースのイベントハンドラーへと渡してあげる必要があるからだ。

タグの追加/削除は tags に対して行われるので、ここへ登録されたタグの情報をカスタムリソースに渡すことが必要となる。

Aspect

ITaggable ではタグの管理のみが行えるようになるが、これを適切に tagging されたタイミングでタグをつけるようなロジックを実装する必要がある。 そんなときに IAspect というものを利用する。

これ自体はタグが作成/削除されたときに呼ばれるコンポーネントのライフサイクルを実装するためのインターフェースである。 これを利用することで、明示的にタグを追加しないでも、 aws-cdk-lib.Tags.of(component).add 等でタグの追加がなされたときにタグの情報を更新することが可能になる。

CfnResource

aws-cdk-lib でカスタムリソースを実装する際には core ライブラリの CustomResource を利用するのが一般的だと思うが、実はタグを実装するときにはある条件では使えない。

タグを上手くカスタムリソースのイベントハンドラーに渡すにはハンドラーへ渡すプロパティを利用するか、イベントハンドラーの環境変数を利用することになる。 いずれにせよ、 visit メソッドが呼ばれたときにこれら情報を更新することになる。 もし、プロパティを更新する方法をとる場合、 core ライブラリの CustomResource にはこのリソースを更新する方法がない。 したがって、この状況では CustomResource を使えないため、変わりに同じ core ライブラリにある CfnResource を利用する。

この CfnResource には addPropertyOverride というメソッドがあって、これに上書きしたいプロパティ名と値を指定することで、既に登録済みのプロパティの値を更新することができるようになっている。 したがって、タグの情報をプロパティとして指定しつつ、都度タグの情報が更新されるたびにこの addPropertyOverride メソッドを呼び出すことでタグ情報を更新するという形でいつでも追加/削除されたタグの最新情報をイベントハンドラーが知ることができる。

実装

以上のものを利用してカスタムリソースにタグをつけるロジックを実装していく。

まずは ITaggable を実装することを明示する。

import { Construct } "construct";
import * as cdk from "aws-cdk-lib";

export class MyConstruct
  extends Construct
  implements cdk.ITaggable
{
  // ...
}

tags 自体は先程も書いたがプロパティは単純に実装だけすればよい。

import { Construct } "construct";
import * as cdk from "aws-cdk-lib";

export class MyConstruct
  extends Construct
  implements cdk.ITaggable
{
  tags: cdk.TagManager;

  constructor(scope: Construct, id: string) {
    // ...
    this.tags = new cdk.TagManager(
      cdk.TagType.KEY_VALYE,
      // ドキュメント的にはタグ管理したい CloudFormation のリソースタイプ
      // (たとえば AWS::EC2::Instance)を指定するのが一般的と思われる。
      "What::You:Want"
    );
  }
}

次に、作りたいカスタムリソースを作る。

import { Construct } "construct";
import * as cdk from "aws-cdk-lib";

export class MyConstruct
  extends Construct
  implements cdk.ITaggable
{
  // ...
  private readonly resource: cdk.CfnResource;

  constructor(scope: Construct, id: string) {
    // ...
    this.resource = new cdk.CfnResource(
      this,
      "Resource",
      {
        type: "AWS::CloudFormation::CustomResource,
        properties: {
          // この ServiceToken には aws-cdk-lib.custom-resources.Provider の
          // serviceToken プロパティを指定する。
          ServiceToken: provider.serviceToken,
          // 以下にリソースで利用したいプロパティを指定する。
        }
      }
    );
  }
}

カスタムリソースができたら cdk.Aspect.of(scope).add でタグを都度更新するようにする。

import { Construct } "construct";
import * as cdk from "aws-cdk-lib";

export class MyConstruct
  extends Construct
  implements cdk.ITaggable
{
  // ...
  private readonly resource: cdk.CfnResource;

  constructor(scope: Construct, id: string) {
    // ...
    cdk.Aspect.of(this).add({
      visit: () => {
        this.resource.addProperty("tags", this.tags.renderTags());
      }
    });
  }
}

あとはカスタムリソースのイベントハンドラー側に渡ってくるイベントにある ResourceProperties から最新のタグ情報をプロパティで取得することが可能となる。

全体

import { Construct } "construct";
import * as cdk from "aws-cdk-lib";

export class MyConstruct
  extends Construct
  implements cdk.ITaggable
{
  tags: cdk.TagManager;
  private readonly resource: cdk.CfnResource;

  constructor(scope: Construct, id: string) {
    // ...
    this.tags = new cdk.TagManager(
      cdk.TagType.KEY_VALYE,
      // ドキュメント的にはタグ管理したい CloudFormation のリソースタイプ
      // (たとえば AWS::EC2::Instance)を指定するのが一般的と思われる。
      "What::You:Want"
    );
    this.resource = new cdk.CfnResource(
      this,
      "Resource",
      {
        type: "AWS::CloudFormation::CustomResource,
        properties: {
          // この ServiceToken には aws-cdk-lib.custom-resources.Provider の
          // serviceToken プロパティを指定する。
          ServiceToken: provider.serviceToken,
          // 以下にリソースで利用したいプロパティを指定する。
        }
      }
    );
    cdk.Aspect.of(this).add({
      visit: () => {
        this.resource.addProperty("tags", this.tags.renderTags());
      }
    });
  }
}

まとめ

カスタムリソースにタグをつけるには ITaggable を実装して Aspect で visit するように仕込めばできる。

参考