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"
EOF
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
EOF
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
│ ├── main.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── variables.tf
├── azure_vm
│ ├── main.tf
│ ├── network.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── variables.tf
├── main.tf
├── terraform.tfvars
└── variables.tf
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 \
{main,variables}.tf
Root Module
First let’s define the root module we want to create, and save this as main.tf
:
We have to define the variables as well, so copy this and safe as variables.tf
:
For future usage, we’ll want to extract these output variables from the module we’ll create, so copy the following and save as outputs.tf
:
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"
EOF
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/provider.tf
.
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/variables.tf
:
The Network Resources
The main.tf
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/main.tf
:
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/outputs.tf
:
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/provider.tf
.
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/variables.tf
:
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/network.tf
:
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/main.tf
:
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/outputs.tf
:
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
terraform 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
terraform refresh # fetch the public IP address
AZURE_VM=$(terraform output -raw public_ip)# log into the system using the private key
ssh 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
Resources
These are links I came across that you may find useful.
Microsoft Documentation
- Configure a Linux VM with infrastructure in Azure using Terraform: https://docs.microsoft.com/azure/developer/terraform/create-linux-virtual-machine-with-infrastructure
Source code
Related Articles
Conclusion
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.