25

I have an ARM template that has and outputs section like the following:

"outputs": {
    "sqlServerFqdn": {
        "type": "string",
        "value": "[reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName]"
    },
    "primaryConnectionString": {
        "type": "string",
        "value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', variables('databaseName'), ';User Id=', parameters('administratorLogin'), '@', variables('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]"
    },
    "envResourceGroup": {
        "type": "string",
        "value": "[parameters('hostingPlanName')]"
    }
}

I have a Azure Resource Group Deployment task that uses the template. I then want to use the variable $(sqlServerFqdn) in the next task for configuration. The variable doesn't seem to just populate and I cannot find anywhere that tells me how to use 'outputs' values on release.

What do I need to do to get the variable to populate for use in configuring tasks after this ARM template runs? An example would be in the parameters to a powershell script task or another ARM template.

AC4
  • 642
  • 1
  • 7
  • 22

7 Answers7

19

VSTS Azure Resource Group Deployment task has outputs section now (since January 2018). So you can set variable name in Deployment outputs of Azure Resource Group Deployment task to, for example, ResourceGroupDeploymentOutputs and add PowerShell Script task with the following inline script:

# Make outputs from resource group deployment available to subsequent tasks

$outputs = ConvertFrom-Json $($env:ResourceGroupDeploymentOutputs)
foreach ($output in $outputs.PSObject.Properties) {
  Write-Host "##vso[task.setvariable variable=RGDO_$($output.Name)]$($output.Value.value)"
}

And in subsequent tasks you can use your template variables. So, for example, if you have sqlServerFqdn variable in your template it will be available as $(RGDO_sqlServerFqdn) after PowerShell Script task is completed.

oderibas
  • 1,575
  • 12
  • 20
  • Amazing answer. A small improvement - in order to use "$($env:ResourceGroupDeploymentOutputs)" as input parameter for a PowerShell task you will need to define it as environment variable first. – JleruOHeP Nov 12 '18 at 01:16
  • And how would you set that as an environment variable? – CoOl Jul 01 '19 at 12:55
  • 2
    I didn't do anything special. Just add this snippet of oderibas as an inline powershell script task and have `deploymentOutputs: ResourceGroupDeploymentOutputs` in the AzureResourceGroupDeployment@2 deployment task. No need to "define it as environment variable first". – Zenuka Aug 14 '19 at 11:44
  • When using this in yaml templates I had to convert it to `$outputs = ConvertFrom-Json '$(ResourceGroupDeploymentOutputs)'` to get it working. Didn't need the `env:` at all. – Arno Peters Feb 10 '20 at 15:53
13

Capturing this answer because I always end up at this question when searching for the solution.

There is a marketplace task which makes ARM template output parameters available further down the pipeline. But in some cases you don't have permission to purchase marketplace items for your subscription, so the following PowerShell will do the same thing. To use it you add it as a powershell script step immediately following the ARM template resource group deployment step. It will look at the last deployment and pull the output variables into pipeline variables.

param(
 [string]  $resourceGroupName
)

$lastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName | Sort Timestamp -Descending | Select -First 1 

if(!$lastDeployment) {
    throw "Deployment could not be found for Resource Group '$resourceGroupName'."
}

if(!$lastDeployment.Outputs) {
    throw "No output parameters could be found for the last deployment of Resource Group '$resourceGroupName'."
}

foreach ($key in $lastDeployment.Outputs.Keys){
    $type = $lastDeployment.Outputs.Item($key).Type
    $value = $lastDeployment.Outputs.Item($key).Value

    if ($type -eq "SecureString") {
        Write-Host "##vso[task.setvariable variable=$key;issecret=true]$value" 
    }
    else {
        Write-Host "##vso[task.setvariable variable=$key;]$value" 
    }
}

Note that the environmental variables won't be available in the context of this script, but will in subsequent tasks.

Josh
  • 4,009
  • 2
  • 31
  • 46
  • 1
    I'm running your script and I'm getting "Get-AzureRmResourceGroupDeployment : Run Login-AzureRmAccount to login." Did you do anything before this script to log in on VSTS? – chris31389 Mar 01 '18 at 13:42
  • This script is will run in the VSTS release pipeline if the identity of the service account for the pipeline has permission in the target subscription – Josh Mar 01 '18 at 17:19
  • I don't think you can access secure strings. "If you specify securestring for the output type, the value isn't displayed in the deployment history and can't be retrieved from another template. To use a secret value in more than one template, store the secret in a Key Vault" from https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authoring-templates#outputs – Dan Friedman Jun 07 '19 at 17:46
  • Use Get-AzResourceGroupDeployment not to get "The term 'Get-AzureRmResourceGroupDeployment' is not recognized as the name of a cmdlet" error. – Kadir Ercetin Sep 24 '20 at 15:13
9

The output value shown on the UI for the Visual Studio Team Services task for Azure Resource Group Deployment only seems to work for the scenario described in Eddie's answer, which is for VMs. In fact, if your deployment doesn't include VMs, you will get an error something like:

No VMs found in resource group: 'MY-RESOURCE-GROUP-NAME'. Could not register environment in the output variable: 'myVariableName'.

For non-VM examples, I created a powershell script that runs after the RG deployment. This script, as an example, takes input variables for resource group $resourceGroupName and the name of the output variable you need $rgDeploymentOutputParameterName. You could customize and use something similar:

#get the most recent deployment for the resource group
$lastRgDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName |
    Sort Timestamp -Descending |
        Select -First 1        

if(!$lastRgDeployment)
{
    throw "Resource Group Deployment could not be found for '$resourceGroupName'."
}

$deploymentOutputParameters = $lastRgDeployment.Outputs

if(!$deploymentOutputParameters)
{
    throw "No output parameters could be found for the last deployment of '$resourceGroupName'."
}

$outputParameter = $deploymentOutputParameters.Item($rgDeploymentOutputParameterName)

if(!$outputParameter)
{
    throw "No output parameter could be found with the name of '$rgDeploymentOutputParameterName'."
}

$outputParameterValue  = $outputParameter.Value

# From here, use $outputParameterValue, for example:
Write-Host "##vso[task.setvariable variable=$rgDeploymentOutputParameterName;]$outputParameterValue"
Chris Melinn
  • 2,046
  • 1
  • 17
  • 18
  • 1
    This looks like it is exactly what I was looking to do. I will try it and get back. – AC4 Aug 31 '16 at 20:02
6

In Nov 2020, after this commit - https://github.com/microsoft/azure-pipelines-tasks/commit/1173324604c3f61ce52cdcc999f6d4d7ea9ab8f9 , the variables could directly be used in the subsequent tasks in the pipeline (No powershell scripts required!!)

This is what the steps look like -

  1. In the ARM template deployment task, give any reference name to the Deployment Outputs section under Advanced drop down. In my case I have given armOutputVariable.

    See image for visual description

  2. Now to use the value of sqlServerFqdn in the subsequent tasks, simply use it in this manner $(armOutputVariable.sqlServerFqdn.value)

For example, let's say I want to use it to override a parameter in my test task which follows the deployment so I can use it in the following manner - Example image

To summarize all the outputs in the ARM could be used in the further steps directly in this manner (make sure you assign a reference name in the ARM template deployment step) -

$(armOutputVariable.sqlServerFqdn.value)
$(armOutputVariable.sqlServerFqdn.type)
$(armOutputVariable.primaryConnectionString.value)
$(armOutputVariable.primaryConnectionString.type)
$(armOutputVariable.envResourceGroup.value)
$(armOutputVariable.envResourceGroup.type)
swapyy28
  • 63
  • 1
  • 4
2

First you define the Azure Resource Deployment Task and in this context the Deployment outputs

enter image description here

In the next step you create a PowerShell Task that takes the Deployment outputs defined above as input arguments

enter image description here

The PowerShell script looks as follows and assigns for each output defined in the ARM template a separate VSTS environment variable with the same name as defined in the ARM template output section. These variables can then be used in subsequent tasks.

param (
    [Parameter(Mandatory=$true)]
    [string]
    $armOutputString
)

Write-Host $armOutputString
$armOutputObj = $armOutputString | convertfrom-json
Write-Host $armOutputObj

$armOutputObj.PSObject.Properties | ForEach-Object {
    $type = ($_.value.type).ToLower()
    $key = $_.name
    $value = $_.value.value

    if ($type -eq "securestring") {
        Write-Host "##vso[task.setvariable variable=$key;issecret=true]$value"
        Write-Host "Create VSTS variable with key '$key' and value '$value' of type '$type'!"
    } elseif ($type -eq "string") {
        Write-Host "##vso[task.setvariable variable=$key]$value"
        Write-Host "Create VSTS variable with key '$key' and value '$value' of type '$type'!"
    } else {
        Throw "Type '$type' not supported!"
    }
}

In a subsequent task you can access the environment variables either by passing them as argument via '$(varName)' (this works for SecureString too) or e.g. in a PowerShell script via $env:varName (this does not work for SecureString)

enter image description here

quervernetzt
  • 10,311
  • 6
  • 32
  • 51
  • I have found zero evidence of being able to retrieve a securestring. If you look at the output variable, there is no value for secure strings. – Dan Friedman Jun 07 '19 at 18:11
1

VSTS allows setting variables in powershell scripts which you can use in other tasks.

The syntax is

Write-Host "##vso[task.setvariable variable=myvariable;]myvalue"

You can have an inline Powershell script which can set the required variable to consume in yet to be executed tasks.You can access it like $(myvariable).

You may need to system.debug variable to true to use this.

Read more details here.

James G
  • 2,069
  • 16
  • 28
Harshil Lodhi
  • 7,274
  • 1
  • 33
  • 42
  • That is good to know, however I am using the supplied 'Azure Resource Group Deployment' task with outputs in an ARM template. No custom PS. Although I may need to use custom PS if this can't be done natively. I will try the system.debug thing to see if it makes a difference. – AC4 May 02 '16 at 16:54
0

You just need to add an output variable name for "Azure Resource Group Deployment" task like following: enter image description here

And then use the variable in "PowerShell on Target Machines" task: enter image description here

"PowerShell on Target Machines" task will use the resource configured in "Azure Resource Group Deployment" task: enter image description here

Output variables:

Create/update action of the Azure Resource Group task now produces an output variable during execution. The output variable can be used to refer to the resource group object in the subsequent tasks. For example "PowerShell on Target Machine" task can now refer to resource group output variable as '$(variableName)' so that it can execute the powershell script on the resource group VM targets.

Limitation: Output variable produced during execution will have details about VM hostname(s) and (public) ports, if any. Credentials to connect to the VM host(s) are to be provided explicitly in the subsequent tasks.

Refer to this link for more details: Azure Resource Group Deployment Task

Eddie Chen - MSFT
  • 29,708
  • 2
  • 46
  • 60
  • I will give that a try, but have you seen this: https://codesnob.wordpress.com/2016/02/03/vsts-release-no-vms-found-in-resource-group/ Fortunately we are dealing with VMS :) – AC4 May 04 '16 at 21:25
  • 1
    great! however VM name seems to be a bad example on my part. What I am trying to do is use the values specified in the outputs section of a ARM template. I clarified the question body to be more specific. – AC4 May 09 '16 at 18:50
  • Hi DrydenMaker, may I know if you have found any solution for this? I have got struck at the same point where I would like to get some custom output defined in ARM template – Mahesh Jasti Aug 19 '16 at 06:15
  • Hmm, that link is now broken. I think it is now [here](https://github.com/Microsoft/vsts-tasks/blob/master/Tasks/AzureResourceGroupDeployment/README.md#output-variables) but the Output Variables section has been removed...anyone know why? – oatsoda Apr 21 '17 at 09:48