Skip to main content

AWS Integration

Infracost already finds tagging and FinOps issues in your CI/CD pipelines before deployment. Our AWS integration completes the picture by enabling Infracost to find post-deployment issues too. For examples, with the AWS integration, Infracost can:

  • find idle and unused resources
  • investigate cost anomalies and pin-point which application or infrastructure pull request caused them
  • find tagging issues from resources not in IaC

Infracost will continuously monitor cloud resources, triage findings, create Jira tickets, drafts PR fixes, and queue them for your team’s approval - ultimately saving engineering time and reducing your cloud costs.

note

This integration requires access to your AWS organization's management account. Reach out to support@infracost.io before getting started if you have questions.

Initial Configuration

Prerequisites

  • Access to the AWS console on your AWS organization's management account
  • Permission to create IAM roles and policies in that account
  • Your Infracost External ID — reach out to support@infracost.io to get this. Used as the external ID for the cross-account role.

The Infracost AWS Integration uses a Terraform module (also available via OpenTofu) to provision a read-only IAM role in your accounts. Infracost uses this role to access the data needed to power cost visibility features.

Add the following to your Terraform configuration for your management account:

module "infracost_management_account" {
source = "infracost/integration/aws"
version = "0.3.1"

infracost_external_id = "YOUR_EXTERNAL_ID"
is_management_account = true

providers = {
aws = aws.management_account
}
}

Replace YOUR_EXTERNAL_ID with the value from the prerequisites.

Repeat this for each member account in your AWS organization, using is_management_account = false:

module "infracost_member_account_1" {
source = "infracost/integration/aws"
version = "0.3.1"

infracost_external_id = "YOUR_EXTERNAL_ID"
is_management_account = false

providers = {
aws = aws.member_account_1
}
}

Include an outputs.tf for each account so you can easily reference the ARNs needed in a later step. Replace MODULE_NAME with the name you used above:

output "account_id" {
description = "The AWS account ID where the module was provisioned."
value = module.MODULE_NAME.account_id
}

output "role_arn" {
description = "The ARN value of the Cross-Account Role with IAM read-only permissions. Provide this to Infracost."
value = module.MODULE_NAME.role_arn
}

output "billing_and_cost_management_export_arn" {
description = "The ARN of the BCM Data Exports export."
value = module.MODULE_NAME.billing_and_cost_management_export_arn
}

output "billing_and_cost_management_bucket_arn" {
description = "The ARN of the S3 bucket used for BCM Data Exports."
value = module.MODULE_NAME.billing_and_cost_management_bucket_arn
}

output "s3_storage_lens_configuration_arn" {
description = "The ARN of the S3 Storage Lens configuration."
value = module.MODULE_NAME.s3_storage_lens_configuration_arn
}

output "s3_storage_lens_bucket_arn" {
description = "The ARN of the S3 bucket used for S3 Storage Lens exports."
value = module.MODULE_NAME.s3_storage_lens_bucket_arn
}

output "cost_anomaly_detection_services_monitor_arn" {
description = "The ARN of the Cost Anomaly Detection SERVICES monitor."
value = module.MODULE_NAME.cost_anomaly_detection_services_monitor_arn
}

Then run:

terraform init
terraform apply

Once applied, Terraform will output Cross-Account Link role ARNs for each account — you'll need them in Step 3.

note

When new features are added, this module may need to be updated to include new permissions. Infracost will notify you when an update is required.

Step 2: Enable cost and usage data exports to Infracost

For deeper visibility into actual AWS spend, including individual billing line items and S3 usage metrics — you can configure data exports to S3 buckets that Infracost will ingest.

Enrollment prerequisites

Enable Cost Optimization Hub and Compute Optimizer

The Cost Optimization Hub recommendations export requires your AWS Organization's management account to be opted in to both AWS Compute Optimizer and AWS Cost Optimization Hub, org-wide.

These enrollments are not managed by the Terraform module — they are global, account-level toggles that must be performed manually, one-time, against the management account before applying:

aws compute-optimizer update-enrollment-status \
--status Active \
--include-member-accounts \
--region us-east-1

aws cost-optimization-hub update-enrollment-status \
--status Active \
--include-member-accounts \
--region us-east-1

AWS documents that enrollment can take up to 24 hours to take effect, though it is often faster.

Enable trusted access for S3 Storage Lens

S3 Storage Lens must have trusted access configured for cross-account visibility.

This configuration is not managed by the Terraform module — it is a global, account-level toggle that must be performed manually, one-time, against the management account before applying:

aws organizations enable-aws-service-access --service-principal storage-lens.s3.amazonaws.com
Ensure the BCM Data Exports service-linked role exists

Creating the cost and usage data exports requires the AWSServiceRoleForBCMDataExports service-linked role in the management account. AWS usually creates it automatically the first time a data export is created, but if it does not yet exist terraform apply will fail to create the exports.

If the role is missing, create it once, manually, before applying:

aws iam create-service-linked-role --aws-service-name bcm-data-exports.amazonaws.com

This is a no-op if the role already exists (it returns an InvalidInput error stating the role has already been created, which is safe to ignore).

Configuration

module "infracost_management_account" {
source = "infracost/integration/aws"
version = "0.3.1"

infracost_external_id = "YOUR_EXTERNAL_ID"
is_management_account = true
enable_data_exports = true # <-- Set this variable to true

providers = {
aws = aws.management_account
}
}

This provisions the configuration and storage for the following exports:

note

The data exports are read periodically by Infracost, which may incur minor AWS query costs (~$0.0004 per 1,000 S3 requests). S3 storage costs also apply.

After completing the export configuration, AWS can take upwards of 24 hours to populate the buckets with the first data exports.

Optional: Encrypt export buckets with a KMS key

By default, export buckets use SSE-S3 (AES-256) encryption. To use a customer-managed KMS key instead, pass its ARN via kms_key_arn:

module "infracost_management_account" {
source = "infracost/integration/aws"
version = "0.3.1"

infracost_external_id = "YOUR_ORG_ID"
is_management_account = true
enable_data_exports = true
kms_key_arn = "arn:aws:kms:us-east-1:111111111111:key/mrk-abc123"

providers = {
aws = aws.management_account
}
}

When kms_key_arn is set, the module automatically grants the Infracost cross-account role kms:Decrypt and kms:DescribeKey on that key. You must also ensure the KMS key policy itself grants permissions to two sets of principals:

  1. AWS service principals — need kms:GenerateDataKey and kms:Decrypt to write and read back the encrypted export objects.
  2. Infracost cross-account role — needs kms:Decrypt and kms:DescribeKey to read the encrypted objects.

The following key policy statements cover both:

{
"Statement": [
{
"Sid": "AllowExportServicePrincipals",
"Effect": "Allow",
"Principal": {
"Service": [
"bcm-data-exports.amazonaws.com",
"billingreports.amazonaws.com",
"storage-lens.s3.amazonaws.com"
]
},
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "111111111111"
}
}
},
{
"Sid": "AllowInfracostRoleDecrypt",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/infracost-readonly"
},
"Action": [
"kms:Decrypt",
"kms:DescribeKey"
],
"Resource": "*"
}
]
}

Replace 111111111111 with your management account ID. The infracost-readonly role ARN is available as a Terraform output from the module (role_arn).

Optional: Provide custom data exports instead

Use this option if you prefer to manage your own data exports or want to provide additional custom data to Infracost. You can enable Infracost access for specific S3 bucket ARNs. When doing so, please contact support@infracost.io to ensure your use case and configuration are supported.

If you are creating the FOCUS billing data export manually (for example, via the AWS Billing and Cost Management console under Data Exports), configure it with the following settings so Infracost can ingest it:

Manual FOCUS settings
SettingValue
Export typeFOCUS 1.2
ColumnsInclude all columns
Compression type and file formatParquet - Parquet
Time granularityDaily
module "infracost_management_account" {
source = "infracost/integration/aws"
version = "0.3.1"

infracost_external_id = "YOUR_EXTERNAL_ID"
is_management_account = true

providers = {
aws = aws.management_account
}

s3_bucket_arns = [
"arn:aws:s3:::my-custom-export" # <-- Add any custom S3 ARNs
]
}

If using this option, please provide the following details to Infracost:

FieldExample
AWS S3 bucket ARNarn:aws:s3:::my-custom-export

Data ownership and lifecycle

Your AWS account retains ownership of the data. Infracost is granted solely read-only access to the export buckets.

Each data exports bucket is configured with a 365-day object lifecycle policy. Intelligent tiering is applied to reduce storage costs.

Step 3: Enable AWS Cost Anomaly Detection monitor

To surface AWS-detected cost anomalies alongside your cost data, the module can provision an AWS-managed Cost Anomaly Detection monitor. Set enable_anomaly_monitors = true on the management account.

Configuration

module "infracost_management_account" {
source = "infracost/integration/aws"
version = "0.3.1"

infracost_external_id = "YOUR_EXTERNAL_ID"
is_management_account = true
enable_anomaly_monitors = true # <-- Set this variable to true

providers = {
aws = aws.management_account
}
}

This provisions a single AWS-managed monitor named InfracostServicesMonitor that evaluates every AWS service for anomalous spend. No alert subscription is created; Infracost reads the detected anomalies via the cross-account role.

Optional: Manually configure the anomaly monitor instead

Use this option if you prefer to manage your own cost anomaly monitors instead of relying on the Infracost provisioned one. Refer to AWS's Getting Started guide for the full procedure:

  • Navigate to Billing and Cost Management > Cost Anomaly Detection
  • Select Cost monitors > Create monitor.
  • Name your monitor and select Managed by AWS with the AWS Services monitor dimension.
  • Set or create a new alert subscription for the monitor (this is unused by Infracost, yet required for the creation process in the AWS console).

Note: If you are managing a custom cross-account role, ensure it has the ce:GetAnomalyMonitors and ce:GetAnomalies permissions set in its IAM policy.

caution

AWS allows only one managed SERVICES (DIMENSIONAL / SERVICE) anomaly monitor per payer account. If your management account already has one — created manually in the AWS console, by another tool, or in a previous setup — terraform apply will fail when the module attempts to create a duplicate, returning a ValidationException from the Cost Explorer API.

Step 4: Add the integration in the Infracost Cloud

After deploying the module, add the integration to Infracost from the dashboard:

  1. Go to Org Settings > Integrations > Add integrations, then click Add integration for Amazon Web Services.
  2. Enter an Alias to identify the integration, and the Role ARN of the management account's cross-account-link role output by the module (for example, arn:aws:iam::111111111111:role/infracost-readonly).
  3. Click Test connection to verify Infracost can assume the role.
  4. Once the test passes, save the integration.

Upgrading

As Infracost adds support for additional capabilities there may be a need to upgrade the Terraform module.

Step 1: Update version references

To upgrade, change the version of the module to the version you'd like to use and make any additional changes needed.

tip

Don't forget to review the release notes for updates and breaking changes.

module "infracost_management_account" {
source = "infracost/integration/aws"
- version = "0.3.0"
+ version = "0.4.0"

+ super_cool_new_feature = true

...
}

Step 2: Download updated module version

Once you've updated the version, rerun terraform init but add the -upgrade flag to force it to check for and download the correct version of the module:

terraform init -upgrade

Step 3: Plan and Apply

Now that you've updated the module version, and made any additional modifications, you can run a plan to verify your work, and apply the changes.

terraform plan
terraform apply

Removing

To remove the Infracost cross-account link components simply run terraform destroy against your project.

Troubleshooting

Error: creating AWS BCM Data Exports Export: operation error BCM Data Exports: CreateExport, https response error StatusCode: 400, RequestID: 00000000-0000-0000-0000-000000000000, ValidationException: This account is unable to create an export against this Table.

This error typically happens for one of the following reasons:

  • Management account is not yet opted in to Compute Optimizer and Cost Optimization Hub.
  • Target is not the management account.
    • Make sure you are targetting the management/payer account.
  • IAM billing access is not enabled. If this setting is deactivated, IAM users and roles in the account can’t access the Billing and Cost Management console pages, even if they have administrator access or the required IAM policies.
    • As the root user, head to the AWS Console and select "Account" from the drop-down in upper-right corner.
    • Scroll down to the "IAM user and role access to Billing information" section.
    • Select "Edit", and check the box in front of "Activate IAM Access".
  • IAM user does not have the correct IAM permissions to create the resources.
    • Ensure the user has cur:PutReportDefinition and bcm-data-exports:CreateExport