The ability to use the Azure DevTest labs within a development inner loop has been documented, but this post will look at how the DevTest labs can be used in the Azure DevOps build and release pipelines. The basic flow is to have a build pipeline to that will build the application code, create the base environment in DevTest Labs, update the environment with custom information, deploy the application to the DevTest Lab environment, and then test the code. Once build has been completed successfully, the release pipeline will use the build artifacts to deploy staging, or production. One of the necessary premises is that all the information needed to recreate the “tested” ecosystem is available within the build artifacts, including the configuration of the Azure resources. As Azure resources incur a cost when used, companies tend to want to either control or track the use of these resources. In some situations, the Azure RM templates which are used to create and configure the resources, may be managed by another department like IT which could be stored in a different repository. This leads to an interesting situation where a build will be created and tested, and both the code and the configuration will need to be stored within the build artifacts to properly recreate the system in production. Using DevTest Labs during the build/test phase we can add the correct ARM templates and supporting files to the build sources, so that during the release phase the exact configuration used to test with is deployed to production. The “Create Azure DevTest Labs Environment” task with the proper configuration will save the ARM templates within the build artifacts. For this example I’ll be using the code from the Tutorial: Build a .NET Core and SQL Database web app in Azure App Service, to deploy and test the web app in Azure.
Code and Configuration in separate repositories
Overall Flow
Setup Azure Resource
There are a couple of items that will need to be created beforehand:
- Two repositories, the first with the code from the tutorial and an ARM template with two additional VMs (in a “ARMTemplates/VMInstance folder”, the second will contain the base ARM template (existing configuration).
- A Resource Group for deployment of the production code and configuration.
- A DevTest Lab (TestLab) will need to be setup with a connection to the configuration repository for the build pipeline. I’ve included the necessary ARM template that will create the Web App and SQL Server to support the Tutorial: Build a .NET Core and SQL Database web app in Azure App Service. The ARM template will need to be checked into the configuration repository as azuredeploy.json with the metadata.json to allow DevTest lab to recognize and deploy the template.
The DevTest Lab is where the build pipeline will create the environment and deploy the code for testing
Setup Build pipeline
In Azure DevOps create a new build pipeline using the code from the Tutorial: Build a .NET Core and SQL Database web app in Azure App Service using the “ASP.NET Core” template which will populate the necessary task to build, test, and publish the code.
Three additional tasks will need to be added to create the environment in DevTest Lab and deploy to the environment.
The “Create Azure DevTest Labs Environment” task before the “Test” task. In the create environment task use the pulldowns to select the appropriate Azure RM Subscription, Lab Name, Repository Name, and Template Name (which shows the folder name where the environment is stored). I would highly recommend using the pulldowns, if you manually enter the information you will need the fully qualified Azure Resource Id for this task to work. The task displays the “friendly” names instead of the resource Ids. The environment name is the displayed name shown within DevTest labs, this should be a unique name for each build ie “TestEnv$(Build.BuildId). Either the Parameters File or the Parameters section can be used to pass information into the ARM template – see Additional information / Azure Resource Management Parameters for an example. The “Create output variables based on the environment template output?” to allow the output to be recognized by the build pipeline. The “Create artifact based on the environment template output?” will need to be enabled with the Reference name for the output variables. For this example, the text “BaseEnv” is used.
The second task is to update the existing DevTest Lab Environment, the Create Environment will pass the “BaseEnv.environmentResourceId” out to the Azure DevOps pipeline as a variable that is used in this task as the Environment Name. The ARM template for this example has two parameter “adminUserName” and “adminPassword” which will need to be added to passed in via the “Source ARM Template Parameters” field.
The third task is the “Azure App Service Deploy” which will be added after the Create task above. The App type will be “Web App” and the App Service name set to $(WebSite) to deploy the app to the app service within the DTL Environment that was created.
Setup Release pipeline
In the release pipeline, the assumption is that the web app already exists, so the two tasks are the “Azure Deployment: Create Or Update Resource Group action” and “Deploy Azure App Service”. The Resource Group Action will need the Azure Subscription where the production resource group is located, the action will be “Create or update resource group”, the name of the Resource Group, the location of the Resource Group, the template location is a “linked artifact”, the template is in the published drop artifact and the Override template parameters for the ARM template. The rest of the options can be left with the defaults. If the ARM template includes linked templates then a custom resource group deployment will need to be implemented. The second task “Deploy Azure App Service” will need the Azure Subscription, the App type will be Web App, and the App Service name which we’ve setup as $(WebSite), the rest can be left to the defaults.
Test Run
Now that both pipelines are setup, manually queue up a build and see it work. The next step is to set the appropriate trigger for the build and connect the build to the release pipeline.
Have a question? Check out the answers or ask a new one at MSDN forum.
Roger Best, Senior Software Engineer
Roger is part of the Visual Studio and .NET engineering team focused on Visual Studio and Azure customers. He has been at Microsoft for over 20 years, focusing on developer technologies for the past decade or so. In his spare time, he watches too many movies, and tries to survive triathlons
Additional information
Demo Build / Release variables
AdministratorLogin: Administrator Name
AdministratorPassword: Administrator Password – secret type
SqlDbName: SQL database name – lower case only
SqlSrvName: SQL server name
WebSite: App Service name
Azure Resource Management Parameters
-hostingPlanName 'hostplan$(Build.BuildId)' -webSiteName '$(WebSite)' -sqlServerName '$(SqlSrvName)' -administratorLogin '$(AdministratorLogin)' -administratorLoginPassword '$(AdministratorPassword)' -databaseName '$(SqlDbName)'
DevTest Lab Environment metadata information (metadata.json)
{
"itemDisplayName": "NET Core application with SQL Db",
"description": "This template creates an Azure Web App with SQL DB."
}
Configuration repository - Azure ARM Template for Web App with SQL Server (azuredeploy.json)
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"type": "string",
"minLength": 1
},
"webSiteName": {
"type": "string",
"defaultValue": "testwebapp"
},
"sqlServerName": {
"type": "string",
"defaultValue": "testsqlsrv"
},
"skuName": {
"type": "string",
"defaultValue": "F1",
"allowedValues": [
"F1",
"D1",
"B1",
"B2",
"B3",
"S1",
"S2",
"S3",
"P1",
"P2",
"P3",
"P4"
],
"metadata": {
"description": "Describes plan's pricing tier and instance size. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
}
},
"skuCapacity": {
"type": "int",
"defaultValue": 1,
"minValue": 1,
"metadata": {
"description": "Describes plan's instance count"
}
},
"administratorLogin": {
"type": "string"
},
"administratorLoginPassword": {
"type": "securestring"
},
"databaseName": {
"type": "string"
},
"collation": {
"type": "string",
"defaultValue": "SQL_Latin1_General_CP1_CI_AS"
},
"edition": {
"type": "string",
"defaultValue": "Basic",
"allowedValues": [
"Basic",
"Standard",
"Premium"
]
},
"maxSizeBytes": {
"type": "string",
"defaultValue": "1073741824"
},
"requestedServiceObjectiveName": {
"type": "string",
"defaultValue": "Basic",
"allowedValues": [
"Basic",
"S0",
"S1",
"S2",
"P1",
"P2",
"P3"
],
"metadata": {
"description": "Describes the performance level for Edition"
}
},
"_artifactsLocation": {
"type": "string",
"defaultValue": ""
},
"_artifactsLocationSasToken": {
"type": "securestring",
"defaultValue": ""
}
},
"variables": {
},
"resources": [
{
"name": "[parameters('sqlserverName')]",
"type": "Microsoft.Sql/servers",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "SqlServer"
},
"apiVersion": "2014-04-01-preview",
"properties": {
"administratorLogin": "[parameters('administratorLogin')]",
"administratorLoginPassword": "[parameters('administratorLoginPassword')]"
},
"resources": [
{
"name": "[parameters('databaseName')]",
"type": "databases",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "Database"
},
"apiVersion": "2014-04-01-preview",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers/', parameters('sqlserverName'))]"
],
"properties": {
"edition": "[parameters('edition')]",
"collation": "[parameters('collation')]",
"maxSizeBytes": "[parameters('maxSizeBytes')]",
"requestedServiceObjectiveName": "[parameters('requestedServiceObjectiveName')]"
}
},
{
"type": "firewallrules",
"apiVersion": "2014-04-01-preview",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers/', parameters('sqlserverName'))]"
],
"location": "[resourceGroup().location]",
"name": "AllowAllWindowsAzureIps",
"properties": {
"endIpAddress": "0.0.0.0",
"startIpAddress": "0.0.0.0"
}
}
]
},
{
"apiVersion": "2015-08-01",
"name": "[parameters('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "HostingPlan"
},
"sku": {
"name": "[parameters('skuName')]",
"capacity": "[parameters('skuCapacity')]"
},
"properties": {
"name": "[parameters('hostingPlanName')]"
}
},
{
"apiVersion": "2015-08-01",
"name": "[parameters('webSiteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverFarms/', parameters('hostingPlanName'))]"
],
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "empty",
"displayName": "Website"
},
"properties": {
"name": "[parameters('webSiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
},
"resources": [
{
"apiVersion": "2015-08-01",
"type": "config",
"name": "connectionstrings",
"dependsOn": [
"[resourceId('Microsoft.Web/Sites/', parameters('webSiteName'))]"
],
"properties": {
"DefaultConnection": {
"value": "[concat('Data Source=tcp:', reference(resourceId('Microsoft.Sql/servers/', parameters('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', parameters('databaseName'), ';User Id=', parameters('administratorLogin'), '@', parameters('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]",
"type": "SQLServer"
}
}
}
]
},
{
"apiVersion": "2014-04-01",
"name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]",
"type": "Microsoft.Insights/autoscalesettings",
"location": "[resourceGroup().location]",
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "AutoScaleSettings"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"profiles": [
{
"name": "Default",
"capacity": {
"minimum": 1,
"maximum": 2,
"default": 1
},
"rules": [
{
"metricTrigger": {
"metricName": "CpuPercentage",
"metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
"timeGrain": "PT1M",
"statistic": "Average",
"timeWindow": "PT10M",
"timeAggregation": "Average",
"operator": "GreaterThan",
"threshold": 80.0
},
"scaleAction": {
"direction": "Increase",
"type": "ChangeCount",
"value": 1,
"cooldown": "PT10M"
}
},
{
"metricTrigger": {
"metricName": "CpuPercentage",
"metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
"timeGrain": "PT1M",
"statistic": "Average",
"timeWindow": "PT1H",
"timeAggregation": "Average",
"operator": "LessThan",
"threshold": 60.0
},
"scaleAction": {
"direction": "Decrease",
"type": "ChangeCount",
"value": 1,
"cooldown": "PT1H"
}
}
]
}
],
"enabled": false,
"name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]",
"targetResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
}
},
{
"apiVersion": "2014-04-01",
"name": "[concat('ServerErrors ', parameters('webSiteName'))]",
"type": "Microsoft.Insights/alertrules",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]": "Resource",
"displayName": "ServerErrorsAlertRule"
},
"properties": {
"name": "[concat('ServerErrors ', parameters('webSiteName'))]",
"description": "[concat(parameters('webSiteName'), ' has some server errors, status code 5xx.')]",
"isEnabled": false,
"condition": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
"dataSource": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
"resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]",
"metricName": "Http5xx"
},
"operator": "GreaterThan",
"threshold": 0.0,
"windowSize": "PT5M"
},
"action": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"sendToServiceOwners": true,
"customEmails": []
}
}
},
{
"apiVersion": "2014-04-01",
"name": "[concat('ForbiddenRequests ', parameters('webSiteName'))]",
"type": "Microsoft.Insights/alertrules",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]": "Resource",
"displayName": "ForbiddenRequestsAlertRule"
},
"properties": {
"name": "[concat('ForbiddenRequests ', parameters('webSiteName'))]",
"description": "[concat(parameters('webSiteName'), ' has some requests that are forbidden, status code 403.')]",
"isEnabled": false,
"condition": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
"dataSource": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
"resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]",
"metricName": "Http403"
},
"operator": "GreaterThan",
"threshold": 0,
"windowSize": "PT5M"
},
"action": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"sendToServiceOwners": true,
"customEmails": []
}
}
},
{
"apiVersion": "2014-04-01",
"name": "[concat('CPUHigh ', parameters('hostingPlanName'))]",
"type": "Microsoft.Insights/alertrules",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "CPUHighAlertRule"
},
"properties": {
"name": "[concat('CPUHigh ', parameters('hostingPlanName'))]",
"description": "[concat('The average CPU is high across all the instances of ', parameters('hostingPlanName'))]",
"isEnabled": false,
"condition": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
"dataSource": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
"resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
"metricName": "CpuPercentage"
},
"operator": "GreaterThan",
"threshold": 90,
"windowSize": "PT15M"
},
"action": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"sendToServiceOwners": true,
"customEmails": []
}
}
},
{
"apiVersion": "2014-04-01",
"name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]",
"type": "Microsoft.Insights/alertrules",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "AutoScaleSettings"
},
"properties": {
"name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]",
"description": "[concat('The HTTP queue for the instances of ', parameters('hostingPlanName'), ' has a large number of pending requests.')]",
"isEnabled": false,
"condition": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
"dataSource": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
"resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
"metricName": "HttpQueueLength"
},
"operator": "GreaterThan",
"threshold": 100.0,
"windowSize": "PT5M"
},
"action": {
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"sendToServiceOwners": true,
"customEmails": []
}
}
},
{
"apiVersion": "2014-04-01",
"name": "[parameters('webSiteName')]",
"type": "Microsoft.Insights/components",
"location": "East US",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('webSiteName'))]": "Resource",
"displayName": "AppInsightsComponent"
},
"properties": {
"ApplicationId": "[parameters('webSiteName')]"
}
}
],
"outputs": {
"EnvironmentLocation":{
"type": "string",
"value": "[parameters('_artifactsLocation')]"
},
"EnvironmentSAS":{
"type": "string",
"value": "[parameters('_artifactsLocationSasToken')]"
},
"appServiceName":{
"type": "string",
"value": "[parameters('webSiteName')]"
},
"sqlSrvName":{
"type": "string",
"value": "[parameters('sqlserverName')]"
}
}
}
Code repository - Azure ARM template to add two VMs to environment
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUsername": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Admin username"
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Admin password"
}
},
"imageSKU": {
"type": "string",
"defaultValue": "2012-R2-Datacenter",
"allowedValues": [
"2008-R2-SP1",
"2012-Datacenter",
"2012-R2-Datacenter"
],
"metadata": {
"description": "The Windows version for the VM"
}
},
"vmSize": {
"type": "string",
"minLength": 1,
"defaultValue": "Standard_D2_v2",
"allowedValues": [
"Basic_A0",
"Basic_A1",
"Basic_A2",
"Basic_A3",
"Basic_A4",
"Standard_A0",
"Standard_A1",
"Standard_A2",
"Standard_A3",
"Standard_A4",
"Standard_A5",
"Standard_A6",
"Standard_A7",
"Standard_D1_v2",
"Standard_D2_v2",
"Standard_D3_v2",
"Standard_D4_v2",
"Standard_D11_v2",
"Standard_D12_v2",
"Standard_D13_v2",
"Standard_D14_v2"
],
"metadata": {
"description": "Size of the virtual machine, must be available in the virtual machine's location"
}
},
"numberOfInstances": {
"type": "int",
"minValue": 1,
"defaultValue": 2,
"metadata": {
"description": "Number of VM instances to be created behind internal load balancer control"
}
}
},
"variables": {
"vmNamePrefix": "[concat('A', uniqueString(resourceGroup().id))]",
"imagePublisher": "MicrosoftWindowsServer",
"imageOffer": "WindowsServer",
"availabilitySetName": "AvSet",
"vhdStorageType": "Standard_LRS",
"vhdStorageAccountName": "[concat('vhdstorage', uniqueString(resourceGroup().id))]",
"virtualNetworkName": "MyVNet",
"vnetId": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]",
"virtualNetworkSubnetName": "BackendSubnet",
"subnetRef": "[concat(variables('vnetId'), '/subnets/', variables('virtualNetworkSubnetName'))]",
"networkInterfaceNamePrefix": "BackendVMNic",
"loadBalancerName": "BackendLB",
"lbId": "[resourceId('Microsoft.Network/loadBalancers', variables('loadBalancerName'))]",
"diagnosticsStorageAccountName": "[variables('vhdStorageAccountName')]"
},
"resources": [
{
"apiVersion": "2016-01-01",
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('vhdStorageAccountName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "StorageAccount"
},
"sku": {
"name": "[variables('vhdStorageType')]"
},
"kind": "Storage"
},
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Compute/availabilitySets",
"name": "[variables('availabilitySetName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "AvailabilitySet"
},
"properties": {}
},
{
"apiVersion": "2016-03-30",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"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('networkInterfaceNamePrefix'), copyindex())]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "NetworkInterface"
},
"copy": {
"name": "nicLoop",
"count": "[parameters('numberOfInstances')]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]",
"[resourceId('Microsoft.Network/loadBalancers/', variables('loadBalancerName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('subnetRef')]"
},
"loadBalancerBackendAddressPools": [
{
"id": "[concat(variables('lbId'), '/backendAddressPools/BackendPool1')]"
}
]
}
}
]
}
},
{
"apiVersion": "2016-03-30",
"type": "Microsoft.Network/loadBalancers",
"name": "[variables('loadBalancerName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "LoadBalancer"
},
"dependsOn": [
"[variables('vnetId')]"
],
"properties": {
"frontendIPConfigurations": [
{
"properties": {
"subnet": {
"id": "[variables('subnetRef')]"
},
"privateIPAddress": "10.0.2.6",
"privateIPAllocationMethod": "Static"
},
"name": "LoadBalancerFrontend"
}
],
"backendAddressPools": [
{
"name": "BackendPool1"
}
],
"loadBalancingRules": [
{
"properties": {
"frontendIPConfiguration": {
"id": "[concat(resourceId('Microsoft.Network/loadBalancers', variables('loadBalancerName')), '/frontendIPConfigurations/LoadBalancerFrontend')]"
},
"backendAddressPool": {
"id": "[concat(resourceId('Microsoft.Network/loadBalancers', variables('loadBalancerName')), '/backendAddressPools/BackendPool1')]"
},
"probe": {
"id": "[concat(resourceId('Microsoft.Network/loadBalancers', variables('loadBalancerName')), '/probes/lbprobe')]"
},
"protocol": "Tcp",
"frontendPort": 80,
"backendPort": 80,
"idleTimeoutInMinutes": 15
},
"name": "lbrule"
}
],
"probes": [
{
"properties": {
"protocol": "Tcp",
"port": 80,
"intervalInSeconds": 15,
"numberOfProbes": 2
},
"name": "lbprobe"
}
]
}
},
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Compute/virtualMachines",
"name": "[concat(variables('vmNamePrefix'), copyindex())]",
"copy": {
"name": "virtualMachineLoop",
"count": "[parameters('numberOfInstances')]"
},
"location": "[resourceGroup().location]",
"tags": {
"displayName": "VirtualMachines"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/', variables('vhdStorageAccountName'))]",
"nicLoop",
"[resourceId('Microsoft.Compute/availabilitySets/', variables('availabilitySetName'))]"
],
"properties": {
"availabilitySet": {
"id": "[resourceId('Microsoft.Compute/availabilitySets', variables('availabilitySetName'))]"
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[concat(variables('vmNamePrefix'), copyIndex())]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "[variables('imagePublisher')]",
"offer": "[variables('imageOffer')]",
"sku": "[parameters('imageSKU')]",
"version": "latest"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('vhdStorageAccountName')), '2016-01-01').primaryEndpoints.blob, 'vhds/osdisk', copyindex(), '.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('networkInterfaceNamePrefix'), copyindex()))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": true,
"storageUri": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('vhdStorageAccountName')), '2016-01-01').primaryEndpoints.blob]"
}
}
},
"resources": [
{
"type": "extensions",
"name": "Microsoft.Insights.VMDiagnosticsSettings",
"apiVersion": "2016-03-30",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "AzureDiagnostics"
},
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachines/', concat(variables('vmNamePrefix'), copyindex()))]"
],
"properties": {
"publisher": "Microsoft.Azure.Diagnostics",
"type": "IaaSDiagnostics",
"typeHandlerVersion": "1.5",
"autoUpgradeMinorVersion": true,
"settings": {
"WadCfg": {
"DiagnosticMonitorConfiguration": {
"overallQuotaInMB": "4096",
"DiagnosticInfrastructureLogs": {
"scheduledTransferLogLevelFilter": "Error"
},
"WindowsEventLog": {
"scheduledTransferPeriod": "PT1M",
"DataSource": [
{
"name": "Application!*[System[(Level = 1) or (Level = 2)]]"
},
{
"name": "Security!*[System[(Level = 1 or Level = 2)]]"
},
{
"name": "System!*[System[(Level = 1 or Level = 2)]]"
}
]
},
"PerformanceCounters": {
"scheduledTransferPeriod": "PT1M",
"PerformanceCounterConfiguration": [
{
"counterSpecifier": "\Processor(_Total)\% Processor Time",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "CPU utilization",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Processor(_Total)\% Privileged Time",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "CPU privileged time",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Processor(_Total)\% User Time",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "CPU user time",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Processor Information(_Total)\Processor Frequency",
"sampleRate": "PT15S",
"unit": "Count",
"annotation": [
{
"displayName": "CPU frequency",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\System\Processes",
"sampleRate": "PT15S",
"unit": "Count",
"annotation": [
{
"displayName": "Processes",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Process(_Total)\Thread Count",
"sampleRate": "PT15S",
"unit": "Count",
"annotation": [
{
"displayName": "Threads",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Process(_Total)\Handle Count",
"sampleRate": "PT15S",
"unit": "Count",
"annotation": [
{
"displayName": "Handles",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Memory\% Committed Bytes In Use",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "Memory usage",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Memory\Available Bytes",
"sampleRate": "PT15S",
"unit": "Bytes",
"annotation": [
{
"displayName": "Memory available",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Memory\Committed Bytes",
"sampleRate": "PT15S",
"unit": "Bytes",
"annotation": [
{
"displayName": "Memory committed",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\Memory\Commit Limit",
"sampleRate": "PT15S",
"unit": "Bytes",
"annotation": [
{
"displayName": "Memory commit limit",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\% Disk Time",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "Disk active time",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\% Disk Read Time",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "Disk active read time",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\% Disk Write Time",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "Disk active write time",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\Disk Transfers/sec",
"sampleRate": "PT15S",
"unit": "CountPerSecond",
"annotation": [
{
"displayName": "Disk operations",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\Disk Reads/sec",
"sampleRate": "PT15S",
"unit": "CountPerSecond",
"annotation": [
{
"displayName": "Disk read operations",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\Disk Writes/sec",
"sampleRate": "PT15S",
"unit": "CountPerSecond",
"annotation": [
{
"displayName": "Disk write operations",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\Disk Bytes/sec",
"sampleRate": "PT15S",
"unit": "BytesPerSecond",
"annotation": [
{
"displayName": "Disk speed",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\Disk Read Bytes/sec",
"sampleRate": "PT15S",
"unit": "BytesPerSecond",
"annotation": [
{
"displayName": "Disk read speed",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\PhysicalDisk(_Total)\Disk Write Bytes/sec",
"sampleRate": "PT15S",
"unit": "BytesPerSecond",
"annotation": [
{
"displayName": "Disk write speed",
"locale": "en-us"
}
]
},
{
"counterSpecifier": "\LogicalDisk(_Total)\% Free Space",
"sampleRate": "PT15S",
"unit": "Percent",
"annotation": [
{
"displayName": "Disk free space (percentage)",
"locale": "en-us"
}
]
}
]
},
"Metrics": {
"resourceId": "[resourceId('Microsoft.Compute/virtualMachines', concat(variables('vmNamePrefix'), copyindex()))]",
"MetricAggregation": [
{
"scheduledTransferPeriod": "PT1H"
},
{
"scheduledTransferPeriod": "PT1M"
}
]
}
}
}
},
"protectedSettings": {
"storageAccountName": "[variables('diagnosticsStorageAccountName')]",
"storageAccountKey": "[listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('diagnosticsStorageAccountName')), '2016-01-01').keys[0].value]"
}
}
}
]
}
]
}