Search code examples
powershellazure-devopsazure-pipelinesazure-pipelines-yaml

Azure pipeline template: dynamically retrieve and story keyvault secrets


As a learning exercise, I'm trying to set up a template to retrieve azure keyvault secrets dynamically. The secrets need to be available between jobs:

parameters:
- name: secretsFilter
  type: string

jobs:
- job: kv
  displayName: Fetch keyvault variables
  pool: MAPLE-STAGING
  steps:
  - checkout: none
  - task: AzureKeyVault@2
    inputs:
      azureSubscription: 'x'
      KeyVaultName: 'y'
      SecretsFilter: ${{ parameters.secretsFilter }}

  - script: |
      secrets="${{ replace(parameters.secretsFilter, ',', ' ') }}"
      for secretName in $secrets; do
        echo "##vso[task.setvariable variable=$secretName;isoutput=true;issecret=false;isreadonly=true]$($secretName)"
      done
    name: kv
  - template: keyvault-practice.yml
    parameters:
      secretsFilter: 'some-user,some-password'

I've managed to split the string and iterate through the items. I'm still having trouble however with storing the item's value. Storing the variable without a loop would look similar to this:

echo "##vso[task.setvariable variable=some-user-name;isoutput=true;issecret=true;isreadonly=true]$(some-user-name)"

I think I'm incorrectly mixing up runtime bash variables with compile-time azure pipeline placeholders when I'm using $($secretName) in my loop, as I receive the message line 4: some-user-name: command not found. My other option would be to declare an array object

- name: secrets
  type: object:
  default: []

instead of a comma separated string and loop the items when rendering the script, instead of what I'm doing right now. However, I haven't figured out how to accomplish this yet.

Would anyone know how my setup can be modified in order to dynamically retrieve keyvault secrets?


Solution

  • Ok, so we answered this question yesterday, outlining different approaches for this.

    I want to address why your script doesn’t work. The KeyVault task pulls down the secrets and adds them as environment variables. However, tasks that use the azure-devops-pipeline-tasks api have logic to filter secrets out of the environment variables as a security precaution to prevent secrets from leaking into places where they weren’t intended. As such, you have to opt-in secrets into a task by passing them through the env argument of the task.

    As your script is dynamically defining the secretsFilter as a parameter, you can dynamic define the env argument.

     - script: |
         secrets="${{ replace(parameters.secretsFilter, ',', ' ') }}"
         for secretName in $secrets; do
            echo "##vso[task.setvariable variable=$secretName;isoutput=true;issecret=false;isreadonly=true]$($secretName)"
         done
       name: secretOutputs 
       displayName: Create output variables
       env:
         ${{ each secretName in secrets }}:
           ${{ secretName }}: $(${{ secretName }})