From d0ed433bec7cf1809a23f9d00d343194cd905e6c Mon Sep 17 00:00:00 2001 From: "laurence.barker@dvsa.gov.uk" Date: Mon, 22 Sep 2025 10:26:08 +0100 Subject: [PATCH 1/5] feat: add workflow that rotates client secrets in azure automatically --- .github/workflows/Azure-token-rotation.yaml | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/Azure-token-rotation.yaml diff --git a/.github/workflows/Azure-token-rotation.yaml b/.github/workflows/Azure-token-rotation.yaml new file mode 100644 index 0000000..d911994 --- /dev/null +++ b/.github/workflows/Azure-token-rotation.yaml @@ -0,0 +1,63 @@ +name: Rotate Azure Client Secret and Push to AWS + +on: + workflow_call: + inputs: + aws-secret-id: + description: "Name of the AWS secret containing Azure client credentials" + required: true + type: string + +jobs: + rotate-and-push: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Logs into Azure using OIDC, requires the secrets AZURE_OIDC_CLIENT_ID, AZURE_TENANT_ID and AZURE_SUBSCRIPTION_ID to be set in the repo secrets. Also requires an OIDC federated credential to be set up in the Azure AD App registration that requires Microsoft Graph API permissions (like Application.ReadWrite.All) to reset app credentials + - name: Azure login with OIDC + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_OIDC_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam:::role/ + aws-region: eu-west-1 + + # Need to ensure that secret-id maps to what is stored in AWS Secrets Manager, static for now. + - name: Retrieve clientId + id: get-clientid + run: | + CLIENT_ID=$(aws secretsmanager get-secret-value \ + --secret-id "${{ inputs.aws-secret-id }}" \ + --query SecretString \ + --output text | jq -r '.clientId') + + echo "::add-mask::$CLIENT_ID" + echo "CLIENT_ID=$CLIENT_ID" >> $GITHUB_ENV + + - name: Create new client secret + run: | + NEW_SECRET=$(az ad app credential reset \ + --id $CLIENT_ID \ + --append \ + --years 1 \ + --query password -o tsv) + + echo "::add-mask::$NEW_SECRET" + echo "AZURE_NEW_SECRET=$NEW_SECRET" >> $GITHUB_ENV + + - name: Update AWS secret + run: | + aws secretsmanager put-secret-value \ + --secret-id "${{ inputs.aws-secret-id }}" \ + --secret-string "{\"clientId\":\"$CLIENT_ID\",\"clientSecret\":\"$AZURE_NEW_SECRET\"}" From 9f946fc231435f377ac4886344e797969d6ca368 Mon Sep 17 00:00:00 2001 From: "laurence.barker@dvsa.gov.uk" Date: Thu, 9 Oct 2025 15:04:19 +0100 Subject: [PATCH 2/5] fix: improve imputs to make this more flexible on workflow call --- .github/workflows/Azure-token-rotation.yaml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Azure-token-rotation.yaml b/.github/workflows/Azure-token-rotation.yaml index d911994..58ee079 100644 --- a/.github/workflows/Azure-token-rotation.yaml +++ b/.github/workflows/Azure-token-rotation.yaml @@ -7,6 +7,19 @@ on: description: "Name of the AWS secret containing Azure client credentials" required: true type: string + aws-account-id: + description: "AWS Account ID where the secret is stored" + required: true + type: string + aws-github-actions-role: + description: "The role to assume in AWS to access Secrets Manager" + required: true + type: string + aws-region: + description: "AWS region where the secret is stored" + required: false + type: string + default: "eu-west-1" jobs: rotate-and-push: @@ -30,8 +43,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: - role-to-assume: arn:aws:iam:::role/ - aws-region: eu-west-1 + role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.aws-github-actions-role }} + aws-region: ${{ inputs.aws-region }} # Need to ensure that secret-id maps to what is stored in AWS Secrets Manager, static for now. - name: Retrieve clientId From 68120c029d5fef545ad9a3fe794a99781ea6c068 Mon Sep 17 00:00:00 2001 From: "laurence.barker@dvsa.gov.uk" Date: Thu, 9 Oct 2025 15:06:55 +0100 Subject: [PATCH 3/5] chore: remove checkout as it isn't needed --- .github/workflows/Azure-token-rotation.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/Azure-token-rotation.yaml b/.github/workflows/Azure-token-rotation.yaml index 58ee079..048ff4a 100644 --- a/.github/workflows/Azure-token-rotation.yaml +++ b/.github/workflows/Azure-token-rotation.yaml @@ -26,11 +26,8 @@ jobs: runs-on: ubuntu-latest permissions: id-token: write - contents: read steps: - - name: Checkout repository - uses: actions/checkout@v4 # Logs into Azure using OIDC, requires the secrets AZURE_OIDC_CLIENT_ID, AZURE_TENANT_ID and AZURE_SUBSCRIPTION_ID to be set in the repo secrets. Also requires an OIDC federated credential to be set up in the Azure AD App registration that requires Microsoft Graph API permissions (like Application.ReadWrite.All) to reset app credentials - name: Azure login with OIDC From c9bbdaa65dfb9de17fa04d3ed8b5e5d4f49f9b70 Mon Sep 17 00:00:00 2001 From: "laurence.barker@dvsa.gov.uk" Date: Thu, 9 Oct 2025 16:28:01 +0100 Subject: [PATCH 4/5] chore: add documentation to readme and amend secrets --- .github/workflows/Azure-token-rotation.yaml | 13 +++- README.md | 74 +++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Azure-token-rotation.yaml b/.github/workflows/Azure-token-rotation.yaml index 048ff4a..074e2b4 100644 --- a/.github/workflows/Azure-token-rotation.yaml +++ b/.github/workflows/Azure-token-rotation.yaml @@ -21,6 +21,17 @@ on: type: string default: "eu-west-1" + secrets: + AZURE_TENANT_ID: + description: "Azure Tenant ID" + required: true + AZURE_SUBSCRIPTION_ID: + description: "Azure Subscription ID" + required: true + AZURE_OIDC_CLIENT_ID: + description: "Azure AD App (client) ID for OIDC login" + required: true + jobs: rotate-and-push: runs-on: ubuntu-latest @@ -29,7 +40,7 @@ jobs: steps: - # Logs into Azure using OIDC, requires the secrets AZURE_OIDC_CLIENT_ID, AZURE_TENANT_ID and AZURE_SUBSCRIPTION_ID to be set in the repo secrets. Also requires an OIDC federated credential to be set up in the Azure AD App registration that requires Microsoft Graph API permissions (like Application.ReadWrite.All) to reset app credentials + # Logs into Azure using OIDC, requires the inputs azure-oidc-client-id, azure-tenant-id and azure-subscription-id to be set in the repo secrets. These are set as inputs to provide some flexibility for adoption. Also requires an OIDC federated credential to be set up in the Azure AD App registration that requires Microsoft Graph API permissions (like Application.ReadWrite.All) to reset app credentials - name: Azure login with OIDC uses: azure/login@v1 with: diff --git a/README.md b/README.md index b329f5c..764a445 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,80 @@ Action to provide ability to add/remove IP addresses from AWS WAF ACL [AWS-WAF Access](.github/actions/aws-waf-access/README.md) +## Azure-token-rotation + +### Background + +This workflow should be used to rotate azure client secrets stored in AWS secret manager for known azure client identities. The indended use case for this workflow to be called as part of a schedule to rotate the secrets in an automated manner. + +The workflow depends on an app registration which must has Microsoft Graph API permissions. This should be scoped to ensure that it is the specific repo, specific branches associated to the project and AWS secrets. There should also be a conditional statement that the OIDC role associated with the repo can only change the clientID's associated with the repo and project. A sample piece of code to call this as part of a different workflow can be found below. + + uses: dvsa/.github/.github/workflows/azure-token-rotation.yml@v5.0.10 + + # 1. Inputs (Non-Sensitive variables) + with: + aws-secret-id: var.secretid # required + aws-account-id: var.nonprodAccount # required + aws-github-actions-role: var.GitHubActionsRole # required + + # optional input, allows user to override region otherwise uses the default (eu-west-1) + aws-region: var.region + + # 2. Secrets (Sensitive, Automatically Masked) + secrets: + # These are passed from the caller repository's Secrets store. + # The key names here MUST match the names defined in the reusable workflow's 'secrets:' block. + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + azure-oidc-client-id: ${{ secrets.AZURE_OIDC_CLIENT_ID }} + +### Steps + + - Azure Login with OIDC (uses clientID, AzureTennantID and SubscriptionID to authenticate) + + - Configure AWS Credentials (uses AccountID and AWSGitHubRole to authenticate) + + - Retrieve clientID (uses known AWS secretID to retrieve clientID that will be used to rotate the Azure Client Secret) + + - Create new client secret (uses existing Azure OIDC to create a new client Secret) + + - Update AWS secret (updates known AWS secret with the new secret value) + + +### Secrets +The below secrets will need to be added as GitHub secrets in the appropriate repository but will also need to pass the appropriate secrets using the secrets keyword when calling the workflow. + + AZURE_TENANT_ID: + description: "Azure Tenant ID" + required: true + AZURE_SUBSCRIPTION_ID: + description: "Azure Subscription ID" + required: true + AZURE_OIDC_CLIENT_ID: + description: "Azure AD App (client) ID for OIDC login" + required: true + +### Inputs + + aws-secret-id: + description: "Name of the AWS secret containing Azure client credentials" + required: true + type: string + aws-account-id: + description: "AWS Account ID where the secret is stored" + required: true + type: string + aws-github-actions-role: + description: "The role to assume in AWS to access Secrets Manager" + required: true + type: string + aws-region: + description: "AWS region where the secret is stored" + required: false + type: string + default: "eu-west-1" + + ## gatling-job-summary Action to provide ability to get the summary of a Gatling job From db3fa27b32823d2e8cc46f88efd58c9108c7a585 Mon Sep 17 00:00:00 2001 From: "laurence.barker@dvsa.gov.uk" Date: Thu, 9 Oct 2025 16:33:56 +0100 Subject: [PATCH 5/5] chore: amend name --- .github/workflows/Azure-token-rotation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Azure-token-rotation.yaml b/.github/workflows/Azure-token-rotation.yaml index 074e2b4..41d6f4d 100644 --- a/.github/workflows/Azure-token-rotation.yaml +++ b/.github/workflows/Azure-token-rotation.yaml @@ -1,4 +1,4 @@ -name: Rotate Azure Client Secret and Push to AWS +name: Rotate Azure Client Secret and Push to AWS Secrets Manager on: workflow_call: