Skip to content

Fix CUR 2.0 export query and crawler IAM boundary#110

Merged
Alexanderamiri merged 1 commit into
mainfrom
fix/cur-deploy-v2
Mar 26, 2026
Merged

Fix CUR 2.0 export query and crawler IAM boundary#110
Alexanderamiri merged 1 commit into
mainfrom
fix/cur-deploy-v2

Conversation

@Alexanderamiri
Copy link
Copy Markdown
Member

Summary

Fixes two CI failures from the CUR 2.0 deployment (#103):

  1. CUR 2.0 query: SELECT * instead of explicit column names — CUR 2.0 column names differ from legacy CUR. table_configurations already controls what's included.
  2. Crawler IAM role: Added permissions_boundary — the org boundary requires all roles to carry a boundary.

Test plan

  • CI plan + apply succeeds
  • CUR export created in us-east-1
  • Glue crawler role created with boundary

- CUR query: use SELECT * (CUR 2.0 column names differ from legacy docs)
- Crawler role: add permissions_boundary (org boundary requires it on all roles)
- Pass org_boundary_arn to cost-analytics module
@Alexanderamiri Alexanderamiri requested a review from a team as a code owner March 26, 2026 23:18
@Alexanderamiri Alexanderamiri enabled auto-merge (squash) March 26, 2026 23:19
@github-actions
Copy link
Copy Markdown

Terraform Plan

🚧 Changes detected — Plan: 7 to add, 0 to change, 0 to destroy.

Plan output

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.cost_analytics.aws_bcmdataexports_export.cur will be created
  + resource "aws_bcmdataexports_export" "cur" {
      + id       = (known after apply)
      + tags_all = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }

      + export {
          + export_arn = (known after apply)
          + name       = "javabin-cur"

          + data_query {
              + query_statement      = "SELECT * FROM COST_AND_USAGE_REPORT"
              + table_configurations = {
                  + "COST_AND_USAGE_REPORT" = {
                      + "INCLUDE_MANUAL_DISCOUNT_COMPATIBILITY" = "FALSE"
                      + "INCLUDE_RESOURCES"                     = "TRUE"
                      + "INCLUDE_SPLIT_COST_ALLOCATION_DATA"    = "FALSE"
                      + "TIME_GRANULARITY"                      = "DAILY"
                    }
                }
            }

          + destination_configurations {
              + s3_destination {
                  + s3_bucket = "javabin-cur-553637109631"
                  + s3_prefix = "cur"
                  + s3_region = "eu-central-1"

                  + s3_output_configurations {
                      + compression = "PARQUET"
                      + format      = "PARQUET"
                      + output_type = "CUSTOM"
                      + overwrite   = "OVERWRITE_REPORT"
                    }
                }
            }

          + refresh_cadence {
              + frequency = "SYNCHRONOUS"
            }
        }
    }

  # module.cost_analytics.aws_glue_crawler.cur will be created
  + resource "aws_glue_crawler" "cur" {
      + arn           = (known after apply)
      + configuration = jsonencode(
            {
              + Grouping = {
                  + TableGroupingPolicy = "CombineCompatibleSchemas"
                }
              + Version  = 1
            }
        )
      + database_name = "javabin_cur"
      + id            = (known after apply)
      + name          = "javabin-cur-crawler"
      + role          = (known after apply)
      + schedule      = "cron(0 6 * * ? *)"
      + tags_all      = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }

      + s3_target {
          + path = "s3://javabin-cur-553637109631/cur/"
        }

      + schema_change_policy {
          + delete_behavior = "DELETE_FROM_DATABASE"
          + update_behavior = "UPDATE_IN_DATABASE"
        }
    }

  # module.cost_analytics.aws_iam_role.cur_crawler will be created
  + resource "aws_iam_role" "cur_crawler" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "glue.amazonaws.com"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "javabin-cur-crawler"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + permissions_boundary  = "arn:aws:iam::553637109631:policy/javabin-org-boundary"
      + tags_all              = {
          + "environment" = "production"
          + "managed-by"  = "terraform"
          + "repo"        = "javaBin/platform"
          + "service"     = "platform"
          + "team"        = "platform"
        }
      + unique_id             = (known after apply)
    }

  # module.cost_analytics.aws_iam_role_policy.cur_crawler_s3 will be created
  + resource "aws_iam_role_policy" "cur_crawler_s3" {
      + id          = (known after apply)
      + name        = "javabin-cur-crawler-s3"
      + name_prefix = (known after apply)
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = [
                          + "s3:GetObject",
                          + "s3:ListBucket",
                        ]
                      + Effect   = "Allow"
                      + Resource = [
                          + "arn:aws:s3:::javabin-cur-553637109631",
                          + "arn:aws:s3:::javabin-cur-553637109631/*",
                        ]
                      + Sid      = "ReadCURData"
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + role        = (known after apply)
    }

  # module.cost_analytics.aws_iam_role_policy_attachment.cur_crawler_glue will be created
  + resource "aws_iam_role_policy_attachment" "cur_crawler_glue" {
      + id         = (known after apply)
      + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole"
      + role       = "javabin-cur-crawler"
    }

  # module.dns.aws_route53_record.javabin_com_www_aaaa will be created
  + resource "aws_route53_record" "javabin_com_www_aaaa" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "www.javabin.com"
      + records         = [
          + "2a01:5b40:0:248::52",
        ]
      + ttl             = 3600
      + type            = "AAAA"
      + zone_id         = "Z0728803H4R1N49X3FE6"
    }

  # module.dns.aws_route53_record.javabin_no_www_aaaa will be created
  + resource "aws_route53_record" "javabin_no_www_aaaa" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "www.javabin.no"
      + records         = [
          + "2a01:5b40:0:248::52",
        ]
      + ttl             = 3600
      + type            = "AAAA"
      + zone_id         = "Z02029092SCAPZOF62LM0"
    }

Plan: 7 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

LLM Review

Risk: 🟢 LOW

Plan adds cost analytics infrastructure (CUR export, Glue crawler) and DNS AAAA records with no destructive or security-impacting changes.

  • [routine] Adding AWS BCM Data Exports resource for Cost and Usage Report (CUR) export to S3 with daily granularity and PARQUET format - standard cost analytics setup
  • [routine] Creating Glue crawler (javabin-cur-crawler) scheduled daily at 6 AM to catalog CUR data in S3 - routine data pipeline automation
  • [routine] Adding IAM role and policies for Glue crawler with S3 read permissions and standard AWSGlueServiceRole - appropriately scoped permissions
  • [routine] Adding IPv6 AAAA DNS records for www.javabin.com and www.javabin.no pointing to 2a01:5b40:0:248::52 - DNS configuration update
  • 💰 [cost] New billable resources: BCM Data Exports (minimal cost), Glue crawler (runs daily), and S3 storage for CUR data - overall cost impact is low for cost analytics infrastructure

@Alexanderamiri Alexanderamiri merged commit 9cdbe65 into main Mar 26, 2026
3 checks passed
@Alexanderamiri Alexanderamiri deleted the fix/cur-deploy-v2 branch March 26, 2026 23:19
Alexanderamiri added a commit that referenced this pull request May 9, 2026
## Summary

Fixes two CI failures from the CUR 2.0 deployment (#103):

1. **CUR 2.0 query**: `SELECT *` instead of explicit column names — CUR
2.0 column names differ from legacy CUR. `table_configurations` already
controls what's included.
2. **Crawler IAM role**: Added `permissions_boundary` — the org boundary
requires all roles to carry a boundary.

## Test plan

- [ ] CI plan + apply succeeds
- [ ] CUR export created in us-east-1
- [ ] Glue crawler role created with boundary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant