Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/workflows/Azure-token-rotation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Rotate Azure Client Secret and Push to AWS Secrets Manager

on:
workflow_call:
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"

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
permissions:
id-token: write

steps:

# 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:
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::${{ 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
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\"}"
Comment on lines +81 to +84
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow looks good to me, the only query I have is re this last bit.

Would it make sense that AZURE_NEW_SECRET is an output to this workflow instead of updating in place?

Currently, for example, the equivalent CVS secret has more detail that just clientId & clientSecret in the JSON, therefore this would overwrite those extra values.

The other option would be we retrieve the secret as is and merge the two objects together, which will update these keys but also retain the other contents?

74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down