6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudFormation でバケット名にハイフンを含む複数の S3 バケットを Fn::ForEach でひな型から一気に作成するのにちょっとハマった話

Posted at

CloudFormation で名前だけが異なる同じ設定の S3 バケットを複数作りたい時に、一つのスタックの中で、バケット名のリストをループして一気に S3 バケットを作れないかと調べたところ、Fn::ForEach 関数を使えば実現できました。

ただし、この方法では、S3 バケット名にハイフンなどの記号を含んでいると、CloudFormation テンプレートの OutputKey に英数字以外の文字を指定できない制約に引っかかるため、テンプレートの書き方を少しだけ工夫する必要がありましたので、手順をメモしておきます。

Fn::ForEach で一気に S3 バケットを作成する CloudFormation テンプレートの全体

S3 バケットは、以下の 3 つの名前で作成します。

  • test01-0001-0018
  • test01-0001-0020
  • test01-0001-0028

デフォルトの暗号化は SSE-KMS とし、特定の IAM ロールからのみ特定のアクションを許可するバケットポリシーも設定します。

ライフサイクルポリシーで 30 日後にオブジェクトを削除するようにします。また、コスト配分タグも設定しておきます。

以上の値は全てパラメーターにしておき、使い回しができるようにしておきました。

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::LanguageExtensions
Description: CloudFormation Template for Create S3 Bucket
Parameters:
  BucketNames:
    Type: CommaDelimitedList
    Default: test01-0001-0018,test01-0001-0020,test01-0001-0028
  AllowedIAMRoleARN:
    Type: String
    Description: Set IAM Role ARN for allowing to access this bucket
    Default: "IAM ロールの ARN をここにセット"
  KMSKeyARN:
    Type: String
    Description: Set KMS Key ARN for encryption bucket
    Default: "SSE-KMS で暗号化するデフォルトの KMS キーの ARN をここにセット"
  CostTagValue:
    Type: String
    Description: Set CostTag Value
    Default: "コスト配分タグの値をここにセット"

Resources:
  "Fn::ForEach::LoopBucketNames":
    - LoopBucketName
    - !Ref BucketNames
    - "CreateBucket&{LoopBucketName}":
        Type: AWS::S3::Bucket
        DeletionPolicy: Retain
        UpdateReplacePolicy: Retain
        Properties:
          AccessControl: Private
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                  SSEAlgorithm: aws:kms
                  KMSMasterKeyID:
                    Ref: KMSKeyARN
          BucketName:
            Ref: LoopBucketName
          LifecycleConfiguration:
            Rules:
              - ExpirationInDays: 30
                Status: Enabled
          OwnershipControls:
            Rules:
              - ObjectOwnership: BucketOwnerEnforced
          PublicAccessBlockConfiguration:
            BlockPublicAcls: True
            BlockPublicPolicy: True
            IgnorePublicAcls: True
            RestrictPublicBuckets: True
          Tags:
            - Key: "コスト配分タグのキーをここにセット"
              Value:
                Ref: CostTagValue
  "Fn::ForEach::LoopBucketPolicyNames":
    - LoopBucketPolicyName
    - !Ref BucketNames
    - "CreatedBucketPolicyFor&{LoopBucketPolicyName}":
        Type: AWS::S3::BucketPolicy
        Properties:
          Bucket:
            Ref: LoopBucketPolicyName
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action:
                  - s3:ListBucket
                  - s3:GetObject
                  - s3:PutObject
                  - s3:DeleteObject
                  - s3:GetObjectTagging
                  - s3:PutObjectTagging
                Effect: Allow
                Resource:
                  - !Sub "arn:aws:s3:::${LoopBucketPolicyName}"
                  - !Sub "arn:aws:s3:::${LoopBucketPolicyName}/*"
                Principal:
                  AWS:
                    Ref: AllowedIAMRoleARN

テンプレートの解説

Fn::ForEach の有効化

CloudFormation テンプレートの中でループを使うためには、テンプレート内に以下の値を追記する必要があります。

Transform: AWS::LanguageExtensions

なお、これによりスタックの作成の際に以下のような確認画面が追加されました。

  • AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。
  • AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。
  • AWS CloudFormation によって、次の機能が要求される場合があることを承認します: CAPABILITY_AUTO_EXPAND

スクリーンショット 2025-09-23 22.46.36.png

S3 バケット名のリストをパラメーターに設定

ここでは "BucketNames" という名前のパラメーターに、カンマ区切り文字列で作成したい S3 バケット名を定義しています。

Parameters:
  BucketNames:
    Type: CommaDelimitedList
    Default: test01-0001-0018,test01-0001-0020,test01-0001-0028

リストからループでリストの値をセットして S3 バケットを作成

ここでは "LoopBucketName" という識別子の変数を設定しました。この変数には、上記で設定した "BucketNames" というリストからループして順に値がセットされていきます。

これを利用して、S3 バケット名に変数 "LoopBucketName" を指定することでリストにある名前で S3 バケットが作成されていく仕組みです。

Resources:
  "Fn::ForEach::LoopBucketNames":
    - LoopBucketName
    - !Ref BucketNames
    - "CreateBucket&{LoopBucketName}":
        Type: AWS::S3::Bucket
        DeletionPolicy: Retain
        UpdateReplacePolicy: Retain
        Properties:
          AccessControl: Private
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                  SSEAlgorithm: aws:kms
                  KMSMasterKeyID:
                    Ref: KMSKeyARN
          BucketName:
            Ref: LoopBucketName

ここでポイントとなるのは、リソースの OutputKey に英数字以外を指定することができないため、今回のようにリストの項目にハイフンを含んでいる場合に変数 "LoopBucketName" をそのまま ${LoopBucketName} と展開してしまうと、以下のようなエラーメッセージが表示されてスタックの作成に失敗します。

OutputKey 'CreateBuckettest01-0001-0018' should be alphanumeric.

このような場合、AWS 公式ドキュメントによると、&{} を使って変数を展開するよう書かれています。

Passing non-alphanumeric characters within the Collection for Fn::ForEach

This example uses the &{} syntax, which allows the non-alphanumeric characters (. and /) in the IP addresses to be passed within the Collection.

そこで、このテンプレートでは以下のように OutputKey の箇所を &{} で変数 "LoopBucketName" を展開するようにしています。

CreateBucket&{LoopBucketName}

あとは同じ仕組みでバケットポリシーも Fn::Each 関数でループして作成するようにしています。

Fn::ForEach 関数の使い方は AWS 公式サイトを参照してください。

スタックから作成された S3 バケットの確認

テンプレートが完成したら、CloudFormation からスタックを作成します。

スクリーンショット 2025-09-23 22.33.47.png

リソースが作成できました。「論理 ID」を見るとバケット名からハイフンが取り除かれた文字列がセットされていることが分かります。

S3 バケットの方もテンプレートで設定した内容で作成できていました。

スクリーンショット 2025-09-23 22.38.11.png

スクリーンショット 2025-09-23 22.41.16.png

変更セットの作成(おまけ)

せっかくなのでスタックの変更セットを作成し、S3 バケットの設定を変えてみます。

ライフサイクルポリシーで 30 日でオブジェクトを削除する設定にしていますが、これを 180 日に変更してみます。

スタックで新規作成の時点のライフサイクルルールを確認します。

スクリーンショット 2025-09-23 21.02.34.png

CloudFormation の変更セットを作成します。手順は割愛しますが、元のテンプレートの YAML の "LifecycleConfiguration" の箇所を修正し、アップロードしています。

LifecycleConfiguration:
  Rules:
    - ExpirationInDays: 180

変更セットが作成できたら、実行する前に差分を確認してみます。

スクリーンショット 2025-09-23 21.06.34.png

差分が期待どおり日数のみ変更となっていることを確認したら、変更セットを実行します。

スクリーンショット 2025-09-23 22.58.41.png

スタックから作成したリソースに変更がかかりました。S3 バケットの方を見てみます。

スクリーンショット 2025-09-23 21.08.46.png

ライフサイクルルールの日数が変更になっていました。

CloudFormation を使うことで、今回のように同じ設定の S3 バケットを複数作るなどの定型の作業を楽に行えるようになりますので、私はもっと勉強して IaC やってるぜと言えるようにしたいと思います。

参考 URL

ガバクラ勢の Kubota さんも Fn::Each 関数を使って S3 バケットを 1,000 個(!?)作ってらっしゃいました。リストをパラメーターストアから呼び出す方法を使われています。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?