Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions CATALOG_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ Verified against `pricing.c3x.dev` on 2026-09-24:

| Result | Kinds |
|---|---:|
| **LIVE** (priced from the upstream API, tracks vendor price changes) | 164 |
| **LIVE** (priced from the upstream API, tracks vendor price changes) | 168 |
| **STATIC** (priced from an inline rate, does not track upstream changes) | 109 |
| **FREE** (not charged at the resource level) | 1,067 |
| **ZERO / DRIFT / NOFIX / STALE / ERRORED** | 0 |
| **Total** | **1,340** |
| **FREE** (not charged at the resource level) | 1,083 |
| **ZERO** (`aws_keyspaces_table`: its AmazonMCS prices arrive with the next pricing-API scrape) | 1 |
| **DRIFT / NOFIX / STALE / ERRORED** | 0 |
| **Total** | **1,361** |

So 273 kinds carry a price and the remaining 1,067 are legitimately free.
So 278 kinds carry a price (one waiting for its first scrape) and the remaining 1,083 are legitimately free.

The per-kind matrix is generated at [`docs/catalog.md`](docs/catalog.md);
it is the list, and this page is only the summary. Counts are deliberately
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Catalog: `aws_keyspaces_table`, `aws_s3tables_table_bucket`,
`aws_cloudfront_function`, `aws_ec2_transit_gateway_peering_attachment`
and `aws_ami_copy` are priced, and 17 more AWS kinds are recognised as
free. Contributed by @juan-vg in c3x-pricing-api#71.

### Fixed

- Transit gateways were never priced: the catalog entry was keyed on
`aws_transit_gateway`, which is not a Terraform resource type. It is
now `aws_ec2_transit_gateway`.

## [0.3.19] - 2026-09-29

### Changed
Expand Down
29 changes: 25 additions & 4 deletions docs/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Status meaning:
See `docs/upstream-gaps.md` for the per-resource explanation.
- **FREE** — no per-resource charge (parent-billed, structural, IAM).

## AWS (625 resources)
## AWS (646 resources)

| Kind | Status | Display name |
|---|---|---|
Expand All @@ -26,6 +26,7 @@ Status meaning:
| `aws_alb_listener_rule` | FREE | aws_alb_listener_rule |
| `aws_alb_target_group` | FREE | aws_alb_target_group |
| `aws_alb_target_group_attachment` | FREE | aws_alb_target_group_attachment |
| `aws_ami_copy` | LIVE | AWS AMI Copy |
| `aws_ami_launch_permission` | FREE | aws_ami_launch_permission |
| `aws_amplify_app` | LIVE | AWS Amplify Hosting App |
| `aws_amplify_backend_environment` | FREE | aws_amplify_backend_environment |
Expand Down Expand Up @@ -113,15 +114,18 @@ Status meaning:
| `aws_batch_job_definition` | FREE | aws_batch_job_definition |
| `aws_batch_job_queue` | FREE | aws_batch_job_queue |
| `aws_batch_scheduling_policy` | FREE | aws_batch_scheduling_policy |
| `aws_bcmdataexports_export` | FREE | aws_bcmdataexports_export |
| `aws_chatbot_slack_channel_configuration` | FREE | AWS Chatbot Slack Channel Configuration |
| `aws_cloudformation_export` | FREE | aws_cloudformation_export |
| `aws_cloudformation_stack_set` | FREE | aws_cloudformation_stack_set |
| `aws_cloudformation_stack_set_instance` | FREE | aws_cloudformation_stack_set_instance |
| `aws_cloudformation_type` | FREE | aws_cloudformation_type |
| `aws_cloudfront_cache_policy` | FREE | aws_cloudfront_cache_policy |
| `aws_cloudfront_continuous_deployment_policy` | FREE | aws_cloudfront_continuous_deployment_policy |
| `aws_cloudfront_distribution` | LIVE | AWS CloudFront Distribution |
| `aws_cloudfront_field_level_encryption_config` | FREE | aws_cloudfront_field_level_encryption_config |
| `aws_cloudfront_field_level_encryption_profile` | FREE | aws_cloudfront_field_level_encryption_profile |
| `aws_cloudfront_function` | LIVE | AWS CloudFront Function |
| `aws_cloudfront_key_group` | FREE | aws_cloudfront_key_group |
| `aws_cloudfront_key_value_store` | FREE | aws_cloudfront_key_value_store |
| `aws_cloudfront_monitoring_subscription` | FREE | aws_cloudfront_monitoring_subscription |
Expand All @@ -137,6 +141,7 @@ Status meaning:
| `aws_cloudwatch_dashboard` | STATIC | AWS CloudWatch Dashboard |
| `aws_cloudwatch_event_api_destination` | FREE | aws_cloudwatch_event_api_destination |
| `aws_cloudwatch_event_archive` | FREE | aws_cloudwatch_event_archive |
| `aws_cloudwatch_event_bus` | FREE | aws_cloudwatch_event_bus |
| `aws_cloudwatch_event_bus_policy` | FREE | aws_cloudwatch_event_bus_policy |
| `aws_cloudwatch_event_connection` | FREE | aws_cloudwatch_event_connection |
| `aws_cloudwatch_event_endpoint` | FREE | aws_cloudwatch_event_endpoint |
Expand Down Expand Up @@ -201,6 +206,7 @@ Status meaning:
| `aws_datasync_location_smb` | FREE | aws_datasync_location_smb |
| `aws_datasync_task` | FREE | aws_datasync_task |
| `aws_dax_cluster` | LIVE | AWS DAX Cluster |
| `aws_db_event_subscription` | FREE | aws_db_event_subscription |
| `aws_db_instance` | LIVE | AWS RDS Instance |
| `aws_db_option_group` | FREE | AWS Db Option Group |
| `aws_db_parameter_group` | FREE | AWS Db Parameter Group |
Expand Down Expand Up @@ -253,17 +259,22 @@ Status meaning:
| `aws_ec2_serial_console_access` | FREE | aws_ec2_serial_console_access |
| `aws_ec2_subnet_cidr_reservation` | FREE | aws_ec2_subnet_cidr_reservation |
| `aws_ec2_tag` | FREE | aws_ec2_tag |
| `aws_ec2_transit_gateway` | LIVE | AWS Transit Gateway |
| `aws_ec2_transit_gateway_peering_attachment` | LIVE | AWS Transit Gateway Peering Attachment |
| `aws_ec2_transit_gateway_peering_attachment_accepter` | FREE | aws_ec2_transit_gateway_peering_attachment_accepter |
| `aws_ec2_transit_gateway_route` | FREE | aws_ec2_transit_gateway_route |
| `aws_ec2_transit_gateway_route_table` | FREE | aws_ec2_transit_gateway_route_table |
| `aws_ec2_transit_gateway_route_table_association` | FREE | aws_ec2_transit_gateway_route_table_association |
| `aws_ec2_transit_gateway_route_table_propagation` | FREE | aws_ec2_transit_gateway_route_table_propagation |
| `aws_ec2_transit_gateway_vpc_attachment` | STATIC | AWS Transit Gateway VPC Attachment |
| `aws_ec2_transit_gateway_vpc_attachment_accepter` | FREE | aws_ec2_transit_gateway_vpc_attachment_accepter |
| `aws_ecr_lifecycle_policy` | FREE | aws_ecr_lifecycle_policy |
| `aws_ecr_pull_through_cache_rule` | FREE | aws_ecr_pull_through_cache_rule |
| `aws_ecr_registry_policy` | FREE | aws_ecr_registry_policy |
| `aws_ecr_registry_scanning_configuration` | FREE | aws_ecr_registry_scanning_configuration |
| `aws_ecr_replication_configuration` | FREE | aws_ecr_replication_configuration |
| `aws_ecr_repository` | LIVE | AWS ECR Repository |
| `aws_ecr_repository_creation_template` | FREE | aws_ecr_repository_creation_template |
| `aws_ecr_repository_policy` | FREE | aws_ecr_repository_policy |
| `aws_ecrpublic_repository` | FREE | aws_ecrpublic_repository |
| `aws_ecrpublic_repository_policy` | FREE | aws_ecrpublic_repository_policy |
Expand Down Expand Up @@ -306,6 +317,8 @@ Status meaning:
| `aws_glacier_vault` | LIVE | AWS S3 Glacier Vault |
| `aws_glacier_vault_lock` | FREE | aws_glacier_vault_lock |
| `aws_globalaccelerator_accelerator` | STATIC | AWS Global Accelerator |
| `aws_globalaccelerator_endpoint_group` | FREE | aws_globalaccelerator_endpoint_group |
| `aws_globalaccelerator_listener` | FREE | aws_globalaccelerator_listener |
| `aws_glue_catalog_database` | FREE | AWS Glue Data Catalog Database |
| `aws_glue_catalog_table` | FREE | aws_glue_catalog_table |
| `aws_glue_classifier` | FREE | aws_glue_classifier |
Expand Down Expand Up @@ -378,6 +391,8 @@ Status meaning:
| `aws_iot_topic_rule` | FREE | aws_iot_topic_rule |
| `aws_kendra_index` | LIVE | AWS Kendra Index |
| `aws_key_pair` | FREE | aws_key_pair |
| `aws_keyspaces_keyspace` | FREE | aws_keyspaces_keyspace |
| `aws_keyspaces_table` | LIVE | AWS Keyspaces Table |
| `aws_kinesis_firehose_delivery_stream` | LIVE | AWS Kinesis Data Firehose |
| `aws_kinesis_resource_policy` | FREE | aws_kinesis_resource_policy |
| `aws_kinesis_stream` | LIVE | AWS Kinesis Data Stream |
Expand All @@ -397,6 +412,7 @@ Status meaning:
| `aws_lambda_function` | LIVE | AWS Lambda Function |
| `aws_lambda_function_event_invoke_config` | FREE | aws_lambda_function_event_invoke_config |
| `aws_lambda_function_url` | FREE | AWS Lambda Function Url |
| `aws_lambda_invocation` | FREE | aws_lambda_invocation |
| `aws_lambda_layer_version` | FREE | AWS Lambda Layer Version |
| `aws_lambda_layer_version_permission` | FREE | aws_lambda_layer_version_permission |
| `aws_lambda_permission` | FREE | AWS Lambda Permission |
Expand Down Expand Up @@ -458,6 +474,7 @@ Status meaning:
| `aws_ram_sharing_with_organization` | FREE | aws_ram_sharing_with_organization |
| `aws_rds_cluster` | LIVE | AWS Aurora Cluster |
| `aws_rds_cluster_instance` | LIVE | AWS Aurora Cluster Instance |
| `aws_rds_cluster_parameter_group` | FREE | aws_rds_cluster_parameter_group |
| `aws_redshift_cluster` | LIVE | AWS Redshift Cluster |
| `aws_redshift_parameter_group` | FREE | AWS Redshift Parameter Group |
| `aws_redshift_resource_policy` | FREE | aws_redshift_resource_policy |
Expand Down Expand Up @@ -511,6 +528,9 @@ Status meaning:
| `aws_s3_object` | FREE | AWS S3 Object |
| `aws_s3control_access_point_policy` | FREE | aws_s3control_access_point_policy |
| `aws_s3control_bucket_policy` | FREE | aws_s3control_bucket_policy |
| `aws_s3tables_namespace` | FREE | aws_s3tables_namespace |
| `aws_s3tables_table_bucket` | LIVE | AWS S3 Table Bucket |
| `aws_s3tables_table_policy` | FREE | aws_s3tables_table_policy |
| `aws_sagemaker_endpoint` | STATIC | AWS SageMaker Endpoint |
| `aws_sagemaker_notebook_instance` | LIVE | AWS SageMaker Notebook Instance |
| `aws_scheduler_schedule` | FREE | aws_scheduler_schedule |
Expand Down Expand Up @@ -546,6 +566,7 @@ Status meaning:
| `aws_ses_domain_mail_from` | FREE | aws_ses_domain_mail_from |
| `aws_ses_email_identity` | FREE | aws_ses_email_identity |
| `aws_ses_event_destination` | FREE | aws_ses_event_destination |
| `aws_ses_identity_notification_topic` | FREE | aws_ses_identity_notification_topic |
| `aws_ses_identity_policy` | FREE | aws_ses_identity_policy |
| `aws_ses_receipt_filter` | FREE | aws_ses_receipt_filter |
| `aws_ses_receipt_rule` | FREE | aws_ses_receipt_rule |
Expand Down Expand Up @@ -596,7 +617,6 @@ Status meaning:
| `aws_transfer_tag` | FREE | aws_transfer_tag |
| `aws_transfer_user` | FREE | aws_transfer_user |
| `aws_transfer_workflow` | FREE | aws_transfer_workflow |
| `aws_transit_gateway` | LIVE | AWS Transit Gateway |
| `aws_volume_attachment` | FREE | aws_volume_attachment |
| `aws_vpc` | FREE | AWS Vpc |
| `aws_vpc_dhcp_options` | FREE | aws_vpc_dhcp_options |
Expand All @@ -605,6 +625,7 @@ Status meaning:
| `aws_vpc_endpoint_connection_accepter` | FREE | aws_vpc_endpoint_connection_accepter |
| `aws_vpc_endpoint_connection_notification` | FREE | aws_vpc_endpoint_connection_notification |
| `aws_vpc_endpoint_policy` | FREE | aws_vpc_endpoint_policy |
| `aws_vpc_endpoint_private_dns` | FREE | aws_vpc_endpoint_private_dns |
| `aws_vpc_endpoint_route_table_association` | FREE | aws_vpc_endpoint_route_table_association |
| `aws_vpc_endpoint_security_group_association` | FREE | aws_vpc_endpoint_security_group_association |
| `aws_vpc_endpoint_service` | FREE | aws_vpc_endpoint_service |
Expand Down Expand Up @@ -640,7 +661,7 @@ Status meaning:
| `aws_workspaces_workspace` | LIVE | AWS WorkSpaces Desktop |
| `aws_xray_group` | LIVE | AWS X-Ray Group |

**AWS totals:** 90 LIVE · 20 STATIC · 515 FREE
**AWS totals:** 95 LIVE · 20 STATIC · 531 FREE

## AZURE (409 resources)

Expand Down Expand Up @@ -1373,4 +1394,4 @@ Status meaning:

---

**Grand total: 1340 resources.** 188 LIVE · 85 STATIC · 1067 FREE.
**Grand total: 1361 resources.** 193 LIVE · 85 STATIC · 1083 FREE.
28 changes: 28 additions & 0 deletions resources/aws/aws_ami_copy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
kind = "aws_ami_copy"
display_name = "AWS AMI Copy"
provider = "aws"

# An AMI itself is free. The copy creates EBS snapshots in the
# destination region, and those bill per GB-month at the standard
# snapshot rate ($0.05/GB-month in us-east-1).

[mappings.snapshot_storage]
service = "AmazonEC2"
product_family = "Storage Snapshot"
attribute_filters = [
{ key = "usagetype", const = "EBS:SnapshotUsage" },
]

[[dimensions]]
id = "snapshot_storage"
label = "Copied snapshot storage"
unit = "GB-month"
when = 'sum_field(ebs_block_device, "volume_size") > 0'
quantity = 'sum_field(ebs_block_device, "volume_size")'
rate = 'price("snapshot_storage")'

[fixture]
attributes = { ebs_block_device = [{ volume_size = 8 }] }
expected_monthly_cost = 0.4
tolerance = 0.05
last_verified = "2026-09-29"
30 changes: 30 additions & 0 deletions resources/aws/aws_cloudfront_function.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
kind = "aws_cloudfront_function"
display_name = "AWS CloudFront Function"
provider = "aws"

# CloudFront Functions bill per execution, at one global rate (the SKU
# carries location "Any", so the mapping pins region = "global").
# $0.10 per 1,000,000 executions after the first 2,000,000 free.
# The free tier is a $0 price dimension on the same SKU; the price
# picker skips zeros and returns the paid rate.

[mappings.executions]
service = "AmazonCloudFront"
product_family = "Request"
region = "global"
attribute_filters = [
{ key = "usagetype", const = "Executions-CloudFrontFunctions" },
]

[[dimensions]]
id = "executions"
label = "Function executions"
unit = "executions"
quantity = "default(monthly_invocations, 0)"
rate = 'price("executions")'

[fixture]
attributes = { monthly_invocations = 10_000_000 }
expected_monthly_cost = 1
tolerance = 0.05
last_verified = "2026-09-29"
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
kind = "aws_transit_gateway"
kind = "aws_ec2_transit_gateway"
display_name = "AWS Transit Gateway"
provider = "aws"

Expand Down
30 changes: 30 additions & 0 deletions resources/aws/aws_ec2_transit_gateway_peering_attachment.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
kind = "aws_ec2_transit_gateway_peering_attachment"
display_name = "AWS Transit Gateway Peering Attachment"
provider = "aws"

# Every Transit Gateway attachment type (VPC, VPN, Connect, DirectConnect,
# Peering) shares usagetype TransitGateway-Hours at $0.05/hour under
# service AmazonVPC. Data processed bills on the parent
# aws_ec2_transit_gateway resource.
# The accepter side of a cross-account peering is free: one attachment,
# one charge.

[mappings.attachment_hours]
service = "AmazonVPC"
product_family = ""
attribute_filters = [
{ key = "usagetype", const = "USE1-TransitGateway-Hours" },
]

[[dimensions]]
id = "attachment_hours"
label = "TGW peering attachment"
unit = "hours"
quantity = "monthly_hours()"
rate = 'price("attachment_hours")'

[fixture]
attributes = {}
expected_monthly_cost = 36.5
tolerance = 0.05
last_verified = "2026-09-29"
103 changes: 103 additions & 0 deletions resources/aws/aws_keyspaces_table.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
kind = "aws_keyspaces_table"
display_name = "AWS Keyspaces Table"
provider = "aws"

# Keyspaces (Cassandra) bills under offer code AmazonMCS, with the same
# shape as DynamoDB: on-demand request units or provisioned capacity
# units, plus storage and optional PITR backup storage.
# Probed us-east-1 2026-09-29: 9 SKUs, group MCS-ReadUnits/MCS-WriteUnits.

[mappings.read_request_units]
service = "AmazonMCS"
product_family = "PayPerRequest Throughput"
attribute_filters = [
{ key = "group", const = "MCS-ReadUnits" },
]

[mappings.write_request_units]
service = "AmazonMCS"
product_family = "PayPerRequest Throughput"
attribute_filters = [
{ key = "group", const = "MCS-WriteUnits" },
]

[mappings.provisioned_read]
service = "AmazonMCS"
product_family = "Provisioned IOPS"
attribute_filters = [
{ key = "group", const = "MCS-ReadUnits" },
]

[mappings.provisioned_write]
service = "AmazonMCS"
product_family = "Provisioned IOPS"
attribute_filters = [
{ key = "group", const = "MCS-WriteUnits" },
]

[mappings.storage]
service = "AmazonMCS"
product_family = "Database Storage"
attribute_filters = [
{ key = "usagetype", const = "TimedStorage-ByteHrs" },
]

[mappings.pitr]
service = "AmazonMCS"
product_family = "Database Storage"
attribute_filters = [
{ key = "usagetype", const = "USE1-TimedPITRStorage-ByteHrs" },
]

[[dimensions]]
id = "read_request_units"
label = "Read request units"
unit = "request units"
when = 'default(capacity_specification_throughput_mode, "PAY_PER_REQUEST") == "PAY_PER_REQUEST"'
quantity = "default(monthly_read_request_units, 0)"
rate = 'price("read_request_units")'

[[dimensions]]
id = "write_request_units"
label = "Write request units"
unit = "request units"
when = 'default(capacity_specification_throughput_mode, "PAY_PER_REQUEST") == "PAY_PER_REQUEST"'
quantity = "default(monthly_write_request_units, 0)"
rate = 'price("write_request_units")'

[[dimensions]]
id = "read_capacity"
label = "Provisioned read capacity"
unit = "RCU-hours"
when = 'default(capacity_specification_throughput_mode, "PAY_PER_REQUEST") == "PROVISIONED"'
quantity = "default(capacity_specification_read_capacity_units, 1) * monthly_hours()"
rate = 'price("provisioned_read")'

[[dimensions]]
id = "write_capacity"
label = "Provisioned write capacity"
unit = "WCU-hours"
when = 'default(capacity_specification_throughput_mode, "PAY_PER_REQUEST") == "PROVISIONED"'
quantity = "default(capacity_specification_write_capacity_units, 1) * monthly_hours()"
rate = 'price("provisioned_write")'

[[dimensions]]
id = "storage"
label = "Data storage"
unit = "GB-month"
quantity = "default(storage_gb, 0)"
rate = 'price("storage")'

[[dimensions]]
id = "pitr_backup"
label = "Point-in-time recovery storage"
unit = "GB-month"
when = 'default(point_in_time_recovery_status, "DISABLED") == "ENABLED" && default(storage_gb, 0) > 0'
quantity = "default(storage_gb, 0)"
rate = 'price("pitr")'

[fixture]
attributes = { capacity_specification_throughput_mode = "PROVISIONED", capacity_specification_read_capacity_units = 10, capacity_specification_write_capacity_units = 10, storage_gb = 100 }
expected_monthly_cost = 30.694
tolerance = 0.05
last_verified = "2026-09-29"
Loading
Loading