- Newest
- Most votes
- Most comments
To avoid circular dependencies between KMS keys and resources/IAM roles in AWS CloudFormation, you can implement several effective patterns:
- Use a two-stage deployment approach:
- First deployment: Create the KMS key with a policy that grants permissions to your AWS account root user or a pre-existing IAM role
- Second deployment: Update the KMS key policy to include the specific resource/role ARNs after they've been created
- Use the
kms:ViaServicecondition key instead of specifying exact resource ARNs:
{ "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/YourRole" }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey*" ], "Resource": "*", "Condition": { "StringEquals": { "kms:ViaService": "ec2.us-west-2.amazonaws.com" } } }
This allows the key to be used only when requests come from specific AWS services.
-
Use service-linked roles for services like Auto Scaling: For services that use service-linked roles (like EC2 Auto Scaling), you can reference the standard service-linked role ARN pattern in your key policy since these roles have predictable ARNs.
-
Use broader permissions initially, then restrict:
- Start with a more permissive key policy that grants access to a broader principal (like the AWS account)
- After deployment, update the policy to follow least privilege principles
- Use CloudFormation DependsOn attribute: Explicitly define the dependency order to ensure resources are created in the correct sequence.
Remember to follow best practices for KMS key policies:
- Avoid using
"Resource": "*"in IAM policies when possible - Specify only the necessary KMS keys and permissions
- Use condition keys to restrict when and how keys can be used
- Follow the principle of least privilege by granting only the permissions needed
Sources
Specifying KMS keys in IAM policy statements - AWS Key Management Service
Key Policies - AWS Key Management Service Best Practices
Best practices for IAM policies - AWS Key Management Service
AWS KMS condition keys - AWS Key Management Service
ASG KMS customer managed key | AWS re:Post
answered a year ago
I ran into this same problem while building a serverless stack and found a reliable pattern that avoids circular dependencies entirely.
The idea is to use grant-based KMS access control managed by a Lambda custom resource instead of hard-coding role ARNs into the key policy.
Root cause
CloudFormation can’t create a KMS key that references a role which doesn’t exist yet, and it can’t create the role until the key exists. The dependency loop happens because both resources require each other’s ARNs during creation.
Solution
- Deploy a Lambda function (KMSGrantFunction) with permissions to call kms:CreateGrant, kms:RevokeGrant, and kms:DescribeKey.*
- Create the KMS key with a minimal policy that only allows CloudFormation and the Lambda function to manage grants.
- For each encrypted resource (S3, SNS, SQS, Lambda role), define a Custom::KmsGrant resource that invokes the function to create a grant once the resource exists.
- Optionally, use EventBridge to monitor CreateGrant and RevokeGrant events for auditing.
KmsGrantForS3Bucket:
Type: Custom::KmsGrant
Properties:
KeyId: !Ref KMSKey
PrincipalArn: s3.amazonaws.com
ServiceToken: !GetAtt KMSGrantFunction.Arn
Operations:
- Decrypt
- Encrypt
- GenerateDataKey
Benefits
- Predictable deployments — CloudFormation can create the key and resources independently.
- Least privilege — each service only gets required KMS operations.
- Auditable — all grant events appear in CloudTrail and can trigger EventBridge notifications.
- Reusable — the grant manager Lambda can be deployed once and used across multiple projects.
Full working example (SAM template, Lambda function, and setup guide):
answered a year ago
