This check fires when an AWS Config delivery channel can no longer ship configuration history to its target S3 bucket or SNS topic, usually because of a missing bucket, a broken IAM role, or an over-tight bucket policy. A broken delivery channel means your configuration audit trail has gaps. Fix it by repairing the bucket policy and Config service role, then run describe-delivery-channel-status to confirm deliveries succeed.
AWS Config is the service that records the configuration of your AWS resources over time. Every time a security group rule changes, an IAM policy gets updated, or an S3 bucket flips to public, Config captures a snapshot. That history feeds compliance evidence, drift detection, and a good chunk of incident investigations. The catch is that Config only matters if it actually delivers what it records. When the delivery channel breaks, the recorder keeps running but the data never lands where you can use it.
This Lensix check, account_configdelivery, looks at the status of your Config delivery channels and flags any that are failing to deliver configuration history.
What this check detects
An AWS Config delivery channel defines where Config sends two things: configuration history files and configuration snapshots. The destination is an S3 bucket, and optionally an SNS topic for notifications. Config attempts deliveries on a schedule and records the outcome of each attempt.
The check inspects the configHistoryDeliveryInfo field returned by the AWS Config API. When the most recent delivery attempt has a status of FAILURE, the delivery channel is considered broken and the check fails.
Note: A delivery channel can be in a failed state while the configuration recorder is still happily recording. Config does not stop recording just because it cannot deliver. This is why a silent failure here is easy to miss. The console shows the recorder as active, but the S3 bucket stops receiving fresh history files.
Common reasons the delivery fails:
- The target S3 bucket was deleted, renamed, or moved to another account.
- The S3 bucket policy no longer grants the Config service the permissions it needs to write.
- The IAM role Config assumes lost its permissions, or the role was deleted.
- A bucket policy
Denystatement (often added for compliance) blocks Config writes. - The bucket has an SCP, KMS key policy, or encryption setting that Config cannot satisfy.
- The SNS topic referenced by the delivery channel was deleted.
Why it matters
The damage from a failing Config delivery channel is rarely visible on the day it breaks. It shows up later, at exactly the wrong moment.
You lose your audit trail when you need it most
Picture an incident where someone widened a security group to 0.0.0.0/0 three weeks ago. Your first move is to pull the Config timeline for that security group to see who changed it and when. If the delivery channel has been failing since before the change, that history is not in your bucket. You are now reconstructing events from CloudTrail alone, which is harder and slower.
Compliance evidence goes stale silently
Frameworks like PCI DSS, SOC 2, and HIPAA expect a continuous record of resource configuration. Auditors ask for it. If your Config history has a multi-month hole because nobody noticed the delivery channel was red, you have a gap that is awkward to explain and impossible to backfill.
Warning: AWS Config does not retroactively re-deliver history that failed to write during the outage window. Once you fix the channel, deliveries resume going forward, but the gap in your S3 bucket stays a gap. There is no replay.
Conformance packs and rules degrade
If you rely on Config rules or conformance packs for automated compliance scoring, a broken delivery pipeline undermines the evidence those rules produce. You may still see rule evaluations, but the underlying history that backs up findings is incomplete.
How to fix it
Start by confirming the failure and reading the exact error AWS hands you. The status output usually tells you precisely what went wrong.
Step 1: Check the delivery channel status
aws configservice describe-delivery-channel-status \
--region us-east-1
Look for the configHistoryDeliveryInfo block. A failing channel looks like this:
{
"DeliveryChannelsStatus": [
{
"name": "default",
"configHistoryDeliveryInfo": {
"lastStatus": "FAILURE",
"lastErrorCode": "AccessDenied",
"lastErrorMessage": "Insufficient delivery policy to s3 bucket: my-config-bucket, unable to write to bucket",
"lastAttemptTime": 1718000000.0
}
}
]
}
The lastErrorMessage is your map. AccessDenied or NoSuchBucket point you straight to the cause.
Step 2: Confirm the bucket exists and find its name
aws configservice describe-delivery-channels \
--query 'DeliveryChannels[*].[name,s3BucketName,s3KeyPrefix,snsTopicARN]' \
--output table
If the bucket is gone, you either recreate it with the same name or repoint the delivery channel at a new bucket (Step 4 covers that).
Step 3: Repair the S3 bucket policy
This is the most common fix. The bucket policy must allow the Config service principal to check the bucket ACL and write objects. Replace my-config-bucket and the account ID with your values.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSConfigBucketPermissionsCheck",
"Effect": "Allow",
"Principal": { "Service": "config.amazonaws.com" },
"Action": ["s3:GetBucketAcl", "s3:ListBucket"],
"Resource": "arn:aws:s3:::my-config-bucket",
"Condition": {
"StringEquals": { "AWS:SourceAccount": "111122223333" }
}
},
{
"Sid": "AWSConfigBucketDelivery",
"Effect": "Allow",
"Principal": { "Service": "config.amazonaws.com" },
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-config-bucket/AWSLogs/111122223333/Config/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"AWS:SourceAccount": "111122223333"
}
}
}
]
}
Apply it:
aws s3api put-bucket-policy \
--bucket my-config-bucket \
--policy file://config-bucket-policy.json
Danger: put-bucket-policy replaces the entire bucket policy, it does not merge. If the bucket already carries other statements, fetch the current policy first with aws s3api get-bucket-policy, add your Config statements to it, and apply the combined document. Overwriting blindly can break other services that write to the same bucket.
Step 4: Verify the Config service role
Config assumes a service-linked role or a customer-managed role to read resource configuration and deliver to S3. Confirm the role attached to your recorder still exists and has the right policy.
aws configservice describe-configuration-recorders \
--query 'ConfigurationRecorders[*].[name,roleARN]' \
--output table
If you use the AWS managed policy, make sure it is attached:
aws iam attach-role-policy \
--role-name AWSConfigRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AWS_ConfigRole
Step 5: Handle KMS encryption, if used
If the bucket uses SSE-KMS with a customer-managed key, the key policy must let Config encrypt objects. Add the Config service principal to the key policy with kms:GenerateDataKey and kms:Decrypt, scoped by source account. A missing KMS grant is a frequent cause of AccessDenied even when the bucket policy looks correct.
Step 6: Force a delivery and confirm
Trigger an immediate delivery so you do not have to wait for the next scheduled attempt:
aws configservice deliver-config-snapshot \
--delivery-channel-name default
Then recheck the status:
aws configservice describe-delivery-channel-status
You want "lastStatus": "SUCCESS" in the configHistoryDeliveryInfo block. Confirm the file actually landed:
aws s3 ls s3://my-config-bucket/AWSLogs/111122223333/Config/ --recursive | tail
How to prevent it from happening again
Most Config delivery failures trace back to someone editing a bucket policy or deleting a resource without realizing Config depended on it. Prevention is about locking the setup into code and alerting on failure.
Define Config in infrastructure as code
Manage the bucket, bucket policy, delivery channel, and recorder together in Terraform so the relationships are explicit and reviewed through pull requests.
# Terraform: Config delivery channel with locked bucket policy
resource "aws_config_delivery_channel" "main" {
name = "default"
s3_bucket_name = aws_s3_bucket.config.id
depends_on = [aws_config_configuration_recorder.main]
snapshot_delivery_properties {
delivery_frequency = "Six_Hours"
}
}
resource "aws_s3_bucket_policy" "config" {
bucket = aws_s3_bucket.config.id
policy = data.aws_iam_policy_document.config_bucket.json
}
Tip: Add a prevent_destroy lifecycle block to the Config bucket resource in Terraform. It stops terraform destroy and accidental replacements from deleting the bucket your audit trail depends on.
Alert on delivery failure with CloudWatch
Config emits the metric you can watch. Better, create an EventBridge rule for the Config Configuration Snapshot Delivery Failed event and route it to SNS or your incident channel:
{
"source": ["aws.config"],
"detail-type": ["Config Configuration Snapshot Delivery Status"],
"detail": {
"messageType": ["ConfigurationSnapshotDeliveryFailed"]
}
}
Gate bucket policy changes in CI
Add a policy-as-code check (OPA, Conftest, or Checkov) to your pipeline that fails any plan removing the Config service principal from the audit bucket policy. This catches the exact mistake that breaks delivery before it reaches production.
Best practices
- Use a dedicated, write-once bucket for Config. Do not share it with application logs or other services. Fewer hands touching the policy means fewer accidental breakages.
- Enable S3 versioning and Object Lock on the Config bucket. This protects the audit trail from deletion or tampering, which matters for compliance integrity.
- Centralize Config in an aggregator account. In multi-account setups, deliver to a central security or log-archive account so you have one trustworthy timeline across the org.
- Record all resource types, including global ones. A partial recorder gives you a partial history, and gaps tend to be in exactly the resources attackers touch.
- Review delivery status as part of your weekly security ops. A failing channel is cheap to spot and expensive to discover after an incident.
Note: If you run AWS Organizations, set delivery channel and recorder configuration through an organization Config rule or a delegated administrator. That way new accounts inherit a working pipeline automatically and you avoid the per-account drift that produces these failures.
A Config delivery channel is one of those pieces of infrastructure that is invisible when healthy and painful when broken. Treat it like the audit system it is: put it in code, alert on its failures, and protect the bucket it writes to. Then this check stays green and your timeline stays complete.

