cfn-lint 2,641 knows what AWS 24 accepts, not that BookNest's tables must be retained. cfn-guard 1,388 (github.com/aws-cloudformation/cloudformation-guard (https://github.com/aws-cloudformation/cloudformation-guard 1,388 ), Apache 2.0, a Rust binary; version 3.2.1 of 25 Aug 2026 here) checks any JSON or YAML document against rules in its own small language: a query selects values, a clause compares them, and << >> holds the message a developer sees. AWS's install-guard.sh script in the repository installs it. BookNest's rules encode four decisions from this chapter:
# BookNest's rules for CloudFormation templates (cfn-guard 3)
let tables = Resources.*[ Type == 'AWS::DynamoDB::Table' ]
let buckets = Resources.*[ Type == 'AWS::S3::Bucket' ]
let roles = Resources.*[ Type == 'AWS::IAM::Role' ]
rule tables_are_retained when %tables !empty {
%tables.DeletionPolicy == 'Retain'
%tables.UpdateReplacePolicy == 'Retain'
<< Tables hold the catalog: set DeletionPolicy and UpdateReplacePolicy to Retain >>
}
rule tables_bill_on_demand when %tables !empty {
%tables.Properties.BillingMode == 'PAY_PER_REQUEST'
<< BookNest's traffic is small and spiky: use BillingMode PAY_PER_REQUEST >>
}
rule buckets_block_public_access when %buckets !empty {
%buckets.Properties.PublicAccessBlockConfiguration {
BlockPublicAcls == true
BlockPublicPolicy == true
IgnorePublicAcls == true
RestrictPublicBuckets == true
<< Block all public access on buckets that are not reviewed exceptions >>
}
}
rule roles_have_boundary when %roles !empty {
%roles.Properties.PermissionsBoundary exists
<< Self-service roles must carry the booknest-boundary permissions boundary >>
}A rule SKIPs for templates without such resources; a filter that selects nothing skips it even without the when clause (Test Yourself! shows this), so when mostly states the intent. Otherwise a rule passes only if every selected resource does:
cd ~/v5-ch7/booknest/infra
cfn-guard validate --rules rules/booknest.guard --data catalog.yaml --data booknest-data.yaml \
--data reader-role.yaml --data seed.yaml --show-summary none --structured \
--output-format json \
| jq -r '.[] | [(.name | split("/")[-1]), .status, (.not_compliant | map(.Rule.name)
| unique[])] | join(" ")'catalog.yaml PASS booknest-data.yaml FAIL buckets_block_public_access tables_are_retained tables_bill_on_demand reader-role.yaml PASS seed.yaml FAIL roles_have_boundary tables_are_retained
seed.yaml's demo table is not retained and its role has no boundary. booknest-data.yaml computes BillingMode with Fn::FindInMap, which cfn-guard, reading the template as plain data, cannot resolve. Rules get unit tests too: cfn-guard test runs infra/rules/booknest-tests.yaml, small inputs with expected PASS, FAIL or SKIP per rule, and here reported every expectation met. For ready-made rules, the AWS Guard Rules Registry 144 (github.com/aws-cloudformation/aws-guard-rules-registry (https://github.com/aws-cloudformation/aws-guard-rules-registry 144 ), last updated March 2024) maps rules such as S3_BUCKET_LEVEL_PUBLIC_ACCESS_PROHIBITED to compliance frameworks; the same rules can run as CloudFormation 24 Hooks that block a noncompliant deployment in the account itself.