У меня есть шаблон конвейера 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"
Насколько я знаю и правильно прочитал документацию, у вас всегда есть пара ключ/значение для переменной конвейера, а это означает, что вы не можете просто преобразовать свой объект в переменную конвейера. Если вы погуглите по анализу вывода руки/бицепса, вы можете найти несколько примеров преобразования вывода руки.
Например, в Баше:
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
, заключается в том, что $[]
является выражением времени выполнения, и если оно не будет ожидать завершения первого этапа/задания/шага и, следовательно, установки переменной, это приведет к пустому значению/нолю для переменной.
Надеюсь, вы сможете решить эту проблему, используя предоставленную информацию, удачи!
Некоторые примечательные моменты, возможно, вы с ними не сталкивались, но важные
Я написал пример для чтения выходных переменных шаблона руки.
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