Safeguard
Infrastructure Security

Scanning AWS CloudFormation templates for misconfigurations

CloudFormation deploys exactly what you write, misconfigurations included. Here's how scanning catches IAM, S3, and security group errors before they ship.

Michael
Cloud Security Architect
7 min read

On July 19, 2019, Paige Thompson exfiltrated data on more than 106 million Capital One customers by pivoting through a misconfigured web application firewall on EC2 whose IAM role carried far more S3 permissions than the WAF needed to function. The root cause wasn't exotic malware — it was an over-permissioned IAM role, provisioned as infrastructure-as-code, that nobody flagged before it shipped. Six years and millions of CloudFormation deployments later, the same class of error still goes to production daily: permissive IAM policies, public S3 buckets, security groups open to 0.0.0.0/0, and unencrypted RDS instances. CloudFormation makes it trivial to define infrastructure at scale, but it has no built-in concept of "safe" — it will deploy exactly what the YAML says, misconfigurations included. This post breaks down what CloudFormation misconfiguration scanning actually checks, which resource types fail most often, how the scanning mechanics work, and where it belongs in a pipeline that ships templates dozens of times a day.

What Is a CloudFormation Misconfiguration, Exactly?

A CloudFormation misconfiguration is any resource property, IAM statement, or stack setting in a template that grants more access, less encryption, or less network isolation than the workload actually needs — and CloudFormation deploys it verbatim because it has no policy engine of its own. Concrete examples show up in nearly every template audit: an AWS::S3::Bucket with no BucketEncryption block, leaving objects stored in plaintext; an AWS::EC2::SecurityGroup ingress rule with CidrIp: 0.0.0.0/0 on port 22 or 3389; an AWS::IAM::Role trust or permission policy using "Action": "*" and "Resource": "*" instead of scoped ARNs; an AWS::RDS::DBInstance with PubliclyAccessible: true; and stateful resources missing DeletionPolicy: Retain, so a routine aws cloudformation delete-stack call wipes a production database along with the stack. None of these trigger a CloudFormation validation error — aws cloudformation validate-template only checks JSON/YAML syntax and parameter references, not security posture. That gap is exactly what misconfiguration scanners are built to close.

Which CloudFormation Resource Types Cause the Most Incidents?

Three resource families account for the overwhelming majority of exploitable CloudFormation misconfigurations: AWS::IAM::Role/AWS::IAM::Policy, AWS::S3::Bucket, and AWS::EC2::SecurityGroup. Palo Alto Networks' Unit 42 has repeatedly found that the vast majority of cloud identities analyzed across customer environments carry permissions well beyond what they use, a gap that's easy to introduce with a single wildcard Action in a CloudFormation-defined IAM policy and hard to notice in review because the template still "works." S3 misconfigurations follow the same pattern from the other direction: a bucket resource with no PublicAccessBlockConfiguration and no BucketEncryption will deploy successfully and serve traffic, so the failure stays invisible until someone runs aws s3api get-bucket-acl against it — or an attacker does. Security groups round out the list because CloudFormation templates are frequently copied from tutorials or previous projects where a 0.0.0.0/0 rule was scoped down "temporarily" for testing and never tightened before the template was reused in production. Toyota disclosed in May 2023 that a misconfigured cloud environment had exposed vehicle and location data for roughly 2.15 million customers in Japan for nearly a decade, undetected — a reminder that these three resource types don't just fail at deploy time, they fail silently for years when nothing is watching.

How Does Automated Scanning Catch These Before Deployment?

Automated scanning works by parsing the CloudFormation template's JSON or YAML into a resource graph, then evaluating each resource's properties against a declarative rule set before the template ever reaches aws cloudformation deploy. Open-source tools like cfn-lint, cfn_nag, and Checkov walk that resource graph looking for specific patterns — an AWS::IAM::Policy statement with Effect: Allow and an unscoped Resource, an AWS::EC2::SecurityGroup rule with a /0 CIDR block, a missing Encryption property on an AWS::SQS::Queue or AWS::SNS::Topic — and report the offending resource's logical ID and template line number. AWS's own AWS::CloudFormation::Hook resource type, generally available since 2022, takes this further by running validation synchronously during CREATE/UPDATE/DELETE stack operations and can block a deployment outright if a hook returns FAILED, rather than just warning after the fact. In a CI/CD pipeline, this scanning step typically runs on every pull request that touches a .yaml or .json template, fails the build if a rule tagged HIGH or CRITICAL fires, and posts the specific resource and remediation back as a PR comment — catching the misconfiguration when it costs a one-line diff to fix, not an incident response retainer.

What Happens When These Misconfigurations Reach Production?

When these misconfigurations reach production, they don't stay theoretical — Capital One's breach cost the company an $80 million penalty from the Office of the Comptroller of the Currency in August 2020 and a further $190 million class-action settlement in December 2022, on top of remediation costs, for a single over-permissioned IAM role tied to infrastructure automation. The attack chain was straightforward: the WAF's IAM role could call s3:GetObject and s3:ListBucket against buckets it had no operational reason to touch, so once the WAF was tricked into making a server-side request forgery (SSRF) call to the EC2 instance metadata service, the attacker retrieved temporary credentials scoped to that same over-broad role and used them directly against S3. If that IAM policy had been provisioned from a template that failed a scanning rule for wildcard Resource values, the deployment would have been blocked before the role ever existed in the account. This is the core argument for shifting misconfiguration detection left into the template itself: fixing an IAM statement in a pull request takes minutes; unwinding a breach that already touched 106 million records takes years and eight-figure settlements.

Should You Scan the Template, the Deployed Stack, or Both?

You need both, because template scanning and runtime scanning catch two different failure modes. Template scanning at PR time catches misconfigurations before they're ever created — a security group with an open ingress rule never gets an ENI attached to it. But it can't see drift: a developer who manually edits a security group in the AWS console after deployment, or a Lambda execution role that gets a broader managed policy attached outside of CloudFormation entirely, will pass every template scan because the change never touched the source YAML. That's where continuous, deployed-state scanning — AWS Config conformance packs, or a platform that ingests live account state via the AWS APIs — picks up the gap, flagging the drifted resource against the same rule set used at PR time. Toyota's decade-long exposure is the pattern this combination is built to prevent: a misconfiguration that either was never caught pre-deploy or drifted afterward, and then went unchecked because nothing was scanning the live environment on a recurring basis.

How Safeguard Helps

Safeguard scans CloudFormation templates in the pull request itself, flags misconfigured IAM policies, security groups, and encryption settings against a continuously updated rule set, and opens an auto-fix PR with the corrected resource property so a reviewer can merge a working fix instead of writing one from scratch. Because template-level findings alone don't tell you which misconfigurations are actually exploitable in your environment, Safeguard's reachability analysis correlates each flagged resource against the live account graph — an overly permissive IAM role attached to an internet-facing Lambda function is ranked and surfaced differently than the same policy attached to an isolated batch job. Griffin AI, Safeguard's detection engine, triages these findings alongside SBOM data generated or ingested from your build pipeline, so a vulnerable dependency inside a container image defined by the same CloudFormation stack shows up in the same risk view as the infrastructure misconfiguration around it. The result is one place to see template risk, runtime drift, and dependency risk together, with a fix path attached to each finding rather than just an alert.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.