GitHub Actions Azure Integration

I recently had some time to play around with GitHub Actions again, and so I wanted to revisit some of the topics I wrote about in my last post. I recommend reading that first, and checking out the workflow file in my repo.

Secrets Management

If we look back at my workflow, it had a job called publish-image which would login to my Azure Container Registry, and push my container image. It looked like this:

publish-image:
  
    needs: build-web
    runs-on: ubuntu-16.04
    
    steps:
    - uses: actions/checkout@master
    - name: Publish to Registry
      uses: elgohr/Publish-Docker-Github-Action@master
      with:
        name: mec35.azurecr.io/eshopwebmvc
        username: ${{ secrets.ACR_USER }}
        password: ${{ secrets.ACR_PASSWORD }}
        registry: ${{ secrets.ACR_SERVER }}
        dockerfile: ./src/Web/Dockerfile

You can see that I am referencing secrets that are set in my GitHub repo, and are retrievable by any Action. I wanted to see what the integration would be like with Azure Key Vault instead - particularly useful for teams who are already using Key Vault for secrets management. What I love about it is the separation of the management plane and the data plane. I can grant access to the management of my key vault using RBAC, and I can use access policies to control permissions on the secrets themselves.

For this workflow, I first created a service principal. This is necessary, as I need it to authenticate to my Key Vault and read my secrets.

az ad sp create-for-rbac --name "eShopSP" --role contributor \
                            --scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} \
                            --sdk-auth

This will output JSON which you will need in a moment:

{
  "clientId": "*************************************",
  "clientSecret": "***********************************",
  "subscriptionId": "************************************",
  "tenantId": "************************************",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

I created a secret in GitHub called AZURE_CREDENTIALS and pasted all of the above into it. I also created a secret to contain my Key Vault name. This isn’t necessary, but why not?!

Next, I created a Key Vault and added my secrets (ACR password, username and registry) to it. I then added an access policy that allowed my service principal to get and list secrets:

az keyvault set-policy -n <keyvault-name> --secret-permissions get list --spn <clientId>

I needed to update my workflow to firstly login to Azure, and then retrieve my secrets. I am using two new Actions for this:

Azure Login Azure Key Vault

I modified my publish-image job to login to Azure - all I did is use the creds I saved earlier as a GitHub secret:

  publish-image: 
    needs: build-web
    runs-on: ubuntu-16.04
    steps:
    - uses: actions/checkout@v1
    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}

Next, I added the Key Vault action. I also added an id called FetchSecrets to this step, as I will need it later. I specify my Key Vault name by referencing the GitHub secret that contains the name, and I use an asterix to get all secrets. I could instead pass in specific secret names, but as my vault only contains secrets that I need for this pipeline, it’s fine.

    - name: Azure Key Vault Action
      uses: Azure/get-keyvault-secrets@v1.0
      id: FetchSecrets
      with:
        keyvault: ${{ secrets.KEYVAULT_NAME }}
        secrets: '*'

What happens after this is that the Key Vault action will get secrets and they will be set as both environment variables, and as outputs of the action. This means we can pass them into other actions in the workflow.

The last thing I need to do here is update the final step. This is really easy; simply reference the Key Vault outputs instead of the GitHub secrets (e.g. ${{ steps.StepId.outputs.mySecret }}) where StepId is FetchSecrets, and mySecret is the name of the secret set in Key Vault.

    - name: Publish to Registry
      uses: elgohr/Publish-Docker-Github-Action@master
      with:
        name: eshopacrmec.azurecr.io/eshopwebmvc
        username: ${{ steps.FetchSecrets.outputs.AcrUser }}
        password: ${{ steps.FetchSecrets.outputs.AcrPassword }}
        registry: ${{ steps.FetchSecrets.outputs.AcrServer }}
        dockerfile: ./src/Web/Dockerfile

That’s it - I now have Key Vault secret integration with my GitHub Actions workflow!

Bonus - Azure Pipelines integration

I’m so pleased this exists - there is now an Action to kick off an Azure DevOps Pipeline! Previously I was using a Logic App, and then I decided to curl the Azure DevOps REST API instead, which worked fine, but I am all about simplicity in workflows. So here it is - I simply insert the action and specify my pipeline name, and my Azure DevOps PAT (personal access token):

Azure Pipelines Action

  create-new-release:
    needs: [build-web, publish-image]
    runs-on: ubuntu-latest
    steps:
    - name: Azure Pipelines Action
      uses: Azure/pipelines@v1
      with:
        azure-devops-project-url: https://dev.azure.com/charlenem/githubactions
        azure-pipeline-name: Deploy image to Web App for Containers
        azure-devops-token: ${{ secrets.AZURE_DEVOPS_TOKEN }}

Now, whenever my workflow successfully builds my application and pushes it to my container registry (and only when both of those succeed), it will kick off a release pipeline in Azure DevOps to deploy that container image to my web app.


See Also

comments powered by Disqus