Azure ARM Template: How to deploy Azure VM using ARM template and PowerShell
Objective
In this article we will focus on automation and will deploy azure VM using ARM template. We will deploy an ARM template using PowerShell. Automation has always been major focus of Azure. Consider a scenario where a user need to deploy 50-100 VM's I am sure no body is going to deploy this using Azure GUI portal it is just too much time consuming. Hence comes automating the deployment.
Out of scope
The article is in no way authoritative study on ARM template. The complete discussion of ARM template and JSON is beyond scope of this article. This article only used in built ARM template from MS to create a VM.
What you need
- ARM template
- PowerShell version 5.1 or higher.
- .Net Framework 4.7.2 ( some bug fixes related to PowerShell are there in latest version)
- Storage Account in your azure subscription. How to create a storage account
How to generate ARM Template
We can use JSON language to write own ARM template but that would need prior JSON knowledge and would need time as ARM templates are big and verbose. Easy way is to generate it from Azure portal. This article will show you how to do that
Log into Azure Portal, click on Create resource
On Next page type Virtual Machine and then click
We would get below page
NOTE: Before you start make sure Vnet which you will use to create VM lies in Resource Group where you are trying to create Azure VM and both RG and Vnet should be in same region. If not the deployment might face if using ARM template.
Fill the details accordingly and on last page click on review and create BUT DO NOT START THE DEPLOYMENT BY CLICKING CREATE.
Click on Download A template for Automation.
Understanding ARM template
Below is how ARM template looks like.
Notice the various tabs Template, Parameters, CLI etc. The CLI and PowerShell tab gives us command which can be used while deploying completely with PowerShell or Azure CLI.
Below is how parameters tab looks like. We have explained some of the values
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"value": "eastus"---Look at location this is EastUS what we selected
},
"networkInterfaceName": {
"value": "azuretestvm974"
},
"networkSecurityGroupName": {
"value": "AzureTestVM-nsg"--This is the NSG name
},
"networkSecurityGroupRules": {
"value": [
{
"name": "RDP",--This is about the port which we allowed
"properties": {
"priority": 300,
"protocol": "TCP",
"access": "Allow",
"direction": "Inbound",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "3389"
}
}
]
},
"subnetName": {
"value": "default"
},
"virtualNetworkId": {
"value": "/subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGroups/test/providers/Microsoft.Network/virtualNetworks/Test-vnet"
},
"publicIpAddressName": {
"value": "AzureTestVM-ip"---This is name of public IP address
},
"publicIpAddressType": {
"value": "Static"
},
"publicIpAddressSku": {
"value": "Standard"
},
"virtualMachineName": {
"value": "AzureTestVM"---This is the VM name
},
"virtualMachineRG": {
"value": "Test"
},
"osDiskType": {
"value": "Standard_LRS"
},
"virtualMachineSize": {
"value": "Standard_DS1_v2"
},
"adminUsername": {
"value": "Shashank"
},
"adminPassword": {
"value": null
},
"diagnosticsStorageAccountName": {
"value": "storageacc621" ---This is storage account name
},
"diagnosticsStorageAccountId": {
"value": "/subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/storageacc621"
},
"zone": {
"value": "3"
}
}
}
Look at the above code carefully. All the variables we defined and options we selected are there in ARM template parameters file.
Download the template and save it on local drive. Extract it and you would see below files
The two ARM templates files which are of interest to us are
1. Parameters.json
2. Template.json
Editing ARM Template
We will briefly look into ARM template and the 2 JSON files. You can open it in Notepad but it is very difficult to read it. Either open it in Visual Studio or you can download TextPad. In this article we will use TextPad to open the JSON files. (You are free to use any software which can cleanly open the JSON files)
We will open the two files in Textpad and use the template to deploy a VM
Lets analyse the Parameter.json file
We can see various values marked in yellow in above image.
The password would be NULL, before deploying you need to replace null with valid strong password.
Lets also analyze template.json file. The template file takes variables/parameters from parameters.json file and does deployment. if you look closely it is calling functions to get values for various options selected via deployment.
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"networkInterfaceName": {
"type": "string"
Above is small code from template.json. The location excepts "string" type parameter and it takes parameter as "EastUS" which is there in parameters file.
"variables": {
"nsgId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
"vnetId": "[parameters('virtualNetworkId')]",
"subnetRef": "[concat(variables('vnetId'), '/subnets/', parameters('subnetName'))]"
Also look at above code, basically it is going to get nsgid value and it will get the value from 'NetworkSecurityGroupName' which is there in parameters.json file. Similarly other values work here. The JSON template looks big but very simple to understand.
We will not edit parameters.json template and create NEW VM, different from what we were creating. You can edit it in Textpad and save it like you save notepad.
New Parameters file looks like below. For more understanding I have added comment after == , you do not need to add this in JSON file
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"value": "Eastus"===Kept location same
},
"networkInterfaceName": {
"value": "azuretestvmnew974"== Provide new name
},
"networkSecurityGroupName": {
"value": "AzureTestVM-nsg-New"==provide new NSG name
},
"networkSecurityGroupRules": {
"value": [
{
"name": "RDP",
"properties": {
"priority": 300,
"protocol": "TCP",
"access": "Allow",
"direction": "Inbound",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "3389"
}
}
]
},
"subnetName": {
"value": "default"
},
"virtualNetworkId": {
"value": "/subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGroups/test/providers/Microsoft.Network/virtualNetworks/Test-vnet"===You can replace test-vnet with vnet which resides in resource group AzureRG( later we changed RG)
},
"publicIpAddressName": {
"value": "AzureTestVM-ip-New" == New IP address name
},
"publicIpAddressType": {
"value": "Static"
},
"publicIpAddressSku": {
"value": "Standard"
},
"virtualMachineName": {
"value": "AzureTestVMNew" ===New VM name
},
"virtualMachineRG": {
"value": "AzureRG"===Resource group changed
},
"osDiskType": {
"value": "Standard_LRS"
},
"virtualMachineSize": {
"value": "Standard_DS1_v2"
},
"adminUsername": {
"value": "Shashank"
},
"adminPassword": {
"value": "Password@1234567" ==Entered password
},
"diagnosticsStorageAccountName": {
"value": "storageacc621"
},
"diagnosticsStorageAccountId": {
"value": "/subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGroups/Test/providers/Microsoft.Storage/storageAccounts/storageacc621"
},
"zone": {
"value": "3"
}
}
}
Deploying Azure VM using ARM template
Now we will use PowerShell to deploy this new ARM template to cerate our VM.
Launch PowerShell as administrator and connect using below code
Connect-AzAccount
Make sure there is NO GAP or else you would get error as Connect is not recognized as internal or external command.
You should see below screen. Copy the link and paste it in URL and run it. Please enter code give and you would be connected.
After you are connected you get your subscription name and other details as shown above.
Let us use below code and deploy VM
New-AzResourceGroupDeployment -ResourceGroupName AzureRG -TemplateFile 'E:\ARM Template\template\Template.json' -TemplateParameterFile 'E:\ARM Template\template\Parameters.json'
Like mentioned above if we do not make sure that Vnet and RG are in same location we might end up with below error.
looking closely at error message
New-AzResourceGroupDeployment : 10:51:32 AM - Resource Microsoft.Network/networkInterfaces 'azuretestvmnew975' failed with message '{
"error": {
"code": "InvalidResourceReference",
"message": "Resource /subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGroups/AzureRG/providers/Microsoft.Network/virtualNetworks/Test-v
net/subnets/default referenced by resource /subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGroups/AzureRG/providers/Microsoft.Network/netw
orkInterfaces/azuretestvmnew975 was not found. Please make sure that the referenced resource exists, and that both resources are in the same region.",
"details": []
}
}'
You get above error for 2 reasons
- 1. The Vnet you were trying to create was in different RG and it is still picking up that region
- 2. If you look at code we missed to change the resource group and it is still using TEST ((resourceGroups/Test/providers) it should be changed to AzureRG.
As a workaround create Vnet in same RG where you are deploying VM
PS C:\Users\Shashank_2> New-AzResourceGroupDeployment -ResourceGroupName AzureRG -TemplateFile 'E:\ARM Template\template\Template.json' -TemplatePara
meterFile 'E:\ARM Template\template\Parameters.json'
DeploymentName : Template
ResourceGroupName : AzureRG
ProvisioningState : Succeeded
Timestamp : 4/29/2019 5:30:09 AM
Mode : Incremental
TemplateLink :
Parameters :
Name Type Value
=============================== ========================= ==========
location String Eastus
networkInterfaceName String azuretestvmnew975
networkSecurityGroupName String AzureTestVM-nsg-New
networkSecurityGroupRules Array [
{
"name": "RDP",
"properties": {
"priority": 300,
"protocol": "TCP",
"access": "Allow",
"direction": "Inbound",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "3389"
}
}
]
subnetName String default
virtualNetworkId String /subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGro
ups/AzureRG/providers/Microsoft.Network/virtualNetworks/Test-vnet
publicIpAddressName String AzureTestVM-ip-New
publicIpAddressType String Static
publicIpAddressSku String Standard
virtualMachineName String AzureTestVMNew
virtualMachineRG String AzureRG
osDiskType String Standard_LRS
virtualMachineSize String Standard_DS1_v2
adminUsername String Shashank
adminPassword SecureString
diagnosticsStorageAccountName String storageacc621
diagnosticsStorageAccountId String /subscriptions/6400b22f-788d-48da-a164-b915c9678bd1/resourceGro
ups/AzureRG/providers/Microsoft.Storage/storageAccounts/storageacc621
zone String 3
Outputs :
Name Type Value
=============== ========================= ==========
adminUsername String Shashank
DeploymentDebugLogLevel :
You can go ahead and change VM name and other parameters. Please note do not change Vnet and RG name. If you do so make sure you use Vnet and RG such that Vnet lies in the RG where you are deploying the VM.
Summary
In this article we saw how to create ARM template, edit it and use it to deploy Azure VM using PowerShell.
References
- Understand the structure and syntax of Azure Resource Manager templates
- Quickstart: Create and deploy Azure Resource Manager templates by using the Azure portal
- VM Create without specifying vnet name fails if VNET already exists in the resource group, but the location differs from where the VM is being created #984