Enforcing Compliance With AWS Config Rules
Managed rules, custom Lambda and Guard rules, conformance packs, and automated remediation — drafted with AI and verified against the evaluation model that actually scores your resources.
- #aws
- #ai
- #config
- #compliance
- #governance
Compliance work has a particular failure mode: you write a beautifully worded policy in a wiki, everyone nods, and three months later half the new resources violate it because nothing was actually checking. AWS Config is the service that closes that gap — it continuously records the configuration of your resources and evaluates them against rules, so “all S3 buckets must block public access” becomes a thing the platform enforces rather than a thing the platform hopes for.
The catch is that Config has a specific evaluation model, and if you reason about it wrong, you’ll build rules that report COMPLIANT on resources you never wanted to allow. I draft a lot of Config rules and remediations with an AI assistant — it’s genuinely good at recalling managed-rule identifiers and writing Guard policies — but I verify every one against how Config actually triggers and scores. Here’s the working model.
Three ways to write a rule
A Config rule is fundamentally a function that takes a resource configuration and returns COMPLIANT, NON_COMPLIANT, or NOT_APPLICABLE. AWS gives you three ways to supply that function.
Managed rules are AWS-authored and the right default. There are hundreds, identified by an uppercase name. You enable one and set parameters:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "s3-bucket-public-read-prohibited",
"Description": "Checks that S3 buckets do not allow public read access.",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
},
"Scope": {
"ComplianceResourceTypes": ["AWS::S3::Bucket"]
}
}'
Custom Guard rules use CloudFormation Guard, a policy-as-code DSL, for logic AWS doesn’t ship. This is the modern choice for custom checks — no Lambda to maintain. This Guard policy requires every EBS volume to be encrypted:
rule encryption_check {
AWS::EC2::Volume {
Encrypted == true
}
}
Custom Lambda rules are the escape hatch when you need to call other APIs or correlate across resources. You pay for invocations and you own the code, so reach for Guard first.
The most common AI-introduced error I catch here is a hallucinated SourceIdentifier. The model will confidently produce S3_BUCKET_NO_PUBLIC_ACCESS when the real identifier is S3_BUCKET_PUBLIC_READ_PROHIBITED. Always verify the identifier against the live list:
# There is no generic "list all managed rules" API call;
# confirm a rule exists by checking it evaluated after you create it.
aws configservice describe-config-rules \
--config-rule-names s3-bucket-public-read-prohibited
Triggers decide what “evaluated” even means
This is the part people get wrong. A rule’s trigger type determines when it runs, and that changes which resources get scored.
A configuration-change trigger fires when a resource of the in-scope type changes. A periodic trigger runs on a fixed cadence (hourly through daily) regardless of changes. Some managed rules support only one; some support both.
Why it matters: a periodic-only rule won’t score a resource the instant it’s created — it scores on the next interval. And a change-triggered rule only evaluates resource types Config is actually recording. If your recorder excludes a resource type, change-triggered rules for it never fire, and the resource silently shows up as neither compliant nor non-compliant. Before trusting any compliance dashboard, confirm the recorder’s scope:
aws configservice describe-configuration-recorders \
--query 'ConfigurationRecorders[0].recordingGroup'
If allSupported is false and your resource type isn’t in the inclusion list, your rules for it are decoration.
Conformance packs: rules as a versioned bundle
Enabling rules one at a time doesn’t scale past a handful. A conformance pack is a YAML template — rules plus optional remediations — deployed as a single immutable unit, which you can roll out org-wide from a delegated admin account. AWS ships starter packs mapped to frameworks (CIS, PCI DSS, NIST), and you can author your own.
Resources:
S3PublicReadProhibited:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-public-read-prohibited
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED
RdsStorageEncrypted:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: rds-storage-encrypted
Source:
Owner: AWS
SourceIdentifier: RDS_STORAGE_ENCRYPTED
aws configservice put-conformance-pack \
--conformance-pack-name acme-baseline \
--template-body file://acme-baseline.yaml \
--delivery-s3-bucket-name acme-config-conformance-results
The win is lifecycle management: the whole pack reports a single aggregated compliance score, and deleting the pack cleanly removes its rules. Don’t enable the same managed rule both standalone and inside a pack — you’ll get duplicate rules and confusing double-counting.
Remediation: detection is half the job
A rule that only reports is a report nobody reads. Config integrates with SSM Automation documents to fix non-compliant resources. You attach a remediation configuration that maps the rule to an Automation document and supplies parameters, optionally automatic rather than manual.
aws configservice put-remediation-configurations \
--remediation-configurations '[{
"ConfigRuleName": "s3-bucket-public-read-prohibited",
"TargetType": "SSM_DOCUMENT",
"TargetId": "AWS-DisableS3BucketPublicReadWrite",
"Automatic": true,
"MaximumAutomaticAttempts": 3,
"RetryAttemptSeconds": 60,
"Parameters": {
"AutomationAssumeRole": {
"StaticValue": { "Values": ["arn:aws:iam::111122223333:role/ConfigRemediationRole"] }
},
"S3BucketName": {
"ResourceValue": { "Value": "RESOURCE_ID" }
}
}
}]'
Automatic: true is where you want a human in the loop the first few cycles. Auto-remediation that flips a setting can break a workload that depended on the “wrong” config, and Config will dutifully re-trigger if the resource drifts back, creating a fight between your remediation and whatever set it. I almost always ship remediations as manual first, watch what they’d touch, then promote to automatic.
Prompt: I want a custom Config rule that flags any security group allowing 0.0.0.0/0 on port 22. Should I use Guard or Lambda, and what’s the gotcha?
Reply: Use Guard — this is pure property inspection on a single resource type, no cross-resource calls, so a Lambda would just be maintenance you don’t need. Scope it to
AWS::EC2::SecurityGroupwith a change trigger so it evaluates the moment a group is modified. The real gotcha is the data shape: a security group’s ingress rules are a list, and each rule can carry multiple IP ranges, so your Guard policy has to iterate theIpPermissionscollection and check everyCidrIp, not just the first — a naiveIpPermissions[0]check passes a group that’s safe on rule one and wide open on rule two. I’m describing the Guard config structure from the model; before relying on it, evaluate the rule against a known-bad group and confirm it actually flags it.
That reply is right on both counts — Guard over Lambda, and the list-iteration trap that produces false COMPLIANT results — and it ends by telling me to test against a known-bad resource. I did exactly that: built a security group with a clean first rule and an open second rule, and confirmed the iterating policy caught it. A rule you haven’t tested against a deliberate violation is a rule you don’t trust.
Make it the platform’s job
The shape of a mature Config setup is: managed rules wherever they exist, Guard for the gaps, the whole set bundled in conformance packs deployed from a delegated admin account, and remediations that start manual and graduate to automatic once you’ve watched them. The AI accelerates the drafting — rule identifiers, Guard syntax, remediation document names — and you own the verification: recorder scope, trigger type, and a deliberate violation to prove each rule fires.
For starting points, the AWS guides cover the surrounding governance setup, and the compliance and security entries in the prompt library give you tested scaffolds for Guard rules and remediation docs. The pairing that’s served me best is a Guard-rule generator alongside a “score this rule against a known-bad resource” checklist — draft fast, verify always.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.