Connect AWS with Terraform instead of CloudFormation
A Terraform equivalent of the generated AWS template, with an Organization variant and a way to compare it to the wizard.
Connect AWS with Terraform instead of CloudFormation
Create the same IAM role, S3 bucket and Cost and Usage export that the CostGlass wizard template creates, but as Terraform you can review and keep in version control.
- CostGlass generates a CloudFormation template for AWS. It does not generate Terraform. This page gives a hand-written Terraform equivalent.
- You still start the AWS wizard in CostGlass, because it creates your connection, your External ID and your CostGlass account ID.
- Nothing secret is hard-coded: the CostGlass account ID and the External ID are variables you fill in.
- You then paste the RoleArn and BucketName outputs into the wizard, exactly as with CloudFormation.
- Because it is maintained by hand, compare its permissions with the wizard template before you apply. A comparison script is below.
terraform validate on AWS provider 5.100). If CostGlass adds a permission to the template later, this page may lag behind. Always run the comparison in the last section before applying.- Terraform 1.5 or newer and AWS provider hashicorp/aws version ~> 5.0. The export resource (aws_bcmdataexports_export) needs a 5.x release that includes it; if init says the resource type is unknown, run terraform init -upgrade.
- AWS credentials for the account that pays the bill (the management account for an Organization), allowed to create IAM roles, S3 buckets and Billing Data Exports.
- The CostGlass wizard open at the Deploy step (explained below).
Get your two values from CostGlass
The Terraform needs two values that belong to your connection. Never type them from memory or share them in chat.
external_id.CostGlassAccountId and copy its Default value, a 12-digit number. This is the value for costglass_account_id.Single account
Create a folder with these files. Everything is applied in us-east-1, like the CloudFormation template.
Variables
| Variable | What it does | Default |
|---|---|---|
costglass_account_id | The CostGlass AWS account allowed to assume the role. | None. Required. |
external_id | Your connection's External ID. Marked sensitive. | None. Required. |
bucket_name_prefix | Bucket is named <prefix>-<your account ID>. | costglass-cur-exports |
export_name | Name of the Cost and Usage export. | CostGlassCURExport |
s3_prefix | Folder in the bucket. | cur |
enable_instance_scheduler | Adds the only write permissions (start, stop, scale). | false |
enable_rightsizing | Adds CloudWatch metrics and Compute Optimizer reads. | false |
enable_uptime_monitoring | Adds status check, alarm and target health reads. | false |
export TF_VAR_external_id='...'. The value is also stored in Terraform state, so protect your state file like a password.Run it and finish in CostGlass
terraform init.terraform plan and read it. You should see the role, its policies, the bucket and its settings, and the export. Nothing is created yet.terraform apply and confirm. When it finishes it prints RoleArn, BucketName and ExportArn.RoleArn into Role ARN (Outputs: RoleArn) and BucketName into S3 bucket (Outputs: BucketName). Leave Prefix as cur/ (change it to match s3_prefix, with a trailing slash, if you changed that) and Region as us-east-1.AWS Organization
In the wizard choose AWS Organization. Run the single-account Terraform above in the management account: it creates the bucket, the export and the role CostGlassCURReader. Member accounts also need the role CostGlassReadOnly. Choose one of two ways.
Option A: a StackSet (closest to the wizard template)
Add this file to the same folder. It creates a service-managed StackSet that deploys the member role to every member account, now and when accounts join later. Do the one-time prerequisite first: in the management account, CloudFormation → StackSets → Enable trusted access with AWS Organizations.
Option B: one Terraform module call per member account
Use this if you want Terraform itself to own the roles, or you only connect a few accounts. Terraform cannot create providers in a loop, so each account needs its own provider alias and module block. The management account assumes OrganizationAccountAccessRole, which AWS Organizations creates in accounts it created; accounts you invited need an equivalent role.
Set member_account_ids in your variables, for example ["111111111111", "222222222222"], and add a provider and module pair for each extra account.
CostGlassCURReader only, the same as the CloudFormation template.Check it matches the wizard
Download the YAML from the wizard (Download), save it next to your Terraform, then compare the permission lists. Tick the same features on both sides first.
Empty output means the two lists are equal. Expected differences: for an Organization template the YAML also lists organizations:ListRoots and organizations:DescribeOrganization (used by a helper function that Option A replaces with a Terraform lookup). For Option B, member-account policies appear in the plan of the module, so compare those separately against the member template inside the YAML. Any other permission appearing on one side only means the Terraform is out of date, or you ticked different features.
Also compare the Billing export columns: they live in local.cur_columns in main.tf and must equal the SELECT list in the template's QueryStatement.
FAQ
terraform destroy. See Connect AWS, removing access.