Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CUR 2.0 support to Terraform cur-source module #1040

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
22 changes: 22 additions & 0 deletions terraform-modules/cur-setup-source/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ one instance of this module for each payer account.
> For complete usage documentation of using this module together with the cur-setup-destination
module, refer to the main Terraform [Deployment Instructions](../README.md#deployment-instructions).

> [!Note]
> If you are **not** using `enable_cur_v1` and do not specify a different query string via `bcm_query`, **all** available fields are selected. This will impact the size of your CUR files.

```hcl
provider "aws" {
region = "us-west-2"
Expand All @@ -35,6 +38,25 @@ module "cur_source" {
}
```

## Legacy CUR v1 usage
> [!Note]
> Legacy CUR v1 is deprecated and no longer supported!

If you require legacy CUR v1 files, you must set `enable_cur_v1` to `true`. Each CUR version requires different CUR resources and associated permissions.

If you do not configure `enable_cur_v1` in your module call it will assume you desire CUR v2 and enable the appropriate resources.

```hcl
module "cur_source" {
source = "github.com/aws-samples/aws-cudos-framework-deployment//terraform-modules/cur-setup-source"

destination_bucket_arn = "UPDATEME"
enable_cur_v1 = true

# etc
}
```

## Version Locking

For production deployments, you should lock the version of this module to a release tag to better
Expand Down
79 changes: 71 additions & 8 deletions terraform-modules/cur-setup-source/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,21 @@ data "aws_iam_policy_document" "bucket_policy" {
]
principals {
type = "Service"
identifiers = ["billingreports.amazonaws.com"]
identifiers = [
"billingreports.amazonaws.com",
"bcm-data-exports.amazonaws.com"
]
}
resources = [
aws_s3_bucket.this.arn,
"${aws_s3_bucket.this.arn}/*",
]
condition {
test = "StringLike"
values = ["arn:${data.aws_partition.this.partition}:cur:us-east-1:${data.aws_caller_identity.this.account_id}:definition/*"]
values = [
"arn:${data.aws_partition.this.partition}:cur:us-east-1:${data.aws_caller_identity.this.account_id}:definition/*",
"arn:${data.aws_partition.this.partition}:bcm-data-exports:us-east-1:${data.aws_caller_identity.this.account_id}:export/*"
]
variable = "aws:SourceArn"
}
condition {
Expand All @@ -143,14 +149,20 @@ data "aws_iam_policy_document" "bucket_policy" {
]
principals {
type = "Service"
identifiers = ["billingreports.amazonaws.com"]
identifiers = [
"billingreports.amazonaws.com",
"bcm-data-exports.amazonaws.com"
]
}
resources = [
"${aws_s3_bucket.this.arn}/*",
]
condition {
test = "StringLike"
values = ["arn:${data.aws_partition.this.partition}:cur:us-east-1:${data.aws_caller_identity.this.account_id}:definition/*"]
values = [
"arn:${data.aws_partition.this.partition}:cur:us-east-1:${data.aws_caller_identity.this.account_id}:definition/*",
"arn:${data.aws_partition.this.partition}:bcm-data-exports:us-east-1:${data.aws_caller_identity.this.account_id}:export/*"
]
variable = "aws:SourceArn"
}
condition {
Expand Down Expand Up @@ -219,10 +231,17 @@ resource "aws_iam_role" "replication" {
name_prefix = "${var.resource_prefix}-replication"
path = "/${var.resource_prefix}/"
assume_role_policy = data.aws_iam_policy_document.s3_assume_role.json
inline_policy {
name = "S3Replication"
policy = data.aws_iam_policy_document.replication.json
}
}

resource "aws_iam_role_policy_attachment" "replication_attach" {
role = aws_iam_role.replication.name
policy_arn = aws_iam_policy.replication.arn
}

resource "aws_iam_policy" "replication" {
name = "CUR-replication"
description = "Replication policy for S3 CUR"
policy = data.aws_iam_policy_document.replication.json
}
Comment on lines -222 to 245
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To match our CloudFormation implementation and avoid creating unnecessary customer-managed policies, can you use the aws_iam_role_policy resource instead?


resource "aws_s3_bucket_replication_configuration" "replication" {
Expand Down Expand Up @@ -250,7 +269,51 @@ resource "aws_s3_bucket_replication_configuration" "replication" {
###
# CUR
###
resource "aws_bcmdataexports_export" "this" {
count = var.enable_cur_v1 ? 0 : 1
provider = aws.useast1

# Make sure versioning and bucket policy is configured first
depends_on = [
aws_s3_bucket_versioning.this,
aws_s3_bucket_policy.this
]

export {
name = "${var.resource_prefix}-${var.cur_name_suffix}"
data_query {
query_statement = var.bcm_query
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This query needs to be adjusted based on var.enable_split_cost_allocation_data . See SCAD query here: https://github.com/aws-samples/aws-cudos-framework-deployment/blob/main/cfn-templates/data-exports-aggregation.yaml#L148

table_configurations = {
COST_AND_USAGE_REPORT = {
TIME_GRANULARITY = "HOURLY",
INCLUDE_RESOURCES = "TRUE",
INCLUDE_MANUAL_DISCOUNT_COMPATIBILITY = "FALSE",
INCLUDE_SPLIT_COST_ALLOCATION_DATA = var.enable_split_cost_allocation_data ? "TRUE" : "FALSE",
}
}
}
destination_configurations {
s3_destination {
s3_bucket = aws_s3_bucket.this.bucket
s3_prefix = "cur/${data.aws_caller_identity.this.account_id}"
s3_region = data.aws_region.this.name
s3_output_configurations {
overwrite = "OVERWRITE_REPORT"
format = "PARQUET"
compression = "PARQUET"
output_type = "CUSTOM"
}
}
}

refresh_cadence {
frequency = "SYNCHRONOUS"
}
}
}

resource "aws_cur_report_definition" "this" {
count = var.enable_cur_v1 ? 1 : 0
provider = aws.useast1

# Make sure versioning and bucket policy is configured first
Expand Down
7 changes: 6 additions & 1 deletion terraform-modules/cur-setup-source/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
output "cur_report_arn" {
description = "ARN of the Cost and Usage Report"
value = aws_cur_report_definition.this.arn
value = one(aws_cur_report_definition.this[*].arn)
}

output "bcm_export_arn" {
description = "ARN of the BCM Data Export"
value = one(aws_bcmdataexports_export.this[*].export)
}

output "cur_bucket_arn" {
Expand Down
12 changes: 12 additions & 0 deletions terraform-modules/cur-setup-source/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,15 @@ variable "tags" {
description = "Map of tags to apply to module resources"
default = {}
}

variable "enable_cur_v1" {
type = bool
description = "Enables the CUR report definition resource required for CUR v1"
default = false
}

variable "bcm_query" {
type = string
description = "The fields you wish to have in your export."
default = "SELECT bill_bill_type, bill_billing_entity, bill_billing_period_end_date, bill_billing_period_start_date, bill_invoice_id, bill_invoicing_entity, bill_payer_account_id, bill_payer_account_name, cost_category, discount, discount_bundled_discount, discount_total_discount, identity_line_item_id, identity_time_interval, line_item_availability_zone, line_item_blended_cost, line_item_blended_rate, line_item_currency_code, line_item_legal_entity, line_item_line_item_description, line_item_line_item_type, line_item_net_unblended_cost, line_item_net_unblended_rate, line_item_normalization_factor, line_item_normalized_usage_amount, line_item_operation, line_item_product_code, line_item_tax_type, line_item_unblended_cost, line_item_unblended_rate, line_item_usage_account_id, line_item_usage_account_name, line_item_usage_amount, line_item_usage_end_date, line_item_usage_start_date, line_item_usage_type, pricing_currency, pricing_lease_contract_length, pricing_offering_class, pricing_public_on_demand_cost, pricing_public_on_demand_rate, pricing_purchase_option, pricing_rate_code, pricing_rate_id, pricing_term, pricing_unit, product, product_comment, product_fee_code, product_fee_description, product_from_location, product_from_location_type, product_from_region_code, product_instance_family, product_instance_type, product_instancesku, product_location, product_location_type, product_operation, product_pricing_unit, product_product_family, product_region_code, product_servicecode, product_sku, product_to_location, product_to_location_type, product_to_region_code, product_usagetype, reservation_amortized_upfront_cost_for_usage, reservation_amortized_upfront_fee_for_billing_period, reservation_availability_zone, reservation_effective_cost, reservation_end_time, reservation_modification_status, reservation_net_amortized_upfront_cost_for_usage, reservation_net_amortized_upfront_fee_for_billing_period, reservation_net_effective_cost, reservation_net_recurring_fee_for_usage, reservation_net_unused_amortized_upfront_fee_for_billing_period, reservation_net_unused_recurring_fee, reservation_net_upfront_value, reservation_normalized_units_per_reservation, reservation_number_of_reservations, reservation_recurring_fee_for_usage, reservation_reservation_a_r_n, reservation_start_time, reservation_subscription_id, reservation_total_reserved_normalized_units, reservation_total_reserved_units, reservation_units_per_reservation, reservation_unused_amortized_upfront_fee_for_billing_period, reservation_unused_normalized_unit_quantity, reservation_unused_quantity, reservation_unused_recurring_fee, reservation_upfront_value, resource_tags, savings_plan_amortized_upfront_commitment_for_billing_period, savings_plan_end_time, savings_plan_instance_type_family, savings_plan_net_amortized_upfront_commitment_for_billing_period, savings_plan_net_recurring_commitment_for_billing_period, savings_plan_net_savings_plan_effective_cost, savings_plan_offering_type, savings_plan_payment_option, savings_plan_purchase_term, savings_plan_recurring_commitment_for_billing_period, savings_plan_region, savings_plan_savings_plan_a_r_n, savings_plan_savings_plan_effective_cost, savings_plan_savings_plan_rate, savings_plan_start_time, savings_plan_total_commitment_to_date, savings_plan_used_commitment FROM COST_AND_USAGE_REPORT"
}
2 changes: 2 additions & 0 deletions terraform-modules/terraform-test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ setup_file() {
source = "./terraform-modules/cur-setup-source"
resource_prefix = "$cur_prefix"
destination_bucket_arn = module.cur_destination.cur_bucket_arn
bcm_query = "SELECT bill_bill_type, bill_billing_entity FROM COST_AND_USAGE_REPORT"
enable_cur_v1 = false
providers = {
aws.useast1 = aws.useast1
Expand Down