Get in touch with us!

VSTS Build Agent + nodejs – Part 2

Del två | In my previous blog post, we examined how we can configure our virtual machine to be a VSTS building agent with support for node.js. We will now continue to look at how we can automate the rollout to Azure. 

ARM template

Start by creating a new blank “Azure Resoruce Group” Project in Visual Studio. Then create a folder named “DSC” int the project root. In the DSC folder, put the DSC file we created in Part 1 and rename the file to “BuildAgentConfig.ps1”.

Copy the following JSON documents and replace all content in “azuredeploy.json” (The ARM template file in your project).


{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "adminPassword": {
      "type": "securestring",
      "defaultValue": "Y0urS3curePassw0rd!"
    },
    "numberOfInstances": {
      "type": "int",
      "minValue": 1,
      "defaultValue": 2
    },
    "_artifactsLocation": {
      "type": "string",
      "metadata": {
        "description": "Auto-generated container in staging storage account to receive post-build staging folder upload"
      }
    },
    "_artifactsLocationSasToken": {
      "type": "securestring",
      "metadata": {
        "description": "Auto-generated token to access _artifactsLocation"
      }
    }
  },
  "variables": {
    "BuildAgentConfigArchiveFolder": "DSC",
    "BuildAgentConfigArchiveFileName": "BuildAgentConfig.zip",
    "environment": "Dev",
    "SystemPrefix": "Agent",
    "NamePrefix": "[concat(variables('SystemPrefix'),variables('environment'))]",
    "VMNicPostFix": "[concat(variables('environment'),'-nic')]",
    "availabilitySetName": "[concat(variables('namePrefix'),'-avset')]",
    "VnetName": "[concat(variables('namePrefix'),'-vnet')]",
    "vnetId": "[resourceId('Microsoft.Network/virtualNetworks', variables('VnetName'))]",
    "virtualNetworkSubnetName": "BackendSubnet",
    "subnetRef": "[concat(variables('vnetId'), '/subnets/', variables('virtualNetworkSubnetName'))]"
  },
  "resources": [
    {
      "apiVersion": "2017-03-30",
      "type": "Microsoft.Compute/availabilitySets",
      "name": "[variables('availabilitySetName')]",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "AvailabilitySet"
      },
      "properties": {
        "PlatformUpdateDomainCount": 3,
        "PlatformFaultDomainCount": 2
      },
      "sku": {
        "name": "Aligned"
      }
    },
    {
      "apiVersion": "2016-03-30",
      "type": "Microsoft.Network/virtualNetworks",
      "name": "[variables('VnetName')]",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "VirtualNetwork"
      },
      "properties": {
        "addressSpace": {
          "addressPrefixes": [
            "10.0.0.0/16"
          ]
        },
        "subnets": [
          {
            "name": "[variables('virtualNetworkSubnetName')]",
            "properties": {
              "addressPrefix": "10.0.2.0/24"
            }
          }
        ]
      }
    },
    {
      "apiVersion": "2016-03-30",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[concat(variables('NamePrefix'), copyindex(),variables('VMNicPostFix'))]",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "NetworkInterface"
      },
      "copy": {
        "name": "nicLoop",
        "count": "[parameters('numberOfInstances')]"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks/', variables('VnetName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              "privateIPAllocationMethod": "Dynamic",
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
            }
          }
        ]
      }
    },
    {
      "apiVersion": "2016-04-30-preview",
      "type": "Microsoft.Compute/virtualMachines",
      "name": "[concat(variables('NamePrefix'), copyindex())]",
      "copy": {
        "name": "virtualMachineLoop",
        "count": "[parameters('numberOfInstances')]"
      },
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "VSTS Build Agent"
      },
      "dependsOn": [
        "nicLoop",
        "[resourceId('Microsoft.Compute/availabilitySets/', variables('availabilitySetName'))]"
      ],
      "properties": {
        "availabilitySet": {
          "id": "[resourceId('Microsoft.Compute/availabilitySets', variables('availabilitySetName'))]"
        },
        "hardwareProfile": {
          "vmSize": "Standard_B2s"
        },
        "osProfile": {
          "computerName": "[concat(variables('NamePrefix'), copyindex())]",
          "adminUsername": "azadmin",
          "adminPassword": "[parameters('adminPassword')]",
          "windowsConfiguration": {
            "provisionVmAgent": "true"
          }
        },
        "storageProfile": {
          "imageReference": {
            "publisher": "MicrosoftVisualStudio",
            "offer": "VisualStudio",
            "sku": "VS-2017-Ent-Latest-WS2016",
            "version": "latest"
          },
          "osDisk": {
            "createOption": "fromImage",
            "managedDisk": {
              "storageAccountType": "Premium_LRS"
            }
          },
          "dataDisks": []
        },
        "networkProfile": {
          "networkInterfaces": [
            {
              "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('NamePrefix'), copyindex(),variables('VMNicPostFix')))]"
            }
          ]
        }
      },
      "resources": [
        {
          "name": "Microsoft.Powershell.DSC",
          "type": "extensions",
          "location": "[resourceGroup().location]",
          "apiVersion": "2016-03-30",
          "dependsOn": [
            "[resourceId('Microsoft.Compute/virtualMachines', concat(variables('NamePrefix'), copyindex()))]"
          ],
          "tags": {
            "displayName": "BuildAgentConfig"
          },
          "properties": {
            "publisher": "Microsoft.Powershell",
            "type": "DSC",
            "typeHandlerVersion": "2.9",
            "autoUpgradeMinorVersion": true,
            "settings": {
              "configuration": {
                "url": "[concat(parameters('_artifactsLocation'), '/', variables('BuildAgentConfigArchiveFolder'), '/', variables('BuildAgentConfigArchiveFileName'))]",
                "script": "BuildAgentConfig.ps1",
                "function": "Main"
              },
              "configurationArguments": {
                "nodeName": "[concat(variables('NamePrefix'), copyindex())]"
              }
            },
            "protectedSettings": {
              "configurationUrlSasToken": "[parameters('_artifactsLocationSasToken')]"
            }
          }
        }
      ]
    }
  ]
}

Deployment

To test the template we will make an deployment via VS.

  1. Make sure that the PAT key and VSTS information in the DSC file is correct
  2. Right-click on the current project in Visual Studio and select Deploy, New …
  3. Sign in and select the right Azure subscription
  4. Create a new resource group
  5. Click Edit Parameters …
  6. Set an admin password, number of instances and then hit save
  7. Click Deploy to start the deployment

After about 10 minutes, the server should be running and the agent installed. Log in to VSTS to check that the agent is online.

Key take away

Resource names
The template will create all the resources with a default name based on Microsoft best practices
https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions

Artifacts
Eftersom våran DSC är en fristående fil så måste vi använda oss av sk. artifacts för att ARM ska hitta filen. _artifactsLocation och _artifactsLocationSasToken sätts automatiskt av Visual Studio vid deployment, men i sista steget när vi går över till VSTS måste vi sätta dessa parametrar själva.

Because our DSC is a standalone file, we have to use our so-called artifacts for ARM to find the file. _artifactsLocation and _artifactsLocationSasToken is automatically set by Visual Studio at deployment. This will change in my last post when we will create the path and key using VSTS.

_artifactsLocation is a URL for an Azure blob storage and _artifactsLocationSasToken is a time-limited access key for this location.

VS Version
Note that the ARM template is set to use a Visual Studio 2017 Enterprise image for our IaaS VM. For example, this image may be replaced by “VS-2017-Ent-Latest-Preview-WS2016” to access the latest preview version of Visual Studio 2017.
“offer”: “VisualStudio”,
“sku”: “VS-2017-Ent-Latest-WS2016”,
“version”: “latest”

PAT Key
In Part 1, we configured the DSC template with a PAT key for VSTS, be sure that this is also included in this project.

Artifacts Location
Instead of allowing Visual Studio to create a storage account, review your infrastructure and create a storage account that can be used in the future with VSTS.

Submit a Comment

Your email address will not be published. Required fields are marked *