0

I am having some issues parsing runtime variables to an azure template.

My template looks like this:

# template.yaml
parameters:
  - name: dockerfilePath
    type: string
  - name: dockerImageName
    type: string
  - name: tag
    type: string
  - name: pushAsLatest
    type: string
    default: false
  - name: DependsOn
    type: string
    default: ""


jobs:
  - job: pushtoharbor
    dependsOn: ${{ parameters.DependsOn }}
    displayName: Push to Harbor
    steps:
    - checkout: self
    - task: Docker@2
      displayName: "Build Docker Image \"${{ parameters.dockerImageName }}\"."
      inputs:
        repository: myrepo/${{ parameters.dockerImageName }}
        containerRegistry: harbor_docker
        command: build
        Dockerfile: ${{ parameters.dockerfilePath }}
        addPipelineData: false
        arguments: --build-arg PAT=$(System.AccessToken)
        tags: |
          ${{parameters.tag}}
          latest

And the call i have from my azure-pipeline looks like this:

  repositories:
    - repository: templates
      type: git
      name: ADO-pipeline-templates
      ref: 0223b9311140ff6128fe40625ba2b4b252d82a73
  variables:
    tag: '$(Build.SourceVersion)'
    imageName: dummy
    isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]
    shorttag: $(git rev-parse --short=8 $tag)
  
  stages:
    - stage: PushToHarbor
      displayName: Push to Harbor
      jobs:
       - template: push-to-harbor.yaml@templates
         parameters:
           dockerfilePath: $(Build.SourcesDirectory)/Dockerfile
           dockerImageName: $(imageName)
           tag:  $(shorttag)
           pushAsLatest: $(isMain)
  

I have found that if i use $(tag) it works, but using $(shorttag) it fails with:

ERROR: invalid tag "registry.mydomain.com/myrepo/dummy:$(git rev-parse --short=8 $tag)": invalid reference format

Looking in microsoft docs, it seems it is because using ${{ }} syntax is evaluated at compile time, and the value of $tag is actually known here while the value of $shorttag is not.

So, calling the changing the template to use:

tags: |
          $[parameters.tag]

which should be processed at runtime.

Now the result is:

ERROR: invalid tag "registry.mydomain.com/myrepo/dummy:$[parameters.tag]": invalid reference format

So my question is how can I parse an argument to a template that is evaluated at runtime?

EDIT: This is not a duplicate of Azure Pipelines: Passing a variable as a parameter to a template, as the accepted answer assumes a static value, and in the end is evaluated at compile time (using ${{ }} syntax). I am looking for how to correctly evaluate at runtime

Bok
  • 537
  • 5
  • 21
  • Does this answer your question? [Azure Pipelines: Passing a variable as a parameter to a template](https://stackoverflow.com/questions/54246102/azure-pipelines-passing-a-variable-as-a-parameter-to-a-template) – Andrew McClement Aug 15 '23 at 11:36
  • Sadly no, the accepted answer works because the parameter passed has a static value, which is known at compile time. In my case, the value I want to pass is not known at compile time, but has to be evaluated later. I could potentially point my template directly to the variable "$(tag)" or "$(shorttag)", but this is not very flexible and quite obscure to the users of my template. – Bok Aug 15 '23 at 11:55
  • 1
    Basically the short answer is what you want is not supported, though there are various potential workarounds (I was just trying to link to one of the many questions asking this). A workaround could be to use an environment variable inside the template instead of passing it as a parameter (obviously this isn't ideal, but does get around the compile time restriction). – Andrew McClement Aug 15 '23 at 12:07
  • I see, that was my worry. I will properly try as you say, but might revaluate if templates are a good idea. I wanted to use them to lower complexity and avoid copy pasting the steps into every microservice. It does remove the need to copy paste, but cross referencing like this might be a bit complex compared to just having the steps in the same file. Anyways, thanks for providing help! :) – Bok Aug 15 '23 at 12:53
  • You need to run a `script` step to run the command, then create a dynamic variable within the script step using the `##vso` variable creation syntax; refer to the documentation for details. – Daniel Mann Aug 15 '23 at 15:21

1 Answers1

0

As suggested by Daniel Mann, I managed to make it work with a parsing step.

This should work for both static values, and for runtime evaluated expressions

# template.yaml
parameters:
  - name: dockerfilePath
    type: string
  - name: dockerImageName
    type: string
  - name: tag
    type: string
  - name: pushAsLatest
    type: string
    default: false
  - name: DependsOn
    type: string
    default: ""


jobs:
  - job: pushtoharbor
    dependsOn: ${{ parameters.DependsOn }}
    displayName: Push to Harbor
    steps:
    - checkout: self
    - task: bash@3
      displayName: Parse inputs
      name: inputs
      inputs:
        targetType: 'inline'
        script: |
          echo "##vso[task.setvariable variable=dockerfilePath;isoutput=true]${{ parameters.dockerfilePath }}"
          echo "##vso[task.setvariable variable=dockerImageName;isoutput=true]${{ parameters.dockerImageName }}"
          echo "##vso[task.setvariable variable=tag;isoutput=true]${{ parameters.tag }}"
    - task: Docker@2
      displayName: "Build Docker Image \"${{ parameters.dockerImageName }}\"." # we cannot use inputs.dockerImageName here, as it is not known at compile time.
      inputs:
        repository: myrepo/$(inputs.dockerImageName) # Here we use inputs.dockerImageName, so we can push even if the display name is wrong
        containerRegistry: harbor_docker
        command: build
        Dockerfile: $(inputs.dockerfilePath)
        addPipelineData: false
        arguments: --build-arg PAT=$(System.AccessToken)
        tags: |
          $(inputs.tag)
          latest
Bok
  • 537
  • 5
  • 21