Skip to content
Closed
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
81 changes: 81 additions & 0 deletions terraform/environments/aws/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# AWS Environment Configuration

This directory contains the Terraform configuration for CUDly on AWS with environment-specific values extracted into `.tfvars` files.

## Structure

```text
aws/
├── main.tf # Provider + locals
├── variables.tf # Variable declarations
├── outputs.tf # Output definitions
├── backend.tf # Backend configuration (S3)
├── networking.tf # VPC, subnets, security groups
├── compute.tf # Lambda / Fargate resources
├── database.tf # RDS PostgreSQL
├── secrets.tf # Secrets Manager
├── build.tf # Docker build (optional)
├── frontend.tf # CloudFront + S3 (optional)
├── archera.tf # Archera integration (gated on enable_archera)
├── dev.tfvars.example # Example dev values (copy to dev.tfvars)
└── ci-cd-permissions/ # Bootstrap-only: deploy IAM role (apply once)
└── README.md
```

## Usage

```bash
# Copy example config
cp dev.tfvars.example dev.tfvars
# Edit dev.tfvars with your values

# Initialize with dev backend
terraform init -backend-config=backends/dev.tfbackend

# Plan
terraform plan -var-file=dev.tfvars

# Apply
terraform apply -var-file=dev.tfvars
```

## Archera Integration

The Archera commitment-optimisation integration is gated behind the
`enable_archera` tfvars flag (default: `false`) so non-Archera customers
see no drift.

### Enabling Archera

> **IMPORTANT — scope confirmation required before enabling.**
> The permission list in `archera.tf` is provisional. Confirm the exact
> AWS IAM actions required against [Archera's integration docs](https://archera.ai/docs)
> and validate with `@cristim` before setting `enable_archera = true` in
> any environment. See `TODO(@cristim)` comments in `archera.tf`.

1. Obtain the Archera **AWS account ID** from Archera during onboarding
(the account whose IAM principal will assume the cross-account role).
2. Set the following in your `*.tfvars`:

```hcl
enable_archera = true
archera_aws_account_id = "123456789012" # Archera's account ID
archera_external_id = "replace-with-archera-extid"
# Optional: only after the purchase approval workflow is confirmed
# enable_archera_purchase_actions = true
```

3. Run `terraform plan` to review the cross-account IAM role and policy
that will be created, then apply.

### What gets created

When `enable_archera = true`:

| Resource | Purpose |
| --- | --- |
| `aws_iam_role.archera_integration[0]` | Cross-account role trusted by Archera's AWS account |
| `aws_iam_policy.archera_read[0]` | Provisional read-only policy for cost / commitment telemetry |
| `aws_iam_role_policy_attachment.archera_read[0]` | Attaches the read-only policy to the role |
| `aws_iam_policy.archera_purchase[0]` | Optional RI/SP purchase policy, created only when `enable_archera_purchase_actions = true` |
| `aws_iam_role_policy_attachment.archera_purchase[0]` | Attaches the optional purchase policy to the role |
197 changes: 197 additions & 0 deletions terraform/environments/aws/archera.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# ==============================================
# Archera Integration — AWS
# ==============================================
#
# When enable_archera = true, this block creates a cross-account IAM role
# that Archera assumes to read commitment and cost data. Purchase-execution
# permissions are separately gated behind enable_archera_purchase_actions
# so telemetry-only rollouts never accidentally include financial writes.
#
# PROVISIONAL SCOPE — must be confirmed against Archera integration docs
# before flipping enable_archera = true in any tfvars.
# TODO(@cristim): confirm Archera scope list against integration docs
# before enabling. Reference: https://archera.ai/docs (integration guide).
#
# Placement rationale (bootstrap vs runtime split):
# Archera is a RUNTIME integration — it reads cost telemetry and submits
# purchases during normal operation, not during Terraform deploys. This
# file therefore lives in the main environment alongside compute.tf /
# database.tf, NOT in ci-cd-permissions/ (which is applied once by a
# privileged human and grants deploy-SA capabilities only).

locals {
archera_role_name = "cudly-archera-integration"
}

# IAM role that Archera's AWS account assumes to access this account.
resource "aws_iam_role" "archera_integration" {
count = var.enable_archera ? 1 : 0

name = local.archera_role_name
description = "Assumed by Archera SaaS to read cost data and (optionally) execute RI/SP purchases (provisional — confirm scope before enabling)"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "ArcheraAssumeRole"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${var.archera_aws_account_id}:root"
}
Action = "sts:AssumeRole"
# ExternalId prevents confused-deputy attacks — always required when
# trusting a third-party SaaS account. Set archera_external_id to
# the value Archera provides during onboarding.
Condition = {
StringEquals = { "sts:ExternalId" = var.archera_external_id }
}
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
]
})

tags = merge(local.common_tags, {
Integration = "archera"
Purpose = "commitment-optimisation"
})
}

# Read-only policy for Archera — PROVISIONAL.
# Safe to attach at initial rollout (no financial writes).
#
# TODO(@cristim): narrow to the exact action list from Archera's onboarding
# docs before setting enable_archera = true in any environment.
resource "aws_iam_policy" "archera_read" {
count = var.enable_archera ? 1 : 0

name = "cudly-archera-read"
description = "Provisional Archera read-only policy — cost Explorer + RI/SP telemetry (no purchase actions)"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
# ── Read-only: Cost Explorer ──────────────────────────────────────────
# Archera needs to read historical usage and costs to size commitments.
# TODO(@cristim): confirm whether Archera also needs ce:GetReservation*
# and ce:GetSavingsPlan* actions — add them if so.
{
Sid = "CostExplorerReadOnly"
Effect = "Allow"
Action = [
"ce:GetCostAndUsage",
"ce:GetCostAndUsageWithResources",
"ce:GetCostForecast",
"ce:GetDimensionValues",
"ce:GetReservationCoverage",
"ce:GetReservationPurchaseRecommendation",
"ce:GetReservationUtilization",
"ce:GetRightsizingRecommendation",
"ce:GetSavingsPlansCoverage",
"ce:GetSavingsPlansUtilization",
"ce:GetSavingsPlansUtilizationDetails",
"ce:ListCostCategoryDefinitions",
]
Resource = "*"
},
# ── Read-only: CUR / Cost and Usage Report ────────────────────────────
# Describes which CUR reports are configured; Archera may consume CUR
# exports directly for more granular data.
{
Sid = "CURDescribe"
Effect = "Allow"
Action = [
"cur:DescribeReportDefinitions",
]
Resource = "*"
},
# ── Read-only: Reserved Instances ─────────────────────────────────────
# Archera needs to see existing RIs to avoid over-purchasing.
{
Sid = "ReservedInstancesRead"
Effect = "Allow"
Action = [
"ec2:DescribeReservedInstances",
"ec2:DescribeReservedInstancesListings",
"ec2:DescribeReservedInstancesModifications",
"ec2:DescribeReservedInstancesOfferings",
"ec2:DescribeInstanceTypeOfferings",
]
Resource = "*"
},
# ── Read-only: Savings Plans ─────────────────────────────────────────
{
Sid = "SavingsPlansRead"
Effect = "Allow"
Action = [
"savingsplans:DescribeSavingsPlans",
"savingsplans:DescribeSavingsPlansOfferings",
"savingsplans:DescribeSavingsPlanRates",
"savingsplans:ListTagsForResource",
]
Resource = "*"
},
]
})

tags = merge(local.common_tags, {
Integration = "archera"
})
}

resource "aws_iam_role_policy_attachment" "archera_read" {
count = var.enable_archera ? 1 : 0

role = aws_iam_role.archera_integration[0].name
policy_arn = aws_iam_policy.archera_read[0].arn
}

# Purchase-execution policy for Archera — PROVISIONAL.
# Gated behind enable_archera_purchase_actions (default false) so financial
# writes are never included by accident at initial rollout.
#
# TODO(@cristim): enable only after confirming approval workflow with
# Archera (i.e. Archera requires customer approval before purchases) and
# only after enable_archera_purchase_actions is explicitly set to true.
resource "aws_iam_policy" "archera_purchase" {
count = (var.enable_archera && var.enable_archera_purchase_actions) ? 1 : 0

name = "cudly-archera-purchase"
description = "Provisional Archera purchase policy — RI/SP write actions (confirm with Archera before enabling)"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
# ── Purchase-execution: Reserved Instances ────────────────────────────
{
Sid = "ReservedInstancesPurchase"
Effect = "Allow"
Action = [
"ec2:PurchaseReservedInstancesOffering",
"ec2:ModifyReservedInstances",
]
Resource = "*"
},
# ── Purchase-execution: Savings Plans ────────────────────────────────
{
Sid = "SavingsPlansPurchase"
Effect = "Allow"
Action = [
"savingsplans:CreateSavingsPlan",
"savingsplans:DeleteQueuedSavingsPlan",
]
Resource = "*"
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
]
})

tags = merge(local.common_tags, {
Integration = "archera"
})
}

resource "aws_iam_role_policy_attachment" "archera_purchase" {
count = (var.enable_archera && var.enable_archera_purchase_actions) ? 1 : 0

role = aws_iam_role.archera_integration[0].name
policy_arn = aws_iam_policy.archera_purchase[0].arn
}
13 changes: 13 additions & 0 deletions terraform/environments/aws/dev.tfvars.example
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,16 @@ admin_email = "admin@example.com"
# ==============================================

additional_env_vars = {}

# ==============================================
# Archera Integration (default: disabled)
# ==============================================
# Set enable_archera = true ONLY after confirming the scope list in
# archera.tf against Archera's integration docs (see TODO(@cristim) there).
# When enabling, also set archera_aws_account_id to the value provided by
# Archera during onboarding.

enable_archera = false
enable_archera_purchase_actions = false # keep false until purchase approval workflow is confirmed
# archera_aws_account_id = "123456789012" # replace with Archera's AWS account ID
# archera_external_id = "replace-with-archera-extid" # required when enable_archera = true
76 changes: 76 additions & 0 deletions terraform/environments/aws/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,79 @@ variable "enable_docker_build" {
type = bool
default = true
}

# ==============================================
# Archera Integration
# ==============================================

variable "enable_archera" {
description = <<-EOT
Enable the Archera commitment-optimisation integration. When true, creates
a cross-account IAM role (cudly-archera-integration) that Archera's AWS
account can assume to read cost data and execute RI/SP purchases.

Defaults to false — non-Archera customers see no drift.

IMPORTANT: the provisional scope list in archera.tf must be confirmed
against Archera's integration docs before setting this to true in any
environment. See TODO(@cristim) comments in archera.tf.
EOT
type = bool
default = false
}

variable "archera_aws_account_id" {
description = <<-EOT
AWS account ID of the Archera SaaS platform that will assume the
cudly-archera-integration role. Obtained from Archera during onboarding.
Only evaluated when enable_archera = true.

Example: "926226587429"
TODO(@cristim): confirm the correct Archera AWS account ID from their
integration docs before enabling.
EOT
type = string
default = ""

validation {
condition = (
!var.enable_archera ||
can(regex("^\\d{12}$", trimspace(var.archera_aws_account_id)))
)
error_message = "archera_aws_account_id must be a 12-digit AWS account ID when enable_archera = true."
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

variable "archera_external_id" {
description = <<-EOT
ExternalId value that Archera's AWS principal must supply when assuming
the cudly-archera-integration role. This prevents confused-deputy attacks
and is required for all third-party cross-account integrations.
Provided by Archera during onboarding.
Only evaluated when enable_archera = true.

TODO(@cristim): obtain the ExternalId from Archera's onboarding docs
before enabling — do not leave this empty in production.
EOT
type = string
default = ""
sensitive = true

validation {
condition = !var.enable_archera || var.archera_external_id != ""
error_message = "archera_external_id must be set (non-empty) when enable_archera = true."
}
}

variable "enable_archera_purchase_actions" {
description = <<-EOT
When true (AND enable_archera = true), attaches a second IAM policy
(cudly-archera-purchase) that grants RI/SP purchase-execution permissions.
Keep false until the approval workflow with Archera is confirmed and the
scope list has been validated against Archera's integration docs.

Defaults to false so initial rollout is read-only.
EOT
type = bool
default = false
}
Loading