Saturday 29 July 2023

Using Object Type Azure DevOps YAML Pipeline Parameter in PowerShell Task

 Using a YAML pipeline paramter in a PowerShell task is straight forward for types such as string. For example, if there is a YAML pipeline parameter named env of type string, we can read it in PowerShell task with $envName = '${{ parameters.env }}; without any issue. However, if the parameter is type of object we cannot read, the paramter the same way we do with string parameters. If we try to read object parameter named apps$apps = '${{ parameters.apps}}; , there will be an YAML validation error staring the pipeline such as below. 

/azure-pipelines.yml (Line: 57, Col: 23): Unable to convert from Array to String. Value: Array

Even if we try to use below it will be same issue.

[String[]]$apps = '${{ parameters.apps}};

[PSObject[]]$apps = '${{ parameters.apps}};

Let's explore the issueand solution in detail.

The issue in detail

Consider below two parameters defined in a YAML pipeline.

parameters:
nameenv
  displayName'Select environment'
  typestring
  defaultdev
  values:
    - dev
    - qa
    - prod
nameapps
  displayName'Apps to deploy'
  typeobject
  default: [
    'customer_api',
    'invoice_api',
    'order_api',
    'payment_api',
    'order_eventhandler',
    'invoice_eventhandler',
    'customer_eventhandler'
    ]

The parameter env is type of string and it can be used in a PowerShell task without any issue as shwon below,

          - taskPowerShell@2
            displayName'Read parameter 2'
            inputs:
              targetType'inline'
              script: |
                $envName = '${{ parameters.env }}';
                Write-Host ($envName);

However, if we try to use the apps parameter, which is type of object we will get the error below, when starting the pipeline.

          - taskPowerShell@2
            displayName'Read parameter 2'
            inputs:
              targetType'inline'
              script: |
                $envName = '${{ parameters.env }}';
                Write-Host ($envName);
                $apps = '${{ parameters.apps}}';



The Solution

There are two ways to fix this issue. Once is to use an env variable setup and join object type contents with a seperator such as ;. Then in PowereShell we can split it and obtain an array. This works fine for the parmeter apps we have used in this example. 

          - taskPowerShell@2
            displayName'Read parameter 1'
            env:
              APP_NAMES${{ join(';', parameters.apps) }}
            inputs:
              targetType'inline'
              script: |
                $envName = '${{ parameters.env }}';
                Write-Host ($envName);

                $apps = $env:APP_NAMES -split ";"

                foreach($app in $apps)
                {
                  Write-Host ($app);
                }




However, if the parameter is more complex object such as below the above solutioon will fail.

parameters: - name: complexparam type: object default: this_is: a_complex: object with: - one - two



To obtain a complex object paramter to a PSObject, we can use syntax below, in a PowerShell task.
$apps = '${{ convertToJson(parameters.apps) }}' | ConvertFrom-Json; converts complex YAML parameter to json representation and the convert it  from json to PSObject, which we can use in PowerShell to read through the complex object.

          - taskPowerShell@2
            displayName'Read parameter 2'
            inputs:
              targetType'inline'
              script: |
                $envName = '${{ parameters.env }}';
                Write-Host ($envName);

                $apps = '${{ convertToJson(parameters.apps) }}' | ConvertFrom-Json;

                foreach($app in $apps)
                {
                  Write-Host ($app);
                }



The full test pipline YAML code is as below.


parameters:
nameenv
  displayName'Select environment'
  typestring
  defaultdev
  values:
    - dev
    - qa
    - prod
nameapps
  displayName'Apps to deploy'
  typeobject
  default: [
    'customer_api',
    'invoice_api',
    'order_api',
    'payment_api',
    'order_eventhandler',
    'invoice_eventhandler',
    'customer_eventhandler'
    ]

triggernone

stages:
  - stageYAML_Params
    displayName"YAML Params"
    jobs:
      - jobYAML_Params
        displayNameYAML Params
        pool:
          vmImageubuntu-latest
        steps:
          - checkoutnone
        
          - taskPowerShell@2
            displayName'Read parameter 1'
            env:
              APP_NAMES${{ join(';', parameters.apps) }}
            inputs:
              targetType'inline'
              script: |
                $envName = '${{ parameters.env }}';
                Write-Host ($envName);

                $apps = $env:APP_NAMES -split ";"

                foreach($app in $apps)
                {
                  Write-Host ($app);
                }
            
          - taskPowerShell@2
            displayName'Read parameter 2'
            inputs:
              targetType'inline'
              script: |
                $envName = '${{ parameters.env }}';
                Write-Host ($envName);

                $apps = '${{ convertToJson(parameters.apps) }}' | ConvertFrom-Json;

                foreach($app in $apps)
                {
                  Write-Host ($app);
                }

No comments:

Popular Posts