Skip to main content

CloudFormation

Deploy the Infracost AWS integration using the cloudformation-aws-integration CloudFormation template — for teams who standardize on CloudFormation or AWS Organizations StackSets instead of Terraform. Prefer Terraform? See the Terraform guide instead.

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 (or CLI) on your AWS organization's management account
  • Permission to create IAM roles and policies in that account (the stack requires CAPABILITY_NAMED_IAM)
  • Your Infracost External ID — in Infracost Cloud, go to Org Settings > General and copy the External ID. It is also shown on the AWS integration page. Used as the external ID for the cross-account role.

Deploy the template.yaml to your management account with the AWS CLI:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides \
InfracostExternalId=YOUR_EXTERNAL_ID \
IsManagementAccount=true \
--capabilities CAPABILITY_NAMED_IAM

Replace YOUR_EXTERNAL_ID with the value from the prerequisites.

Repeat this for each member account in your AWS organization, using IsManagementAccount=false (the default):

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides InfracostExternalId=YOUR_EXTERNAL_ID \
--capabilities CAPABILITY_NAMED_IAM

Once the stack reaches CREATE_COMPLETE, the Outputs tab (console) or aws cloudformation describe-stacks --query 'Stacks[0].Outputs' (CLI) shows the Role ARN you'll need in Step 4 — no separate outputs file to write, unlike the Terraform module.

note

When new features are added, this template 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

Cost and usage data exports are required for Infracost to see individual billing line items and S3 usage metrics. This template can create the data exports for you, or you can bring your own existing export instead.

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 CloudFormation template — they are global, account-level toggles that must be performed manually, one-time, against the management account before deploying:

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 CloudFormation template — it is a global, account-level toggle that must be performed manually, one-time, against the management account before deploying:

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. The template creates this role for you (with DeletionPolicy: Retain, so it's never deleted along with the stack).

caution

If your account has previously used BCM Data Exports (via this template, the AWS console, or another tool), stack creation can fail on the BcmDataExportsServiceLinkedRole resource with an already-exists error — CloudFormation's AWS::IAM::ServiceLinkedRole resource is stricter about this than the AWS CLI. If you hit this, delete the failed stack, temporarily comment out that resource block in a local copy of the template, and redeploy — the export resources will attach to the existing service-linked role.

Get your Organization ARN and trusted service principals

Unlike the Terraform module, which reads this live from AWS Organizations, the CloudFormation template takes these as explicit parameters (avoiding the need to grant the stack its own organizations:Describe*/List* permissions). Run these once and keep the output handy:

aws organizations describe-organization --query 'Organization.Arn' --output text

aws organizations list-aws-service-access-for-organization \
--query 'EnabledServicePrincipals[].ServicePrincipal' --output text

The second command's output must include storage-lens.s3.amazonaws.com — if it's missing, that means the previous step wasn't applied yet (or hasn't propagated).

Configuration

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides \
InfracostExternalId=YOUR_EXTERNAL_ID \
IsManagementAccount=true \
EnableDataExports=true \
OrganizationArn=arn:aws:organizations::111111111111:organization/o-abc123xyz \
TrustedServicePrincipals=storage-lens.s3.amazonaws.com,ce.amazonaws.com \
--capabilities CAPABILITY_NAMED_IAM

This provisions the configuration and storage for the following exports:

caution

BCM Data Exports only exists in us-east-1. Deploy this stack with EnableDataExports=true in us-east-1 — the template's Rules section fails fast (before creating anything) if you don't. The exported cost/usage data itself still covers every AWS region in your account; this only restricts where the export resources themselves can be created.

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 the KmsKeyArn parameter:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides \
InfracostExternalId=YOUR_EXTERNAL_ID \
IsManagementAccount=true \
EnableDataExports=true \
OrganizationArn=arn:aws:organizations::111111111111:organization/o-abc123xyz \
TrustedServicePrincipals=storage-lens.s3.amazonaws.com,ce.amazonaws.com \
KmsKeyArn=arn:aws:kms:us-east-1:111111111111:key/mrk-abc123 \
--capabilities CAPABILITY_NAMED_IAM

When KmsKeyArn is set, the template 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 stack output (RoleArn).

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. The template supports up to 3 extra S3 bucket ARNs via ExtraS3BucketArn1/ExtraS3BucketArn2/ExtraS3BucketArn3 — contact support@infracost.io if you need more, or if you want to confirm your use case is 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
aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides \
InfracostExternalId=YOUR_EXTERNAL_ID \
IsManagementAccount=true \
ExtraS3BucketArn1=arn:aws:s3:::my-custom-export \
--capabilities CAPABILITY_NAMED_IAM

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 template can provision an AWS-managed Cost Anomaly Detection monitor. Set EnableAnomalyMonitors=true on the management account.

Configuration

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides \
InfracostExternalId=YOUR_EXTERNAL_ID \
IsManagementAccount=true \
EnableAnomalyMonitors=true \
--capabilities CAPABILITY_NAMED_IAM

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.

If your account already has a Cost Anomaly Detection monitor

AWS allows only one managed SERVICE-dimension, DIMENSIONAL-type Cost Anomaly Detection monitor per account — many accounts already have one (for example, a Default-Services-Monitor created via the Cost Explorer console). Check first:

aws ce get-anomaly-monitors --query \
"AnomalyMonitors[?MonitorType=='DIMENSIONAL' && MonitorDimension=='SERVICE'].MonitorArn" \
--output text

If that returns an ARN, pass it as ExistingAnomalyMonitorArn alongside EnableAnomalyMonitors=true — the stack will use your existing monitor as-is instead of trying to create a duplicate (which would otherwise fail):

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides \
InfracostExternalId=YOUR_EXTERNAL_ID \
IsManagementAccount=true \
EnableAnomalyMonitors=true \
ExistingAnomalyMonitorArn=arn:aws:ce::111111111111:anomalymonitor/abc123-def456 \
--capabilities CAPABILITY_NAMED_IAM

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.

Step 4: Add the integration in the Infracost Cloud

After deploying the stack, 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 from the stack's Outputs (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.

If your team signs in through IAM Identity Center (AWS SSO), set your access portal URL so console links to resources on findings route through the portal and open in the correct account — without it, the links can fail for federated users who aren't already signed in.

In the dashboard, go to Org Settings > Agent settings and set AWS access portal to your portal URL, for example https://your-subdomain.awsapps.com/start. You can find this in the AWS console under IAM Identity Center > Settings > AWS access portal URL. Leave it blank to keep using direct console links.

Upgrading

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

tip

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

Redeploy with the same stack name and an updated template — CloudFormation computes and applies only the differences:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name infracost-aws-integration \
--parameter-overrides InfracostExternalId=YOUR_EXTERNAL_ID IsManagementAccount=true \
--capabilities CAPABILITY_NAMED_IAM

To preview what would change before applying it, create a change set first:

aws cloudformation create-change-set \
--stack-name infracost-aws-integration \
--change-set-name upgrade-preview \
--template-body file://template.yaml \
--change-set-type UPDATE \
--capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=InfracostExternalId,ParameterValue=YOUR_EXTERNAL_ID \
ParameterKey=IsManagementAccount,ParameterValue=true

aws cloudformation describe-change-set \
--stack-name infracost-aws-integration \
--change-set-name upgrade-preview

Removing

Delete the stack:

aws cloudformation delete-stack --stack-name infracost-aws-integration

The AWSServiceRoleForBCMDataExports service-linked role (if created) is retained rather than deleted — it's a shared, AWS-managed role, not something a single stack should remove.

Troubleshooting

Stack creation fails on BcmDataExportsServiceLinkedRole with an already-exists error

See Ensure the BCM Data Exports service-linked role exists above.

Stack creation fails on CostAnomalyServicesMonitor with HandlerErrorCode: AlreadyExists

See If your account already has a Cost Anomaly Detection monitor above.

Stack creation fails with "EnableDataExports requires deploying this stack in us-east-1"

BCM Data Exports only exists in us-east-1. Redeploy the stack in that region — see the note in Step 2.

PutStorageLensConfiguration / BCM Data Exports creation fails with a validation error

This 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 targeting 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".
  • Your IAM user does not have the correct IAM permissions to create the resources.
    • Ensure the user has cur:PutReportDefinition and bcm-data-exports:CreateExport.