From e7b9d6a11160a2fc51cd2bc6583da384728e58d1 Mon Sep 17 00:00:00 2001 From: Matt Dean Date: Tue, 10 Feb 2026 08:40:57 +0000 Subject: [PATCH 1/5] [NRL-1938] WIP - Move NRLF-CI policies to acc-wide mgmt TF --- .../account-wide-infrastructure/mgmt/iam.tf | 325 ++++++++++++------ .../account-wide-infrastructure/mgmt/vars.tf | 6 + 2 files changed, 223 insertions(+), 108 deletions(-) diff --git a/terraform/account-wide-infrastructure/mgmt/iam.tf b/terraform/account-wide-infrastructure/mgmt/iam.tf index 834e922d7..9f227b364 100644 --- a/terraform/account-wide-infrastructure/mgmt/iam.tf +++ b/terraform/account-wide-infrastructure/mgmt/iam.tf @@ -1,110 +1,219 @@ -module "developer_policy" { - source = "../modules/role-policy" - name = "${local.prefix}--developer-policy" - role_name = aws_iam_role.developer_role.name - iam_permissions = [ - { - Action = [ - "s3:PutObject", - "s3:DeleteObject", - "s3:GetObject", - "dynamodb:PutItem", - "dynamodb:GetItem", - "dynamodb:DeleteItem", - "s3:ListBucket" - ] - Effect = "Allow" - Resource = [ - data.aws_dynamodb_table.terraform_state_lock.arn, - data.aws_s3_bucket.terraform_state.arn, - "${data.aws_s3_bucket.terraform_state.arn}/*" - ] - }, - { - Action = [ - "s3:PutObject", - "s3:GetObject", - "s3:DeleteObject" - ] - Effect = "Deny" - Resource = [ - "${data.aws_s3_bucket.terraform_state.arn}/${local.project}/prod/*", - "${data.aws_s3_bucket.terraform_state.arn}/${local.project}/mgmt/*", - ] - }, - { - Action = [ - "s3:DeleteObject" - ] - Effect = "Deny" - Resource = [ - "${data.aws_s3_bucket.terraform_state.arn}/${local.project}/dev/*" - ] - }, - { - Action = "sts:AssumeRole" - Effect = "Allow" - Resource = [ - "arn:aws:iam::${data.aws_secretsmanager_secret_version.dev_account_id.secret_string}:role/terraform", - "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_account_id.secret_string}:role/terraform", - "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_backup_account_id.secret_string}:role/terraform", - "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_restore_account_id.secret_string}:role/terraform" - ] - }, - { - Action = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds" - ] - Effect = "Allow" - Resource = [ - data.aws_secretsmanager_secret.dev_account_id.arn, - data.aws_secretsmanager_secret.test_account_id.arn - ] - }, - { - Action = [ - "s3:ListAllMyBuckets" - ] - Effect = "Allow" - Resource = [ - "arn:aws:s3:::*" - ] - }, - { - Action = [ - "s3:GetObject", - "s3:ListBucket" - ] - Effect = "Allow" - Resource = [ - data.aws_s3_bucket.ci_logging.arn, - "${data.aws_s3_bucket.ci_logging.arn}/*" - ] - }, - { - Action = [ - "s3:PutObject", - "s3:GetObject", - "s3:DeleteObject" - ] - Effect = "Deny" - Resource = [ - "${data.aws_s3_bucket.truststore.arn}/ca/prod*", - "${data.aws_s3_bucket.truststore.arn}/client/prod*", - "${data.aws_s3_bucket.truststore.arn}/server/prod*" - ] - }, - { - Action = [ - "s3:GetObject" - ] - Effect = "Allow" - Resource = [ - "${data.aws_s3_bucket.truststore.arn}/*" - ] - } +resource "aws_iam_openid_connect_provider" "github_action" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ + "sts.amazonaws.com", ] + + thumbprint_list = [ + # pragma: allowlist nextline secret + "6938fd4d98bab03faadb97b34396831e3780aea1" + ] +} + +resource "aws_iam_role" "github_ci" { + name = "${local.prefix}--github-ci" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRoleWithWebIdentity" + Effect = "Allow" + Principal = { + Federated = aws_iam_openid_connect_provider.github_action.arn + } + Condition = { + StringLike = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + "token.actions.githubusercontent.com:sub" = "repo:NHSDigital/NRLF:*" + } + } + } + ] + }) +} + +resource "aws_iam_policy" "github_ci_policy" { + name = "${local.prefix}--github-ci-policy" + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "s3:PutObject", + "s3:DeleteObject", + "s3:GetObject", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "s3:ListBucket" + ] + Effect = "Allow" + Resource = [ + data.aws_dynamodb_table.terraform_state_lock.arn, + data.aws_s3_bucket.terraform_state.arn, + "${data.aws_s3_bucket.terraform_state.arn}/*" + ] + }, + { + Action = "sts:AssumeRole" + Effect = "Allow" + Resource = [ + "arn:aws:iam::${data.aws_secretsmanager_secret_version.dev_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.prod_account_id.secret_string}:role/terraform" + ] + }, + { + Action = [ + "secretsmanager:GetResourcePolicy", + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds" + ] + Effect = "Allow" + Resource = [ + data.aws_secretsmanager_secret.dev_account_id.arn, + data.aws_secretsmanager_secret.test_account_id.arn, + data.aws_secretsmanager_secret.prod_account_id.arn + ] + }, + { + Action = [ + "s3:PutObject", + "s3:DeleteObject", + "s3:GetObject", + "s3:ListBucket" + ] + Effect = "Allow" + Resource = [ + aws_s3_bucket.github_ci_logging.arn, + "${aws_s3_bucket.github_ci_logging.arn}/*" + ] + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "github_ci_policy_attachment" { + role = aws_iam_role.github_ci.name + policy_arn = aws_iam_policy.github_ci_policy.arn +} + +resource "aws_iam_policy" "developer_policy" { + name = "${local.prefix}--developer-policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "s3:PutObject", + "s3:DeleteObject", + "s3:GetObject", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "s3:ListBucket" + ] + Effect = "Allow" + Resource = [ + data.aws_dynamodb_table.terraform_state_lock.arn, + data.aws_s3_bucket.terraform_state.arn, + "${data.aws_s3_bucket.terraform_state.arn}/*" + ] + }, + { + Action = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject" + ] + Effect = "Deny" + Resource = [ + "${data.aws_s3_bucket.terraform_state.arn}/${local.project}/prod/*", + "${data.aws_s3_bucket.terraform_state.arn}/${local.project}/mgmt/*", + ] + }, + { + Action = [ + "s3:DeleteObject" + ] + Effect = "Deny" + Resource = [ + "${data.aws_s3_bucket.terraform_state.arn}/${local.project}/dev/*" + ] + }, + { + Action = "sts:AssumeRole" + Effect = "Allow" + Resource = [ + "arn:aws:iam::${data.aws_secretsmanager_secret_version.dev_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_backup_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_restore_account_id.secret_string}:role/terraform" + ] + }, + { + Action = [ + "secretsmanager:GetResourcePolicy", + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds" + ] + Effect = "Allow" + Resource = [ + data.aws_secretsmanager_secret.dev_account_id.arn, + data.aws_secretsmanager_secret.test_account_id.arn + ] + }, + { + Action = [ + "s3:ListAllMyBuckets" + ] + Effect = "Allow" + Resource = [ + "arn:aws:s3:::*" + ] + }, + { + Action = [ + "s3:GetObject", + "s3:ListBucket" + ] + Effect = "Allow" + Resource = [ + data.aws_s3_bucket.ci_logging.arn, + "${data.aws_s3_bucket.ci_logging.arn}/*" + ] + }, + { + Action = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject" + ] + Effect = "Deny" + Resource = [ + "${data.aws_s3_bucket.truststore.arn}/ca/prod*", + "${data.aws_s3_bucket.truststore.arn}/client/prod*", + "${data.aws_s3_bucket.truststore.arn}/server/prod*" + ] + }, + { + Action = [ + "s3:GetObject" + ] + Effect = "Allow" + Resource = [ + "${data.aws_s3_bucket.truststore.arn}/*" + ] + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "developer_policy_attachment" { + role = aws_iam_role.developer_role.name + policy_arn = aws_iam_policy.developer_policy.arn } diff --git a/terraform/account-wide-infrastructure/mgmt/vars.tf b/terraform/account-wide-infrastructure/mgmt/vars.tf index 4785b5af3..ea019f5ad 100644 --- a/terraform/account-wide-infrastructure/mgmt/vars.tf +++ b/terraform/account-wide-infrastructure/mgmt/vars.tf @@ -1,3 +1,9 @@ +variable "developer_role" { + description = "Name of the IAM role for developers" + type = string + default = "DeveloperRole" +} + variable "private_subnet_cidr_blocks" { description = "Available CIDR blocks for private subnets" type = list(string) From ecd80a85f0df292983d8540f325f7f3b2da2e539 Mon Sep 17 00:00:00 2001 From: Matt Dean Date: Tue, 10 Feb 2026 12:25:25 +0000 Subject: [PATCH 2/5] [NRL-1938] Port CI policies over from NRLF-CI repo --- .../workflows/deploy-account-wide-infra.yml | 12 +- .github/workflows/persistent-environment.yml | 6 +- .../workflows/update-lambda-permissions.yml | 6 +- .../account-wide-infrastructure/mgmt/data.tf | 8 +- .../mgmt/{iam.tf => iam_developer.tf} | 108 +----------------- .../mgmt/iam_github-ci.tf | 104 +++++++++++++++++ .../account-wide-infrastructure/mgmt/s3.tf | 36 ++++++ .../account-wide-infrastructure/mgmt/vars.tf | 4 +- 8 files changed, 161 insertions(+), 123 deletions(-) rename terraform/account-wide-infrastructure/mgmt/{iam.tf => iam_developer.tf} (50%) create mode 100644 terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf create mode 100644 terraform/account-wide-infrastructure/mgmt/s3.tf diff --git a/.github/workflows/deploy-account-wide-infra.yml b/.github/workflows/deploy-account-wide-infra.yml index 564a8e1d1..82cbed87f 100644 --- a/.github/workflows/deploy-account-wide-infra.yml +++ b/.github/workflows/deploy-account-wide-infra.yml @@ -88,9 +88,9 @@ jobs: run: | terraform -chdir=terraform/account-wide-infrastructure/${ACCOUNT_NAME} show -no-color tfplan > terraform/account-wide-infrastructure/$ACCOUNT_NAME/tfplan.txt - aws s3 cp terraform/account-wide-infrastructure/$ACCOUNT_NAME/tfplan s3://nhsd-nrlf--mgmt--github-ci-logging/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan - aws s3 cp terraform/account-wide-infrastructure/$ACCOUNT_NAME/tfplan.txt s3://nhsd-nrlf--mgmt--github-ci-logging/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan.txt - aws s3 cp terraform/account-wide-infrastructure/modules/glue/files/src.zip s3://nhsd-nrlf--mgmt--github-ci-logging/acc-$ACCOUNT_NAME/${{ github.run_id }}/glue-src.zip + aws s3 cp terraform/account-wide-infrastructure/$ACCOUNT_NAME/tfplan s3://nhsd-nrlf--mgmt--github-ci-data/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan + aws s3 cp terraform/account-wide-infrastructure/$ACCOUNT_NAME/tfplan.txt s3://nhsd-nrlf--mgmt--github-ci-data/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan.txt + aws s3 cp terraform/account-wide-infrastructure/modules/glue/files/src.zip s3://nhsd-nrlf--mgmt--github-ci-data/acc-$ACCOUNT_NAME/${{ github.run_id }}/glue-src.zip terraform-apply: name: Terraform Apply - ${{ inputs.environment }} @@ -120,11 +120,11 @@ jobs: env: ACCOUNT_NAME: ${{ vars.ACCOUNT_NAME }} run: | - aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-logging/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan terraform/account-wide-infrastructure/${ACCOUNT_NAME}/tfplan - aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-logging/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan.txt terraform/account-wide-infrastructure/${ACCOUNT_NAME}/tfplan.txt + aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-data/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan terraform/account-wide-infrastructure/${ACCOUNT_NAME}/tfplan + aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-data/acc-$ACCOUNT_NAME/${{ github.run_id }}/tfplan.txt terraform/account-wide-infrastructure/${ACCOUNT_NAME}/tfplan.txt mkdir -p terraform/account-wide-infrastructure/modules/glue/files - aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-logging/acc-$ACCOUNT_NAME/${{ github.run_id }}/glue-src.zip terraform/account-wide-infrastructure/modules/glue/files/src.zip + aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-data/acc-$ACCOUNT_NAME/${{ github.run_id }}/glue-src.zip terraform/account-wide-infrastructure/modules/glue/files/src.zip - name: Retrieve Server Certificates env: diff --git a/.github/workflows/persistent-environment.yml b/.github/workflows/persistent-environment.yml index 3559e39dd..bca092c4b 100644 --- a/.github/workflows/persistent-environment.yml +++ b/.github/workflows/persistent-environment.yml @@ -143,8 +143,8 @@ jobs: ENVIRONMENT: ${{ inputs.environment }} run: | terraform -chdir=terraform/infrastructure show -no-color tfplan > terraform/infrastructure/tfplan.txt - aws s3 cp terraform/infrastructure/tfplan s3://nhsd-nrlf--mgmt--github-ci-logging/$ENVIRONMENT/${{ github.run_id }}/tfplan - aws s3 cp terraform/infrastructure/tfplan.txt s3://nhsd-nrlf--mgmt--github-ci-logging/$ENVIRONMENT/${{ github.run_id }}/tfplan.txt + aws s3 cp terraform/infrastructure/tfplan s3://nhsd-nrlf--mgmt--github-ci-data/$ENVIRONMENT/${{ github.run_id }}/tfplan + aws s3 cp terraform/infrastructure/tfplan.txt s3://nhsd-nrlf--mgmt--github-ci-data/$ENVIRONMENT/${{ github.run_id }}/tfplan.txt terraform-apply: name: Terraform Apply - ${{ inputs.environment }} @@ -186,7 +186,7 @@ jobs: - name: Download Terraform Plan artifact env: ENVIRONMENT: ${{ inputs.environment }} - run: aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-logging/$ENVIRONMENT/${{ github.run_id }}/tfplan terraform/infrastructure/tfplan + run: aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-data/$ENVIRONMENT/${{ github.run_id }}/tfplan terraform/infrastructure/tfplan - name: Retrieve Server Certificates env: diff --git a/.github/workflows/update-lambda-permissions.yml b/.github/workflows/update-lambda-permissions.yml index c7017724d..886df9da9 100644 --- a/.github/workflows/update-lambda-permissions.yml +++ b/.github/workflows/update-lambda-permissions.yml @@ -220,8 +220,8 @@ jobs: ENVIRONMENT: ${{ inputs.environment }} run: | terraform -chdir=terraform/infrastructure show -no-color tfplan > terraform/infrastructure/tfplan.txt - aws s3 cp terraform/infrastructure/tfplan s3://nhsd-nrlf--mgmt--github-ci-logging/$ENVIRONMENT/${{ github.run_id }}/tfplan - aws s3 cp terraform/infrastructure/tfplan.txt s3://nhsd-nrlf--mgmt--github-ci-logging/$ENVIRONMENT/${{ github.run_id }}/tfplan.txt + aws s3 cp terraform/infrastructure/tfplan s3://nhsd-nrlf--mgmt--github-ci-data/$ENVIRONMENT/${{ github.run_id }}/tfplan + aws s3 cp terraform/infrastructure/tfplan.txt s3://nhsd-nrlf--mgmt--github-ci-data/$ENVIRONMENT/${{ github.run_id }}/tfplan.txt terraform-apply: name: Apply permissions @@ -265,7 +265,7 @@ jobs: - name: Download Terraform Plan artifact env: ENVIRONMENT: ${{ inputs.environment }} - run: aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-logging/$ENVIRONMENT/${{ github.run_id }}/tfplan terraform/infrastructure/tfplan + run: aws s3 cp s3://nhsd-nrlf--mgmt--github-ci-data/$ENVIRONMENT/${{ github.run_id }}/tfplan terraform/infrastructure/tfplan - name: Terraform Init env: diff --git a/terraform/account-wide-infrastructure/mgmt/data.tf b/terraform/account-wide-infrastructure/mgmt/data.tf index ebfc46ff1..931dc9b2e 100644 --- a/terraform/account-wide-infrastructure/mgmt/data.tf +++ b/terraform/account-wide-infrastructure/mgmt/data.tf @@ -10,10 +10,6 @@ data "aws_s3_bucket" "terraform_state" { bucket = "${local.project}--terraform-state" } -data "aws_s3_bucket" "ci_logging" { - bucket = "${local.project}--mgmt--github-ci-logging" -} - data "aws_s3_bucket" "truststore" { bucket = "${local.project}--truststore" } @@ -53,3 +49,7 @@ data "aws_secretsmanager_secret_version" "test_backup_account_id" { data "aws_secretsmanager_secret_version" "test_restore_account_id" { secret_id = data.aws_secretsmanager_secret.test_restore_account_id.name } + +data "aws_secretsmanager_secret_version" "prod_account_id" { + secret_id = data.aws_secretsmanager_secret.prod_account_id.name +} diff --git a/terraform/account-wide-infrastructure/mgmt/iam.tf b/terraform/account-wide-infrastructure/mgmt/iam_developer.tf similarity index 50% rename from terraform/account-wide-infrastructure/mgmt/iam.tf rename to terraform/account-wide-infrastructure/mgmt/iam_developer.tf index 9f227b364..164968b7b 100644 --- a/terraform/account-wide-infrastructure/mgmt/iam.tf +++ b/terraform/account-wide-infrastructure/mgmt/iam_developer.tf @@ -1,105 +1,3 @@ -resource "aws_iam_openid_connect_provider" "github_action" { - url = "https://token.actions.githubusercontent.com" - - client_id_list = [ - "sts.amazonaws.com", - ] - - thumbprint_list = [ - # pragma: allowlist nextline secret - "6938fd4d98bab03faadb97b34396831e3780aea1" - ] -} - -resource "aws_iam_role" "github_ci" { - name = "${local.prefix}--github-ci" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRoleWithWebIdentity" - Effect = "Allow" - Principal = { - Federated = aws_iam_openid_connect_provider.github_action.arn - } - Condition = { - StringLike = { - "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" - "token.actions.githubusercontent.com:sub" = "repo:NHSDigital/NRLF:*" - } - } - } - ] - }) -} - -resource "aws_iam_policy" "github_ci_policy" { - name = "${local.prefix}--github-ci-policy" - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = [ - "s3:PutObject", - "s3:DeleteObject", - "s3:GetObject", - "dynamodb:PutItem", - "dynamodb:GetItem", - "dynamodb:DeleteItem", - "s3:ListBucket" - ] - Effect = "Allow" - Resource = [ - data.aws_dynamodb_table.terraform_state_lock.arn, - data.aws_s3_bucket.terraform_state.arn, - "${data.aws_s3_bucket.terraform_state.arn}/*" - ] - }, - { - Action = "sts:AssumeRole" - Effect = "Allow" - Resource = [ - "arn:aws:iam::${data.aws_secretsmanager_secret_version.dev_account_id.secret_string}:role/terraform", - "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_account_id.secret_string}:role/terraform", - "arn:aws:iam::${data.aws_secretsmanager_secret_version.prod_account_id.secret_string}:role/terraform" - ] - }, - { - Action = [ - "secretsmanager:GetResourcePolicy", - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret", - "secretsmanager:ListSecretVersionIds" - ] - Effect = "Allow" - Resource = [ - data.aws_secretsmanager_secret.dev_account_id.arn, - data.aws_secretsmanager_secret.test_account_id.arn, - data.aws_secretsmanager_secret.prod_account_id.arn - ] - }, - { - Action = [ - "s3:PutObject", - "s3:DeleteObject", - "s3:GetObject", - "s3:ListBucket" - ] - Effect = "Allow" - Resource = [ - aws_s3_bucket.github_ci_logging.arn, - "${aws_s3_bucket.github_ci_logging.arn}/*" - ] - } - ] - }) -} - -resource "aws_iam_role_policy_attachment" "github_ci_policy_attachment" { - role = aws_iam_role.github_ci.name - policy_arn = aws_iam_policy.github_ci_policy.arn -} - resource "aws_iam_policy" "developer_policy" { name = "${local.prefix}--developer-policy" @@ -183,8 +81,8 @@ resource "aws_iam_policy" "developer_policy" { ] Effect = "Allow" Resource = [ - data.aws_s3_bucket.ci_logging.arn, - "${data.aws_s3_bucket.ci_logging.arn}/*" + aws_s3_bucket.github_ci_data.arn, + "${aws_s3_bucket.github_ci_data.arn}/*" ] }, { @@ -214,6 +112,6 @@ resource "aws_iam_policy" "developer_policy" { } resource "aws_iam_role_policy_attachment" "developer_policy_attachment" { - role = aws_iam_role.developer_role.name + role = var.developer_role_name policy_arn = aws_iam_policy.developer_policy.arn } diff --git a/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf b/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf new file mode 100644 index 000000000..31b2ffc3e --- /dev/null +++ b/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf @@ -0,0 +1,104 @@ +resource "aws_iam_openid_connect_provider" "github_action" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ + "sts.amazonaws.com", + ] + + thumbprint_list = [ + # pragma: allowlist nextline secret + "6938fd4d98bab03faadb97b34396831e3780aea1" + ] +} + +resource "aws_iam_role" "github_ci" { + name = "${local.prefix}--github-ci" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRoleWithWebIdentity" + Effect = "Allow" + Principal = { + Federated = aws_iam_openid_connect_provider.github_action.arn + } + Condition = { + StringLike = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + "token.actions.githubusercontent.com:sub" = "repo:NHSDigital/NRLF:*" + } + } + } + ] + }) +} + +resource "aws_iam_policy" "github_ci_policy" { + name = "${local.prefix}--github-ci-policy" + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "s3:PutObject", + "s3:DeleteObject", + "s3:GetObject", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "s3:ListBucket" + ] + Effect = "Allow" + Resource = [ + data.aws_dynamodb_table.terraform_state_lock.arn, + data.aws_s3_bucket.terraform_state.arn, + "${data.aws_s3_bucket.terraform_state.arn}/*" + ] + }, + { + Action = [ + "sts:AssumeRole", + "sts:TagSession" + ], + Effect = "Allow" + Resource = [ + "arn:aws:iam::${data.aws_secretsmanager_secret_version.dev_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.test_account_id.secret_string}:role/terraform", + "arn:aws:iam::${data.aws_secretsmanager_secret_version.prod_account_id.secret_string}:role/terraform" + ] + }, + { + Action = [ + "secretsmanager:GetResourcePolicy", + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds" + ] + Effect = "Allow" + Resource = [ + data.aws_secretsmanager_secret.dev_account_id.arn, + data.aws_secretsmanager_secret.test_account_id.arn, + data.aws_secretsmanager_secret.prod_account_id.arn + ] + }, + { + Action = [ + "s3:PutObject", + "s3:DeleteObject", + "s3:GetObject", + "s3:ListBucket" + ] + Effect = "Allow" + Resource = [ + aws_s3_bucket.github_ci_data.arn, + "${aws_s3_bucket.github_ci_data.arn}/*" + ] + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "github_ci_policy_attachment" { + role = aws_iam_role.github_ci.name + policy_arn = aws_iam_policy.github_ci_policy.arn +} diff --git a/terraform/account-wide-infrastructure/mgmt/s3.tf b/terraform/account-wide-infrastructure/mgmt/s3.tf new file mode 100644 index 000000000..1db60e182 --- /dev/null +++ b/terraform/account-wide-infrastructure/mgmt/s3.tf @@ -0,0 +1,36 @@ +resource "aws_s3_bucket" "github_ci_data" { + bucket = "${local.prefix}--github-ci-data" +} + +resource "aws_s3_bucket_acl" "github_ci_data" { + bucket = aws_s3_bucket.github_ci_data.id + acl = "private" + + depends_on = [ + aws_s3_bucket.github_ci_data + ] +} + +resource "aws_s3_bucket_public_access_block" "github_ci_data" { + bucket = aws_s3_bucket.github_ci_data.id + + block_public_acls = true + block_public_policy = true + restrict_public_buckets = true + ignore_public_acls = true + + depends_on = [ + aws_s3_bucket.github_ci_data + ] +} + +resource "aws_s3_bucket_versioning" "github_ci_data" { + bucket = aws_s3_bucket.github_ci_data.id + versioning_configuration { + status = "Enabled" + } + + depends_on = [ + aws_s3_bucket.github_ci_data + ] +} diff --git a/terraform/account-wide-infrastructure/mgmt/vars.tf b/terraform/account-wide-infrastructure/mgmt/vars.tf index ea019f5ad..a05502b52 100644 --- a/terraform/account-wide-infrastructure/mgmt/vars.tf +++ b/terraform/account-wide-infrastructure/mgmt/vars.tf @@ -1,7 +1,7 @@ -variable "developer_role" { +variable "developer_role_name" { description = "Name of the IAM role for developers" type = string - default = "DeveloperRole" + default = "AWSReservedSSO_NHSDDeveloperRole_fa10ba0474290a64" } variable "private_subnet_cidr_blocks" { From 9f97bc70d89c50d0a0373e20e598d8a1b2371cc7 Mon Sep 17 00:00:00 2001 From: Matt Dean Date: Tue, 10 Feb 2026 12:41:48 +0000 Subject: [PATCH 3/5] [NRL-1938] Lockdown codebuild ECR perms. Rename ci-data bucket --- .../mgmt/codebuild.tf | 4 +++- .../mgmt/iam_developer.tf | 4 ++-- .../mgmt/iam_github-ci.tf | 4 ++-- .../account-wide-infrastructure/mgmt/s3.tf | 22 +++++++++---------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/terraform/account-wide-infrastructure/mgmt/codebuild.tf b/terraform/account-wide-infrastructure/mgmt/codebuild.tf index 52f7db234..d6c6ce925 100644 --- a/terraform/account-wide-infrastructure/mgmt/codebuild.tf +++ b/terraform/account-wide-infrastructure/mgmt/codebuild.tf @@ -63,7 +63,9 @@ data "aws_iam_policy_document" "codebuild_policy" { statement { effect = "Allow" actions = [ - "ecr:*" + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability" ] resources = [ "${aws_ecr_repository.repository.arn}", diff --git a/terraform/account-wide-infrastructure/mgmt/iam_developer.tf b/terraform/account-wide-infrastructure/mgmt/iam_developer.tf index 164968b7b..109140c3d 100644 --- a/terraform/account-wide-infrastructure/mgmt/iam_developer.tf +++ b/terraform/account-wide-infrastructure/mgmt/iam_developer.tf @@ -81,8 +81,8 @@ resource "aws_iam_policy" "developer_policy" { ] Effect = "Allow" Resource = [ - aws_s3_bucket.github_ci_data.arn, - "${aws_s3_bucket.github_ci_data.arn}/*" + aws_s3_bucket.ci_data.arn, + "${aws_s3_bucket.ci_data.arn}/*" ] }, { diff --git a/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf b/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf index 31b2ffc3e..c12ec150d 100644 --- a/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf +++ b/terraform/account-wide-infrastructure/mgmt/iam_github-ci.tf @@ -90,8 +90,8 @@ resource "aws_iam_policy" "github_ci_policy" { ] Effect = "Allow" Resource = [ - aws_s3_bucket.github_ci_data.arn, - "${aws_s3_bucket.github_ci_data.arn}/*" + aws_s3_bucket.ci_data.arn, + "${aws_s3_bucket.ci_data.arn}/*" ] } ] diff --git a/terraform/account-wide-infrastructure/mgmt/s3.tf b/terraform/account-wide-infrastructure/mgmt/s3.tf index 1db60e182..1d7371418 100644 --- a/terraform/account-wide-infrastructure/mgmt/s3.tf +++ b/terraform/account-wide-infrastructure/mgmt/s3.tf @@ -1,18 +1,18 @@ -resource "aws_s3_bucket" "github_ci_data" { - bucket = "${local.prefix}--github-ci-data" +resource "aws_s3_bucket" "ci_data" { + bucket = "${local.prefix}--ci-data" } -resource "aws_s3_bucket_acl" "github_ci_data" { - bucket = aws_s3_bucket.github_ci_data.id +resource "aws_s3_bucket_acl" "ci_data" { + bucket = aws_s3_bucket.ci_data.id acl = "private" depends_on = [ - aws_s3_bucket.github_ci_data + aws_s3_bucket.ci_data ] } -resource "aws_s3_bucket_public_access_block" "github_ci_data" { - bucket = aws_s3_bucket.github_ci_data.id +resource "aws_s3_bucket_public_access_block" "ci_data" { + bucket = aws_s3_bucket.ci_data.id block_public_acls = true block_public_policy = true @@ -20,17 +20,17 @@ resource "aws_s3_bucket_public_access_block" "github_ci_data" { ignore_public_acls = true depends_on = [ - aws_s3_bucket.github_ci_data + aws_s3_bucket.ci_data ] } -resource "aws_s3_bucket_versioning" "github_ci_data" { - bucket = aws_s3_bucket.github_ci_data.id +resource "aws_s3_bucket_versioning" "ci_data" { + bucket = aws_s3_bucket.ci_data.id versioning_configuration { status = "Enabled" } depends_on = [ - aws_s3_bucket.github_ci_data + aws_s3_bucket.ci_data ] } From cff34d381e011511dedfd71f11f53b53c44cd3c0 Mon Sep 17 00:00:00 2001 From: Matt Dean Date: Tue, 10 Feb 2026 13:58:25 +0000 Subject: [PATCH 4/5] [NRL-1938] Add HTTPSOnly to policy for new ci-data bucket --- .../account-wide-infrastructure/mgmt/s3.tf | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/terraform/account-wide-infrastructure/mgmt/s3.tf b/terraform/account-wide-infrastructure/mgmt/s3.tf index 1d7371418..b3fd09fc4 100644 --- a/terraform/account-wide-infrastructure/mgmt/s3.tf +++ b/terraform/account-wide-infrastructure/mgmt/s3.tf @@ -34,3 +34,29 @@ resource "aws_s3_bucket_versioning" "ci_data" { aws_s3_bucket.ci_data ] } + +resource "aws_s3_bucket_policy" "ci_data" { + bucket = aws_s3_bucket.ci_data.id + + policy = jsonencode({ + Version = "2012-10-17" + Id = "${local.prefix}--ci-data-bucket-policy" + Statement = [ + { + Sid = "HTTPSOnly" + Effect = "Deny" + Principal = "*" + Action = "s3:*" + Resource = [ + aws_s3_bucket.ci_data.arn, + "${aws_s3_bucket.ci_data.arn}/*", + ] + Condition = { + Bool = { + "aws:SecureTransport" = "false" + } + } + }, + ] + }) +} From a10ce745968870a60947066be730f251f6316d9f Mon Sep 17 00:00:00 2001 From: Matt Dean Date: Fri, 13 Feb 2026 12:40:20 +0000 Subject: [PATCH 5/5] [NRL-1938] Add deployment version and caller id to TF outputs --- terraform/account-wide-infrastructure/mgmt/data.tf | 7 +++++++ terraform/account-wide-infrastructure/mgmt/outputs.tf | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 terraform/account-wide-infrastructure/mgmt/outputs.tf diff --git a/terraform/account-wide-infrastructure/mgmt/data.tf b/terraform/account-wide-infrastructure/mgmt/data.tf index 931dc9b2e..619199098 100644 --- a/terraform/account-wide-infrastructure/mgmt/data.tf +++ b/terraform/account-wide-infrastructure/mgmt/data.tf @@ -2,6 +2,13 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} +data "external" "current-info" { + program = [ + "bash", + "../../../scripts/get-current-info.sh", + ] +} + data "aws_dynamodb_table" "terraform_state_lock" { name = "${local.project}--terraform-state-lock" } diff --git a/terraform/account-wide-infrastructure/mgmt/outputs.tf b/terraform/account-wide-infrastructure/mgmt/outputs.tf new file mode 100644 index 000000000..86cbc87a1 --- /dev/null +++ b/terraform/account-wide-infrastructure/mgmt/outputs.tf @@ -0,0 +1,7 @@ +output "version" { + value = data.external.current-info.result.version +} + +output "caller_identity" { + value = data.aws_caller_identity.current +}