Search code examples
azure-devopspowerbiterraformazure-pipelines

Pass a Terraform Output to an Azure DevOps Varibale


i'm trying to create an Azure DEvOps pipeline that deploy a VM using terraform, and then connect to that VM using WinRm to execute script but i can't find a way to pass the Public IP captured from output.tf to the pipeline to be used on the connection. Hope somebody can give some advice to solve this problem.

See below my .yml file

    trigger:
  none

pool:
  vmImage: 'windows-latest'

variables:
  bkrg: 'CloudOPs_OPS_RG'
  bkstorage: 'xxxxxxxxxx'
  bkcontainer: 'terraformmtp'
  bkkey: 'terraform.tfstate'
  adminUsername: 'xxxxxxxxxxxx'
  adminPassword: 'xxxxxxxxxxxxx' 

parameters:
  - name: bacpacSasToken
    displayName: 'Bacpac SAS Token'
    type: string

stages:
- stage: 'tfvalidate'
  jobs:
    - job: 'validate'
      continueOnError: false
      steps:
        - task: TerraformInstaller@0
          displayName: 'Install Terraform'
          inputs:
            terraformVersion: 'latest'

        - task: TerraformTaskV3@3
          displayName: 'Terraform init'
          inputs:
            provider: 'azurerm'
            command: 'init'
            backendServiceArm: 'xxxxxxxxxxxxxxxx'
            backendAzureRmResourceGroupName: '$(bkrg)'
            backendAzureRmStorageAccountName: '$(bkstorage)'
            backendAzureRmContainerName: '$(bkcontainer)'
            backendAzureRmKey: '$(bkkey)'

        - task: TerraformTaskV3@3
          displayName: 'Terraform validate'
          inputs:
            provider: 'azurerm'
            command: 'validate'

- stage: 'tfdeploy'
  condition: succeeded('tfvalidate')
  dependsOn: 'tfvalidate'
  jobs:
    - job: 'apply'
      steps:
        - task: TerraformInstaller@0
          displayName: 'Install Terraform'
          inputs:
            terraformVersion: 'latest'

        - task: TerraformTaskV3@3
          displayName: 'Terraform init'
          inputs:
            provider: 'azurerm'
            command: 'init'
            backendServiceArm: 'xxxxxxxxxxxxxxxx'
            backendAzureRmResourceGroupName: '$(bkrg)'
            backendAzureRmStorageAccountName: '$(bkstorage)'
            backendAzureRmContainerName: '$(bkcontainer)'
            backendAzureRmKey: '$(bkkey)'

        - task: TerraformTaskV3@3
          displayName: 'Terraform plan'
          inputs:
            provider: 'azurerm'
            command: 'plan'
            environmentServiceNameAzureRM: 'xxxxxxxxxxxxxxxx'

        - task: TerraformTaskV3@3
          displayName: 'Terraform apply'
          inputs:
            provider: 'azurerm'
            command: 'apply'
            environmentServiceNameAzureRM: 'xxxxxxxxxxxxxxxx'

        - task: PowerShell@2
          displayName: 'Capture public IP and push to pipeline variable'
          inputs:
            targetType: 'inline'
            script: |
              $tfOutput = terraform output -json | ConvertFrom-Json
              $publicIp = $tfOutput.public_ip.value
              Write-Host "##vso[task.setvariable variable=publicIp;isOutput=true]$publicIp"
              Write-Host "Captured Public IP: $publicIp"

- stage: 'bacpacoperations'
  condition: succeeded('tfdeploy')
  dependsOn: 'tfdeploy'
  jobs:
    - job: 'downloadbacpac'
      variables:
        public_ip: $[ stageDependencies.tfdeploy.apply.outputs['publicIp'] ]
      steps:
        - task: AzureCLI@2
          displayName: 'Azure CLI'
          inputs:
            azureSubscription: 'xxxxxxxxxxxxxxxx'
            scriptType: 'ps' 
            scriptLocation: 'inlineScript'
            inlineScript: |
              $publicip = "$(public_ip)"
              $username = "$(adminUsername)"
              $password = "$(adminPassword)"
              $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
              $credential = New-Object System.Management.Automation.PSCredential ($username, $securePassword)
              $session = New-PSSession -ComputerName $publicip -Credential $credential -UseSSL -Port 5986
              Invoke-Command -Session $session -ScriptBlock { 
                cd $(System.DefaultWorkingDirectory)
                ./downloadbacpac.ps1 -bacpacSasToken "$(bacpacSasToken)" -localPath "f:\\"
              }
              Remove-PSSession -Session $session

this is the error i'm getting

New-PSSession : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an 
argument that is not null or empty, and then try the command again.
At D:\a\_temp\azureclitaskscript1726051757039_inlinescript.ps1:6 char:40
+ $session = New-PSSession -ComputerName $publicip -Credential $credent ...
+                                        ~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [New-PSSession], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.NewPSSessionCommand
 

##[error]Script failed with exit code: 1

Solution

  • To use variable in next stage, please add task name in the powershell task in tfdeploy stage.

            - task: PowerShell@2
              displayName: 'Capture public IP and push to pipeline variable'
              name: getip             # <-- add task name
              inputs:
                targetType: 'inline'
                script: |
                  $tfOutput = terraform output -json | ConvertFrom-Json
                  $publicIp = $tfOutput.public_ip.value
                  Write-Host "##vso[task.setvariable variable=publicIp;isOutput=true]$publicIp"
                  Write-Host "Captured Public IP: $publicIp"   # < -- make sure ip value output is correct
    

    And in next stage, include the task name in variable expression:

          variables:
            public_ip: $[ stageDependencies.tfdeploy.apply.outputs['getip.publicIp'] ]
    

    Please refer to the doc Use outputs in a different stage for the details.

    In powershell task, please make sure the agent ip $publicIp is correctly output, as the value will be set as the output variable value to next stage.