Posted in:

I’ve been really impressed with the Azure CLI, and have been using it to automate all kinds of things recently. Here’s some instructions on how you can create and configure an Azure Virtual Machine using the CLI.

1. Pick an image and size

If you’re going to create a Virtual Machine, you need to do so from a base image. Azure has hundreds to choose from, and so you can use the az vm image list command with the --all flag specified in order to find a suitable one.

For example if I want to find all VM images with elasticsearch in the “offer” name I can use:

az vm image list --all -f elasticsearch -o table

Or if I know I want to use the VS-2017 “sku” for a Visual Studio 2017 VM I can use:

az vm image list -s VS-2017 --all -o table

You’ll also want to decide what VM size you want. There are loads available, but not necessarily all in in every region, so you can check what sizes are available in your location with the following command:

az vm list-sizes --location westeurope -o table

2. Create the VM

Resources which share a common lifetime should be in the same resource group. It makes sense to create your VM in its own resource group, so that when you’re done you can clear it up by deleting the resource group. So let’s create a resource group, using a variable containing its name for convenience in future commands:

ResourceGroupName="CreateVmDemo"
az group create --name $ResourceGroupName --location westeurope

Now we’re ready to create our VM in this resource group. There are loads of parameters to az vm create, which you can explore with the az vm create -h command. The good news is that lots of sensible defaults are picked for you so you don’t have to provide values for everything. However, it will choose for you to have a reasonably powerful VM with a managed disk by default, so if you want to keep costs down you might want to supply some money saving parameters like I show below.

In my example I’m using the Windows 2016 data center VM image and supplying my own username and password. I’m going for a smaller VM size and the cheaper option of using unmanaged disks.

VmName="ExampleVm"
AdminPassword="ARe@11y5ecur3P@ssw0rd!"

az vm create \
    --resource-group $ResourceGroupName \
    --name $VmName \
    --image win2016datacenter \
    --admin-username azureuser \
    --admin-password $AdminPassword \
    --size Basic_A1 \
    --use-unmanaged-disk \
    --storage-sku Standard_LRS

This will take a few minutes to complete, and it’s created more than just a VM. There’s a network interface, a network security group, a public IP address and a disk (or storage account for the VHD if you chose unmanaged disk).

You can see everything that got created with:

az resource list -g $ResourceGroupName -o table

3. Configure the VM

For this demo, I’m going to show how we can configure the VM as a simple web server. First we need to ensure port 80 is open. We can do that easily with:

az vm open-port --port 80 --resource-group $ResourceGroupName --name $VmName

The next step is to install IIS and set up our website. Here’s a simple example of a PowerShell script I might want to run on the VM in order to get the website set up. It installs the IIS windows feature, then downloads a simple webpage from a public URL (but this could easily be securely downloading a zip with a Shared Access signature), then deletes the default website and creates a new website pointing at our custom web page.

Install-WindowsFeature -Name Web-Server
$sitePath = "c:\example-site"
$output = "$sitePath\index.html"
$url = "https://mystorage.blob.core.windows.net/public/index.html"
New-Item -ItemType Directory $sitePath -Force
Invoke-WebRequest -Uri $url -OutFile $output
Remove-Website -Name 'Default Web Site'; `
New-Website -Name 'example-site' `
         -Port 80 -PhysicalPath $sitePath

But how can we get this script to run on our virtual machine? Well, we can use the Custom Script Extension. This allows us to either provide a simple command to run, or if its more complex like this, a URI for a script to download and then run.

Here’s how we can invoke the custom script on our VM:

az vm extension set \
--publisher Microsoft.Compute \
--version 1.8 \
--name CustomScriptExtension \
--vm-name $VmName \
--resource-group $ResourceGroupName \
--settings '{"fileUris":["https://my-assets.blob.core.windows.net/public/SetupSimpleSite.ps1"],"commandToExecute":"powershell.exe -ExecutionPolicy Unrestricted -file SetupSimpleSite.ps1"}'

By the way, if you’re doing this from a PowerShell prompt instead of a bash prompt, getting the quotes escaped correctly in the settings parameter can be a real pain. I find its easier to just pass the path of a file containing the JSON like this

az vm extension set `
    --publisher Microsoft.Compute `
    --version 1.8 `
    --name CustomScriptExtension `
    --vm-name $VmName `
    --resource-group $ResourceGroupName `
    --settings extensionSettings.json

This will take a minute or two (and will be a bit slower if you chose the cheap options like I did), but assuming it completes successfully we now have our fully configured VM. To check it worked, we can visit its public IP address in a web browser, but in case we forgot what that was, we can query for it with:

az vm show -d -g $ResourceGroupName -n $VmName --query "publicIps" -o tsv

Notice I like to use the tab separated output for this when I’m getting just a single value, as it allows me to easily store the result in a variable. You can learn more about this in my blog on using queries with the Azure CLI.

Tip: if your custom script fails for some reason, you can troubleshoot by RDPing into the machine and looking at the logs in the “C:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension” folder.

4. Stopping or Deleting the VM

Obviously, once you’ve created your Virtual Machine you’re going to be paying for it, and that can be very expensive if you chose a powerful VM size. You might think that you could save money by stopping it with az vm stop, but you’d be wrong. You still pay for stopped VMs in Azure – they have to be “stopped deallocated” in order for you not to be billed.

Fortunately, there’s an easy way to put our VM into a stopped deallocated state with the Azure CLI:

az vm deallocate -n $VmName -g $ResourceGroupName

And we can check it worked with:

az vm show -d -g $ResourceGroupName -n $VmName --query "powerState" -o tsv

Of course, when you’re ready to use it again it’s a simple matter of az vm start to get it going again, and of course the public IP address will be different as it was relinquished when we deallocated the VM.

Finally, when you’re all done with the VM, you’ll need to clear up after yourself, and there’s a nice easy way to do that with az group delete, passing the --yes flag if you don’t want to be asked “are you sure”. Of course, only run this if you are sure – there’s no undo!

az group delete --name $ResourceGroupName --yes

Summary

The Azure CLI is a great tool to have at your disposal for all kinds of VM management tasks. There are loads more great examples on the Azure CLI docs site for both Windows and Linux VMs.

Want to learn more about the Azure CLI? Be sure to check out my Pluralsight course Azure CLI: Getting Started.