Retrieve powershell variable in azure devops task and assign to terraform conf - powershell

I have an Azire Devops release pipeline, in which i have a task executing a powershell script. In this script, i retrieve the uuid of a (nutanix) vlan according to its name, like this :
parameters:
- name: vlan
displayName: vlan
type: string
default: VLAN_DMZ_1
values:
- VLAN_DMZ_1
- VLAN_DMZ_2
steps:
- task: petergroenewegen.PeterGroenewegen-Xpirit-Vsts-Build-InlinePowershell.Xpirit-Vsts-Build-InlinePowershell.InlinePowershell#1
displayName: 'Check Parameters'
inputs:
Script:
$NIC = Get-NTNXNetwork | where { $_.name -eq "${{ parameters.vlan }}" } | select uuid -ExpandProperty uuid
How can i retrieve the value of this variable into the .tf file?
I tried this :
nic_list {
subnet_uuid = "$(NIC)"
}
But it doesn't work (it takes $(NIC) as the value itself), tried the replace token but it does not seem to be what i am looking for.
Thank your for your help.

The correct way to implement would be to define your terraform variable in the variables.tf file and then pass the variable through Azure devops pipeline from terraform plan command. For example in the below plan I pass into environment terraform variable the azure devops environment parameter.
terraform plan -input=false -out=tf_plan -var=environment="${{ parameters.environment }}"
Otherwise you could just append the value on the .tfvars file that you want. This should be done in powershell
Write-Output "subnet_uuid = $(NIC)" >> terraform_folder\terraform.tfvars

Related

Azure Pipeline - Exporting Terraform output as environment variable for subsequent stage

I've written a yaml script and run in Azure DevOps, and I would like to see if it is possible to set the output of the terraform apply stage as an environment variable and being used in subsequent stages. My script is like:
- stage1: Operation on state file 1
jobs:
...
- bash: |
terraform apply 'dev.plan'
- stage2: Operation on state file 2
jobs:
...
- bash: |
terraform init
```
Appreciate a lot for any advice.
Tried to use the stagedependency.xx.xx feature in Azure DevOps with no luck. Not sure if this is the appropriate choice.
You can follow below steps:
1 store Terraform state in Azure storage. please refer to Store Terraform State
exampl.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>2.0"
}
}
backend "azurerm" {
resource_group_name = "your resource group name"
storage_account_name = "your storage account name"
container_name = "your container name"
key = "terraform.tfstate"
}
}
provider "azurerm" {
features {}
}
# this is important, you can get the remote outputs for this
output "password" {
value = xxxxxx
}
2 use terraform command to print the output and you can see the output.
terraform output
3 configure output variable in azure pipeline
4 Create a PowerShell step in your pipeline and insert the following inline PowerShell:
$json = Get-Content $env:jsonPath | Out-String | ConvertFrom-Json
foreach($prop in $json.psobject.properties) {
Write-Host("##vso[task.setvariable variable=$($prop.Name);]$($prop.Value.value)")
}
And remember to set environment variable jsonPath.
update 5/12/2022
You could use the command task.setvariable to set the output name(example:password) as an azure devops pipeline variable.
echo ##vso[task.setvariable variable=password]$(terraform output password)
YAML script Sample
stages:
- stage: stage1
jobs:
- job: A1
steps:
- task: Bash#3
inputs:
targetType: 'inline'
script: |
terraform apply 'dev.plan'
workingDirectory: 'Directory containing the Terraform configuration file'
- task: Bash#3
inputs:
targetType: 'inline'
# terraform output 'your name'
# your name is the name you defined in output.tf.Here is `password` example
script: |
terraform output password
workingDirectory: 'Directory containing the Terraform configuration file'
- stage: stage2
jobs:
- job: A2
steps:
- task: Bash#3
inputs:
targetType: 'inline'
script: |
echo ##vso[task.setvariable variable=password]$(terraform output password)

Evaluating Azure Devops Expressions within Powershell

I want to be able to invoke the Counter expression within a template but I am unsure how to do so; my current template yml file looks like this:
parameters:
- name: major
type: string
default: '1'
- name: minor
type: string
default: '0'
steps:
- task: PowerShell#2
displayName: Set NuGet package version
inputs:
targetType: 'inline'
script: |
$isMain = ('$(Build.SourceBranch)' -eq 'refs/heads/main')
$minor = ${{ parameters.minor }}
$revision1 = $[counter($minor, 1)]
Write-Host $revision
exit 0
But I get:
The term '$[counter' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I am guessing there is way I can invoke the function via Powershell.
The reason for me wanting to do this in the template as opposed to passing it in as a parameter from the consumer of the template is because seemingly parameters are not evaluated when passed into a template.
Evaluating Azure Devops Expressions within Powershell
Just as Daniel said that:
The powershell script you're trying to run is getting finalized
during compile time. This line: $revision1 = $[counter($minor, 1)]
cannot possibly work. You are trying to take the results of a
powershell script expression and use it in a YAML function.
That is reason why it is not work for you.
And personally think you can go the easier way, just define the variable in the main YAML file:
azure-pipelines.yml:
variables:
- name: minor
value: 0
- name: revision1
value: $[counter(variables['minor'], 0)]
stages:
- stage: Run
jobs:
- job:
steps:
- template: Template.yml
And we could use the the Counter in the template directly:
Template.yml:
steps:
- task: PowerShell#2
displayName: Set NuGet package version
inputs:
targetType: 'inline'
script: |
Write-Host '$(revision1)'
The test result:

Accessing variables in Azure loops using templates

I want to loop through the pipeline artifacts and pass them as variables to a task.
Followed this answer here, but no luck:
https://stackoverflow.com/a/59451690/5436341
Have this powershell script in the build stage to get the artifact names and store them in a variable:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
# Write your PowerShell commands here.
Write-Host "Fetching value files"
cd $(Build.ArtifactStagingDirectory)\MSI
$a=dir -Filter "*.msi"
$List = $a | foreach {$_}
Write-Host $List
$d = '"{0}"' -f ($List -join '","')
Write-Host $d
Write-Host "##vso[task.setvariable variable=MSINames;isOutput=true]$d"
name: getMSINames
And passing them as parameters to a template from another stage as below:
- stage: deployPoolsStage
displayName: Deploy Pools
dependsOn:
- Build
jobs:
- job: CloudTest_AgentBased_Job
displayName: 'CloudTest AgentBased Job'
timeoutInMinutes: 120
variables:
MSIFiles: $[dependencies.Build.outputs['getMSINames.MSINames']]
steps:
- template: TestPipeline.yml
parameters:
files : $(MSIFiles)
Now, my template looks like this:
parameters:
files : []
steps:
- ${{ each filename in parameters.files }}:
- task: SomeTask
inputs:
Properties: worker:VsTestVersion=V150;worker:MSIFile=${{ filename }}
displayName: 'tests'
Now this is failing with an error saying: "Expected a sequence or mapping. Actual value '$(MSIFiles)'". It's the same error even without using the template and directly accessing the variable in the original yml file.
Please let me know of a way to loop through my pipeline artifacts and pass them to my task.
You are exporting the variable MSINames from a task in Build stage that needs to be inside a job (say BuildJob). Also, you are trying to access the exported variable from another stage but inside a job. Thus, you should use the format to read variable from another stage but within a job. What you have used is a wrong format in deployPoolsStage stage. Try correcting the format as below inside CloudTest_AgentBased_Job job,
variables:
MSIFiles: $[stageDependencies.Build.BuildJob.outputs['getMSINames.MSINames']]
NOTE: I have assumed that the getMSINames task is defined inside the BuildJob job under Build stage according to what you have provided.
See docs related to this here.

DevOps Pipeline AzureCLI#2 with dynamic azureSubscription

I have a DevOps pipeline that gives me this error:
There was a resource authorization issue: "The pipeline is not valid. Job ExecutionTerraform: Step AzureCLI input connectedServiceNameARM references service connection Azure: $(subscriptionName) which could not be found. The service connection does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz."
The configuration I am using is looking up the Subscription name dynamically.
The step I use for that is:
- bash: |
# pull the subscription data
# ... read data into local variables
# set the shared variables
echo "##vso[task.setvariable variable=subscriptionId]${SUBSCRIPTION_ID}"
echo "##vso[task.setvariable variable=subscriptionName]${SUBSCRIPTION_NAME}"
From there I attempt to call the Azure CLI via a template:
- template: execution-cli.yml
parameters:
azureSubscriptionId: $(subscriptionId)
azureSubscriptionName: $(subscriptionName)
Inside the template my CLI step uses:
steps:
- task: AzureCLI#2
displayName: Test CLI
inputs:
azureSubscription: "ARMTest ${{ parameters.azureSubscriptionName }}"
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az --version
addSpnToEnvironment: true
useGlobalConfig: true
It looks like Pipelines is trying to preemptively check authorization without noticing that there's a variable in there. What am I doing wrong here that is causing Azure to attempt to resolve that at the wrong time?
I do this in other pipelines without issues and I am not sure what is different in this particular instance.
Update 1: Working Template I have Elsewhere
Full template:
parameters:
- name: environment
type: string
jobs:
- job: AKSCredentials
displayName: "AKS Credentials Pull"
steps:
- task: AzureCLI#2
displayName: AKS Credentials
inputs:
azureSubscription: "Azure: testbed-${{ parameters.environment }}"
scriptType: bash
scriptLocation: inlineScript
inlineScript: az aks get-credentials -g testbed-${{ parameters.environment }} -n testbed-${{ parameters.environment }}-aks
addSpnToEnvironment: true
useGlobalConfig: true
This is not possible because azure subscription needs to be known at compilation time. You set your variable on run time.
Here an issue with similar case when it is explained:
run time variables aren't supported for service connection OR azure subscription. In your code sample, you are referring to AzureSubscription variable which will get initialized at the run time (but not at save time). Your syntax is correct but you need to set AzureSubscription variable as part of variables.
If you define your variables like:
variables:
subscriptionId: someValue
subscriptionName: someValue
and then you will use it
- template: execution-cli.yml
parameters:
azureSubscriptionId: $(subscriptionId)
azureSubscriptionName: $(subscriptionName)
it should work. But since you set up your variables on runtime it causes your issue.

Azure DevOps yaml pipeline - output variable from one job to another

I'm trying to pass a variable value from one job to another in a Azure Devops pipeline.
I've tried following the documentation in Microsoft docs diligently.
For example here:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops
However, it's not working as expected.
Can anyone point out where the problem is?
Here's the pipeline
https://github.com/ossentoo/azure-devops-pipelines
jobs:
- job: A
steps:
- script: echo "##vso[task.setvariable variable=applicationId;isOutput=true]629ae9cb-95e0-46b7-8a88-a4034b68323e"
name: mytask
- job: B
variables:
newValue: $[dependencies.A.outputs['mytask.applicationId']]
dependsOn: A
steps:
- powershell: |
Write-Host "This value is: ${{variables.newValue}}"
displayName: 'Output the value'
the output from this pipeline in the Powershell task is:
This value is: $[dependencies.A.outputs['mytask.applicationId']]
You can see the output here:
https://dev.azure.com/mercle/Concepts/_build/results?buildId=6151&view=logs&j=b79b690b-82d1-5750-8a0a-452d70195841&t=744de4c6-b805-5a86-cd25-7a780a8b3a55&l=12
It is almost as if $[] is not being recognized by Azure DevOps as a variable value.
thanks
In your last powershell step invoke newValue with $() syntax instead of ${{}}
- powershell: |
Write-Host "This value is: $(newValue)"
I think the reason ${{}} not working might be that ${{}} is for parse time and executed before $[[]] is executed.