Analyze AWS Config Costs

Analyze AWS Config Costs

Analyze AWS Config Costs

Analyze AWS Config Costs with Athena

AWS Config can feel expensive because usage scales with how many configuration items are recorded and how often they change. Cost questions usually boil down to: “Which resources are generating the most records?” This post shows how to answer that using Athena against centralized AWS Config logs.

Prerequisites

  • AWS Config is aggregated to a centralized S3 bucket in a logs account.
  • You know the bucket name (replace aws-config-logs-bucket below).
  • You have Athena access in the logs account.

Create an Athena table (Config history)

The following table uses partition projection for accounts, regions, and dates.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
CREATE EXTERNAL TABLE `awsconfig_history`(
  `fileversion` string COMMENT 'from deserializer',
  `configsnapshotid` string COMMENT 'from deserializer',
  `configurationitems` array<struct<configurationitemversion:string,configurationitemcapturetime:string,configurationstateid:bigint,awsaccountid:string,configurationitemstatus:string,resourcetype:string,resourceid:string,resourcename:string,arn:string,awsregion:string,availabilityzone:string,configurationstatemd5hash:string,resourcecreationtime:string>> COMMENT 'from deserializer')
PARTITIONED BY (
  `account_id` string,
  `region` string,
  `year` string,
  `month` string,
  `day` string)
ROW FORMAT SERDE
  'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS INPUTFORMAT
  'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://aws-config-logs-bucket/AWSLogs'
TBLPROPERTIES (
  'projection.account_id.type'='injected',
  'projection.day.range'='1,31',
  'projection.day.type'='integer',
  'projection.enabled'='true',
  'projection.month.range'='1,12',
  'projection.month.type'='integer',
  'projection.region.type'='injected',
  'projection.year.range'='2020,2035',
  'projection.year.type'='integer',
  'storage.location.template'='s3://aws-config-logs-bucket/AWSLogs/${account_id}/Config/${region}/${year}/${month}/${day}/ConfigHistory/',
  'transient_lastDdlTime'='1770045064')

After the table is created, you can query it directly.

Find the noisiest resource types (last month)

This query lists how many configuration items were recorded per resource type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
SELECT
  ci.awsAccountId AS account_id,
  ci.awsRegion    AS region,
  ci.resourceType,
  count(*) AS changes,
  max(ci.configurationItemCaptureTime) AS latest_capture_time
FROM aws_config_logs.awsconfig_history
CROSS JOIN UNNEST(configurationitems) AS t(ci)
WHERE account_id IN ('111111111111')
  AND region IN ('us-east-1', 'us-west-2')
  AND year  = '2026'
  AND month = '1'
  AND CAST(day AS integer) BETWEEN 1 AND 31
GROUP BY 1,2,3
ORDER BY latest_capture_time DESC, changes DESC;

This returns the resource types that generated the most records for the selected accounts and regions.

Example output:

1
2
3
4
5
6
7
account_id    region     resourceType                          changes  latest_capture_time
111111111111  us-east-1  AWS::Config::ResourceCompliance        8401     2026-01-31T23:59:24.561Z
111111111111  us-east-1  AWS::EC2::LaunchTemplate               2125     2026-01-31T23:58:55.285Z
111111111111  us-east-1  AWS::EC2::NetworkInterface             1070     2026-01-31T23:16:48.316Z
111111111111  us-east-1  AWS::EC2::Subnet                        936     2026-01-31T23:16:48.553Z
111111111111  us-east-1  AWS::EC2::SecurityGroup                 897     2026-01-31T23:16:48.470Z
111111111111  us-east-1  AWS::EC2::Volume                        869     2026-01-31T23:16:48.213Z

Summarize by account and region

Use this to see which accounts or regions are the loudest.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
SELECT
  ci.awsAccountId AS account_id,
  ci.awsRegion    AS region,
  count(*) AS changes
FROM aws_config_logs.awsconfig_history
CROSS JOIN UNNEST(configurationitems) AS t(ci)
WHERE year = '2026'
  AND month = '1'
GROUP BY 1,2
ORDER BY changes DESC;

Cost reduction levers (without losing audit value)

Once you see which types are noisy, you can tune AWS Config in a few safe ways:

  • Record only required resource types instead of “all supported types.”
  • Exclude low-value resource types that change frequently but are not audit-critical.
  • Narrow the global recording scope if global resources are covered elsewhere.

Common high-volume candidates to review (examples only):

1
2
3
4
5
6
7
AWS::Config::ResourceCompliance
AWS::ElasticLoadBalancingV2::TargetGroup
AWS::Backup::RecoveryPoint
AWS::EC2::NetworkInterface
AWS::EC2::EIPAssociation
AWS::SSM::ManagedInstanceInventory
AWS::EC2::EC2Fleet