From ac2cb1968a49239526f7431a4f6838fe993f54e9 Mon Sep 17 00:00:00 2001 From: Alexander Amiri Date: Wed, 18 Mar 2026 00:27:24 +0100 Subject: [PATCH] Add team-scoped Terraform backend access to CI team roles Team roles need access to the shared state bucket and lock table. - S3: scoped to apps/{team}/* prefix (can't touch other teams' state) - DynamoDB: scoped via LeadingKeys to team's state paths only --- terraform/platform/iam/main.tf | 53 +++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/terraform/platform/iam/main.tf b/terraform/platform/iam/main.tf index 732032b..6ed47b9 100644 --- a/terraform/platform/iam/main.tf +++ b/terraform/platform/iam/main.tf @@ -359,18 +359,49 @@ resource "aws_iam_role_policy" "ci_team_allow" { policy = jsonencode({ Version = "2012-10-17" - Statement = [{ - Sid = "AllowWithTeamTagIsolation" - Effect = "Allow" - Action = "*" - Resource = "*" - Condition = { - StringEqualsIfExists = { - "aws:ResourceTag/team" = each.key - "aws:RequestTag/team" = each.key + Statement = [ + { + Sid = "AllowWithTeamTagIsolation" + Effect = "Allow" + Action = "*" + Resource = "*" + Condition = { + StringEqualsIfExists = { + "aws:ResourceTag/team" = each.key + "aws:RequestTag/team" = each.key + } } - } - }] + }, + { + Sid = "AllowTerraformBackend" + Effect = "Allow" + Action = [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:ListBucket", + ] + Resource = [ + "arn:aws:s3:::${var.project}-terraform-state-${var.aws_account_id}", + "arn:aws:s3:::${var.project}-terraform-state-${var.aws_account_id}/apps/${each.key}/*", + ] + }, + { + Sid = "AllowTerraformLocking" + Effect = "Allow" + Action = [ + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + ] + Resource = "arn:aws:dynamodb:${var.region}:${var.aws_account_id}:table/${var.project}-terraform-app-locks" + Condition = { + "ForAllValues:StringLike" = { + "dynamodb:LeadingKeys" = "${var.project}-terraform-state-${var.aws_account_id}/apps/${each.key}/*" + } + } + }, + ] }) }