それぞれ異なるリージョンの CDK スタックで作成された AWS WAF Web ACL と CloudFront Distribution の紐付けができるのか試してみた

それぞれ異なるリージョンの CDK スタックで作成された AWS WAF Web ACL と CloudFront Distribution の紐付けができるのか試してみた

結果:できました。
Clock Icon2025.01.30

こんにちは、製造ビジネステクノロジー部の若槻です。

グローバルリソースである Amazon CloudFront Distribution に対して紐付けられる AWS WAF Web ACL は、北部バージニア (us-east-1) リージョンに作成される必要があるという制限があります。

https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works-resources.html

その場合に、それぞれ異なるリージョンの AWS CDK スタックで作成された AWS WAF Web ACL と CloudFront Distribution の紐付けができるのか?試す機会があったので共有します。

試してみた

クロススタック参照手法

今回、CDK においてクロススタック参照を行うのですが、その手法としてスタック同士が密結合とならない「パラメーターストア経由」の参照方法を利用します。
https://dev.classmethod.jp/articles/aws-cdk-props-cross-stack-reference-problem-and-handle

Web ACL の作成

まず、北部バージニア (us-east-1) リージョンに Web ACL を作成し、その Arn を ssm パラメータストアに保存します。

main-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
import * as ssm from 'aws-cdk-lib/aws-ssm';

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

    // Web ACL の作成
    const webAcl = new wafv2.CfnWebACL(this, 'WebAcl', {
      defaultAction: { block: {} },
      scope: 'CLOUDFRONT',
      visibilityConfig: {
        cloudWatchMetricsEnabled: true,
        sampledRequestsEnabled: true,
        metricName: 'WafWebAclForCloudfront',
      },
      rules: [],
    });

    // Web ACL の Arn を SSM パラメータストアに保存
    new ssm.StringParameter(this, 'WebAclArn', {
      parameterName: 'web-acl-arn',
      stringValue: webAcl.attrArn,
    });
  }
}

上記をデプロイします。

パラメータストアに格納される Arn は次のようになります。

aws ssm get-parameter \
  --name "/web-acl-arn" \
  --region us-east-1 \
  --query 'Parameter.Value' \
  --output text
> arn:aws:wafv2:us-east-1:XXXXXXXXXXXX:global/webacl/WebAcl-KpSSk9rbWP3W/7bf29e1e-17bb-4c26-aeb6-3d6c23d5be34

CloudFront Distribution の作成、Web ACL との紐付け

次に、Web ACL とは別リージョンである東京 (ap-northeast-1) リージョンに CloudFront Distribution を作成し、Web ACL との紐付けを行います。

ここで、ap-northeast-1 リージョンの CDK スタックから us-east-1 リージョンの SSM パラメータストアに格納された Web ACL の Arn を取得するために、次のようなカスタムリソースを利用します。

lib/cross-region-get-parameter-custom-resource.ts
import { custom_resources } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CrossRegionGetParameter extends Construct {
  public readonly parameterValue: string;

  constructor(
    scope: Construct,
    id: string,
    props: {
      readonly parameterName: string;
      readonly region: string;
    }
  ) {
    super(scope, id);

    const getParameter = new custom_resources.AwsCustomResource(
      this,
      'GetParameterCustomResource',
      {
        onUpdate: {
          service: 'SSM',
          action: 'getParameter',
          parameters: {
            Name: props.parameterName,
          },
          physicalResourceId: custom_resources.PhysicalResourceId.of(
            props.parameterName
          ),
          region: props.region,
        },
        policy: custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
          resources: custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
        }),
        installLatestAwsSdk: false,
      }
    );

    this.parameterValue = getParameter.getResponseField('Parameter.Value');
  }
}

カスタムリソースで取得した Web ACL の Arn を CloudFront Distribution に紐付けます。

lib/main-2-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';

import { CrossRegionGetParameter } from './cross-region-get-parameter-custom-resource';

export class Main2Stack extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    // SSM パラメータストアから Web ACL の Arn を取得
    const webAclArnParameter = new CrossRegionGetParameter(
      this,
      'WebAclArnParameter',
      {
        parameterName: 'web-acl-arn',
        region: 'us-east-1',
      }
    );

    // CloudFront Distribution の作成
    new cloudfront.Distribution(this, 'Distribution', {
      defaultBehavior: {
        origin: new origins.HttpOrigin('classmethod.jp'),
      },
      // Web ACL を CloudFront Distribution に紐付け
      webAclId: webAclArnParameter.parameterValue,
    });
  }
}

上記をデプロイします。

作成された CloudFront Distribution のセキュリティ設定にマネジメントコンソールからアクセスすると、Web ACL が紐付けられていることが確認できました。

トラブルシュート

Web ACL が us-east-1 リージョンに作成されていない場合

冒頭で述べた通り、CloudFront Distribution に紐付ける Web ACL は北部バージニア (us-east-1) リージョンに作成される必要があるため、us-east-1 リージョン以外で Web ACL を作成しようとすると次のようなエラーが発生します。

WebAcl Resource handler returned message: "Error reason: The scope is not valid., field: SCOPE_VALUE, parameter: CLOUDFRONT

おわりに

それぞれ異なるリージョンの CDK スタックで作成された AWS WAF Web ACL と CloudFront Distribution の紐付けができるのか試してみました。

作成に利用した CloudFormation スタックのリージョンは影響しないと想定はしていたのですが、実際に可能であると確認できて良かったです。

参考

https://dev.classmethod.jp/articles/aws-cdk-parameter-store-get-value-two-methods/
https://dev.classmethod.jp/articles/parameter-store-across-regions-with-aws-cdk-custom-resource/

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.