JSON Variable Substitution in Multi-Stage YAML Pipeline with Azure Key Vault

In my last post I showed how to configure JSON variable substitution with secret variables right in the pipeline. Now in this follow up post I want to expand on that to show how you could instead store your secrets in an Azure Key Vault and reference them in the pipeline. See the publicly visible TimeApi project on Azure DevOps for the source code, ARM templates, and the YAML file.

How do we go about creating the key vault? Should we create a single global vault and put everything in there? You could. But then it’s hard to get security right because there are production secrets in there. Operations has to lock the whole vault down. Also, you end up with names like my-secret-dev,  my-secret-sbx and my-secret-prd. Then in your YAML file you have to remember these names and not introduce a copy-paste bug like:

- stage: Development

      jobs:
        - deployment: Development
          displayName: Development
          environment: Development

          variables:
            - name: my-secret-prd  # epic fail...

So the best practice is to create a key vault for each environment and configure security accordingly. Developers should have the Key, Secret, and Certificate Management access policy  in lower environments but just Get and List in the production key vault.

In my TimeApi solution from the last post I have two resource groups, one for development and one for sandbox testing:

Inside each resource group is a key vault for that environment. Here are the resources under services-dev-rg:

In the services-sbx-rg there is another key vault named widget-services-sbx-kv. I’ve added these two secrets to each vault. They have different values but the names are identical and do not betray knowledge of the environment:

You can’t create variable groups in YAML (or at least not as of this writing). So I have to go to the Azure DevOps portal to create them and link the secrets to the key vault. This is pretty routine and I’ll assume you are familiar with this task. Here is my development resource group where my project service connection has Get and List rights on the linked key vault:

All that’s left now is to modify the YAML file. The syntax for using a variable group in YAML is:

variables:
  - group: variable-group-name-goes-here
  - name: name-of-variable-goes-here
    value: $(macro-syntax-name-in-key-vault)

I have two stages, two key vaults, and two variable groups. I want to substitute two secrets in my appsettings.json file. So my YAML looks like this:

# -----------------------------------------
# TimeApi
# -----------------------------------------

  trigger:
    - master

  pool:
    vmImage: 'ubuntu-latest'

  stages:
    - stage: Build

      jobs:
      - job: Build

        steps:
      
          # tasks go here

    - stage: Development

      jobs:
        - deployment: Development
          displayName: Development
          environment: Development

          variables:
            - group: timeapi-development-variable-group
            - name: ConnectionStrings.DefaultConnection
              value: $(timeapi-connection-string)
            - name: Secret
              value: $(timeapi-secret)

          strategy:
            runOnce:
                deploy:
                    steps:

                    # tasks go here

                            
    - stage: Sandbox
      dependsOn: Development

      jobs:
         - deployment: Sandbox
           displayName: Sandbox
           environment: Sandbox

           variables:
            - group: timeapi-sandbox-variable-group
            - name: ConnectionStrings.DefaultConnection
              value: $(timeapi-connection-string)
            - name: Secret
              value: $(timeapi-secret)

           strategy:
             runOnce:
                deploy:
                    steps:

                    # tasks go here

The variable group name must match the name you gave it in the Azure DevOps portal. And the variable name must match the name in your appsettings.json file for the substitution to occur. The variable value uses the standard macro syntax $() to reference the linked secret name in the key vault. With this in place your CI/CD pipeline will produce the correct results for each environment. Here is my appsettings.json file after the deploy:

 

One thought on “JSON Variable Substitution in Multi-Stage YAML Pipeline with Azure Key Vault”

Comments are closed.