У меня проблема с настройкой и доступом к переменным в конвейере DevOps (yaml)

У меня есть шаблон конвейера Azure DevOps, который используется для развертывания шаблона Arm. Он также предназначен для анализа выходных данных шаблона ARM в переменных Devops, которые должны быть доступны на протяжении всего конвейера. Но у меня проблема с установкой переменной и доступом к ней.

Вот отпечаток прогона, на котором мы можем видеть результат работы шаблона. Это массив, содержащий строки.

Когда я пытаюсь получить к нему доступ в сценарии Azure PowerShell, я делаю это, и все работает хорошо.

$outputJson = $deployment.outputs | ConvertTo-Json
Write-Host "print: $output"


Итак, я могу распечатать результат. Но после этого я хочу установить переменную, которую я могу использовать при запуске конвейера:

Вот моя проблема: я пытаюсь установить переменную вот так: (пробовал разные способы)

Write-Output "##vso[task.setvariable variable=outputTest;isOutput=true]$outputJson"

Вот несколько примеров моего синтаксиса при попытке доступа к переменной.

Write-Host "print var test1: $outputTest"
Write-Host "print var test2: $(outputTest)"
Write-Host "print var test3: ${{ $variables.outputTest }}"
Write-Host "print var test4: '${{ $variables.outputTest }}'"

но что бы я ни делал, результат печати пуст. Я чувствую себя такой глупой.. хаха Есть идеи?

Дополнительная информация:

Это структура файла yaml, в котором выполняются команды.


steps:
  - script: |
      echo parameters.serviceconnection - ${{parameters.serviceconnection}}
      echo resourceGroup - ${{parameters.resourceGroup}}
      echo resourceGroupLocation - ${{parameters.resourceGroupLocation}}
      echo overrideParameters - ${{parameters.overrideParameters}}
    displayName: 'arm-template.yml - Variable list'

task: AzurePowerShell@5
    displayName: "PowerShell template deploy"
    inputs:
      azureSubscription: ${{parameters.serviceConnection}}
      scriptType: InlineScript
      azurePowerShellVersion: LatestVersion
      inline: |
    "my code"
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
1 373
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Насколько я знаю и правильно прочитал документацию, у вас всегда есть пара ключ/значение для переменной конвейера, а это означает, что вы не можете просто преобразовать свой объект в переменную конвейера. Если вы погуглите по анализу вывода руки/бицепса, вы можете найти несколько примеров преобразования вывода руки.

Например, в Баше:

      deploymentOutput=$(az deployment group show \
        --resource-group $(rgName) \
        --name $(vnetName)-$deploymentSuffixAttempt \
        --query properties.outputs | jq -c 'to_entries[] | [.key, .value.value]')
      while IFS=$'\n' read -r c; do
        outputname=$(echo "$c" | jq -r '.[0]')
        outputvalue=$(echo "$c" | jq -r '.[1]')
        echo "Log variabele: $outputname : $outputvalue"
        declare $outputname=$outputvalue 
        echo "##vso[task.setvariable variable=$outputname;isoutput=true]$outputvalue"
      done <<< "$deploymentOutput"

Или в powerhshell:

[CmdletBinding()] 
param ( 
    [Parameter(Mandatory)] 
    [ValidateNotNullOrEmpty()] 
    [string]$ArmOutputString, 
    [Parameter()] 
    [ValidateNotNullOrEmpty()] 
    [switch]$MakeOutput 
) 
Write-Output "Retrieved input: $ArmOutputString" 
$armOutputObj = $ArmOutputString | ConvertFrom-Json 
$armOutputObj.PSObject.Properties | ForEach-Object { 
    $type = ($_.value.type).ToLower() 
    $keyname = $_.Name 
    $vsoAttribs = @("task.setvariable variable=$keyName") 
    if ($type -eq "array") { 
        $value = $_.Value.value.name -join ',' ## All array variables will come out as comma-separated strings 
    } elseif ($type -eq "securestring") { 
        $vsoAttribs += 'isSecret=true' 
    } elseif ($type -ne "string") { 
        throw "Type '$type' is not supported for '$keyname'" 
    } else { 
        $value = $_.Value.value 
    } 
 
    if ($MakeOutput.IsPresent) { 
        $vsoAttribs += 'isOutput=true' 
    } 
    $attribString = $vsoAttribs -join ';' 
    $var = "##vso[$attribString]$value" 
    Write-Output -InputObject $var 
}

Обратите внимание, что это всего лишь примеры, и иногда для вашего развертывания также требуются некоторые дополнительные действия.

После успешного преобразования вы сможете получить к ним доступ в PowerShell как к системным переменным:

Write-Host "print var test1: $env:outputTest"
Ответ принят как подходящий

Если не ошибаюсь, причина, по которой вы не видите переменную, заключается в том, что при использовании isOutput=true необходимо будет указать саму задачу/шаг.

Этот флаг важен для передачи переменных/данных между этапами и заданиями, но его можно пропустить для задач в рамках одного задания.

Я дам вам 2 варианта решения этой проблемы.

Опция 1

Уберите isOutput=true из Write-Output "##vso[task.setvariable variable=outputTest;isOutput=true]$outputJson"

Это должно превратиться в обычную переменную, и после этого ее можно будет вызывать с помощью $(outputTest).

Обратите внимание, что эта переменная существует только внутри одного и того же job и известна только агенту, запускающему job. На него можно ссылаться внутри того же job, но после этого на него нельзя ссылаться/вызывать где-либо еще.

Вариант 2

Если вы сохраните isOutput=true, вам понадобится способ сослаться на саму задачу:

- task: AzurePowerShell@5
    displayName: "PowerShell template deploy"
    name: NameOfMyTask
    inputs:
      azureSubscription: ${{parameters.serviceConnection}}
      scriptType: InlineScript
      azurePowerShellVersion: LatestVersion
      inline: |
       "your code"

Добавив name: NameOfMyTask, сохранив isOutput=true, вы сможете ссылаться на него по $(NameOfMyTask.outputTest).

Передача на другую работу

С помощью isOutput=true на него также можно ссылаться в другом job, используя следующий пример:

- job: MySecondJob
  dependsOn: [MyFirstJob] # This is your job where you set the variable with isOutput=true
  variables:
    myOutput: $[ dependencies.MyFirstJob.outputs['NameOfMyTask.outputTest'] ]

  steps:

  - pwsh: |
      Write-Output $(myOutput)

С помощью isOutput=true на него также можно ссылаться в другом stage, используя следующий пример:

Передача его на другой этап

- stage: MySecondStage
  dependsOn: [MyFirstStage] # This is your stage where you set the variable with isOutput=true
  variables:
    myOutput: $[ stageDependencies.MyFirstStage.MyFirstJob.outputs['NameOfMyTask.outputTest'] ]
  
  jobs:

  - job: MySecondJob

    steps:

    - pwsh: |
        Write-Output $(myOutput)

Причина, по которой необходим dependsOn, заключается в том, что $[] является выражением времени выполнения, и если оно не будет ожидать завершения первого этапа/задания/шага и, следовательно, установки переменной, это приведет к пустому значению/нолю для переменной.

Надеюсь, вы сможете решить эту проблему, используя предоставленную информацию, удачи!

Некоторые примечательные моменты, возможно, вы с ними не сталкивались, но важные

  1. Setvariable должен устанавливать только строковые переменные, а не массив, предпочтительнее однострочная строка.
  2. Используйте переменную setvariable на другом шаге, она не подействует на текущем шаге.
  3. Тип данных массива в сценариях yaml, bicep, powershell может обрабатываться по-разному.

Я написал пример для чтения выходных переменных шаблона руки.

test.bicep

param privateTypes array = [
  'blob'
  'file'
  'queue'
  'table'
]

output myoutput array = privateTypes

azure-pipelines.yml

parameters:
- name: serviceconnection
  type: string
  default: 'xxx'
- name: resourceGroup
  type: string
  default: 'xxx'
- name: resourceGroupLocation
  type: string
  default: 'eastus'
# - name: overrideParameters
#   type: object
#   default: 'xxx'

jobs:
# Set an output variable from job A
- job: A
  pool:
    vmImage: 'windows-latest'
  steps:
  - script: |
      echo parameters.serviceconnection - ${{parameters.serviceconnection}}
      echo resourceGroup - ${{parameters.resourceGroup}}
      echo resourceGroupLocation - ${{parameters.resourceGroupLocation}}
    displayName: 'arm-template.yml - Variable list'

  - task: AzurePowerShell@5
    name: armDeployment
    inputs:
      azureSubscription: ${{parameters.serviceconnection}}
      azurePowerShellVersion: LatestVersion
      ScriptType: 'InlineScript'
      Inline: |
        New-AzResourceGroup -Name ${{parameters.resourceGroup}} -Location ${{parameters.resourceGroupLocation}} -Force

        $deploy = New-AzResourceGroupDeployment -Name 'testDeployment' -ResourceGroupName ${{parameters.resourceGroup}} -TemplateFile $(System.DefaultWorkingDirectory)\test.bicep

        $deployOutputString = $deploy.Outputs.myoutput.Value | ConvertTo-Json -Compress

        Write-Host "type is: $($deployOutputString.GetType())"
        Write-Host "string is: $deployOutputString"

        $temp = $deployOutputString | convertFrom-Json
        foreach($item in $temp){
          Write-Host "item is: $item"
        }

        Write-Host "##vso[task.setvariable variable=outputTest;isOutput=true]'$deployOutputString'"

  - task: PowerShell@2
    name: testOutputWithString
    inputs:
      targetType: 'inline'
      script: |
        Write-Host "variable type is $($(armDeployment.outputTest).GetType())"
        Write-Host "variable is: $(armDeployment.outputTest)"

Насколько я тестировал на основе вашего описания, мы не можем использовать многострочное содержимое json (массив значений) для определения выходных переменных в сценарии конвейера; для этого вы можете вместо этого протестировать с помощью $outputString = ($deployment.Outputs.privateTypes.Value | ConvertTo-Json -Compress), чтобы преобразовать ожидаемые результаты развертывания вашего шаблона ARM в однострочную строку;

Чтобы ссылаться на выходную переменную конвейера в последующих шагах/заданиях/этапах конвейера, нам нужно добавить свойство name (не displayName) для задачи конвейера AzurePowerShell@5. Вот пример для справки, который может генерировать выходную переменную конвейера с выходными данными массива развертывания ARM, а затем печатать значение выходной переменной на следующем этапе из того же задания.

parameters:
- name: serviceconnection
  type: string
  default: ARMSvcCnnAutoSubX
- name: resourceGroup
  type: string
  default: rg-armdeployment
- name: resourceGroupLocation
  type: string
  default: 'West US'
- name: overrideParameters
  type: object
  default:
    resourceGroupName: rg-armdeployment
    location: 'West US'
    storageAccountType: Standard_LRS
    storageAccountName: armdeploymentsa

stages:
- stage: Stage_ARMDeploy
  displayName: ARM Template Deployment
  jobs:
  - job: Job_ARMDeploy
    displayName: Traditional job to deploy ARM Template
    steps:
    - task: AzurePowerShell@5
      name: Step_ARMDeploy
      displayName: Deploy ARM template via Azure PowerShell
      inputs:
        azureSubscription: '${{parameters.serviceconnection}}'
        ScriptType: 'InlineScript'
        Inline: |
          Write-Host parameters.serviceconnection - ${{parameters.serviceconnection}}
          Write-Host resourceGroup - ${{parameters.resourceGroup}}
          Write-Host resourceGroupLocation - ${{parameters.resourceGroupLocation}}
          # Creating hashtable for parameters
          $overrideParameters = @{
              "storageAccountType" = "${{ parameters.overrideParameters.storageAccountType }}"
              "storageAccountName" = "${{ parameters.overrideParameters.storageAccountName }}$(Build.BuildId)"
          }

          Write-Host overrideParameters:
          $overrideParameters
          $resourceGroupName = "${{parameters.resourceGroup}}-$(Build.BuildId)"

          Write-Host Creating new resource group
          New-AzResourceGroup `
            -Name $resourceGroupName `
            -Location "${{parameters.resourceGroupLocation}}"
          
          Write-Host Deploying storage account in the resource group via arm json template
          $deployment = New-AzResourceGroupDeployment `
            -Name ARMDeployment$(Build.BuildId) `
            -ResourceGroupName $resourceGroupName `
            -TemplateFile $(System.DefaultWorkingDirectory)/JSONTEMPLATES/StorageAccount/StorageAccount.json `
            -TemplateParameterObject $overrideParameters `
            -verbose
          
          Write-Host ARM deployment outputs:
          $deployment.Outputs.privateTypes
          Write-Host Convert outputs into json:
          $outputJson = $deployment.Outputs.privateTypes.Value | ConvertTo-Json
          $outputJson
          #************************************************************************************
          Write-Host Output the single-line string
          $outputString = ($deployment.Outputs.privateTypes.Value | ConvertTo-Json -Compress)
          Write-Host outputSring - $outputString
          Write-Host Defining output variable for downstream steps/jobs/stages
          Write-Host "##vso[task.setvariable variable=myOutputTest;isoutput=true]$outputString"
          #************************************************************************************
        azurePowerShellVersion: 'LatestVersion'
    - powershell: |
        Write-Host "$(Step_ARMDeploy.myOutputTest)"
        # Convert the single-line JSON string back to an array
        $outputArray = '$(Step_ARMDeploy.myOutputTest)' | ConvertFrom-Json
        Write-Host outputArray:
        $outputArray
      displayName: Print output variable value in downstream step from the same job

Сведения об использовании выходных переменных на этапах и в заданиях см. в разделе Установка переменных в скриптах — Azure Pipelines | Microsoft Learn

Другие вопросы по теме