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

### ๐Ÿ’” What You Expected
  • Centralized deletion tracking across all accounts
  • Historical queries for deleted resources
  • Compliance reporting with deletion timelines
  • Security investigation capabilities
### ๐Ÿ˜ž What You Actually Get
  • 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:

  1. Source account Config marks the resource as ResourceDeleted
  2. Aggregator sync removes the deleted resource from centralized view
  3. 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

### โœ… What Aggregators ARE Good For
  • Current resource inventory across accounts
  • Live compliance monitoring organization-wide
  • Active resource queries and reporting
  • Real-time configuration drift detection
### โŒ What Aggregators CAN'T Do
  • 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

### ๐Ÿ—๏ธ Current Resources
  • Config Aggregators: Live inventory
  • Compliance monitoring: Real-time status
  • Resource queries: Cross-account search
### ๐Ÿ—‘๏ธ Deleted Resources
  • CloudTrail: API call tracking
  • Custom Lambda: Multi-account queries
  • S3/DynamoDB: Centralized storage
### ๐Ÿ“Š Reporting
  • 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.