-
Notifications
You must be signed in to change notification settings - Fork 5
feat(iac): scaffolding for Archera integration perms across AWS/Azure/GCP (closes #305) #310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
cristim
wants to merge
3
commits into
feat/multicloud-web-frontend
from
feat/issue-305-archera-iac-perms
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 } | ||
| } | ||
| }, | ||
| ] | ||
| }) | ||
|
|
||
| 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 = "*" | ||
| }, | ||
|
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 | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.