<p>I have a few places, where I need to define a set of K8s secrets during deployment at various stages, so I want to extract the recurring script into a template:</p>
<pre><code>parameters:
  - name: secretName
    type: string
    default: &quot;&quot;
  - name: secrets
    type: object
    default:
      Foo: Bar

steps:
  - task: Bash@3
    displayName: Create generic secret ${{ parameters.secretName }}
    inputs:
      targetType: inline
      script: |
        echo &quot;Creating generic secret ${{ parameters.secretName }}&quot;
        microk8s kubectl delete secret ${{ parameters.secretName }}
        microk8s kubectl create secret generic ${{ parameters.secretName }} ${{ each secret in parameters.secrets }}: --from-literal=${{ secretKey }}=&quot;${{ secret.value }}&quot; 
</code></pre>
<p>I want to call it like this multiple times, to create all neccessary secrets for the deployment to each stage</p>
<pre class="lang-yaml prettyprint-override"><code>- job: CreateSecrets
  pool:
    name: $(poolName)
  steps:
    - template: &quot;Templates/template-create-secret.yml&quot;
      parameters:
        secretName: &quot;testSecret&quot;
        secrets:
          username: $(staging-user)
          password: $(staging-password)
          foo: $(bar)
</code></pre>
<p>And it should simply execute a scriptn similar to this one:</p>
<pre class="lang-sh prettyprint-override"><code>kubectl create secret generic secretName \
  -- from-literal=username=user1 \
  -- from-literal=password=pass1 \
  -- ...etc 

</code></pre>
<p>With my current approach I am receiving the error:</p>
<blockquote>
<p>/Code/BuildScripts/Templates/template-create-secret.yml (Line: 18,
Col: 15): The directive 'each' is not allowed in this context.
Directives are not supported for expressions that are embedded within
a string. Directives are only supported when the entire value is an
expression.</p>
</blockquote>
<p>How is it possible to iterate over a parameter of type object and use its key and value to build a string for bash? The alternative would be to simply use a single key-value-pair per secret and create multiple secrets, which I'd like to avoid</p>

Marco
<p>If you concatenate the command arguments into a variable you can use that in a future step/ task. This example will concatenate all secrets within the key vaults indicated in the keyVaultSecretSources parameter into one command. It shouldn't be too hard to adjust so you can specify which secrets you'd like to include/ exclude:</p>
<pre><code>  - name: environment
    type: string
  - name: namespace
    type: string
  - name: releaseName
    type: string
# contains an array of Azure key vault names
  - name: keyVaultSecretSources
    type: object

stages:
- stage: MountSecrets${{ parameters.releaseName }}
  pool: [Your k8s Pool]
  displayName: Mount Key Vault Secrets ${{ parameters.releaseName }}
# Then key vault arguments will be concatenated into the stage variable secretArgs
  variables:
    secretArgs: &quot;&quot;
  jobs:
- deployment: [Your Job Deployment Name]
  displayName: [Your Job Display Name]
  strategy:
    runOnce:
      deploy:
        steps:
# skip artifacts download for this stage
          - download: none
          - ${{ each keyVault in parameters.keyVaultSecretSources }}:    
# 1. obtain all secrets from keyVault.name key vault
# 2. remove all Json formatting, left with each secret name on one line
# 3. assign to local variable secretNameArray as an array
# 4. loop through secretNameArray and assign the secret to local variable kvs          
# 5. construct the argument --from-literal and append to local variable mountCommand
# 6. append mountCommand to the stage variable secretArgs
            - task: AzureCLI@2
              displayName: 'Concatenate Keyvault Secrets'                                        
              inputs:
                azureSubscription: [Your subscription]
                scriptType: 'bash'
                failOnStandardError: true
                scriptLocation: 'inlineScript'                
                inlineScript: |
                    secretNameArray=($(az keyvault secret list --vault-name ${{ keyVault.name }} --query &quot;[].name&quot; | tr -d '[:space:][]&quot;' | sed -r 's/,+/ /g'));
                    for i in &quot;${secretNameArray[@]}&quot;; do kvs=&quot;$(az keyvault secret show --vault-name ${{ keyVault.name }} --name &quot;$i&quot; --query &quot;value&quot; -o tsv)&quot;; mountCommand=&quot;$mountCommand --from-literal=$(echo -e &quot;$i&quot; | sed -r 's/-/_/g')='$kvs'&quot;; done;
                    echo &quot;##vso[task.setvariable variable=secretArgs;issecret=true]$(secretArgs)$mountCommand&quot;
          - task: Kubernetes@1
            displayName: 'Kubectl Login'
            inputs:
              kubernetesServiceEndpoint: [Your Service Connection Name]
              command: login
              namespace: ${{ parameters.namespace }}
          - task: AzureCLI@2
            displayName: 'Delete Secrets'
            inputs:
              azureSubscription: [Your subscription]
              scriptType: 'bash'
              failOnStandardError: false
              scriptLocation: 'inlineScript'
              inlineScript: |
                  kubectl delete secret ${{ parameters.releaseName }}-keyvault -n '${{ parameters.namespace}}'
                  exit 0
          - task: AzureCLI@2
            displayName: 'Mount Secrets'
            inputs:
              azureSubscription: [Your subscription]
              scriptType: 'bash'
              failOnStandardError: false
              scriptLocation: 'inlineScript'
              inlineScript: |
                  kubectl create secret generic ${{ parameters.releaseName }}-keyvault$(secretArgs) -n '${{ parameters.namespace}}'
                  exit 0
          - task: Kubernetes@1
            displayName: 'Kubectl Logout'
            inputs:
              command: logout
</code></pre>
