diff --git a/terraform-modules/cur-setup-source/README.md b/terraform-modules/cur-setup-source/README.md index e29f8e20..c78ddb56 100644 --- a/terraform-modules/cur-setup-source/README.md +++ b/terraform-modules/cur-setup-source/README.md @@ -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" @@ -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 diff --git a/terraform-modules/cur-setup-source/main.tf b/terraform-modules/cur-setup-source/main.tf index 04943f52..db559384 100644 --- a/terraform-modules/cur-setup-source/main.tf +++ b/terraform-modules/cur-setup-source/main.tf @@ -118,7 +118,10 @@ 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, @@ -126,7 +129,10 @@ data "aws_iam_policy_document" "bucket_policy" { ] 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 { @@ -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 { @@ -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 } resource "aws_s3_bucket_replication_configuration" "replication" { @@ -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 + 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 diff --git a/terraform-modules/cur-setup-source/outputs.tf b/terraform-modules/cur-setup-source/outputs.tf index 9aee6987..2b12fa94 100644 --- a/terraform-modules/cur-setup-source/outputs.tf +++ b/terraform-modules/cur-setup-source/outputs.tf @@ -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" { diff --git a/terraform-modules/cur-setup-source/variables.tf b/terraform-modules/cur-setup-source/variables.tf index 6aa530b2..f4960fa4 100644 --- a/terraform-modules/cur-setup-source/variables.tf +++ b/terraform-modules/cur-setup-source/variables.tf @@ -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" +} diff --git a/terraform-modules/terraform-test.bats b/terraform-modules/terraform-test.bats index dbb7d644..90e766fa 100644 --- a/terraform-modules/terraform-test.bats +++ b/terraform-modules/terraform-test.bats @@ -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