Policy, scanning & least privilege
cfn-guard, cfn-nag, stack policies.
A CloudFormation template both describes security posture (it can create wide-open security groups and over-broad IAM) and is itself deployed with powerful permissions. Two fronts to harden: scan the template for misconfigurations before deploy, and constrain what the deployment is allowed to do. On the scanning front, cfn-lint checks correctness, while cfn-nag and cfn-guard check security — flagging public buckets, 0.0.0.0/0 ingress, unencrypted volumes, wildcard IAM.
$ cfn-lint template.yaml # correctness + best practice$ cfn_nag_scan --input-path template.yamlWARN W2: Security group allows ingress from 0.0.0.0/0FAIL F1000: IAM policy grants Action:* on Resource:*# cfn-guard: policy-as-code rules the template must satisfy$ cfn-guard validate --rules security.guard --data template.yaml
# every S3 bucket must block public accessAWS::S3::Bucket {Properties.PublicAccessBlockConfiguration {BlockPublicAcls == trueRestrictPublicBuckets == true}}
Least privilege for the deployment itself
CloudFormation can assume a role you specify and provision with only that role’s permissions — so instead of deploying as an admin, you give the stack a scoped role that can create just the resource types this template needs. Add a stack policy to protect critical resources from accidental update/replace, and IAM condition keys to restrict who can pass which roles. The deployment identity is part of the attack surface; scope it.
{"Statement": [{ "Effect": "Allow", "Action": "Update:*", "Principal": "*", "Resource": "*" },{ "Effect": "Deny", "Action": "Update:Replace", "Principal": "*","Resource": "LogicalResourceId/ProductionDatabase" }]}