AWS Config
Continuous compliance and resource history. Managed rules, conformance packs, auto-remediation with SSM, and Config versus CloudTrail.
Config records what your AWS resources look like over time and continuously evaluates them against rules. CloudTrail tells you someone called AuthorizeSecurityGroupIngress; Config tells you the security group now allows 0.0.0.0/0 on port 22, that this is non-compliant, and what it looked like before.
The two answer different questions, who did it and what is it now, and a mature setup uses both.
Turn on the recorder
Nothing happens until a configuration recorder and a delivery channel exist:
aws configservice put-configuration-recorder \
--configuration-recorder name=default,roleARN="$CONFIG_ROLE_ARN" \
--recording-group '{
"allSupported": true,
"includeGlobalResourceTypes": true,
"recordingStrategy": {"useOnly": "ALL_SUPPORTED_RESOURCE_TYPES"}
}'
aws configservice put-delivery-channel \
--delivery-channel '{
"name": "default",
"s3BucketName": "my-config-history",
"configSnapshotDeliveryProperties": {"deliveryFrequency": "TwentyFour_Hours"}
}'
aws configservice start-configuration-recorder --configuration-recorder-name default
includeGlobalResourceTypes covers IAM, which is not regional. Enable it in one region only. Enabling it everywhere records every IAM change once per region and multiplies that portion of the bill for no benefit.
Cost is the thing people get wrong
Config bills per configuration item recorded and per rule evaluation. Both sound small and both scale with change.
An Auto Scaling group replacing instances continuously, a busy EKS cluster, or anything creating and destroying resources on a loop generates configuration items constantly, and with allSupported: true you record every one.
If the bill surprises you, record selectively:
aws configservice put-configuration-recorder \
--configuration-recorder name=default,roleARN="$CONFIG_ROLE_ARN" \
--recording-group '{
"allSupported": false,
"resourceTypes": ["AWS::EC2::SecurityGroup","AWS::IAM::Role","AWS::IAM::Policy",
"AWS::S3::Bucket","AWS::RDS::DBInstance","AWS::KMS::Key"]
}'
You can also exclude high-churn types while keeping everything else, and set periodic rather than continuous recording for types that change constantly. Start broad, look at the bill after a month, then narrow.
Rules
A rule evaluates resources and marks them COMPLIANT or NON_COMPLIANT. Around 300 managed rules cover most common requirements:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "s3-bucket-public-read-prohibited",
"Source": {"Owner": "AWS", "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"},
"Scope": {"ComplianceResourceTypes": ["AWS::S3::Bucket"]}
}'
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "required-tags",
"Source": {"Owner": "AWS", "SourceIdentifier": "REQUIRED_TAGS"},
"InputParameters": "{\"tag1Key\":\"Owner\",\"tag2Key\":\"Environment\",\"tag3Key\":\"CostCentre\"}"
}'
aws configservice get-compliance-details-by-config-rule \
--config-rule-name s3-bucket-public-read-prohibited \
--compliance-types NON_COMPLIANT \
--query 'EvaluationResults[].EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId'
Rules are either change-triggered, evaluating when a resource changes, or periodic, running on a schedule. Change-triggered is what gives you near-real-time detection; periodic covers things with no configuration change to hook, such as "an access key older than 90 days".
For requirements no managed rule covers, write a custom rule, as a Lambda, or more simply with Guard syntax:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "rds-encrypted",
"Source": {
"Owner": "CUSTOM_POLICY",
"SourceDetails": [{"EventSource":"aws.config","MessageType":"ConfigurationItemChangeNotification"}],
"CustomPolicyDetails": {
"PolicyRuntime": "guard-2.x.x",
"PolicyText": "let rds = Resources.*[ Type == \"AWS::RDS::DBInstance\" ]\nrule encrypted when %rds !empty { %rds.Properties.StorageEncrypted == true }"
}
}
}'
Guard rules need no Lambda to deploy, maintain or pay for.
Conformance packs
A pack is a collection of rules deployed as a unit, and AWS publishes packs mapped to real frameworks. CIS, PCI DSS, HIPAA, NIST:
aws configservice put-conformance-pack \
--conformance-pack-name cis-foundations \
--template-s3-uri s3://aws-config-conformance-packs/Operational-Best-Practices-for-CIS-AWS-v1.4-Level1.yaml \
--delivery-s3-bucket my-config-history
Deploying one across an organisation is how a baseline reaches every account, including accounts created later. Expect a large initial non-compliance count. Treat it as a backlog to work through rather than a signal to disable the pack.
Automatic remediation
This is where Config becomes useful rather than merely informative. Attach an SSM Automation document to a rule:
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": {
"S3BucketName": {"ResourceValue": {"Value": "RESOURCE_ID"}},
"AutomationAssumeRole": {"StaticValue": {"Values": ["arn:aws:iam::123456789012:role/ConfigRemediation"]}}
}
}]'
A public bucket is now private within minutes of becoming public, without anyone reading a dashboard.
Introduce automatic remediation carefully. Start with "Automatic": false, which makes remediation a one-click manual action, and watch what it would have done for a couple of weeks. An over-eager rule that revokes a security group rule something legitimately depends on causes an outage, and does so at the moment nobody expects a change.
Good candidates: making buckets private, enabling encryption, removing 0.0.0.0/0 SSH rules, enabling versioning. Poor candidates: anything terminating resources or altering networking.
The configuration timeline
The feature that repays enabling Config on its own:
aws configservice get-resource-config-history \
--resource-type AWS::EC2::SecurityGroup \
--resource-id sg-0123456789abcdef0 \
--limit 5 \
--query 'configurationItems[].[configurationItemCaptureTime,configurationItemStatus]' \
--output table
Every recorded change is a snapshot you can diff, so "what did this look like before Tuesday's incident" is a query rather than an archaeology exercise. Combined with CloudTrail's record of who made the call, that is a complete answer.
Advanced queries work across your whole inventory in SQL:
aws configservice select-resource-config \
--expression "SELECT resourceId, resourceName, configuration.instanceType
WHERE resourceType = 'AWS::EC2::Instance'
AND configuration.state.name = 'running'"
That is also a genuinely good inventory tool. Finding every unencrypted volume or every instance of a retired type across accounts.
Aggregators
For multi-account estates, an aggregator gives one view:
aws configservice put-configuration-aggregator \
--configuration-aggregator-name org-wide \
--organization-aggregation-source \
'RoleArn=arn:aws:iam::123456789012:role/ConfigAggregator,AllAwsRegions=true'
Compliance across every account and region in one place, which is the only practical way to answer "are we compliant" at organisational scale.
Config, CloudTrail or Security Hub
| Answers | |
|---|---|
| CloudTrail | Who called which API, when, from where |
| Config | What does this resource look like, is it compliant, what changed |
| Security Hub | Aggregated findings from Config, GuardDuty, Inspector and others |
They stack rather than compete. Security Hub consumes Config rule results alongside other sources and presents one prioritised view, which is where a team with several security services should be looking.
Common errors
NoAvailableConfigurationRecorderException: Configuration recorder is not
available to put configuration rule
The recorder is not started. start-configuration-recorder first.
InsufficientDeliveryPolicyException: Insufficient delivery policy to s3 bucket
The bucket policy must allow the Config service principal to write, with the expected ACL condition.
Rules stuck at NOT_APPLICABLE. No resources of that type exist, or the recorder is not recording that type. Likely with a restricted resourceTypes list.
A surprising bill. allSupported: true plus high-churn resources, or global resource types recorded in every region.
Quick reference
| Task | Command |
|---|---|
| Start recording | put-configuration-recorder + start-configuration-recorder |
| Add a rule | aws configservice put-config-rule |
| Who is failing? | get-compliance-details-by-config-rule --compliance-types NON_COMPLIANT |
| Resource history | aws configservice get-resource-config-history |
| Inventory query | aws configservice select-resource-config --expression "SELECT …" |
| Conformance pack | aws configservice put-conformance-pack |
| Auto-remediate | aws configservice put-remediation-configurations |
| Aggregate accounts | aws configservice put-configuration-aggregator |
Key takeaways
- Config records state; CloudTrail records calls. Use both.
- Nothing happens until the recorder is started.
- Enable global resource types in one region only, or IAM is recorded everywhere.
- Cost scales with change. High-churn resources plus
allSupportedis the usual bill shock. - Around 300 managed rules exist: check before writing a custom one.
- Guard-based custom rules need no Lambda.
- Start remediation with
Automatic: falseand observe before enabling it. - The configuration timeline answers "what changed" without archaeology.
- Use an aggregator for any multi-account estate.
Check your understanding
Config or CloudTrail?
An S3 bucket became publicly readable overnight. You need both who made the change and what the bucket policy looked like before it. Which services answer which half?
Remediation that caused an outage
A rule with automatic remediation revoked a security group rule that an internal service depended on, causing an outage. How should automatic remediation be introduced?
Frequently Asked Questions
What is the difference between AWS Config and CloudTrail?
CloudTrail is an API audit log: who called what, when, from which IP and whether it succeeded. Config is a configuration database: what each resource currently looks like, what it looked like previously, and whether it complies with your rules. So CloudTrail answers "who opened that security group" and Config answers "what does the security group allow now, and what did it allow yesterday". They overlap in an investigation and neither replaces the other.
Why is AWS Config expensive?
Because it bills per configuration item recorded and per rule evaluation, and both scale with how much your infrastructure changes. An Auto Scaling group cycling instances, a busy container platform, or anything creating and destroying resources continuously generates configuration items constantly, and allSupported: true records every one. Narrow the recorded resource types to what you actually evaluate, exclude high-churn types, and enable global resource types such as IAM in a single region rather than all of them.
How do Config rules work?
Each rule evaluates resources and marks them compliant or non-compliant. Change-triggered rules run when a resource configuration changes, giving near-real-time detection; periodic rules run on a schedule and cover things with no configuration change to hook, such as an access key exceeding an age limit. AWS provides around 300 managed rules covering most common requirements, and you can write custom rules either as a Lambda function or with Guard policy syntax, which requires no Lambda at all.
Can AWS Config fix problems automatically?
Yes, by attaching an SSM Automation document to a rule as a remediation configuration, so a bucket that becomes public is made private within minutes without anyone reading a dashboard. Introduce it carefully: begin with Automatic: false, which makes remediation a reviewed one-click action, and watch what it would have changed before enabling automation. Suitable candidates are making buckets private, enabling encryption and removing open SSH rules; anything that terminates resources or alters networking should stay manual.
What is a conformance pack?
A collection of Config rules and remediation actions deployed and managed as a single unit, defined as a template. AWS publishes packs mapped to recognised frameworks including CIS, PCI DSS, HIPAA and NIST, so adopting a baseline is one deployment rather than dozens of individual rules. Deployed across an organisation, a pack applies to every account including ones created afterwards. Expect a high initial non-compliance count and treat it as a backlog rather than a reason to remove the pack.
Do I need Config if I already have Security Hub?
Security Hub depends on Config for many of its checks, so the question does not quite separate them. Its CIS and foundational security standards are implemented as Config rules, and disabling the recorder leaves those unable to evaluate. What Security Hub adds is aggregation: findings from Config, GuardDuty, Inspector, Macie and third-party tools in one prioritised view across accounts. Config remains the source of configuration history and the mechanism for your own rules and remediation.