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 *

Starkare tillsammans

Bolag inom både publik och offentlig sektor efterfrågar en alltmer kostnadseffektiv, platsoberoende och säker digital arbetsplats. Därför går nu Altitude 365 och Uclarity samman och bildar ett gemensamt specialistbolag.
Fortsätt på Altitude 365Kolla in Exobe

Altitude 365 + Uclarity – Årets Modern Work Partner!

Vinnaren är ett bra exempel på hur en “Born in the Cloud” ständigt förbättrar sitt erbjudande, arbetar nära och proaktivt med Microsoft för att hjälpa kunderna på deras digitaliseringsresa. Plus att vi på Microsoft verkligen ser fram mot den nya bolags-konstellationen (Altitude 365 + Uclarity) för ett “Starkare Tillsammans”.

Uclarity och Altitude 365 - Starkare tillsammans

Uclarity är specialister på digitala möten, telefoni, kontaktcenter och digitalt arbetssätt. Altitude 365 är specialister på säkerhet, mobilitet och hur bolag kan optimera resan till Microsoft365. Nu gör vi gemensam sak och bildar bolag tillsammans.

– Pandemin har tydliggjort behoven av en modern digital arbetsplats och vi har diskuterat ett samgående med Altitude 365 under en längre tid. Våra kunder har behov av specialistkompetens och tillsammans blir vi en ledande specialist inom Digital Workplace-området, säger Niklas Olsson Hellström, VD Uclarity AB.

Tommy Clark, Partner, Altitude 365, kommenterar:
– Inget bolag köper det andra utan båda bolagen får lika stora delar i det nya bolaget. Vår ledstjärna är att vi blir starkare tillsammans och att vi kan hjälpa våra kunder under hela deras resa.
Målet med sammanslagningen är att kunna hjälpa kunder med både teknik och effektiva arbetssätt.

– Det är då våra kunder får önskad effekt av sin investering i den digitala arbetsplatsen, säger Niklas Olsson Hellström.

Båda bolagen har svenska och internationella kunder från både privat och offentlig sektor. Sammanslagningen resulterar i en organisation på 50+ anställda baserade i Stockholm, Örebro och Göteborg.

För frågor, vänligen kontakta;
Tommy Clarke, Partner, Altitude 365 AB, 0703-593854, tommy.clarke@altitude365.com
Niklas Olsson Hellström, VD, Uclarity AB, 0734-198016, niklas.olsson@uclarity.com

Fortsätt på Altitude 365Kolla in Exobe