infra: optional ce:GetCostAndUsage grant for the console box
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 files changed,
+27
−3
docs/superpowers/specs/2026-08-11-unit-economics-design.md+1 −3infra/modules/region-host/main.tf+20 −0infra/modules/region-host/variables.tf+6 −0
diff --git a/docs/superpowers/specs/2026-08-11-unit-economics-design.md b/docs/superpowers/specs/2026-08-11-unit-economics-design.md| index 9a03c9797..e605721e1 100644 |
| --- a/docs/superpowers/specs/2026-08-11-unit-economics-design.md |
| +++ b/docs/superpowers/specs/2026-08-11-unit-economics-design.md |
| @@ -266,9 +266,7 @@ as a follow-up gap rather than built now. |
| - All additive. Boxes need the new edge binary (ship → promote → |
| converge) before egress data accrues; console deploys with the usual |
| restart rule. |
| -- The Cost Explorer IAM grant (pinfra for flagship, `infra/tito` for |
| - tito) is an operator-queue tofu apply; until it lands, the reconcile |
| - panel runs in manual-entry mode. |
| +- The Cost Explorer IAM grant (`enable_cost_explorer = true` on the console box's `region-host` module block — pinfra for flagship, `infra/tito` for tito) is an operator-queue tofu apply; until it lands, the reconcile panel runs in manual-entry mode. |
| - The dashboard is useful on day one from existing CPU/mem history; |
| edge, storage and census series fill in as they accrue. |
diff --git a/infra/modules/region-host/main.tf b/infra/modules/region-host/main.tf| index 21afdcbca..fb939d063 100644 |
| --- a/infra/modules/region-host/main.tf |
| +++ b/infra/modules/region-host/main.tf |
| @@ -141,6 +141,26 @@ resource "aws_iam_role_policy" "host_route53" { |
| }) |
| } |
| +# Cost Explorer read for the economics dashboard's daily bill pull |
| +# (internal/console/cepull.go). Off by default: only the box that runs the |
| +# console needs it, and ce:GetCostAndUsage has no resource-level scoping — |
| +# it reads the whole account's cost lines, so the grant is deliberate. |
| +resource "aws_iam_role_policy" "host_costexplorer" { |
| + count = var.enable_cost_explorer ? 1 : 0 |
| + name = "${var.iam_name}-costexplorer" |
| + role = aws_iam_role.host.id |
| + policy = jsonencode({ |
| + Version = "2012-10-17" |
| + Statement = [ |
| + { |
| + Effect = "Allow" |
| + Action = ["ce:GetCostAndUsage"] |
| + Resource = "*" |
| + }, |
| + ] |
| + }) |
| +} |
| + |
| resource "aws_iam_role_policy_attachment" "host_ssm" { |
| role = aws_iam_role.host.name |
| policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" |
diff --git a/infra/modules/region-host/variables.tf b/infra/modules/region-host/variables.tf| index 5003e4d35..1570628d8 100644 |
| --- a/infra/modules/region-host/variables.tf |
| +++ b/infra/modules/region-host/variables.tf |
| @@ -76,3 +76,9 @@ variable "route53_zone_ids" { |
| error_message = "route53_zone_ids entries must be bare hosted-zone ids (Z...), not /hostedzone/-prefixed paths or wildcards." |
| } |
| } |
| + |
| +variable "enable_cost_explorer" { |
| + description = "Grant ce:GetCostAndUsage so a console on this box can pull the monthly bill for the economics dashboard" |
| + type = bool |
| + default = false |
| +} |