5 minutes
CloudFormation all the way down - Debug Guide
Introduction
Recently, as in a few weeks ago, AWS finally announced CloudFormation for StackSets.
StackSets are an awesome idea when you approach AWS from a purely Infrastructure-as-Code mindset. However, you had to create and manage them manually. But not anymore!
Unsure what StackSets are? The tl;dr version is that they allow you to deploy the same CloudFormation Stack across multiple regions and/or AWS accounts.
For me there is no better way to learn than by doing; so, I dusted off my old suite of AWS accounts and dove into deploying my very first CloudFormation StackSet by CloudFormation.
This post explains how I got everything working in my accounts (the oldest account being created circa 2015) and the (mis)steps I took along the way. A follow up piece on “what I deployed” will be released soon.
Let’s dive in!
Diving In
-
My first task was to ensure my accounts were managed via AWS Organisations and that all features were enabled (rather than consolidated billing). One ✅ for me as I had already enabled all features sometime in the distant past:
-
The next item to do was to enable CloudFormation StackSets for my organisation:
I just clicked the Enabled access button, and ta-daa!
-
From here, I created a new Organisational Unit (OU) and moved all my child accounts under it. I did this as I knew that StackSets can automatically deploy stack instances to all accounts under an OU, which suited me perfectly for my handful of accounts.
-
From here, I tried to deploy my StackSet and encountered a weird error. I also needed to enable StackSets via the CloudFormation console:
-
One more try and it’s still not working. If you have ever worked with StackSets before, you’ll know that the error reporting is basically non-existent. You are forced to use the command-line and guesswork to try and uncover what is going on. What follows is a command line tale of frustration and woe…
# First, lets list the StackSets aws cloudformation list-stack-sets { "Summaries": [ { "StackSetName": "MyStackSet", "StackSetId": "MyStackSet:00000000-0000-0000-0000-000000000000", "Description": "MyStackSet Description", "Status": "DELETED", "DriftStatus": "NOT_CHECKED" } ] } # Not super helpful, but it gives me the StackSetId which I will need laster. Deleted stacks can only be described by the full id. aws cloudformation describe-stack-set --stack-set-name "MyStackSet:00000000-0000-0000-0000-000000000000" { "StackSet": { "StackSetName": "MyStackSet", "StackSetId": "MyStackSet:00000000-0000-0000-0000-000000000000", "Description": "MyStackSet Description", "Status": "DELETED", "TemplateBody": "AWSTemplateFormatVersion: 2010-09-09\n ...", "Parameters": [], "Capabilities": [ "CAPABILITY_NAMED_IAM" ], "Tags": [], "StackSetARN": "arn:aws:cloudformation:ap-southeast-2:000000000000:stackset/MyStackSet:00000000-0000-0000-0000-000000000000", "AdministrationRoleARN": "arn:aws:iam::000000000000:role/aws-service-role/stacksets.cloudformation.amazonaws.com/AWSServiceRoleForCloudFormationStackSetsOrgAdmin", "ExecutionRoleName": "stacksets-exec-deadbeefdeadbeefdeadbeefdeadbeef", "StackSetDriftDetectionDetails": { "DriftStatus": "NOT_CHECKED", "TotalStackInstancesCount": 0, "DriftedStackInstancesCount": 0, "InSyncStackInstancesCount": 0, "InProgressStackInstancesCount": 0, "FailedStackInstancesCount": 0 } } } # Not very helpful, so I dig in further aws cloudformation list-stack-set-operations --stack-set-name "MyStackSet:00000000-0000-0000-0000-000000000000" { "Summaries": [ { "OperationId": "22222222-2222-2222-2222-222222222222", "Action": "DELETE", "Status": "SUCCEEDED", "CreationTimestamp": "2020-09-22T01:58:27.847Z", "EndTimestamp": "2020-09-22T01:58:52.502Z" }, { "OperationId": "11111111-1111-1111-1111-111111111111", "Action": "CREATE", "Status": "FAILED", "CreationTimestamp": "2020-09-22T01:55:01.515Z", "EndTimestamp": "2020-09-22T01:58:07.163Z" } ] } # Getting some more details aws cloudformation describe-stack-set-operation --stack-set-name "MyStackSet:00000000-0000-0000-0000-000000000000" --operation-id "11111111-1111-1111-1111-111111111111" { "StackSetOperation": { "OperationId": "11111111-1111-1111-1111-111111111111", "StackSetId": "MyStackSet:00000000-0000-0000-0000-000000000000", "Action": "CREATE", "Status": "FAILED", "OperationPreferences": { "RegionOrder": [], "FailureToleranceCount": 1, "MaxConcurrentCount": 1 }, "AdministrationRoleARN": "arn:aws:iam::000000000000:role/aws-service-role/stacksets.cloudformation.amazonaws.com/AWSServiceRoleForCloudFormationStackSetsOrgAdmin", "ExecutionRoleName": "stacksets-exec-deadbeefdeadbeefdeadbeefdeadbeef", "CreationTimestamp": "2020-09-22T01:55:01.515Z", "EndTimestamp": "2020-09-22T01:58:07.163Z" } } # Grr, not helpful at all! Maybe there is detail in the stack instances aws cloudformation list-stack-instances --stack-set-name "MyStackSet:00000000-0000-0000-0000-000000000000" { "Summaries": [] } # Makes sense, they'd be in the child accounts. aws --profile child-account cloudformation list-stack-sets { "Summaries": [] }
-
At this point, I gave up and just tried redeploying while watching the AWS Console to see if any errors would appear. I got super lucky as I saw this error appear!
ResourceLogicalId:RbacDeployerRole, ResourceType:AWS::IAM::Role, ResourceStatusReason:1 validation error detected: Value '900' at 'maxSessionDuration' failed to satisfy constraint: Member must have value greater than or equal to 3600 (Service: AmazonIdentityManagement; Status Code: 400; Error Code: ValidationError; Request ID: 44444444-4444-4444-4444-444444444444; Proxy: null).
-
Changed the erroneous value, commit, push and voila! It all deploys smoothly and without further errors!
Conclusion
One last item to keep in mind is the difference in behaviour between TemplateURL
and TemplateBody
. Initially I used TemplateURL
to keep the template that StackSets was deploying separate from the CloudFormation template that deployed the StackSet itself. This meant that when the referenced template changes, the StackSet pipeline doesn’t detect a change and so won’t deploy anything. My solution was to embed the template within the StackSet definition (using TemplateBody
) to ensure that changes would always be detected and deployed.
The caveat, of course, is that I lost linting support. I’m happy with this trade-off as I’m the only person in my accounts. I’ll cut the existing template out and paste into a temporary local file. I’ll make the changes there and then paste them back into once I’m done. I’m not expecting to deploy many changes in the future so I can live this this workaround.
StackSet:
Type: AWS::CloudFormation::StackSet
Properties:
...
# TemplateURL: !Sub 'https://${ParamBucketName}.s3-ap-southeast-2.amazonaws.com/template.yml'
TemplateBody: |
AWSTemplateFormatVersion: 2010-09-09
Parameters:
...
Resources:
...
Outputs:
...
In future post I’ll be covering how I use CloudFormation StackSets to manage roll-based access control to all my accounts.
Bonus content - StackSet announcement timeline
- 2020-09-21 - AWS CloudFormation now supports StackSets Resource Type in the CloudFormation Registry
- 2020-08-25 - AWS CloudFormation StackSets is now available in AWS GovCloud (US-East), EU (Stockholm), and Asia Pacific (Hong Kong) Regions
- 2020-05-19 - AWS CloudFormation StackSets is now available in the AWS GovCloud (US-West) Region
- 2020-02-12 - AWS CloudFormation StackSets introduces automatic deployments across accounts and regions through AWS Organizations
- 2019-11-19 - CloudFormation Announces Drift Detection Support in StackSets
- 2019-08-01 - AWS CloudFormation now supports higher StackSets limits
- 2019-05-08 - AWS Service Catalog Connector for ServiceNow supports CloudFormation StackSets
- 2018-07-06 - Automate Amazon GuardDuty Provisioning Over Multiple Accounts and Regions with AWS CloudFormation StackSets Integration
- 2018-05-30 - AWS CloudFormation StackSets Supports Multiple Execution Roles and Selective Update Operation on Stack Instances
- 2018-04-10 - AWS CloudFormation StackSet Supports Multiple User Roles and Stack Set Level Access Controls
- 2017-11-21 - AWS CloudFormation Supports Parameterizing Configurations with StackSets Parameter overrides and EC2 Systems Manager Parameter Store
- 2017-11-06 - AWS CloudFormation Increases Stack Instance Limits for StackSets
- 2017-07-25 - AWS CloudFormation Supports Multiple Account and Region Provisioning with StackSet