carlos / platform Public

infra: optional ce:GetCostAndUsage grant for the console box

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell a4a12c8cddcdbad84b875d7bdc8e6b42ba9ecb3f parent 7fd1add
3 files changed, +27 −3
  • docs/superpowers/specs/2026-08-11-unit-economics-design.md +1 −3
  • infra/modules/region-host/main.tf +20 −0
  • infra/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
+}