🗒️

AWS CDKのカスタムリソースでAWS-managed prefix listsを扱う

2024/06/25に公開

はじめに

2024/6月時点でaws cdkでS3やCloudFrontのAWS-managed prefix listsを扱うには、プレフィックスのIDを事前に把握する必要があります。

例えば、以下のようにすれば、SecurityGroupのインバウンドに許可を追加できます。
ここでは、pl-02cd2c6bをマネジメントコンソールなどから把握しておきます。

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

    const vpc = new Vpc(this, 'Vpc', {
      natGateways: 0,
    });

    const sg = new SecurityGroup(this, 'SecurityGroup', {
      vpc: vpc,
    });

    // us-east-1のDynamoDB用のAWS-magaged prefix lists
    sg.connections.allowFrom(Peer.prefixList('pl-02cd2c6b'), Port.HTTPS);
  }
}

問題点というほどでもないのですが、リージョンごとに異なるプレフィックスIDを事前に用意しておくことはめんどうです。
そこで、カスタムリソースを使用して入力されたプレフィックスリスト名からプレフィックスIDを動的に取得するAwsManagedPrefixListというConstructを作ってみました。

AwsManagedPrefixList

作ったもの

このブログを書きながら作ったので誤字脱字はあるかもしれません。
https://github.com/raihalea/cdk-managed-prefix-list-construct?tab=readme-ov-file

使い方

main.tsをみればだいたいわかると思います。

nameにプレフィックスリスト名を入れることで、プレフィックスIDを取得できます。
CDK上でリージョンを動的に取得するようにすれば、いちいちコードを書き換えなくても問題ありません。
https://docs.aws.amazon.com/vpc/latest/userguide/working-with-aws-managed-prefix-lists.html

main.ts
import { AwsManagedPrefixList } from './utils/aws-managed-prefix-list';

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

    const vpc = new Vpc(this, 'Vpc', {
      natGateways: 0,
    });

    const sg = new SecurityGroup(this, 'SecurityGroup', {
      vpc: vpc,
    });

    // S3 AWS-managed prefix lists
    const s3PrefixList = new AwsManagedPrefixList( this, 'S3PrefixList',
      { name: `com.amazonaws.${Aws.REGION}.s3` },
    ).prefixList;

    sg.connections.allowFrom(Peer.prefixList(s3PrefixList.prefixListId), Port.HTTPS);

    // CloudFront AWS-managed prefix lists
    const cloudfrontPrefixList = new AwsManagedPrefixList(
      this,
      'CloudfrontOriginPrefixList',
      { name: 'com.amazonaws.global.cloudfront.origin-facing' },
    ).prefixList;

    sg.connections.allowFrom(Peer.prefixList(cloudfrontPrefixList.prefixListId), Port.HTTPS);
  }
}

仕組み

元ネタはこのissueです。
https://github.com/aws/aws-cdk/issues/13668

やっていることは簡単で、AwsCustomResourceを使ってDescribeManagedPrefixListsCommandをたたいています。
実行するためにIAMポリシーも付与していますが、Resouce句にはドキュメントを参考にすべてを指定しています。
https://docs.aws.amazon.com/vpc/latest/userguide/managed-prefix-lists.html#managed-prefix-lists-iam

physicalResourceIdがいまいちよくわかってないので、適当にかわらなそうなIDにしておきました。

AwsCustomResourceの説明は以下がわかりやすいです。
https://dev.classmethod.jp/articles/create-custom-resources-with-aws-cdk-without-using-lambda-functions/

export class AwsManagedPrefixList extends Construct {
  public readonly prefixList: IPrefixList;

  constructor(scope: Construct, id: string, { name }: AwsManagedPrefixListProps) {
    super(scope, id);

    const prefixListId = new AwsCustomResource(this, 'GetPrefixListId', {
      installLatestAwsSdk: true,
      onUpdate: {
        service: '@aws-sdk/client-ec2',
        action: 'DescribeManagedPrefixListsCommand',
        parameters: {
          Filters: [
            {
              Name: 'prefix-list-name',
              Values: [name],
            },
          ],
        },
        physicalResourceId: PhysicalResourceId.of(`${id}-${this.node.addr.slice(0, 16)}`),
      },
      policy: AwsCustomResourcePolicy.fromStatements([
        new PolicyStatement({
          effect: Effect.ALLOW,
          actions: ['ec2:DescribeManagedPrefixLists'],
          resources: ['*'],
        }),
      ]),
    }).getResponseField('PrefixLists.0.PrefixListId');

    this.prefixList = PrefixList.fromPrefixListId(this, 'PrefixList', prefixListId);
  }
}

実行結果

GitHubに上がっているCDKを実行した結果です。
ちゃんとインバウンドに許可が追加されました!
実行結果

おわりに

簡単ですが、AwsCustomResourceを使ってAWS-managed prefix listsのプレフィックスIDを取得するコンストラクタを作りました。

内部で使う場合には利用するリージョンもある程度見えていると思いますが、GitHubなどで公開するときに、プレフィックスIDを取得する方法が欲しくて作ってみました。

簡単にできるかわりにLambdaが増えるという問題点が生まれるのが気になりポイント
Construct Hubに上げるかは悩み中。公開するなら将来名前が衝突しなさそうな名前に変えたいところ

Discussion