This content originally appeared on DEV Community and was authored by Abhinav Pandey
While working in a cloud based environment, we are rapidly creating and deploying new resources. Azure provides few different ways to deploy new resources. Let's have a quick look:
- Portal - GUI based. Select the type of resource and set its properties. Azure will validate the settings provided by you and deploy the resource.
- Azure CLI(Bash)/Powershell/Cloudshell - Create resources using command line interface. Settings are provided as options while running commands. Can perform the same tasks as portal and some additional tasks like running powershell or bash scripts after resources are created. Scripts can also be used to group multiple tasks which should be performed together.
- Azure Resource Manager - Uses a template that contains the same information as we were providing in other tools. Main advantage over other tools is that the template can be shared across the team and can serve as the single source of truth regarding resource configurations. No developer or admin needs to remember the configurations to be used.
Features of ARM templates
- Declarative Syntax - Uses a JSON to describe properties
- Repeatable results - Multiple deployments of the template lead to exact same results.
- Can run external scripts - It can include additional scripts to be run after resources are created.
- Validation - The template is validated before creating any resources. For e.g. if multiple resources are declared to be created and one of them has a wrong configuration, the entire creation will not execute. This is useful for resources that are always created together. For e.g. VMs and Vnets.
- Integrates with CI/CD - Can be part of deployment pipeline where resources need to be created before deploying code. One common use is for Dev/Test environments.
Let's have a look at a sample template and identify its different components
// 20210516130113
// https://raw.githubusercontent.com/Abh1navv/azure-quickstart-templates/master/101-vm-simple-linux/azuredeploy.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"functions": [
{
"namespace": "quickstart",
"members": {
"uniqueName": {
"parameters": [
{
"name": "namePrefix",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[concat(toLower(parameters('namePrefix')), uniqueString(resourceGroup().id))]"
}
}
}
}
],
"parameters": {
"vmName": {
"type": "string",
"defaultValue": "simpleLinuxVM",
"metadata": {
"description": "The name of you Virtual Machine."
}
},
"adminUsername": {
"type": "string",
"metadata": {
"description": "Username for the Virtual Machine."
}
},
"authenticationType": {
"type": "string",
"defaultValue": "password",
"allowedValues": [
"sshPublicKey",
"password"
],
"metadata": {
"description": "Type of authentication to use on the Virtual Machine. SSH key is recommended."
}
},
"adminPasswordOrKey": {
"type": "securestring",
"metadata": {
"description": "SSH Key or password for the Virtual Machine. SSH key is recommended."
}
},
"dnsLabelPrefix": {
"type": "string",
"defaultValue": "[toLower(concat('simplelinuxvm-', uniqueString(resourceGroup().id)))]",
"metadata": {
"description": "Unique DNS Name for the Public IP used to access the Virtual Machine."
}
},
"ubuntuOSVersion": {
"type": "string",
"defaultValue": "18.04-LTS",
"allowedValues": [
"12.04.5-LTS",
"14.04.5-LTS",
"16.04.0-LTS",
"18.04-LTS"
],
"metadata": {
"description": "The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"VmSize": {
"type": "string",
"defaultValue": "Standard_B2s",
"metadata": {
"description": "The size of the VM"
}
},
"virtualNetworkName": {
"type": "string",
"defaultValue": "vNet",
"metadata": {
"description": "Name of the VNET"
}
},
"subnetName": {
"type": "string",
"defaultValue": "Subnet",
"metadata": {
"description": "Name of the subnet in the virtual network"
}
},
"networkSecurityGroupName": {
"type": "string",
"defaultValue": "SecGroupNet",
"metadata": {
"description": "Name of the Network Security Group"
}
}
},
"variables": {
"publicIpAddressName": "[concat(parameters('vmName'), 'PublicIP' )]",
"networkInterfaceName": "[concat(parameters('vmName'),'NetInt')]",
"subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]",
"osDiskType": "Standard_LRS",
"subnetAddressPrefix": "10.1.0.0/24",
"addressPrefix": "10.1.0.0/16",
"linuxConfiguration": {
"disablePasswordAuthentication": true,
"ssh": {
"publicKeys": [
{
"path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]",
"keyData": "[parameters('adminPasswordOrKey')]"
}
]
}
}
},
"resources": [
{
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2020-06-01",
"name": "[variables('networkInterfaceName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]",
"[resourceId('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
"[resourceId('Microsoft.Network/publicIpAddresses/', variables('publicIpAddressName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"subnet": {
"id": "[variables('subnetRef')]"
},
"privateIPAllocationMethod": "Dynamic",
"publicIpAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
}
}
}
],
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups',parameters('networkSecurityGroupName'))]"
}
}
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2020-06-01",
"name": "[parameters('networkSecurityGroupName')]",
"location": "[parameters('location')]",
"properties": {
"securityRules": [
{
"name": "SSH",
"properties": {
"priority": 1000,
"protocol": "TCP",
"access": "Allow",
"direction": "Inbound",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "22"
}
}
]
}
},
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2020-06-01",
"name": "[parameters('virtualNetworkName')]",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[parameters('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]",
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
]
}
},
{
"type": "Microsoft.Network/publicIpAddresses",
"apiVersion": "2020-06-01",
"name": "[variables('publicIpAddressName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Basic",
"tier": "Regional"
},
"properties": {
"publicIpAllocationMethod": "Dynamic",
"publicIPAddressVersion": "IPv4",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsLabelPrefix')]"
},
"idleTimeoutInMinutes": 4
}
},
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2020-06-01",
"name": "[quickstart.uniqueName(parameters('vmName'))]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces/', variables('networkInterfaceName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('VmSize')]"
},
"storageProfile": {
"osDisk": {
"createOption": "fromImage",
"managedDisk": {
"storageAccountType": "[variables('osDiskType')]"
}
},
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "[parameters('ubuntuOSVersion')]",
"version": "latest"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]"
}
]
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPasswordOrKey')]",
"linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), json('null'), variables('linuxConfiguration'))]"
}
}
}
],
"outputs": {
"adminUsername": {
"type": "string",
"value": "[parameters('adminUsername')]"
},
"hostname": {
"type": "string",
"value": "[reference(variables('publicIPAddressName')).dnsSettings.fqdn]"
},
"sshCommand": {
"type": "string",
"value": "[concat('ssh ', parameters('adminUsername'), '@', reference(variables('publicIPAddressName')).dnsSettings.fqdn)]"
}
}
}
This template is used to create a Linux VM on Azure. You can find details of the template at its github repo.
Let's look at a few important details from this sample.
Components of an ARM template
- Parameters - Parameters which can be accepted while deploying the template. Example from above
"parameters": {
"vmName": {
"type": "string",
"defaultValue": "simpleLinuxVM",
"metadata": {
"description": "The name of you Virtual Machine."
}
},
"authenticationType": {
"type": "string",
"defaultValue": "password",
"allowedValues": [
"sshPublicKey",
"password"
],
"metadata": {
"description": "Type of authentication to use on the Virtual Machine. SSH key is recommended."
}
}
}
vmName is the variable that the user can use to set name for the new VM. Optionally user can keep it empty and a defaultValue will be used. You can also specify limits on user inputs - In the second parameter authenticationType, we are specifying that only allowedValues are "sshPublicKey" and "password".
- Variables and Functions- Same as in any programming language, additional variables and functions can be defined for re-use while setting other properties. Variables example:
"osDiskType": "Standard_LRS",
"subnetAddressPrefix": "10.1.0.0/24",
"addressPrefix": "10.1.0.0/16",
Function example:
"namespace": "quickstart",
"members": {
"uniqueName": {
"parameters": [
{
"name": "namePrefix",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[concat(toLower(parameters('namePrefix')), uniqueString(resourceGroup().id))]"
}
}
}
}
Checks that the provided parameter namePrefix is unique. It is then used when we are setting our VM name.
- Resources - Define resources to create and their properties based on parameters provided and default values.
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2020-06-01",
"name": "[quickstart.uniqueName(parameters('vmName'))]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces/', variables('networkInterfaceName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('VmSize')]"
},
"storageProfile": {
"osDisk": {
"createOption": "fromImage",
"managedDisk": {
"storageAccountType": "[variables('osDiskType')]"
}
},
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "[parameters('ubuntuOSVersion')]",
"version": "latest"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]"
}
]
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPasswordOrKey')]",
"linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), json('null'), variables('linuxConfiguration'))]"
}
}
}
Have a look at how the syntax uses parameters, functions and variables:
name - we are using parameter vmName which we talked about earlier and checking if its unique with function defined above.
dependsOn - defines the resources that should be available before creating the VM. It also makes use of variable networkInterfaceName to specify the network interface dependency.
- Outputs - Details about created resources. The template deployment will return an object corresponding to each created resource. We can use the resource name to further read details of created resource. Example
"hostname": {
"type": "string",
"value": "[reference(variables('publicIPAddressName')).dnsSettings.fqdn]"
}
This code will look for publicIPAddressName object and read its dnsSettings.fqdn field to return its fully qualified domain name and return it with the output variable hostname. This is very useful in CI/CD workflows where we need resource details to immediately interact with it.
Deploy resources using the template
So we have now looked at the basic building blocks. Let's go ahead and see how to deploy the template using CLI.
Before deploying the template, make sure you are logged in to the CLI client -> select a subscription -> create resource group if it doesn't already exist.
Once we are ready, we can run the below command to deploy our templates.
az group deployment create --name "name of your deployment" --resource-group "resource-group" --template-file "./azuredeploy.json"
CLI will ask for required parameter upon execution. It will use default values wherever you do not provide values.
An alternate way is to use parameter files in which case we modify our command to point to a JSON to pick the parameters from. This also works great in automation workflows. The syntax is as below:
az group deployment create --name "name of your deployment" --resource-group "resource-group" --template-file "./azuredeploy.json" --parameters @azure.parameters.json
Once all values are provided, the deployment will be created -> accepted or rejected -> deployed if valid.
Thanks for reading! Stay tuned for more on Azure.
This content originally appeared on DEV Community and was authored by Abhinav Pandey
Abhinav Pandey | Sciencx (2021-05-16T08:30:53+00:00) Azure – Intro to ARM templates. Retrieved from https://www.scien.cx/2021/05/16/azure-intro-to-arm-templates/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.