Appendix: Azure Build Agent

An important part of the CI/CD process for the wwt-windows-client repository is to build the WWT installer MSI program. At the moment (July 2020), the Azure Pipelines Microsoft-hosted “agents” do not come equipped with the tooling needed to build such an installer. So, annoyingly, we need to create and run our own Windows VM to run these builds. This Appendix documents how such an agent is set up.

These instructions are likely to get out of date quickly. The hope is that they’ll remain helpful even if they’re not precisely accurate.

References🔗

Initialize Template Machine🔗

It doesn’t seem that there's any good way to set up the VM image automatically. So, the first step is to create a cloud VM and set it all up by hand.

  1. Install Azure command line tool az.
  2. az login if needed to log in to the WWT Azure subscription.
  3. az account list -o table to list logged-in subscriptions
  4. az account set -s $SUBSCRIPTION_ID to set the default subscription
  5. az group create --location westus --name devops-support to create a containing Resource Group.
  6. Create the VM:
    az vm create \
      --resource-group devops-support \
      --name hostedagentbox \
      --image Win2019Datacenter \
      --size Standard_D2_v3 \
      --admin-username wwt \
      --admin-password $PASSWORD
    
    The password must be at least twelve characters, one upper/lower/number/special. The machine name cannot be more than 15 characters. For the setup phase, it’s nice to use a beefier machine image. (It can be resized later.)
  7. Once the machine is created, locate in in the Azure Portal and check parameters.

It should now be possible to RDP into the machine. On Linux, something like:

xfreerdp /u:wwt /v:$IP_ADDR:3389 /size:1280x960

Now the graphical setup can begin:

  1. Turn off network discoverability when prompted upon login
  2. In the Server Manager, go to "Local Server", then click on "IE Enhanced Security Configuration". Turn the settings to off. Otherwise, IE literally won't allow you to download files without jumping through a lot of hoops. (Ref).
  3. Navigate to the Visual Studio Community downloads page.
  4. Download the vs_community.exe installer stub.
  5. Run it in an elevated Powershell (which should be the default kind, since we’re logged in as the admin user):
    vs_community.exe --allWorkloads --includeRecommended --passive `
       --add Microsoft.Net.Component.4.7.SDK `
       --add Microsoft.Net.Component.4.7.TargetingPack `
       --add Microsoft.Net.Component.4.6.2.SDK `
       --add Microsoft.Net.Component.4.6.2.TargetingPack `
       --add Microsoft.Net.ComponentGroup.4.7.DeveloperTools
    
    This command cribbed from the Visual Studio on a VM docs. I am told that it’s important to not start up the graphical interface to avoid triggering Visual Studio from wanting a license. If you typed it all right, the installer will run and take a while as it downloads several gigs of files. If you made a mistake, it will start up and exit pretty quickly without displaying a clear error message.
  6. Reboot the machine after the install. (Not 100% sure but I strongly suspect this is necessary.)
  7. Download the Microsoft Visual Studio Installer Projects Visual Studio extension, using the IE Trusted website trick again. The file is downloaded with a .zip extension but can be treated as a .vsix.
  8. Install the extension from the command line, as guided by this post:
    &"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\VSIXInstaller.exe" plugin.zip
    
  9. Probably doesn't hurt to reboot again.

If you want to do a test build at this juncture, you’ll need to apply the fix for the HRESULT = 8000000A error:

cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild"
.\DisableOutOfProcBuild.exe

We need to apply the same fix on-the-fly in the VMs since the fix is user-specific and the Azure provisioning uses a different user.

If you want the VM to to act as an Azure Pipelines build agent on its own, follow the Microsoft self-hosted agent docs. However, we’re setting up the image to run in a VM scale set, in which case the agent will automatically be provisioned upon VM boot. So for the main workflow you should not install the agent.

Convert the VM to work in a scale set🔗

Now we start following the Microsoft VM scale set agent docs.

  1. “Generalize” the online template VM:
    &"C:\Windows\System32\sysprep\sysprep.exe" /generalize /oobe /shutdown
    
    This will shut down the VM. The docs suggest that the process might take a very long time, but in at least some cases it’s pretty quick.
  2. Deallocate the VM:
    az vm deallocate --resource-group devops-support --name hostedagentbox
    
  3. Tell Azure to generalize it:
    az vm generalize --resource-group devops-support --name hostedagentbox
    
  4. Create a VM image out of it:
    az image create \
      --resource-group devops-support \
      --source hostedagentbox \
      --name hostedagent-$YYYYMM
    
  5. Turn it into a VM scale set:
    az vmss create \
      --resource-group devops-support \
      --name agent-vmss \
      --instance-count 2 \
      --disable-overprovision \
      --upgrade-policy-mode manual \
      --load-balancer "" \
      --vm-sku Standard_D2s_v3 \
      --admin-username wwt \
      --admin-password $PASSWORD \
      --image hostedagent-$YYYYMM
    
    Note: I'd like to use the Standard_D2_v3 VM SKU, which doesn't call for fancy "premium disk", but when I tried that I got an error about it being required anyway.
  6. Follow the rest of the instructions to wire up the scale set to Azure Pipelines. The agent pool is currently called "Custom Windows".