Search code examples
yamlazure-pipelines

Using a multiline string as parameter in an Azure DevOps task template


In a yml step template I'm using the DownloadPipelineArtifact@2 to download a specific artifact produced from my build pipeline.

Since the artifact is quite big and, depending on the cases, I don't need all the whole artifact content, I'm providing the itemPattern property as input parameter to my step template and passing as it is to the DownloadPipelineArtifact@2 task to perform some filtering operations and speed up the build.

Below a simplified version of the step template I've implemented, called filteredDownloadArtifact:

parameters:
artifactItemPattern: '**'

steps:
- task: DownloadPipelineArtifact@2
  displayName: 'Download artifact'
  inputs:
    buildType: 'current'
    artifactName: 'MyArtifact'
    itemPattern: |
       ${{ parameters.artifactItemPattern}}

It seems it's working fine when I provide just one pattern but I'm experiencing some issue when I provided multiple item patterns to be downloaded.

For example, in another yml step template I've a pwsh task used to determine which folders should be downloaded

- powershell: |
     $itemPattern= (("$(fileCollection)" -split ' ') | ForEach-Object {
        $dllName = [System.IO.Path]::GetFileName($_)
        $_.replace($dllName, '**')
      })  -join "`r`n"
      
      Write-Host "##vso[task.setvariable variable=_itemPattern]$itemPattern"

and then I consume the filteredDownloadArtifact this way:

- template: filteredDownloadArtifact.yml
  parameters:
    artifactItemPattern: |
      $(_itemPattern)

But it doesn't seem to work. Only one item pattern is provided to my step template.

So, is there a different way to consume multiline parameters when passed to an Azure DevOps step template? Thanks


Solution

  • I have checked your YAML sample. Here are the two limitations:

    1.When you use the setvariable logging command to create the variable, it only supports One line string. If you need to use multiple lines string, you need to hardcode the variable with the following format:

    variables:
    - name: files
      value: |
        file1
        file2
    

    2.The setvariable logging command will set the variable at runtime, but the parameter: artifactItemPattern in the template will read the value at compile time. In this case, the variable value can not passed to the template successfully.

    To use parameters to pass the multiple line string, you can use the following format:

    parameters:
    - name: artifactItemPattern
      type: object
      default: |
         file1
         file2
    
    
    
    steps:
    ....
    
    - task: DownloadPipelineArtifact@2
      displayName: 'Download artifact'
      inputs:
        buildType: 'current'
        artifactName: 'MyArtifact'
        itemPattern: ${{ parameters.artifactItemPattern}}
    

    Based on your description, you need to use PowerShell to set the variable and download the files based on the variable value.

    To meet this requirement, I suggest that you can split the Pipelines into two. One is used to set the files value and use Rest API to trigger Pipeline two. Pipeline two is used to collect the input value and download artifacts.

    Here is an example:

    Pipeline One:

    variables:
    - name: fileCollection
      value: value1 value2
    
    steps:
    
    
    - powershell: |
         $itemPattern= (("$(fileCollection)" -split ' ') | ForEach-Object {
            $dllName = [System.IO.Path]::GetFileName($_)
            $_.replace($dllName, '**')
          })  -join "\n  "
         echo $itemPattern
          $token = "$(pat)"      
          $url="https://dev.azure.com/{organizationname}/{ProjectName}/_apis/pipelines/{PipelineID}/runs?api-version=5.1-preview"
          
          $token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
          
          $JSON = "
          {
            
          
          
            `"resources`": {
              `"repositories`": {
                `"self`": {
                  `"ref`": `"refs/heads/main`"
                }
              }
            },
            `"templateParameters`": {
              `"InputartifactItemPattern`":`"|\n  $itemPattern`"
            },
          
          
          
          }"
          
          
          $response = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Basic $token"} -Method Post -Body $JSON -ContentType application/json
    

    Pipeline Two

    Pipeline Two Template:

    parameters:
    - name: artifactItemPattern
      type: object
    
    
    
    steps:
    ....
    
    
    - task: DownloadPipelineArtifact@2
      displayName: 'Download artifact'
      inputs:
        buildType: 'current'
        artifactName: 'MyArtifact'
        itemPattern: ${{ parameters.artifactItemPattern}}
    
    - script: |
         cd $(pipeline.workspace)
         ls
    

    Pipeline Two Main Yaml:

    parameters:
      - name: InputartifactItemPattern
        type: object
    
      
    jobs:
    - template: filteredDownloadArtifact.yml
      parameters:
        artifactItemPattern: ${{parameters.InputartifactItemPattern}}
    

    Update:

    To hardcode the variables or parameters to pass the multiline string, we can use the following format:

    Parameters:

    parameters:
    - name: InputartifactItemPattern
      type: object
      default: |
         file1
         file2
    
    jobs:
    - template: filteredDownloadArtifact.yml
      parameters:
        artifactItemPattern: ${{parameters.InputartifactItemPattern}}
    

    Template YAML:

    parameters:
    - name: artifactItemPattern
      type: object
    
    
    
    steps:
    ....
    
    - task: DownloadPipelineArtifact@2
      displayName: 'Download artifact'
      inputs:
        buildType: 'current'
        artifactName: 'MyArtifact'
        itemPattern: ${{ parameters.artifactItemPattern}}
    

    Variables:

    variables:
    - name: _itemPattern
      value: |
        file1
        file2
    
    - template: filteredDownloadArtifact.yml
      parameters:
        artifactItemPattern: $(_itemPattern)
    

    Template YAML:

    parameters:
    artifactItemPattern: '**'
    
    steps:
    - task: DownloadPipelineArtifact@2
      displayName: 'Download artifact'
      inputs:
        buildType: 'current'
        artifactName: 'MyArtifact'
        itemPattern: |
           ${{ parameters.artifactItemPattern}}