Mar 23, 2016

Deploying Virtual Machines in Azure using Azure Resource Manager - ARM Templates (Part 2)

This article is the second of a two-part series on deploying virtual machines in Azure. In Part 1, you learned how to use Azure Resource Manager (ARM) PowerShell APIs to programmatically provision virtual machines. In this article, you perform the same provisioning actions using ARM templates.
If you would like to read the first part in this article series please go to Deploying Virtual Machines in Azure using Azure Resource Manager - PowerShell API (Part 1).

 

Azure Resource Manager Templates

Azure Resource Manager templates are only supported when using the Azure Resource Management APIs. Templates allow you to define the configuration of a set of Azure objects and use that configuration to provision new objects. It does not require any programming skills, but it does require you to follow a specific format for the template, namely JSON (JavaScript Object Notation).

Templates can be submitted for deployment using one of three methods:
  • Azure PowerShell using New-AzureRMResourceGroupDeployment cmdlet
  • Using Azure Portal Template Deployment
  • Using GitHub


ARM Template Format

An ARM template JSON file has a defined format for the sections that make up the configuration of the objects to provision. Some sections allow nesting of configuration settings, defining dependencies between objects, and defining values as part of the template definition. You can optionally define values in a parameter file that replace variables in the template, making the template highly reusable.

{
   "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
   "contentVersion": "",
   "parameters": {  },
   "variables": {  },
   "resources": [  ],
   "outputs": {  }
}

Table 1 explains the sections of the template, whether the section is required, and the information contained in each section.
SectionRequiredDescription
$SchemaYesURI to the JSON schema file that describes the version of the template language.
ContentVersionYesTemplate version. Since templates can have multiple versions, this ensures a reference value to select the correct template.
ResourcesYesTypes of objects that are deployed or updated with the template in a resource group. Resources can be nested.
ParametersNoInput values that are provided when the deployment is executed and used throughout the template. You must declare a parameter in this section to define it in the template. You cannot use a parameter to construct another parameter.
VariablesNoValues that are used within the template to assign or construct values. Parameters can be used to construct variables. Variable definition can be based on an expression or function to create the literal value of the variable.
OutputsNoValues that are returned from the template deployment.
Table 1: ARM Template Sections

While parameters and variables are not required, they will almost always be used in a template file.
For more information on template expressions that can be used to create variables, you can refer to this link.
For more information on authoring ARM templates and template syntax, you can refer to this link.

 

Defining the Template Deployment Parameters

Similar to deploying a virtual machine using PowerShell, using an ARM template requires you to define the values to use during deployment. Values fall into two categories, namely values that change with every deployment, and values that are constant or constructed based on the values during deployment. For ARM templates, the values that must be provided for every deployment are the template parameters, and the values that are constant or can be constructed are the template variables.

Table 2 lists values defined in an ARM template, which were also used or prompted for as part of the PowerShell script described in Part I of this article. The values are defined in the template as a variable, or passed in as a parameter during deployment. Table 2 defines each parameter or variable name, and the value assigned or passed at deployment time.

NameTypeValue
STANameParameterjscvmstorageaccount
STATypeVariableStandard_GRS
LocationVariableWest US
NICNameVariable<vmName>NIC
IPAddressParameter10.0.0.10
pubIPTypeVariableDynamic
pubIPNameVaraible<vmName>publicip
vNetNameVariableVirtualNetwork1
SubnetNameParameterSubnet1
vmNameParameterVM1
vmSizeVariableStandard_DS1
pubNameVariableMicrosoftWindowsServer
offerNameVariableWindowsServer
skuNameVariable2012-R2-Datacenter
diskNameVariable<vmName>OSDisk
adminPasswordParameterPass@word1
adminUsernameParameterAdminuser
Table 2: ARM Template Values

 

Defining the Template Deployment Resources

An ARM template must define all of the objects that it will provision or references to existing objects that it depends on as part of the provisioning process. In Part I of this article, the PowerShell script provisioned the following objects:
  • Storage Account
  • Public IP Address
  • Network Adapter
  • Virtual Machine
The virtual machine network adapter was also connected to an existing subnet. Therefore, the template must also create these objects as well as the subnet.

 

Creating the JSON ARM Template

When you create a JSON ARM template, you must include the sections defined in Table I. For this template, the Schema and ContentVersion sections are single line sections, as shown below.
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",

 

ARM Template Parameters

The next step is to define the parameters that are specified during deployment, including data values that are unique for a specific deployment. When you define theses parameters, you specify the parameter name, the value type, and any metadata that is useful, as shown below.
"parameters": {
    "STAName": {
        "type": "string",
        "metadata": {
            "Description": "Unique Storage Account name where the Virtual Machine's disks will be placed. Must be all lower case."
        }
    },
    "vmName": {
        "type": "string",
        "metadata": {
           "Description": "Name for the Virtual Machine."
        }
    },
    "nicName": {
        "type": "string",
        "metadata": {
           "Description": "Name for the Network Adapter."
        }
    },
    "subnetName": {
        "type": "string",
        "metadata": {
           "Description": "Name for the Subnet to attach the NIC."
        }
    },
    "IPaddress": {
        "type": "string",
        "metadata": {
           "Description": "Private IP Address of the NIC."
        }
    },
    "adminUsername": {
        "type": "string",
        "metadata": {
           "Description": "Admin Username for the Virtual Machine."
        }
    },
    "adminPassword": {
        "type": "securestring",
        "metadata": {
            "Description": "Password for the Virtual Machine."
        }
    }
},

 

ARM Template Variables

After defining the parameters, you must define the variables that are constant or that can be constructed from the parameters and other variables. When you specify a variable you include the variable name and the constant value, or an expression that constructs a value. For example, the diskName variable is constructed using a string concatenation function by combining the vmName parameter with the string OSDisk. Assuming the vmName value is VM1, the diskName value will be VM1OSDisk, as shown below.

"variables": {
    "location": "West US",
    "pubName": "MicrosoftWindowsServer",
    "ResourceGroup": "VMResourceGroup"
    "offerName": "WindowsServer",
    "diskName": "[concat(parameters('vmName'), 'OSDisk']",
    "nicName": "[concat(parameters('vmName'),'NIC')]",
    "skuName": "2012-R2-Datacenter",
    "STAType": "Standard_GRS",
    "pubIPName": "[concat(parameters('vmName'),'publicip']",
    "pubIPType": "Dynamic",
    "vmStorageAccountContainerName": "vhds",
    "vmSize": "Standard_DS1",
    "vNetName": "VirtualNetwork1",
    "vnetID": "[resourceId(variables('ResourceGroup'),'Microsoft.Network/virtualNetworks',variables('vNetName'))]",
    "subnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName'))]"
},

 

ARM Template Resources

The resources section of the ARM template defines all of the objects that the template creates during deployment. In the template resources section, you define the storage account, network interface, public IP address for the network interface, and the virtual machine. The virtual machine object and the network interface object contain dependsOn statements as part of the resource definition. These are used to help determine the order during resource deployment. For example, since the network interface depends on the public IP address resource, the public IP address resource must be provisioned successfully before the network interface can be provisioned.

In addition, the virtual machine resource has a dependency on the network interface and the storage account. There, these must be provisioned successfully before the virtual machine can be provisioned. The dependencies are nested since the network interface also has a dependency on the public IP address.

In summary, the order of the resources deployment will be the public IP address and the storage account, since these have no dependencies and they can be provisioned in parallel. Once these objects are provisioned, the network interface is provisioned, followed by the provisioning of the virtual machine as shown below.

"resources": [
    {
        "type": "Microsoft.Storage/storageAccounts",
        "name": "[parameters('STAName')]",
        "apiVersion": "2015-05-01-preview",
        "location": "[variables('location')]",
        "properties": {
            "accountType": "[variables('STAType')]"
        }
    },
    {
        "apiVersion": "2015-05-01-preview",
        "type": "Microsoft.Network/publicIPAddresses",
        "name": "[variables('publicIPAddressName')]",
        "location": "[variables('location')]",
        "properties": {
            "publicIPAllocationMethod": "[variables('pubIPType')]",
            "dnsSettings": {
                "domainNameLabel": "[parameters('vmName')]"
            }
        }
    },
    {
        "apiVersion": "2015-05-01-preview",
        "type": "Microsoft.Network/networkInterfaces",
        "name": "[parameters('nicName')]",
        "location": "[variables('location')]",
        "dependsOn": [
            "[concat('Microsoft.Network/publicIPAddresses/', variables('pubIPName'))]"
        ],
        "properties": {
            "ipConfigurations": [
                {
                    "name": "ipconfig1",
                    "properties": {
                        "privateIPAllocationMethod": "Static",
                 "privateIPAddress": "[parameters(‘IPAddress’)]",
                        "publicIPAddress": {
                            "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('pubIPName'))]"
                        },
                        "subnet": {
                            "id": "[variables('subnetRef')]"
                        }
                    }
                }
            ]
        }
    },
    {
        "apiVersion": "2015-05-01-preview",
        "type": "Microsoft.Compute/virtualMachines",
        "name": "[parameters('vmName')]",
        "location": "[variables('location')]",
        "dependsOn": [
            "[concat('Microsoft.Storage/storageAccounts/', parameters('STAName'))]",
            "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
        ],
        "properties": {
            "hardwareProfile": {
                "vmSize": "[variables('vmSize')]"
            },
            "osProfile": {
                "computername": "[parameters('vmName')]",
                "adminUsername": "[parameters('adminUsername')]",
                "adminPassword": "[parameters('adminPassword')]"
            },
            "storageProfile": {
                "imageReference": {
                    "publisher": "[variables('pubName')]",
                    "offer": "[variables('offerName')]",
                    "sku" : "[parameters('skuName')]",
                    "version":"latest"
                },
               "osDisk" : {
                    "name": "osdisk",
                    "vhd": {
                        "uri": "[concat('http://',parameters('STAName'),'.blob.core.windows.net/',variables('vmStorageAccountContainerName'),'/',variables('diskName'),'.vhd')]"
                    },
                    "caching": "ReadWrite",
                    "createOption": "FromImage"
                }
            },
            "networkProfile": {
                "networkInterfaces": [
                    {
                        "id": "[resourceId('Microsoft.Network/networkInterfaces',parameters('nicName'))]"
                    }
                ]
            }
        }
    }
]
}

 

Deploying the Template

Once the ARM template is complete, you can initiate a deployment against a resource group using the New-AzureRMResoruceGroupDeploymnet cmdlet, as shown below.

$deployName="TestDeployment"
$RGName="VMResourceGroup"
$locname="West US"
$templateFile=".\azuredeploy.json"
New-AzureRmResourceGroupDeployment -Name $deployName -ResourceGroupName $RGName -TemplateFile $templateFile

When the template-based deployment begins, you are prompted for the parameter values, as shown below.

PS C:\temp\ARMTemplate> New-AzureRmResourceGroupDeployment -Name $deployName -ResourceGroupName $RGName -Templatefile .\
AzureDeploy.json
cmdlet New-AzureRmResourceGroupDeployment at command pipeline position 1

Supply values for the following parameters:
(Type !? for Help.)

STAName: jscvmstorageaccount
vmName: jscvm1
subnetName: Subnet1
IPaddress: 10.0.0.10
adminUsername: Adminuser
adminPassword: **********

When the deployment completes successfully, you will see an output with a ProvisioningState of Succeeded, as shown below. You can verify the status of the provisioning using PowerShell or through the Azure Portal.
 
DeploymentName    : TestDeployment
ResourceGroupName : VMResourceGroup
ProvisioningState : Succeeded
Timestamp         : 12/29/2015 5:06:22 PM
Mode              : Incremental
TemplateLink      :
Parameters        :
                    Name             Type                       Value
                    ===============  =========================  ==========
                    staName          String                     jscvmstorageaccount
                    vmName           String                     jscvm1
                    subnetName       String                     Subnet1
                    iPaddress        String                     10.0.0.10
                    adminUsername    String                     Adminuser
                    adminPassword    SecureString
 
Outputs           :

 

Conclusion

In this article, you learned how to provision a virtual machine in Azure using Resource Management APIs in a PowerShell script, and within an ARM template. If you are occasionally deploying an Azure virtual machine, you may find it quicker to use the Azure Portal. However, if you are required to deploy a large number of virtual machines, using a PowerShell script or an ARM template provides you with repeatable, and less error prone method to perform the virtual machine provisioning steps.

If you would like to read the first part in this article series please go to Deploying Virtual Machines in Azure using Azure Resource Manager - PowerShell API (Part 1).


Post a Comment

 
TECH SUPPORT © 2012 - Designed by INFOSBIRD