Azure Linux VM with Infra

Getting Started on Azure VM and Infrastructure

When getting started on a new technology, one method is to take what you know and just do the same sort of things on the new platform.

For this guide, I will essentially do this: ❶ create network infrastructure and ❷ put a Linux VM machine on that network infrastructure.

We’ll also create some storage that can be used for boot diagnostics, should the system fail in a way that is not captured by the logs.

This article will teach two conceptual domains:

  • Azure cloud resources: Linux VM + network infrastructure
  • Terraform modularization using modules

Tool Requirements

  • Azure CLI tool (az): command line tool that interacts with Azure API
  • Terraform (terraform): command line tool to provision cloud resources easily

Overview of Azure cloud resources

These are the Azure cloud resources we will create and their relationships.

In Azure, access to a system is centered around a Virtual NIC (Network Interface Card) that will be attached to a IP on a subnet. We will attach a network security group to limit access and add a public IP address.

This virtual NIC will then be attached to the Linux VM.

This will become more clear with the Terraform code to define these resources.

The First Steps

If you do not have an Azure subscription, you can create a free account before starting this tutorial. Once this is created, you can login using Azure CLI with az login.

The Resource Group

Azure uses a resource group to organize a collection of resources. All resources will need to reference this resource group. Using the Azure CLI, we can create a resource group for the rest of this project and any other future projects.

az group create --location westus --resource-group devapp

With this, we can create a Terraform variable definition with these two values:

cat  <<-EOF >> terraform.tfvars
resource_group_name = "devapp"
location = "westus"

Choosing an operating system

When we get to the point of creating an Linux VM, we need to chose the operating system that will be installed on this system. We will go with Ubuntu.

Typically, you can get a list of available systems for UbuntuServer with the following command:

az vm image list --offer UbuntuServer --output table --all

The problem with this, as you can see, is that the latest LTS image is Ubuntu 18.04 from three years ago, April 26, 2018.

Below is a technique you can use to fetch more up to date Ubuntu versions:

OFFER=$(az vm image list-offers \
--location westus \
--publisher Canonical \
--output table | awk '/server-focal$/{print $2}'
SKU=$(az vm image list-skus \
--location westus \
--publisher Canonical \
--offer $OFFER
--output table | awk '/lts$/{print $2}')
# The values should be:
# * OFFER=0001-com-ubuntu-server-focal
* SKU=20_04-lts

With these values, we can add them to the existing Terraform variable definition file:

cat  <<-EOF >> terraform.tfvars
image_publisher = "Canonical"
image_offer = $OFFER
image_sku = $SKU

Terraform Module Structure

For this exercise, we’ll create two modules, azure_net for the subnet, and azure_vm for the virtual machine and virtual network components on that subnet.

To get started we’ll create the following structure:

├── azure_net
│ ├──
│ ├──
│ ├──
│ └──
├── azure_vm
│ ├──
│ ├──
│ ├──
│ ├──
│ └──
├── terraform.tfvars

We can create this in Bash with the following:

mkdir -p azure_vm
touch \
azure_vm/{main,network,outputs,provider,variables}.tf \
azure_net/{main,outputs,provider,variables}.tf \

Root Module

First let’s define the root module we want to create, and save this as

We have to define the variables as well, so copy this and safe as

For future usage, we’ll want to extract these output variables from the module we’ll create, so copy the following and save as

We have defined the above input variables in the Terraform variable definition except for computer_name and admin_username. We can add these now:

cat  <<-EOF >> terraform.tfvars
computer_name = "appvm"
admin_username = "azureuser"

Now that we have top-down view in how we want to use this module, here comes the hard part, creating the module azure_vm module to do the hard work.

The Azure Net Module

For any module, we’ll need the following components:

  • provider: what cloud resources we’ll use to create our resources
  • input variables: parameters we pass into the module that modifies (mutate) the behavior.
  • output variables: results of things we create that may be useful to the user.
  • resources: all of the resources we will create provided the above

The Provider

Naturally, it make sense to define the provider, which for Azure, this will work. Copy the following and save as azure_net/

The Input Variables

These input variables will modify or mutate the behavior of the module. Think of these like parameters you pass to a function, or command line arguments (flags) that you pass to a command line.

Copy the following and save as azure_net/

The Network Resources

The script will create a virtual network and then create a subnet within that virtual network. This subnet will be used later for the azure_vm module.

Copy the following and save as azure_net/

The Output Variables

After the network infrastructure is created, we will need output the subnet id that can be pass to azure_vm module.

Copy the following and save as azure_net/

The Azure VM Module

Similar to the Azure Net module we created earlier, we’ll add similar components: provider, input variables, output variables, and other Terraform scripts to create cloud resources.

The Provider

This is the exactly the same as the previous module. Copy the following and save as azure_vm/

The Input Variables

For the azure_vm module, we’ll need information about the desired Linux VM image to use for the virtual machine, an admin username, computer host name, as the subnet id.

Copy the following and save as azure_vm/

In case the caller did not specify default image, we default it to Ubuntu 18.04 Bionic Beaver.

The network resources

Now for the big one, we need to create the network resources for the virtual machine. This will include these resources:

  • virtual NIC (Network Interface Card) that will be added to the Linux VM
  • public IP address that will be added to the virtual NIC
  • network security group to allow SSH traffic

We’ll associate the network security group to the virtual NIC. Later this virtual NIC will be added to the Linux virtual machine. Copy the following and save as azure_vm/

The Virtual Machine resource

This will create the final virtual machine and connected to the network infrastructure. We’ll also create an SSH key for this system, so that we can access the system, and we’ll create some external storage so that we can add boot diagnostics for an extra layer of observability for this system.

Copy the following and save as azure_vm/

The Output Variables

We want to get the public IP address that is created as well as the secret key that can be used to access this system. Copy the following and save as azure_vm/

Running The Scripts

When ready, you can run all of this by the following:

terraform init
terraform apply

This will create the Linux VM system. We can access the system with the following:

# retreive the private key
output -raw tls_private_key > azure_vm.pem
chmod 400 azure_vm.pem
# NOTE: it may take time for the public IP to be assigned;
# after a few minutes run
terraform refresh
# fetch the public IP address
AZURE_VM=$(terraform output -raw public_ip)
# log into the system using the private key
azureuser@$AZURE_VM -i ./azure_vm.pem

Cleaning up

All of these resources can be deleted with:

terraform destroy

The resource group we created earlier can be destroyed with:

az group delete devapp


These are links I came across that you may find useful.

Microsoft Documentation

Source code

Related Articles


For this guide, I really want give a big shout out to Microsoft’s documentation team around Azure. Their guides are just incredible and really encourage learning and experimentation.

This guide is a good baseline to get started with Azure and Terraform with an introduction to creating modules.

There are many directions you can go, such as adding DNS server to refer to the public IP address with a friendlier name, scaling VMs horizontally with using VMSS (Virtual Machines Scale Sets), deploying a web application with secure access to databases, and running some change configuration on the systems to install a service.




Linux NinjaPants Automation Engineering Mutant — exploring DevOps, o11y, k8s, progressive deployment (ci/cd), cloud native infra, infra as code

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

The Programming War Between Python And Java.

From Zero to Hero

CSS at Scale: Cosmetic vs Layout Properties

The Basic Checklist For Finalizing a Web Hosting Provider

Django Raw Sql Queries

Mongo-TLS Implementation

Download In #PDF Programming with STM32: Getting Started with the Nucleo Board and C/C++ Read @book…

Programming with a purpose

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Joaquín Menchaca (智裕)

Joaquín Menchaca (智裕)

Linux NinjaPants Automation Engineering Mutant — exploring DevOps, o11y, k8s, progressive deployment (ci/cd), cloud native infra, infra as code

More from Medium

Running an NGINX Ingress Controller for each Kubernetes Namespace

Secure AKS Ingress with LetsEncrypt

Monitoring Azure Kubernetes Service (AKS)

Azure Kubernetes (AKS) Events with Azure Log Analytics