【小ネタ】AWS WAF v2 の WebACL (CloudFront用)を東京リージョンから CloudFormation で作成しようとしたら怒られた

作成するリソースがどのリージョンに紐づくものなのかを意識するのは大事ですね。知ってたはずなんですけどね。本当に。

コンバンハ、千葉(幸)です。

言われればそうだよなぁと思わなくもない落とし穴にハマったので、次はハマらないように書いておきます。

目次

やりたかったこと

以下のリソースを一つの CloudFormation スタックで東京リージョンで一気に作成したかったです。

  • CloudFront ディストリビューション
    • AWS::CloudFront::Distribution
  • オリジンアクセスアイデンティティ
    • AWS::CloudFront::CloudFrontOriginAccessIdentity
  • S3 バケット(オリジンに指定)
    • AWS::S3::Bucket
  • S3 バケットポリシー
    • AWS::S3::BucketPolicy
  • AWS WAF v2 WebACL
    • AWS::WAFv2::WebACL

CloudFront はグローバルなサービスでありながら、東京リージョンから CloudFormation を実行して問題なく作成できるので、 WAF も一気に作れるだろうという考えでいました。

やれなかったこと

WebACL の作成時にエラーが吐かれました。

はじめに WebACL 以外のリソースを定義した CloudFormation テンプレートを流してみて問題なくスタックが作成できたので、以下を追加したテンプレートでスタックの更新を試みました。

(お試しで枠だけ作ろうとしたのでルールは一切設定していないです。)

# ------------------------------------------------------------#
# AWS WAFv2
# ------------------------------------------------------------#
  webAclforcloudfront:
    Type: AWS::WAFv2::WebACL
    Properties:
      DefaultAction:
        Allow: {}
      Description: WebACL for CloudFront
      Name: test-cf-webacl
      # Rules:
      #   - Rule
      Scope: CLOUDFRONT
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: test-cf-webacl

ちなみに WebACL は以下の2種類がありますが、その定義は上記ハイライト部で実施しています。

  • リージョナル:ALB もしくは API Gateway に適用
  • グローバル:CloudFrontに適用

スタックの更新を実行したところ、作成に失敗しました。悲しい。

Error reason: The scope is not valid., field: SCOPE_VALUE, parameter: CLOUDFRONT

ひとまず上記のエラーで調べてみたところ、以下のフォーラムがヒットしました。

https://forums.aws.amazon.com/thread.jspa?messageID=927178

同様のエラーが発生している事象で、そこでは「us-east-1 (バージニア北部) で CloudFormation を実行していないからだよ」という回答がなされていました。

そんなことドキュメントに書いてあったっけ!? 暗黙の了解のように扱われても困るよ、明記してくれないと! と思ったのですが、きちんと書いてありました。すみません。

CLOUDFRONT の場合、米国東部 (バージニア北部) リージョン (us-east-1) で WAFv2 リソースを作成する必要があります。

AWS::WAFv2::WebACL - AWS CloudFormation

手前の確認不足で、すみません。

やることにしたこと

WebACL の部分だけ切り出して独立したテンプレートとすることにしました。

ひとまず切り出して以下のような形式とすることで us-east-1 で問題なくデプロイできることを確認しました。

AWSTemplateFormatVersion: '2010-09-09'
Description: "Create webAcl"

Resources:
# ------------------------------------------------------------#
# AWS WAFv2
# ------------------------------------------------------------#
  webAclforcloudfront:
    Type: AWS::WAFv2::WebACL
    Properties:
      DefaultAction:
        Allow: {}
      Description: WebACL for CloudFront
      Name: test-cf-webacl
      # Rules:
      #   - Rule
      Scope: CLOUDFRONT
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: test-cf-webacl

CloudFront の作成を us-east-1 に寄せることも考えたのですが、他のリソースとの兼ね合いを考慮してそれは断念しました。

そうすると残る課題は、作成した WebACL をどう割り当てるかです。

CloudFront ディストリビューションに WebACL を割り当てる設定を CloudFormation で行う場合、AWS::CloudFront::Distribution 内の DistributionConfig の中で定義する必要があります。

Aliases: 
    - String
  CacheBehaviors: 
    - CacheBehavior
  Comment: String
  CustomErrorResponses: 
    - CustomErrorResponse
  DefaultCacheBehavior: 
    DefaultCacheBehavior
  DefaultRootObject: String
  Enabled: Boolean
  HttpVersion: String
  IPV6Enabled: Boolean
  Logging: 
    Logging
  OriginGroups: 
    OriginGroups
  Origins: 
    - Origin
  PriceClass: String
  Restrictions: 
    Restrictions
  ViewerCertificate: 
    ViewerCertificate
  WebACLId: String

AWS::CloudFront::Distribution DistributionConfig - AWS CloudFormation

WebACLId に設定する値として、 WAF v2 の WebACL を適用する場合には ARN を渡す必要があります。

このディストリビューションに関連付ける AWS WAF ウェブ ACL がある場合は、それを指定する一意の識別子。最新バージョンの AWS WAF を使用して作成されたウェブ ACL を指定するには、ACL ARN を使用します

同一のテンプレート内で記述されていれば単純に !GetAtt で引っ張ってこれるのですが、分かれてしまったためそうもいきません。

CloudFormation の Outputs を利用してテンプレートを跨いで参照する方式が使えないかと考えたのですが、リージョンを跨いでの参照はできないようです。

最終的に、CloudFront への WebACL の適用はそこだけ手動で行うか、 WebACL の ARN を手動でスタックのパラメータとして渡すような形で更新をかける方針にしました。

ちょっと納まりが悪いですが致し方ないですね。

おわりに

そう言えばグローバルなリソースだとちょくちょくこういう注意事項があったなーと思い出すハマりポイントでした。

CloudFront は東京リージョンからでも作成できるので、 WebACL も併せて作成できると大変助かります!(アップデートお待ちしています!)

以上、ドキュメント読み取り能力の低さを露呈した千葉(幸)がお送りしました。

おまけに

一通り書き上げた後に気付いたのですが、こちらのブログでも同じような事象に対する記述をされていました。

  • 従来の WAF であれば us-east-1 縛りはない
  • 2020年3月時点ではドキュメントに記載が無かった(と思われる)

東京リージョンで実行する CloudFormation スタックにパラメータを渡すという部分は同じですが、 AWS CLI を用いて工夫されているようですね。

勉強になります。