My Learning Experience

Navigating environment variables, outputs, parameters, and other variables in pipelines can be a daunting task within Azure DevOps. With various methods for accessing variables, inconsistencies in casing, and numerous other challenges, the process can be far from straightforward.

My advice, learned through rigorous trial and error, is to steer clear of these complexities whenever possible or, at the very least, to encapsulate any code snippets exceeding two lines within PowerShell scripts.

By adopting this approach, not only can you streamline your pipeline processes, but you also gain the flexibility to test individual components separately.

A Practical Example

Executing a Bicep file within pipelines is a common requirement, yet passing parameters to Bicep can often be time-consuming and convoluted. To mitigate these challenges, I strongly recommend crafting a helper script. Such a script can be developed locally and seamlessly integrated into the pipeline once finalized.

param (
  [Parameter(Mandatory=$true)]
  [string]$templateFile1,

  [Parameter(Mandatory=$true)]
  [string]$resourceGroup,

  [Parameter(Mandatory=$true)]
  [string]$projectname,

  [Parameter(Mandatory=$true)]
  [string]$location,

  [Parameter(Mandatory=$true)]
  [string]$environment,

  [Parameter(Mandatory=$true)]
  [string]$iotHubName
)

$deploymentName = "deploy-part-1-$projectname-$environment"

Write-Host "Deploy Infrastructure with Bicep"
Write-Host "- deploymentName : $deploymentName"
Write-Host "- bicepScriptPath: $templateFile1"
Write-Host "- location       : $location"
Write-Host "- projectname    : $projectname"
Write-Host "- resourceGroup  : $resourceGroup"
Write-Host "- env            : $environment"
Write-Host "- iotHubName     : $iotHubName"

$deploymentResult = az deployment group create `
  --resource-group $resourceGroup `
  --template-file $templateFile1 `
  --name $deploymentName `
  --parameters `
    name=$projectname `
    location=$location `
    env=$environment `
    iothubName=$iotHubName 
  | ConvertFrom-Json

$outputs = $deploymentResult.properties.outputs
$powerbiFunctionAppName = $outputs.powerbiFunctionAppName.value
$settingsFunctionAppName = $outputs.settingsFunctionAppName.value
$storageAccountName = $outputs.storageAccountName.value

# Some outputs for the pipeline later
Write-Host "##vso[task.setvariable variable=POWERBI_FUNCTION_APP_NAME;isOutput=true;]$powerbiFunctionAppName"
Write-Host "##vso[task.setvariable variable=SETTINGS_FUNCTION_APP_NAME;isOutput=true;]$settingsFunctionAppName"
Write-Host "##vso[task.setvariable variable=STORAGE_ACCOUNT_NAME;isOutput=true;]$storageAccountName"

Write-Host "- Power Bi Function App Name : $powerbiFunctionAppName"
Write-Host "- Settings Function App Name : $settingsFunctionAppName"
Write-Host "- Storage Account Name       : $storageAccountName"

# Some outputs for local usage
$env:SETTINGS_FUNCTION_APP_NAME = $settingsFunctionAppName
$env:POWERBI_FUNCTION_APP_NAME = $powerbiFunctionAppName
$env:STORAGE_ACCOUNT_NAME = $storageAccountName

if ($null -eq $powerbiFunctionAppName) {
  Write-Error "- Powerbi Function App Name is null"
  Exit 1  
  return
}

if ($null -eq $settingsFunctionAppName) {
  Write-Error "- Settings Function App Name is null"
  Exit 1  
  return
}

if ($null -eq $storageAccountName) {
  Write-Error "- Storage Account Name is null"
  Exit 1  
  return
}

$waitSeconds = 30
write-Host "- Wait $waitSeconds seconds for the function app to be ready"
Start-Sleep -Seconds $waitSeconds

Conclusion

By segregating scripts for local testing and subsequent integration within pipelines, you can enhance both the efficiency and reliability of your deployment processes.