From 2e5af4dcb1c6e47e7a37867bee81b68e9a74c952 Mon Sep 17 00:00:00 2001 From: Hadi Qureshi Date: Wed, 20 May 2026 17:30:20 +0100 Subject: [PATCH 1/4] feat: service IAM role assumable via SSO --- terraform/service/main.tf | 70 ++++++++---------------------------- terraform/service/secrets.tf | 14 -------- 2 files changed, 14 insertions(+), 70 deletions(-) delete mode 100644 terraform/service/secrets.tf diff --git a/terraform/service/main.tf b/terraform/service/main.tf index e7205dc..f43df53 100644 --- a/terraform/service/main.tf +++ b/terraform/service/main.tf @@ -73,6 +73,20 @@ resource "aws_iam_role" "lambda_function_role" { Principal = { Service = "lambda.amazonaws.com" } + }, + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + AWS = "arn:aws:iam::${var.aws_account_id}:root" + } + Condition = { + ArnLike = { + "aws:PrincipalArn" = [ + "arn:aws:iam::${var.aws_account_id}:role/aws-reserved/sso.amazonaws.com/eu-west-2/AWSReservedSSO_Standard_Administrator_Access_*" + ] + } + } } ] }) @@ -138,59 +152,3 @@ resource "aws_cloudwatch_log_group" "loggroup" { name = "/aws/lambda/${aws_lambda_function.lambda_function.function_name}" retention_in_days = var.log_retention_days } - -# IAM User Group -resource "aws_iam_group" "group" { - name = "${var.env_name}-${var.lambda_name}-user-group" - path = "/" -} - -resource "aws_iam_group_policy_attachment" "group_vpc_permissions_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.vpc_permissions.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_logging_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_logging.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_s3_policy_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_s3_policy.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_secret_manager_policy_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_secret_manager_policy.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_eventbridge_policy_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_eventbridge_policy.arn -} - -# IAM User -resource "aws_iam_user" "user" { - name = "${var.env_name}-${var.lambda_name}" - path = "/" -} - -# Assign IAM User to group -resource "aws_iam_user_group_membership" "user_group_attach" { - user = aws_iam_user.user.name - - groups = [ - aws_iam_group.group.name - ] -} - -# IAM Key Rotation Module -module "iam_key_rotation" { - source = "git::https://github.com/ONS-Innovation/keh-aws-iam-key-rotation.git?ref=v0.1.1" - - iam_username = aws_iam_user.user.name - access_key_secret_arn = aws_secretsmanager_secret.access_key.arn - secret_key_secret_arn = aws_secretsmanager_secret.secret_key.arn - rotation_in_days = 45 -} diff --git a/terraform/service/secrets.tf b/terraform/service/secrets.tf deleted file mode 100644 index 32fd11d..0000000 --- a/terraform/service/secrets.tf +++ /dev/null @@ -1,14 +0,0 @@ -# Secrets for rotated IAM user access keys -resource "aws_secretsmanager_secret" "access_key" { - name = "${var.env_name}-${var.lambda_name}-access-key" - description = "Access Key ID for archive script IAM user" - recovery_window_in_days = 0 // Secret will be deleted immediately - force_overwrite_replica_secret = true // Allow overwriting the secret in case of changes -} - -resource "aws_secretsmanager_secret" "secret_key" { - name = "${var.env_name}-${var.lambda_name}-secret-key" - description = "Secret Access Key for archive script IAM user" - recovery_window_in_days = 0 // Secret will be deleted immediately - force_overwrite_replica_secret = true // Allow overwriting the secret in case of changes -} From 533ea5dded09342b5628bada0e31a411e5178e8d Mon Sep 17 00:00:00 2001 From: Hadi Qureshi Date: Thu, 21 May 2026 12:32:35 +0100 Subject: [PATCH 2/4] chore: update README --- README.md | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index cc39aba..4b6659b 100644 --- a/README.md +++ b/README.md @@ -128,18 +128,26 @@ Before the doing the following, make sure your Daemon is running. If using Colim github-repository-archive-script latest b4a1e32ce51b 12 minutes ago 840MB ``` -3. Run the image. +3. Sign in with AWS SSO: + + ```bash + aws sso login + ``` + + **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. This is essential as the `~/.aws` directory is mounted to the container, so it can use the SSO session for AWS authentication. + +4. Run the image. ```bash docker run --platform linux/amd64 -p 9000:8080 \ - -e AWS_ACCESS_KEY_ID= \ - -e AWS_SECRET_ACCESS_KEY= \ - -e AWS_DEFAULT_REGION= \ + -v ~/.aws:/root/.aws \ + -e AWS_PROFILE=github-repository-archive-script \ + -e AWS_DEFAULT_REGION=eu-west-2 \ -e AWS_SECRET_NAME= \ -e GITHUB_ORG= \ -e GITHUB_APP_CLIENT_ID= \ - -e S3_BUCKET_NAME=\ - -e AWS_LAMBDA_FUNCTION_TIMEOUT=300 + -e S3_BUCKET_NAME= \ + -e AWS_LAMBDA_FUNCTION_TIMEOUT=300 \ github-repository-archive-script ``` @@ -156,7 +164,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim Once the container is running, a local endpoint is created at `localhost:9000/2015-03-31/functions/function/invocations`. -4. Check the container is running (Optional). +5. Check the container is running (Optional). ```bash docker ps @@ -169,7 +177,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim ca890d30e24d github-repository-archive-script "/lambda-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:9000->8080/tcp, :::9000->8080/tcp recursing_bartik ``` -5. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). +6. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). ```bash curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' @@ -177,7 +185,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim This will run the Lambda function and, once complete, will return a success message. -6. After testing stop the container. +7. After testing stop the container. ```bash docker stop @@ -198,11 +206,21 @@ To run the Lambda function outside of a container, we need to execute the `handl **Please Note:** If uncommenting the above in `main.py`, make sure you re-comment the code _before_ pushing back to GitHub. -2. Export the required environment variables: +2. Sign in with AWS SSO, and export the correct profile for this service: + + ```bash + aws sso login + + export AWS_PROFILE=github-repository-archive-script + ``` + + This allows you to assume the AWS IAM role for the service, enabling the most secure development experience. This also means you will have limited permissions until you exit out of the profile. + + **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. + +3. Export the required environment variables: ```bash - export AWS_ACCESS_KEY_ID= - export AWS_SECRET_ACCESS_KEY= export AWS_DEFAULT_REGION=eu-west-2 export AWS_SECRET_NAME= export S3_BUCKET_NAME= @@ -212,7 +230,7 @@ To run the Lambda function outside of a container, we need to execute the `handl An explanation of each variable is available within the [containerised instructions](#containerised-recommended). -3. Run the script. +4. Run the script. ```bash python3 src/main.py From 44d818aeac1be6d19db05590e48f7ed56b22f0f0 Mon Sep 17 00:00:00 2001 From: Hadi Qureshi Date: Thu, 21 May 2026 14:53:26 +0100 Subject: [PATCH 3/4] chore: add markdown lint fix script and run it --- Makefile | 7 ++++++- README.md | 8 ++++---- poetry.lock | 12 ++++++------ shell_scripts/md_fix.sh | 3 +++ 4 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 shell_scripts/md_fix.sh diff --git a/Makefile b/Makefile index 7d7c2e6..f934ec7 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,15 @@ format: ## Format the code. poetry run black src poetry run ruff check src --fix +.PHONY: md-fix +md-fix: ## Run markdown linting with Markdownlint and fix issues. + sh ./shell_scripts/md_fix.sh + .PHONY: lint -lint: ## Run all linters (black/ruff/pylint/mypy). +lint: ## Run all linters (black/ruff/pylint/mypy/markdownlint). poetry run black --check src poetry run ruff check src + make md-fix make mypy .PHONY: test diff --git a/README.md b/README.md index 4b6659b..97e5288 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. This is essential as the `~/.aws` directory is mounted to the container, so it can use the SSO session for AWS authentication. -4. Run the image. +1. Run the image. ```bash docker run --platform linux/amd64 -p 9000:8080 \ @@ -164,7 +164,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim Once the container is running, a local endpoint is created at `localhost:9000/2015-03-31/functions/function/invocations`. -5. Check the container is running (Optional). +2. Check the container is running (Optional). ```bash docker ps @@ -177,7 +177,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim ca890d30e24d github-repository-archive-script "/lambda-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:9000->8080/tcp, :::9000->8080/tcp recursing_bartik ``` -6. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). +3. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). ```bash curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' @@ -185,7 +185,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim This will run the Lambda function and, once complete, will return a success message. -7. After testing stop the container. +4. After testing stop the container. ```bash docker stop diff --git a/poetry.lock b/poetry.lock index dc7a0a1..65662ae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "astroid" @@ -397,7 +397,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} +markers = {dev = "sys_platform == \"win32\" or platform_system == \"Windows\""} [[package]] name = "coverage" @@ -707,18 +707,18 @@ pypi = ["pip (>=24.0)", "platformdirs (>=4.2)", "wheel (>=0.42)"] [[package]] name = "idna" -version = "3.11" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["main", "docs"] files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, + {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, + {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "iniconfig" diff --git a/shell_scripts/md_fix.sh b/shell_scripts/md_fix.sh new file mode 100644 index 0000000..e8359b2 --- /dev/null +++ b/shell_scripts/md_fix.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run -v "$PWD:/workdir" ghcr.io/igorshubovych/markdownlint-cli:latest "**/*.md" --fix From 01c0b52a4902d26cd3bf80076cd1b24e1223f9d8 Mon Sep 17 00:00:00 2001 From: Hadi Qureshi Date: Thu, 21 May 2026 15:01:07 +0100 Subject: [PATCH 4/4] chore: resolve further markdown lint errors --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 97e5288..e87969b 100644 --- a/README.md +++ b/README.md @@ -130,13 +130,13 @@ Before the doing the following, make sure your Daemon is running. If using Colim 3. Sign in with AWS SSO: - ```bash - aws sso login - ``` + ```bash + aws sso login + ``` - **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. This is essential as the `~/.aws` directory is mounted to the container, so it can use the SSO session for AWS authentication. + **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. This is essential as the `~/.aws` directory is mounted to the container, so it can use the SSO session for AWS authentication. -1. Run the image. +4. Run the image. ```bash docker run --platform linux/amd64 -p 9000:8080 \ @@ -164,7 +164,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim Once the container is running, a local endpoint is created at `localhost:9000/2015-03-31/functions/function/invocations`. -2. Check the container is running (Optional). +5. Check the container is running (Optional). ```bash docker ps @@ -177,7 +177,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim ca890d30e24d github-repository-archive-script "/lambda-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:9000->8080/tcp, :::9000->8080/tcp recursing_bartik ``` -3. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). +6. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). ```bash curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' @@ -185,7 +185,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim This will run the Lambda function and, once complete, will return a success message. -4. After testing stop the container. +7. After testing stop the container. ```bash docker stop @@ -208,15 +208,15 @@ To run the Lambda function outside of a container, we need to execute the `handl 2. Sign in with AWS SSO, and export the correct profile for this service: - ```bash - aws sso login + ```bash + aws sso login - export AWS_PROFILE=github-repository-archive-script - ``` + export AWS_PROFILE=github-repository-archive-script + ``` - This allows you to assume the AWS IAM role for the service, enabling the most secure development experience. This also means you will have limited permissions until you exit out of the profile. + This allows you to assume the AWS IAM role for the service, enabling the most secure development experience. This also means you will have limited permissions until you exit out of the profile. - **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. + **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. 3. Export the required environment variables: