Software Packaging
In our packer template we used chocolatey to install apps from their repository and now we are going to build our own scripts from the installers we uploaded into the Azure blob. We are doing this because not all software is packaged in choco, some companies might not like using chocolatey so as a first step you can create your own “repo” and slowly make the case for a local choco repo and later from choco public.
We are now talking about the steps highlighted in our packer template below:

The first step is to download the installer at build. We break the url from our installer so that we can replicate this step with other software. We uploaded tightVNC and got the following url
https://azmdfstorage.blob.core.windows.net/devopsartifacts/tightvnc-2.8.8-gpl-setup-64bit.msi
We can break this down in 2 parts:
- Filename: tightvnc-2.8.8-gpl-setup-64bit.msi
- URL of the blob: https://azmdfstorage.blob.core.windows.net/devopsartifacts/
You will most likely need to pass arguments and switches to the installer like /quiet so there is no user interaction. In the example below I use the Citrix VDA since there are multiple switches to use at installation:
$filename = "VDAWorkstationSetup_2009.exe"
$url = "https://azmdfstorage.blob.core.windows.net/devopsartifacts/"
$UnattendedArgs = "/quiet /optimize /components vda /controllers 'ctxconn01.acme.com' /noreboot /noresume /masterimage"
$filepath = "C:\Installers\"
Invoke-WebRequest -Uri ($url + $filename) -OutFile "$filepath\$filename" -Verbose -UseBasicParsing
I like to add a file path so I can download all the installers to a specific location. The directory is created in the packer template, a step before the custom installers.

Then, if using powershell, you can use the invoke-webrequest cmdlet to download the file and place it in the directory you instructed
Invoke-WebRequest -Uri ($url + $filename) -OutFile "$filepath\$filename" -Verbose -UseBasicParsing
Then you write a condition so that you can see if the program is already installed and exit codes so you know if the install ended successfully, needs a reboot or failed.
if (Test-Path ("C:\ProgramData\Citrix\XenDesktopSetup\XenDesktopVdaSetup.exe"))
{
Write-Host "File already exists. Resuming install"
$exit = (Start-Process ("C:\ProgramData\Citrix\XenDesktopSetup\XenDesktopVdaSetup.exe") -Wait -Verbose -Passthru).ExitCode
}
else
{
#Write-Host "Downloading $filename"
#Invoke-WebRequest -Uri ($url + $filename) -OutFile "$filepath\$filename" -Verbose -UseBasicParsing
Write-Host "Installing VDA..."
$exit = (Start-Process ("$filepath\$filename") $UnattendedArgs -Wait -Verbose -Passthru).ExitCode
}
#Track the exit codes.
if ($exit -eq 0)
{
Write-Host "VDA INSTALL COMPLETED!"
}
elseif ($exit -eq 3)
{
Write-Host "REBOOT NEEDED!"
}
elseif ($exit -eq 1)
{
#dump log
Get-Content "C:\Windows\Temp\VDA\Citrix\XenDesktop Installer\XenDesktop Installation.log"
throw "Install FAILED! Check Log"
}
What I do to confirm and test the script is to run it on VM that has access to the internet or the Azure Blob and track the progress of the installation and exit codes. Some executables might not have standard exit codes, in these cases you can take out the lines for the exit codes and use the -Wait command to have powershell exit after the install process ends.
Start-Process ("$filepath\$filename") $UnattendedArgs -Wait -Verbose -Passthru
You can use this as a template to install other programs. I recommend testing the scripts individually before running the pipeline in case you have to debug. You can find a sample script in this git repo: https://git
Next we will show how to apply configurations.