AWS Config Aggregators: The Hidden Gap in Deleted Resource Tracking
You've set up AWS Config Aggregators expecting comprehensive visibility across your organization, including deleted resources. But when you try to query for recently deleted S3 buckets or EC2 instances, you discover a frustrating limitation: Config Aggregators don't retain deleted resource data. This gap can leave you blind during critical compliance audits and security investigations.
๐จ The Problem: Aggregators Don't Track Deletions
- Centralized deletion tracking across all accounts
- Historical queries for deleted resources
- Compliance reporting with deletion timelines
- Security investigation capabilities
- Only current resources in aggregator views
- No deletion history in centralized queries
- Account-by-account investigation still required
- Compliance gaps for audit requirements
Understanding the Limitation
AWS Config Aggregators collect configuration data from multiple accounts and regions into a centralized view. However, they only aggregate currently existing resources. When a resource is deleted from the source account's Config service, it disappears from the aggregator as well.
What Aggregators Actually Provide
Current Resource Inventory: View active resources across accounts and regions
Live Compliance Status: Check current compliance state organization-wide
Active Resource Queries: Search for existing resources across your AWS organization
What's Missing
Deleted Resource History: No centralized view of deleted resources
Deletion Timelines: Can't query when resources were removed
Historical Compliance: Limited ability to show past compliance states
The Reality: Individual Account Queries Required
Why Aggregators Don't Show Deleted Resources
Config Aggregators work by collecting data from individual Config services in member accounts. When a resource is deleted:
- Source account Config marks the resource as
ResourceDeleted - Aggregator sync removes the deleted resource from centralized view
- Historical data remains only in the source account's Config history
Querying Deleted Resources (Account-by-Account)
To find deleted resources, you must query each account individually:
import boto3
from datetime import datetime, timedelta
def find_deleted_resources_per_account(account_ids, regions, resource_type, days_back=30):
"""
Find deleted resources by querying each account individually
Note: This CANNOT use Config Aggregators - must query each account
"""
deleted_resources = []
for account_id in account_ids:
for region in regions:
try:
# Assume role in target account
sts = boto3.client('sts')
role_arn = f"arn:aws:iam::{account_id}:role/ConfigQueryRole"
assumed_role = sts.assume_role(
RoleArn=role_arn,
RoleSessionName='DeletedResourceQuery'
)
# Create Config client for target account
config_client = boto3.client(
'config',
region_name=region,
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
# Query configuration history for deleted resources
end_time = datetime.utcnow()
start_time = end_time - timedelta(days=days_back)
# This is the key limitation: must query each account individually
response = config_client.get_resource_config_history(
resourceType=resource_type,
laterTime=start_time,
earlierTime=end_time
)
for item in response['configurationItems']:
if item['configurationItemStatus'] == 'ResourceDeleted':
deleted_resources.append({
'ResourceId': item['resourceId'],
'ResourceType': resource_type,
'AccountId': account_id,
'Region': region,
'DeletionTime': item['configurationItemCaptureTime'],
'ResourceName': item.get('resourceName', 'N/A')
})
except Exception as e:
print(f"Error querying account {account_id} in {region}: {str(e)}")
continue
return deleted_resources
# Example usage - requires individual account queries
deleted_resources = find_deleted_resources_per_account(
account_ids=['111111111111', '222222222222', '333333333333'],
regions=['us-east-1', 'us-west-2'],
resource_type='AWS::S3::Bucket',
days_back=7
)
for resource in deleted_resources:
print(f"Deleted {resource['ResourceType']}: {resource['ResourceName']} in {resource['AccountId']}/{resource['Region']}")
SQL Queries Don't Work Across Aggregators
Config's advanced query feature has the same limitation - you cannot query deleted resources across aggregators:
-- โ This WON'T work in Config Aggregator advanced queries
-- Deleted resources are not available in aggregated data
SELECT
resourceId,
resourceType,
accountId,
awsRegion,
configurationItemCaptureTime
WHERE
configurationItemStatus = 'ResourceDeleted'
AND configurationItemCaptureTime > '2024-12-17T00:00:00.000Z'
-- Returns: No results (even if resources were deleted)
-- โ
This ONLY works when querying individual accounts
-- Must run this query in each account's Config service separately
SELECT
resourceId,
resourceType,
configurationItemCaptureTime,
tags.Environment
WHERE
configurationItemStatus = 'ResourceDeleted'
AND tags.Environment = 'Production'
AND configurationItemCaptureTime > '2024-12-17T00:00:00.000Z'
๐ง Alternative Solutions
1. CloudTrail for Deletion Events
Use CloudTrail to track deletion API calls across accounts:
def find_deletions_via_cloudtrail(account_ids, start_time, end_time):
"""
Query CloudTrail for deletion events across accounts
"""
deletion_events = []
for account_id in account_ids:
cloudtrail = boto3.client('cloudtrail')
# Query for deletion events
response = cloudtrail.lookup_events(
LookupAttributes=[
{
'AttributeKey': 'EventName',
'AttributeValue': 'DeleteBucket' # Example for S3
}
],
StartTime=start_time,
EndTime=end_time
)
for event in response['Events']:
deletion_events.append({
'EventName': event['EventName'],
'EventTime': event['EventTime'],
'Username': event['Username'],
'Resources': event.get('Resources', [])
})
return deletion_events
2. Custom Multi-Account Deletion Tracker
Build your own centralized deletion tracking:
def build_deletion_inventory(organization_accounts):
"""
Build centralized deletion inventory by querying all accounts
"""
all_deletions = []
for account in organization_accounts:
account_deletions = find_deleted_resources_per_account(
account_ids=[account['Id']],
regions=['us-east-1', 'us-west-2'],
resource_type='AWS::S3::Bucket',
days_back=30
)
all_deletions.extend(account_deletions)
# Store in centralized database/S3 for reporting
store_deletion_data(all_deletions)
return all_deletions
3. EventBridge for Real-Time Tracking
Set up EventBridge rules to capture deletion events:
{
"Rules": [
{
"Name": "S3BucketDeletions",
"EventPattern": {
"source": ["aws.s3"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["s3.amazonaws.com"],
"eventName": ["DeleteBucket"]
}
},
"Targets": [
{
"Id": "1",
"Arn": "arn:aws:lambda:us-east-1:123456789012:function:ProcessDeletion"
}
]
}
]
}
๐ ๏ธ Workaround Strategies
1. Understand the Limitation
- Current resource inventory across accounts
- Live compliance monitoring organization-wide
- Active resource queries and reporting
- Real-time configuration drift detection
- Track deleted resources centrally
- Provide deletion history across accounts
- Support historical compliance queries
- Show resource lifecycle end-to-end
2. Implement Complementary Solutions
CloudTrail Integration: Track deletion API calls across accounts
Custom Automation: Build Lambda functions to query accounts individually
EventBridge Rules: Capture deletion events in real-time
External Storage: Store deletion data in S3/DynamoDB for centralized access
3. Set Proper Expectations
For Current Resources: Use Config Aggregators for live inventory and compliance
For Deleted Resources: Plan for individual account queries or alternative solutions
For Compliance: Combine Config data with CloudTrail logs for complete audit trails
For Automation: Build custom solutions that work around the aggregator limitation
๐ก Recommended Architecture
- Config Aggregators: Live inventory
- Compliance monitoring: Real-time status
- Resource queries: Cross-account search
- CloudTrail: API call tracking
- Custom Lambda: Multi-account queries
- S3/DynamoDB: Centralized storage
- Combine both sources: Complete picture
- Automated collection: Scheduled queries
- Dashboard integration: Unified view
Conclusion
AWS Config Aggregators are powerful for tracking current resources across your organization, but they have a critical limitation: deleted resources disappear from aggregated views. Understanding this gap is essential for:
- Setting realistic expectations for compliance and audit capabilities
- Planning complementary solutions for deletion tracking
- Designing proper architecture that combines multiple AWS services
- Avoiding surprises during critical investigations
Config Aggregators remain valuable for live resource inventory and compliance monitoring. For deleted resource tracking, plan to implement CloudTrail analysis, custom automation, or third-party solutions that can provide the centralized deletion visibility you need.
Have you discovered this Config Aggregator limitation in your environment? What alternative approaches have you implemented for tracking deleted resources? Share your workarounds in the comments below.