CloudFormation StackSetsで作成したグローバルリソースを同一Stackから参照する際は実行リージョンとリージョンの実行順序を意識しよう
StackSetsでAWS Configの設定をばら撒きたいのにできないな
こんにちは、のんピ(@non____97)です。
皆さんは「CloudFormation StackSets(以降StackSets)でAWS Configの設定をばら撒きたいのにできないな」と思ったことはありますか? 私はあります。
2023年4月ごろまでは以下記事で紹介れているテンプレートを全リージョンに対してStackSetsでばら撒いても動作しました。
しかし、現在は同じテンプレートを同じ方法で実行しても「作ろうとしているService-linked Roleは既に存在しているため作成できない」と怒られるようになりました。
「それなら特定リージョンのみStack Instanceを作成するように設定すれば良いじゃん」と思われるかもしれません。最もです。
ですが、上述のAWS Configの有効化をする際に「1つのリージョンでService-linked Roleを作成」と「全リージョンでAWS Configの有効化」と2つのStackSetsとテンプレートを用意することになります。なるべくStackSetsをまとめたい場合は、どのように対応すれば良いのでしょうか。
そんな時は「一部リソースは特定リージョンのみ作成する」と「Stack Instanceのリージョンの実行順序の指定する」ことによって対応することが可能です。
実際に検証してみます。
いきなりまとめ
- StackSetsのパラメーターの
RegionOrder
でリージョンの実行順序を指定可能 - 全てのリージョンを指定する必要はない
RegionConcurrencyType=PARALLEL
と同時に指定することはできない
StackSetsの作成
まず、適当にService-linked Roleを作成するテンプレートをStackSetsで複数リージョンにばら撒いてみます。
使用するテンプレートは以下のとおりです。
AWSTemplateFormatVersion: "2010-09-09" Resources: AWSServiceRoleForGlobalAccelerator: Type: "AWS::IAM::ServiceLinkedRole" Properties: AWSServiceName: globalaccelerator.amazonaws.com
まず、StackSetsを作成します。
$ aws cloudformation create-stack-set \ --stack-set-name service-linked-role-globalaccelerator \ --description "Service-linked Role Global Accelerator" \ --template-body file://service-linked-role.yml \ --permission-model SELF_MANAGED \ --administration-role-arn arn:aws:iam::<AWSアカウントID>:role/AWSCloudFormationStackSetAdministrationRole \ --execution-role-name AWSCloudFormationStackSetExecutionRole { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212" }
次にStack Instanceを作成します。Stack Instanceは以下4リージョンを同時並行して作成するようにします。
- ap-northeast-1
- us-east-1
- us-east-2
- us-west-1
$ aws cloudformation create-stack-instances \ --stack-set-name service-linked-role-globalaccelerator \ --accounts $(aws sts get-caller-identity --query Account --output text) \ --regions ap-northeast-1 us-east-1 us-east-2 us-west-1 \ --operation-preferences RegionConcurrencyType=PARALLEL { "OperationId": "591553fe-36ab-4d11-bfbc-964d08642f70" }
作成したStackSetsのオペレーションの状態を確認します。
$ aws cloudformation describe-stack-set-operation \ --stack-set-name service-linked-role-globalaccelerator \ --operation-id 591553fe-36ab-4d11-bfbc-964d08642f70 { "StackSetOperation": { "OperationId": "591553fe-36ab-4d11-bfbc-964d08642f70", "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Action": "CREATE", "Status": "FAILED", "OperationPreferences": { "RegionConcurrencyType": "PARALLEL", "RegionOrder": [] }, "AdministrationRoleARN": "arn:aws:iam::<AWSアカウントID>:role/AWSCloudFormationStackSetAdministrationRole", "ExecutionRoleName": "AWSCloudFormationStackSetExecutionRole", "CreationTimestamp": "2023-07-29T02:43:59.153000+00:00", "EndTimestamp": "2023-07-29T02:46:47.637000+00:00", "StatusDetails": { "FailedStackInstancesCount": 3 } } }
失敗していますね。
各Stack Instanceの状態を確認します。
aws cloudformation list-stack-instances \ --stack-set-name service-linked-role-globalaccelerator { "Summaries": [ { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "ap-northeast-1", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:ap-northeast-1:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccelerator-40bfa296-e857-41b9-a379-211d3237286a/c783fe20-2db9-11ee-a7bc-0649a590e823", "Status": "OUTDATED", "StatusReason": "ResourceLogicalId:AWSServiceRoleForGlobalAccelerator, ResourceType:AWS::IAM::ServiceLinkedRole, ResourceStatusReason:Resource handler returned message: \"Service role name AWSServiceRoleForGlobalAccelerator has been taken in this account, please try a different suffix. (Service: Iam, Status Code: 400, Request ID: 03b4fb80-fe39-49f0-9bc2-4226ff03ff3e)\" (RequestToken: cf679c29-382a-f92e-0cc3-250c3c38654a, HandlerErrorCode: AlreadyExists).", "StackInstanceStatus": { "DetailedStatus": "FAILED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "591553fe-36ab-4d11-bfbc-964d08642f70" }, { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "us-east-1", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccelerator-ed2eb2ef-effb-417d-b358-54462de2ba87/c78b0300-2db9-11ee-821f-1223dd1d788d", "Status": "OUTDATED", "StatusReason": "ResourceLogicalId:AWSServiceRoleForGlobalAccelerator, ResourceType:AWS::IAM::ServiceLink edRole, ResourceStatusReason:Resource handler returned message: \"Service role name AWSServiceRoleForGlobalAccelerato r has been taken in this account, please try a different suffix. (Service: Iam, Status Code: 400, Request ID: fd9bef1 c-c8c2-4eeb-bc1f-a663b6f89a50)\" (RequestToken: 61ae0347-425f-c8ae-e71a-ffb6ab4336c9, HandlerErrorCode: AlreadyExists ).", "StackInstanceStatus": { "DetailedStatus": "FAILED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "591553fe-36ab-4d11-bfbc-964d08642f70" }, { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "us-east-2", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:us-east-2:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccele rator-be908822-2bfd-480e-9333-0d9271724fb7/c7a9ae90-2db9-11ee-83c6-06265f1183a3", "Status": "CURRENT", "StackInstanceStatus": { "DetailedStatus": "SUCCEEDED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "591553fe-36ab-4d11-bfbc-964d08642f70" }, { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "us-west-1", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:us-west-1:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccele rator-62619545-cf53-4795-8c91-7474776ec0c3/c7b40ed0-2db9-11ee-8410-06eda47e7443", "Status": "OUTDATED", "StatusReason": "ResourceLogicalId:AWSServiceRoleForGlobalAccelerator, ResourceType:AWS::IAM::ServiceLink edRole, ResourceStatusReason:Resource handler returned message: \"Service role name AWSServiceRoleForGlobalAccelerato r has been taken in this account, please try a different suffix. (Service: Iam, Status Code: 400, Request ID: a6dbfc2 d-9cc4-4ed6-b7b7-5de84a441130)\" (RequestToken: 07e59d04-d077-121a-dfaf-dd6801283f37, HandlerErrorCode: AlreadyExists ).", "StackInstanceStatus": { "DetailedStatus": "FAILED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "591553fe-36ab-4d11-bfbc-964d08642f70" } ] }
us-east-2のみ成功していますね。
us-east-2以外は「作ろうとしている AWSServiceRoleForGlobalAccelerator は既にこのアカウントにあるぞ。違うサフィックスを付けてトライしろ。」とエラーになっています。
マネジメントコンソールから見ると以下のとおりです。
Stack Instanceの更新順番を指定して更新する
このような時はどのように対応したら良いのでしょうか。
まず、考えられるのは「CloudFormationのCondition
を使って、現在の特定のリージョンのみ作成する」ことです。
例えば、us-east-1のみService-linked Roleを作成する場合は以下のようなテンプレートになります。
AWSTemplateFormatVersion: "2010-09-09" Conditions: IsControlPlaneRegion: !Equals [!Ref AWS::Region, us-east-1] Resources: AWSServiceRoleForGlobalAccelerator: Condition: IsControlPlaneRegion Type: "AWS::IAM::ServiceLinkedRole" Properties: AWSServiceName: globalaccelerator.amazonaws.com
ただし、それだけだとStack Instance更新時に失敗すると予想します。
先ほど作成したService-linked RoleはStack Instanceのus-east-2で作成されました。このままStack Instanceの更新を行い、仮にus-east-1から走った場合、us-east-2で作成したService-linked Roleが既に存在するためまたエラーとなってしまいます。
また、Stack Instanceを新規に作成する場合も困ります。
例として挙げたテンプレートはService-linked Roleのみを作成するシンプルなものです。ただし、同じテンプレート内でこのService-linked Roleを使用する場合、us-east-1でService-linked Roleが作成されることを待ってからでなければ他のリージョンのStack Instanceは「Service-linked Roleがない」として失敗してしまいます。
その対応としてリージョンの実行順序RegionOrder
を指定しましょう。Stack Instanceの作成時やStackSetsの更新時に指定できます。
- create-stack-instances — AWS CLI 1.29.15 Command Reference
- update-stack-set — AWS CLI 1.29.15 Command Reference
今回の場合だとus-east-1でエラーなくService-linked Roleを作成するためには、us-east-2で既存のService-linked Roleを削除してから、us-east-1でService-linked Roleを作成する必要があります。
そのため、StackSets更新時にRegionOrder=us-east-2,us-east-1
を指定してあげます。全てのリージョンを指定する必要はないようです。
$ aws cloudformation update-stack-set \ --stack-set-name service-linked-role-globalaccelerator \ --description "Service-linked Role Global Accelerator" \ --template-body file://service-linked-role.yml \ --operation-preferences RegionOrder=us-east-2,us-east-1 { "OperationId": "c932f341-f641-4ba6-9524-8286e48642ce" }
ちなみに、以下のようにRegionOrder
とRegionConcurrencyType=PARALLEL
を同時に指定することはできません。
$ aws cloudformation update-stack-set \ --stack-set-name service-linked-role-globalaccelerator \ --description "Service-linked Role Global Accelerator" \ --template-body file://service-linked-role.yml \ --operation-preferences RegionConcurrencyType=PARALLEL,RegionOrder=us-east-2,us-east-1 An error occurred (ValidationError) when calling the UpdateStackSet operation: Cannot specify both RegionOrder and RegionConcurrencyType PARALLEL in operation preferences
RegionConcurrencyType=PARALLEL
は各リージョンのStack Instanceを並列で更新するパラメーターです。「リージョンの実行順序を指定した箇所は直列で、以降は並列で更新する」といったことはできないようです。
さて、StackSetsのオペレーションの状態を確認しましょう。
$ aws cloudformation describe-stack-set-operation \ --stack-set-name service-linked-role-globalaccelerator \ --operation-id c932f341-f641-4ba6-9524-8286e48642ce { "StackSetOperation": { "OperationId": "c932f341-f641-4ba6-9524-8286e48642ce", "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Action": "UPDATE", "Status": "SUCCEEDED", "OperationPreferences": { "RegionOrder": [ "us-east-2", "us-east-1" ] }, "AdministrationRoleARN": "arn:aws:iam::<AWSアカウントID>:role/AWSCloudFormationStackSetAdministrationRole", "ExecutionRoleName": "AWSCloudFormationStackSetExecutionRole", "CreationTimestamp": "2023-07-29T02:56:42.054000+00:00", "EndTimestamp": "2023-07-29T02:57:14.538000+00:00", "StatusDetails": { "FailedStackInstancesCount": 0 } } }
更新に成功していますね。
各Stack Instanceの状態も確認します。
aws cloudformation list-stack-instances \ --stack-set-name service-linked-role-globalaccelerator { "Summaries": [ { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "ap-northeast-1", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:ap-northeast-1:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccelerator-40bfa296-e857-41b9-a379-211d3237286a/9b103910-2dbb-11ee-938d-0aa510102155", "Status": "CURRENT", "StackInstanceStatus": { "DetailedStatus": "SUCCEEDED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "c932f341-f641-4ba6-9524-8286e48642ce" }, { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "us-east-1", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccelerator-ed2eb2ef-effb-417d-b358-54462de2ba87/971034a0-2dbb-11ee-b006-0a8d6d22a55b", "Status": "CURRENT", "StackInstanceStatus": { "DetailedStatus": "SUCCEEDED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "c932f341-f641-4ba6-9524-8286e48642ce" }, { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "us-east-2", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:us-east-2:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccele rator-be908822-2bfd-480e-9333-0d9271724fb7/c7a9ae90-2db9-11ee-83c6-06265f1183a3", "Status": "CURRENT", "StackInstanceStatus": { "DetailedStatus": "SUCCEEDED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "c932f341-f641-4ba6-9524-8286e48642ce" }, { "StackSetId": "service-linked-role-globalaccelerator:f65d6fd6-b499-4590-a799-04f5c1578212", "Region": "us-west-1", "Account": "<AWSアカウントID>", "StackId": "arn:aws:cloudformation:us-west-1:<AWSアカウントID>:stack/StackSet-service-linked-role-globalaccele rator-62619545-cf53-4795-8c91-7474776ec0c3/9d707030-2dbb-11ee-94c1-02595b0bb097", "Status": "CURRENT", "StackInstanceStatus": { "DetailedStatus": "SUCCEEDED" }, "OrganizationalUnitId": "", "DriftStatus": "NOT_CHECKED", "LastOperationId": "c932f341-f641-4ba6-9524-8286e48642ce" } ] }
いずれのStack InstanceもSUCCEEDED
となっていますね。
マネジメントコンソールから確認すると以下のとおりです。
Stack Instanceのイベントも確認しましょう。
まず、us-east-2のイベントです。
11:56:45に更新がかかり、11:56:59にService-linked Roleの削除に成功していますね。
次にus-east-1のイベントです。
11:57:01にStackの作成が開始され、11:57:05にService-linked Roleの作成に成功していますね。
RegionOrderで設定した順序でStack Instanceが更新・作成されていることが分かりました。
念の為、マネジメントコンソールからService-linked Roleが作成されていることを確認しておきましょう。
Global AcceleratorのService-linked Roleがありますね。
グローバルリソースはどのリージョンで管理しているか意識しよう
「CloudFormation StackSetsで作成したグローバルリソースを同一Stackから参照する際は実行リージョンとリージョンの実行順序を意識しよう」というお話をしました。
「ただ、サービス有効化したいだけなのにIAMロールを作成するStackSetsを分離するのは何だか避けたい」という場合はCondition
とRegionOrder
を使ってみると良いでしょう。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!