🐵

CloudFormationのStackSetsをやってみる

2022/11/06に公開

2022/11/05 CloudFormation StackSetsのAPIに更新がありました。

https://aws.amazon.com/jp/about-aws/whats-new/2022/11/aws-cloudformation-stacksets-insights-stack-instances-set-operations/?nc1=h_ls

  • DescribeStackSetOperationで、失敗したスタックの数を返すようになりました
  • ListStackInstancesで、operation IDを返すようになりました

そもそもStackSetsを使ったことがなかったのですが、やってみました。

基本知識、準備

スタックセットとは、1つのテンプレートで、複数リージョン、複数アカウントにスタックを作成する機能です。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacksets-concepts.html

準備として、IAM Roleが2つ必要です。こちらで書いてあるYAMLテンプレートを使わせていただきました。手順も丁寧に書かれているので、このページでは割愛します。

https://dev.classmethod.jp/articles/cloudformation-stacksets-sample-sns-topic/

複数リージョンにデプロイして失敗させたい

今回は、失敗したときにAPIを使った出力を見たいので、そのためのテンプレートを用意します。リージョンを固定したリソースをいろいろなリージョンのLambdaに紐付けて失敗させることにしました。

こちらからSQSとLambdaのマッピングをしているテンプレートを拝借してきました。

Type: AWS::Lambda::EventSourceMappingで、Lambdaの名前かArnと、EventSourceのArnを設定すればマッピングされます。

  EventSourceMapping:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      Enabled: true
      EventSourceArn: arn:aws:sqs:ap-northeast-1:111122223333:your-sqs-name
      FunctionName: !GetAtt Lambda.Arn

デプロイした場合、コンソール上だとこういう表示になります。

テンプレート用意

SQSを先に作る

先に東京リージョンでSQSを作ります。コンソールで作ったほうが早いです(CreateQueue -> 名前入れてあとはデフォルトで作成、Arnをコピーしておく)。

テンプレートでも作るならこんな具合でしょうか。

sqs-sample.yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  Queue:
    Type: AWS::SQS::Queue
    Properties: 
      QueueName: sqs-sample

デプロイして、

aws cloudformation deploy \
    --template-file sqs-sample.yaml \
    --stack-name SQS-Sample \
    --capabilities CAPABILITY_NAMED_IAM

Outputの値だけ取り出します。

aws cloudformation describe-stacks --stack-name SQS-Sample | jq -r '.Stacks[0].Outputs[0]'

SQSとLambdaをマッピングさせるテンプレート

次に以下のテンプレートで、作成したSQSのARNを、arn:aws:sqs:ap-northeast-1:111122223333:your-sqs-nameの部分(2箇所)にハードコードします。

sqs-mappling.yaml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  Name:
    Description: identifier
    Type: String
    Default: sqs-lambda-event
Resources:
  EventSourceMapping:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      Enabled: true
      EventSourceArn: arn:aws:sqs:ap-northeast-1:111122223333:your-sqs-name
      FunctionName: !GetAtt Lambda.Arn
      BatchSize: 1
  Lambda:
      Type: AWS::Lambda::Function
      Properties:
        FunctionName: !Ref Name
        Handler: index.lambda_handler
        Role: !GetAtt LambdaRole.Arn
        Runtime: nodejs16.x
        Code:
          ZipFile: |
            exports.lambda_handler = async (event, context) => {
              const util = require('util');
              console.log(util.inspect(event,false,null));
              return 200;
            }
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
      Policies:
        - PolicyName: sqs-access
          PolicyDocument:
            Statement:
              - Sid: 1
                Effect: Allow
                Action:
                  - sqs:DeleteMessage
                  - sqs:GetQueueAttributes
                  - sqs:ReceiveMessage
                Resource:
                  - arn:aws:sqs:ap-northeast-1:111122223333:your-sqs-name

デプロイ

上のテンプレートsqs-mappling.yamlを使ってスタックを作成します。デプロイは、コンソールでもCLIでもどちらでもできます。パラメータは適宜変更します。

項目
スタック名 sqs-mapping
アカウント 自分のみ 111122223333
リージョン 東京(ap-northeast-1)、大阪(ap-northeast-3)、N.Virginia(us-east-1)
Maximum concurrent accounts 1(default)
FailureToleranceCount 0(default)

StackSetを作って、

aws cloudformation create-stack-set --stack-set-name sqs-mapping --template-body file://sqs-mappling.yaml --capabilities CAPABILITY_IAM

StackのInstanceを作ります。

aws cloudformation create-stack-instances \
    --stack-set-name my-stack-set \
    --accounts 123456789012 \
    --regions ap-northeast-1 ap-northeast-3 us-east-1 \
    --operation-preferences FailureToleranceCount=0

(--operation-preferences FailureToleranceCount=0はなくても大丈夫です。ここを1に変えてみたりすると、別の結果が出せて面白いかも)

OperationIDが出力されるので、この値を次で使います。

予定通り失敗する

しばらくすると、2つめのスタックの作成で失敗して終了します。

想定通り、SQSが東京リージョンなので、大阪リージョンのとマッピングしようとして失敗しています。囲んだところのエラー文はこういうのです→Resource handler returned message: "Invalid request provided: Event source region must match Lambda region us-east-1 (Service: Lambda...

StackSetsをデプロイしたリージョンのStackSetsのページでまとめた情報が見れますが、他リージョンの各スタックの確認は、そのリージョンで見る必要があります。

DescribeStackSetOperation

ここからはCLIを使います。describe-stack-set-operationでオペレーションの結果を見ます(途中でも見れます)。

aws cloudformation   describe-stack-set-operation --stack-set-name sqs-mapping --operation-id 2da494d3-78e7-4df3-9a42-6fa73491b57d

CREATEがFAILEDしたことや、設定情報が出ていますが、下の方に"FailedStackInstancesCount": 1があります。これが今回追加された機能です。

{
    "StackSetOperation": {
        "OperationId": "2da494d3-78e7-4df3-9a42-6fa73491b57d",
        "StackSetId": "sqs-mapping:aac80512-b702-42ec-9a3f-daa5236c1e8f",
        "Action": "CREATE",
        "Status": "FAILED",
        "OperationPreferences": {
            "RegionConcurrencyType": "SEQUENTIAL",
            "RegionOrder": [
                "ap-northeast-1",
                "ap-northeast-3",
                "us-east-1"
            ],
            "FailureToleranceCount": 0,
            "MaxConcurrentCount": 1
        },
        "AdministrationRoleARN": "arn:aws:iam::111122223333:role/AWSCloudFormationStackSetAdministrationRole",
        "ExecutionRoleName": "AWSCloudFormationStackSetExecutionRole",
        "CreationTimestamp": "2022-11-05T02:09:01.920000+00:00",
        "EndTimestamp": "2022-11-05T02:13:49.064000+00:00",
        "StatusDetails": {
            "FailedStackInstancesCount": 1
        }
    }
}

ListStackInstances

続いてスタックインスタンスを見てみます。以下は失敗してから実行した結果ですが、実行中でも見れます。

aws cloudformation list-stack-instances --stack-set-name sqs-mapping
  • 各リージョンのスタックの情報の中に、LastOperationIdが入っていて、これが今回追加された機能です。
    • ちなみに、上のコンソールキャプチャの画面でもこの項目はあります。
  • 1つめのap-northeast-1はSUCCEEDED、2つめのap-northeast-3はFAILED、3つめのus-east-1はCANCELLEDになっています。これは、FailureToleranceCountが0なので、失敗すると次のスタックに行かずにキャンセルしているということです(StatusReasonにも書いてある)。
{
    "Summaries": [
        {
            "StackSetId": "sqs-mapping:aac80512-b702-42ec-9a3f-daa5236c1e8f",
            "Region": "ap-northeast-1",
            "Account": "111122223333",
            "StackId": "arn:aws:cloudformation:ap-northeast-1:111122223333:stack/StackSet-sqs-mapping-a943d1b4-9e2f-40e4-9a37-d3547bf801e7/d32f7100-5cae-11ed-943e-0e04d872daa5",
            "Status": "CURRENT",
            "StackInstanceStatus": {
                "DetailedStatus": "SUCCEEDED"
            },
            "OrganizationalUnitId": "",
            "DriftStatus": "NOT_CHECKED",
            "LastOperationId": "2da494d3-78e7-4df3-9a42-6fa73491b57d"
        },
        {
            "StackSetId": "sqs-mapping:aac80512-b702-42ec-9a3f-daa5236c1e8f",
            "Region": "ap-northeast-3",
            "Account": "111122223333",
            "StackId": "arn:aws:cloudformation:ap-northeast-3:111122223333:stack/StackSet-sqs-mapping-86f59d85-1ce2-4841-8d81-cf5fee5be2fb/fce75f80-5cae-11ed-b93c-0e319530c894",
            "Status": "OUTDATED",
            "StatusReason": "ResourceLogicalId:EventSourceMapping, ResourceType:AWS::Lambda::EventSourceMapping, ResourceStatusReason:Resource handler returned message: \"Invalid request provided: Event source region must match Lambda region ap-northeast-3 (Service: Lambda, Status Code: 400, Request ID: c51c41e2-7837-448f-863e-f1fccefa328e, Extended Request ID: null)\" (RequestToken: 4a0c1f48-4d2c-463e-278e-ea8ef6c4dc77, HandlerErrorCode: InvalidRequest).",
            "StackInstanceStatus": {
                "DetailedStatus": "FAILED"
            },
            "OrganizationalUnitId": "",
            "DriftStatus": "NOT_CHECKED",
            "LastOperationId": "2da494d3-78e7-4df3-9a42-6fa73491b57d"
        },
        {
            "StackSetId": "sqs-mapping:aac80512-b702-42ec-9a3f-daa5236c1e8f",
            "Region": "us-east-1",
            "Account": "111122223333",
            "Status": "OUTDATED",
            "StatusReason": "Cancelled since failure tolerance has exceeded",
            "StackInstanceStatus": {
                "DetailedStatus": "CANCELLED"
            },
            "OrganizationalUnitId": "",
            "DriftStatus": "NOT_CHECKED",
            "LastOperationId": "2da494d3-78e7-4df3-9a42-6fa73491b57d"
        }
    ]
}

削除する

先にスタックインスタンスを消して、

aws cloudformation delete-stack-instances \
    --stack-set-name sqs-mapping \
    --accounts 111122223333 \
    --regions ap-northeast-1 ap-northeast-3 us-east-1 \
    --no-retain-stacks

StackSetを消します。

aws cloudformation delete-stack-set --stack-set-name sqs-mapping
# 何も出力されない

まとめ

  • CloudFormationのStackSetsをやってみました
  • CLIで、DescribeStackSetOperationとListStackInstances新しい機能を確認しました

Discussion