From dff409bfa595a35e955e6a8a0dac78ef4a5512ed Mon Sep 17 00:00:00 2001 From: jolyonbrownnhs Date: Thu, 5 Mar 2026 17:33:15 +0000 Subject: [PATCH 1/2] HCW-315 Add publish spec to Bloomreach UAT portal workflow - Add __VERSION__ placeholder to spec version field - Add sed version stamping to apim_instance_deploy.sh for CodeBuild deploys - Add GitHub Actions workflow_dispatch workflow for manual UAT publish - Add GitHub OIDC provider and IAM role in Terraform (dev account only) - Add proxygen-cli config templates for GitHub Actions --- .github/proxygen-credentials-template.yaml | 4 ++ .github/proxygen-settings.yaml | 2 + .github/workflows/publish-spec-uat.yml | 60 ++++++++++++++++++ .../modules/hcw-api/apim_instance_deploy.sh | 4 ++ infrastructure/modules/hcw-api/github_oidc.tf | 63 +++++++++++++++++++ specification/healthcare-worker-api.yaml | 2 +- 6 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 .github/proxygen-credentials-template.yaml create mode 100644 .github/proxygen-settings.yaml create mode 100644 .github/workflows/publish-spec-uat.yml create mode 100644 infrastructure/modules/hcw-api/github_oidc.tf diff --git a/.github/proxygen-credentials-template.yaml b/.github/proxygen-credentials-template.yaml new file mode 100644 index 00000000..465478b6 --- /dev/null +++ b/.github/proxygen-credentials-template.yaml @@ -0,0 +1,4 @@ +client_id: healthcare-worker-api-client +key_id: key-1 +private_key_path: key +base_url: https://identity.prod.api.platform.nhs.uk/realms/api-producers diff --git a/.github/proxygen-settings.yaml b/.github/proxygen-settings.yaml new file mode 100644 index 00000000..1c585e37 --- /dev/null +++ b/.github/proxygen-settings.yaml @@ -0,0 +1,2 @@ +endpoint_url: https://proxygen.prod.api.platform.nhs.uk +api: healthcare-worker-api diff --git a/.github/workflows/publish-spec-uat.yml b/.github/workflows/publish-spec-uat.yml new file mode 100644 index 00000000..52a7211d --- /dev/null +++ b/.github/workflows/publish-spec-uat.yml @@ -0,0 +1,60 @@ +name: "Publish Spec to Bloomreach UAT Portal" +run-name: Publish ${{ github.ref_name }} to UAT by @${{ github.actor }} + +on: workflow_dispatch + +jobs: + publish: + name: "Publish spec to UAT" + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + timeout-minutes: 5 + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Set OAS info.version to ${{ github.ref_name }}-${{ github.sha }}" + run: | + sed -i 's/__VERSION__/${{ github.ref_name }}-${{ github.sha }}/g' specification/healthcare-worker-api.yaml + + - name: "Lint OpenAPI specification" + run: | + npx -y @redocly/cli bundle \ + specification/healthcare-worker-api.yaml \ + -o specification/healthcare-worker-api.resolved.json + npx -y @redocly/cli lint --config redocly.yaml + + - name: "Setup proxygen-cli" + run: | + pipx install proxygen-cli + pipx upgrade proxygen-cli + mkdir -p ${HOME}/.proxygen + cp ./.github/proxygen-credentials-template.yaml ${HOME}/.proxygen/credentials.yaml + cp ./.github/proxygen-settings.yaml ${HOME}/.proxygen/settings.yaml + + - name: "Configure AWS credentials" + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.DEV_AWS_ACCOUNT_ID }}:role/GitHubActionsPublishSpecRole + aws-region: eu-west-2 + + - name: "Fetch proxygen private key from AWS Secrets Manager" + run: | + aws secretsmanager get-secret-value \ + --secret-id "apim-deploy-private-key" \ + --query SecretString \ + --output text > ${HOME}/.proxygen/key + proxygen status + env: + AWS_DEFAULT_REGION: eu-west-2 + + - name: "Publish spec to UAT" + run: | + proxygen spec publish specification/healthcare-worker-api.yaml --uat --no-confirm + + - name: "Instructions for viewing UAT Specification" + run: | + echo "View UAT spec changes at https://uat2.nhsd.io/developer/api-catalogue/healthcare-worker-api" + echo "Remember Bloomreach pulls UAT changes every 5-10 minutes so changes may not appear right away. You must be connected to the VPN." diff --git a/infrastructure/modules/hcw-api/apim_instance_deploy.sh b/infrastructure/modules/hcw-api/apim_instance_deploy.sh index d684110e..4bb96444 100755 --- a/infrastructure/modules/hcw-api/apim_instance_deploy.sh +++ b/infrastructure/modules/hcw-api/apim_instance_deploy.sh @@ -9,6 +9,10 @@ api_gw_url=$4 cp ../specification/healthcare-worker-api.yaml temp_spec.yaml +# Stamp version into spec (replace placeholder with short commit SHA) +short_sha=${CODEBUILD_RESOLVED_SOURCE_VERSION:0:8} +sed -i "s/__VERSION__/${short_sha}/g" temp_spec.yaml + yq -i ".x-nhsd-apim.target.url = \"${api_gw_url}\"" temp_spec.yaml if [[ "$environment_name" == pr-* ]]; then uppercase_env_name=$(echo "$environment_name" | tr '[:lower:]' '[:upper:]') diff --git a/infrastructure/modules/hcw-api/github_oidc.tf b/infrastructure/modules/hcw-api/github_oidc.tf new file mode 100644 index 00000000..0ffbf3ec --- /dev/null +++ b/infrastructure/modules/hcw-api/github_oidc.tf @@ -0,0 +1,63 @@ +# GitHub Actions OIDC - allows the "Publish Spec to UAT" workflow to assume a role +# and read the proxygen private key from Secrets Manager. +# Only created in ft (dev account) since UAT publishing is done from there. + +resource "aws_iam_openid_connect_provider" "github_actions" { + count = var.env == "ft" ? 1 : 0 + + url = "https://token.actions.githubusercontent.com" + + client_id_list = ["sts.amazonaws.com"] + + # GitHub's OIDC thumbprint - this is a well-known value + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"] +} + +resource "aws_iam_role" "github_actions_publish_spec" { + count = var.env == "ft" ? 1 : 0 + + name = "GitHubActionsPublishSpecRole" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Federated = aws_iam_openid_connect_provider.github_actions[0].arn + } + Action = "sts:AssumeRoleWithWebIdentity" + Condition = { + StringEquals = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + } + StringLike = { + "token.actions.githubusercontent.com:sub" = "repo:NHSDigital/healthcare-worker-api:*" + } + } + } + ] + }) +} + +resource "aws_iam_role_policy" "github_actions_read_proxygen_key" { + count = var.env == "ft" ? 1 : 0 + + name = "ReadProxygenPrivateKey" + role = aws_iam_role.github_actions_publish_spec[0].id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "secretsmanager:GetSecretValue" + ] + Resource = [ + data.aws_secretsmanager_secret.apim_account_private_key.arn + ] + } + ] + }) +} diff --git a/specification/healthcare-worker-api.yaml b/specification/healthcare-worker-api.yaml index de74ff64..f4203c1d 100644 --- a/specification/healthcare-worker-api.yaml +++ b/specification/healthcare-worker-api.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: v0.1 + version: __VERSION__ title: Healthcare Worker API description: | ## Overview From d8f3feafc76e5734acea108a8bfdfe564e208621 Mon Sep 17 00:00:00 2001 From: jolyonbrownnhs Date: Fri, 6 Mar 2026 09:13:52 +0000 Subject: [PATCH 2/2] HCW-315 pin GitHub Actions dependencies to full commit SHAs --- .github/workflows/publish-spec-uat.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-spec-uat.yml b/.github/workflows/publish-spec-uat.yml index 52a7211d..1a50d5f2 100644 --- a/.github/workflows/publish-spec-uat.yml +++ b/.github/workflows/publish-spec-uat.yml @@ -13,7 +13,7 @@ jobs: timeout-minutes: 5 steps: - name: "Checkout code" - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: "Set OAS info.version to ${{ github.ref_name }}-${{ github.sha }}" run: | @@ -35,7 +35,7 @@ jobs: cp ./.github/proxygen-settings.yaml ${HOME}/.proxygen/settings.yaml - name: "Configure AWS credentials" - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@90a7a1fb1ee4e8d6a99f60d2fd22287e1657e51e # v2 with: role-to-assume: arn:aws:iam::${{ secrets.DEV_AWS_ACCOUNT_ID }}:role/GitHubActionsPublishSpecRole aws-region: eu-west-2