Azure conditional VM template

Lately I have been looking for a quickstart template, or something similar, which can deploy a VM with Linux or Windows Server based on parameters. Couldn’t initially find a clear cut, finished template, and at first I thought to just write two different templates; one for Windows Server and one for Linux. 

After some initial writing on two separate templates, I figured there would be many similarities between the two, and I needed to brush up on template functions anyway. The resulting template is based on a quickstart template, and a blog post from MS. I have later found some posts with similar information, but why not make my own spin on it. That’s how you learn!

The template and parameter file can be found in my public GitHub repository.

Prerequisites

You need a few things to try this:

  • An active Azure subscription.
  • An Azure account.
  • A resource group.
  • A virtual network.
  • Az CLI or Az PowerShell Module

Create Resource Group and vnet

I mostly prefer using PowerShell, because this has been my go-to-scripting language the last few years, but will outline Az CLI here also. Run the following commands in your environment of choice. Ever since Windows Terminal and Windows Subsystem for Linux 2 (WSL2), I have been running Ubuntu with PSCore. Also running docker desktop with WSL2 support, but that’s a different story.

Powershell

Login-AzAccount

$rg = New-AzResourceGroup -Name rg-vmdeploytest `
 -Location westeurope

$vnet = New-AzVirtualNetwork `
  -ResourceGroupName rg-vmdeploytest `
  -Location westeurope `
  -Name vnet-myVnet `
  -AddressPrefix 10.254.0.0/16

$subnet = Add-AzVirtualNetworkSubnetConfig `
  -Name subnet1 `
  -AddressPrefix 10.254.0.0/24 `
  -VirtualNetwork $vnet

$vnet | Set-AzVirtualNetwork

Azure CLI

az login
az group create --name rg-vmdeploytest \
    --location westeurope

az network vnet create --resource-group rg-vmdeploytest \
    --name vnet-myVnet \
    --address-prefix 10.254.0.0/16 \
    --subnet-name subnet1 \
    --subnet-prefix 10.254.0.0/24

ARM Template

I will assume you have at least a fundamental understanding of how ARM templates work, so I will not go into detail on the entire template. Some key elements are nevertheless worth pointing out.

In this template ARM Template functions are used to assert if a Windows Server or Linux VM should be deployed. 

Parameters

You choose the VM type with a simple “platform” parameter:

"platform": {
    "type": "string",
    "defaultValue": "WinSrv",
    "allowedValues": [
        "WinSrv",
        "Linux"
    ],
    "metadata": {
        "description": "Select the OS type to deploy."
    }
}

The default value is “WinSrv”, so if nothing is specified, a Windows Server will be deployed. If “Linux” is specified, an Ubuntu server will be deployed. Always the latest available version of the image.

You can choose password or ssh public key for authentication (defaults to password auth):

"authenticationType": {
    "type": "string",
    "defaultValue": "password",
    "allowedValues": [
        "password",
        "sshPublicKey"
    ],
    "metadata": {
        "description": "Select the authentication type."
    }
}

SSH authentication is recommended for Linux VMs, but not mandatory.

Variables

There are three important variables for the purpose of OS selection:

"Linux": {
    "publisher": "Canonical",
    "offer": "UbuntuServer",
    "sku": "[parameters('ubuntuOSVersion')]",
    "version": "latest"
        },
"WinSrv": {
    "publisher": "MicrosoftWindowsServer",
    "offer": "WindowsServer",
    "sku": "[parameters('windowsOSVersion')]",
    "version": "latest"
        },
"linuxConfiguration": {
    "disablePasswordAuthentication": true,
    "ssh": {
        "publicKeys": [
            {
                "path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]",
                "keyData": "[parameters('adminPasswordOrKey')]"
            }
        ]
    }
 }

Resources

This template creates the following in a single resource group:

  • Storage account for diagnostics
  • NSG for some VM protection
  • Public IP for management (testing only!)
  • NIC for the VM
  • VM

This is mostly basic stuff, but the important parts are in osProfile and storageProfile for the VM.

"osProfile": {
    "computerName": "[parameters('vmName')]",
    "adminUsername": "[parameters('adminUsername')]",
    "adminPassword": "[parameters('adminPasswordOrKey')]",
    "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), json('null'), variables('linuxConfiguration'))]"
}

This block evaluates if you have entered password or sshPublicKey. If you have entered password (or used the default), there will be no “linuxConfiguration” content, and no SSH login is available after deployment.

If you enter sshPublicKey, the linuxConfiguration variable will be used. This configures an ssh public key, and password authentication is disabled.

"storageProfile": {
    "imageReference": {
        "publisher": "[if(equals(parameters('platform'), 'WinSrv'), variables('WinSrv').publisher, variables('Linux').publisher)]",
        "offer": "[if(equals(parameters('platform'), 'WinSrv'), variables('WinSrv').offer, variables('Linux').offer)]",
        "version": "latest",
        "sku": "[if(equals(parameters('platform'), 'WinSrv'), variables('WinSrv').sku, variables('Linux').sku)]"
    }
}

This block bases deployment on a logical function (if) and a comparison function (equals).

If the platform chosen is WinSrv, the properties from WinSrv variable are used. If platform chosen is Linux, the properties from Linux variable are used. When a different flavor of Linux is needed, you just have to update the Linux variable with the correct publisher and image name.

Another thing to note is the automatic creation of an NSG. The NSG will be created for you by default, and gives some basic network protection. If you enter your client public IP, a security rule for RDP and SSH is also automagically created.

In summary

There you have it. A template for creating Windows Server or Linux VM based on parameter input. This is not the one right way of doing it, and there will be many other methods for reaching this outcome. Some templates can be more complex, some are maybe simpler.

You can of course use this template as it is, or you can modify it. It can be modified to create the necessary vnet, if you want. The same principles can be used for many other template types, and I encourage making reusable templates. That way you can always reap the benefits of your previous hard work.

If you find any errors or issues, please let me know and I will try to fix it, or assist in the deployment.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.