In this tutorial we will try provisioning a vm on azure using their terraform provider and reproduce the same deployment I have completed on AWS and Oracle Cloud. As usual we won’t just deploy an instance but also configure an nginx website linked to its public IP. I’ll end this post with some notes on Azure/AWS differences.
I used azurerm_linux_virtual_machine resource instead of the classic vm resource because azure decided it was better to split both windows/Linux for vm deployments(provider 2.0) which is unlike anything seen elsewhere.
Note: I have done the same task with my AZ-CLI based bash scripts (avaialble in Github) which was very helpful to understand the logic behind components needed during the vm creation (More details: deploy-webserver-vm-using-azure-cli).
Overview and Concepts
The following illustration shows the layers involved between your workstation an Oracle cloud infrastructure while running the terraform actions along with the instance attributes we will be provisioning.
- Files are merged in alphabetical order but resource definition order doesn't matter (subfolders are not read).
- Common configurations have 3 type of tf files and a statefile.
- 1- main.tf: terraform declaration code (configuration) . The file name can be anything you choose
2- variables.tf: Resource variables needed for the deploy
3- outputs.tf: displays the resources detail at the end of the deploy
4- terraform.tfstate: keeps track of the state of the stack(resources) after each terraform apply run
Example for a Vnet >>
1- Create a shell resource declaration for the vnet in a file called vnet.tf
2- Get the id of the vnet resource from your Azure portal
3- Run the Terraform import then run Terraform show to extract the vnet full declaration from azure to the same file (vnet.tf)
4- Now you can remove the id attribute with all non required attributes to create a vnet resource (Do that for each resource)
If you want to import all the existing resources in your account in bulk mode (not one by one) there are tools like py-az2tf or terraformer, which can import both code and state from your azure account automatically.
Terraform lab content: I have deliberately split this lab in 2:
- Vnet Deployment: To grasp the basics of a single resource deployment.
- Instance Deployment: Includes the instance provisioning configured as web sever(includes above vnet) .
Although I’m on windows I tried the lab using WSL (Ubuntu) terminal (but same applies to Mac).
Linux: Download, unzip and move the binary to the local bin directory
Once installed run the version command to validate your installation
To authenticate with your azure account, Terraform will only need you to login using az cli . This can be done by running az cli commands described here >> az-cli installation
- - I will assume that below authentication option is present in your workstation:
- AZCLI default profile configured with your azure credentials. Refer to my Blog post for more details
- I’ll also assume the presence of an ssh key pair to attach to your vm instance. If not here is a command to generate a PEM based key pair.
az account show
II. Clone the repository
- Pick an area that is close to your azure-terraform directory on your file system and issue the following command.
terraform-provider-azure/create-vnet/To grasp how we deploy a single Vnet.
terraform-provider-azure/launch-instance/For the final instance deploy.
III. Provider setup
INSTALL AND SETUP THE AZURE PROVIDER
- Cd Into “
terraform-provider-azure/create-vnet”where our configuration resides (i.e vnet)
- Azure provider plugin will be automatically installed by running
- Let's see what's in the
”create-vnet”directory. Here, only
*.tffiles matter (click to see content)
IV. Partial Deployment
DEPLOY A SIMPLE VNET
- Once the authentication is setup and provider installed , we can run
terraform plancommand to create an execution plan (quick dry run to check the desired state/actions). Specify the prefix for your resource names
- The output being too verbose I deliberately kept only relevant attributes for the Vnet resource plan
- Next, we can run
”terraform deploy”to provision the resources to create our Vnet (listed in the plan)
Note: We’ll now destroy the Vnet as the next instance deploy contains the same Vnet specs.
V. Full deployment (Instance)
- After our small intro to Vnet creation, let's launch a vm and configure nginx in it in one command.
- First we need to switch to the second directory
Here's the content:
- Cloud-init: is a cloud instance initialization method that executes tasks upon instance Startup by providing the user_data entry of the azure vm resource definition (See below). I have created enough for 6 OS’ (Centos,Ubuntu,Windows,RHEL,OL,SUSE)
- In my lab, I used cloud-init to install nginx and write an html page that will replace the HomePage at Startup.
- Make sure you your public ssh key is in your home directory or just modify the path below (see variables.tf)
LAUNCH THE INSTANCE
- Once in “
launch-instance”directory, you can run the plan command to validate the 9 resources required to launch our vm instance. The output has been truncated to reduce verbosity
Note: As you can see we have 2 additional files and one Subfolder. compute.tf is where the compute instance and all its attributes are declared. All the other “.tf” files come from my vnet example with some additions for variables.tf and output.tf
public_key = file("~/id_rsa_az.pub") } ## Change me
- Now let’s launch our CENTOS7 vm using terraform apply (I left a map of different OS ids in the variables.tf you can choose from)
- Once the instance is provisioned, juts copy the public IP address(i.e 126.96.36.199) in chrome and Voila!
- Although a bug of azurerm_linux_virtual_machine doesn’t show the public IP , just go to the console and copy past the IP into your browser
- You can also tear down this configuration by simply running terraform destroy from the same directory
- You can fetch any of the specified attributes in outputs.tf using terraform output command i.e:
- Terraform Console:
Although terraform is a declarative language, there are still myriads of functions you can use to process strings/number/lists/mappings etc. There is an excellent all in one script with examples of most terraform functions >> here
- I added cloud-init files for different distros you can play with by adapting var.user_data & var.OS
- Network In Azure every subnet is a public subnet because as soon as you associate a public IP to a Vm’s VNIC, you'll magically have internet access. Internet gateway is not needed here because system routes are taking care of that.
- CIDR range in azure is slightly larger than aws ( from /8 to /29).
- ID Azure doesn’t provide regular alpha numeric IDs for its resources but a sort of path based identification (see below)
$ SUBNET ID
- Naming Case insensitive but unique, a resource group can’t have 2 resources of the same type with the same name
- Bummer: I wish azure kept one terraform resource to create vms instead of having one for Linux and one for Windows since v2.0. This implies that we will have extra blocks/modules if we want to do both in one stack instead of leveraging loops. The classic one is not handy for Linux either.
- We have demonstrated in this tutorial how to quickly deploy a web server instance using terraform in Azure and leverage Cloud-init to configure the vm during the bootstrap .
- Remember that all used attributes in this exercise can be modified in the
- Route table and internet gateway setting in our code were replaced by the Public IP and VNICs
- I found azure vm spinning time so slow comparing to AWS or OCI, not sure if it’s it’s regional thing
- Improvement: I will look to improve the compute.tf code to allow both windows & Linux type of resource to provision. Validate that userdata works for windows too, unlike on az cli.
Another improvement can be reached in terms of display of the security rules using formatlist .
Differences between Azure/AWS & things I wish azure kept simple
Thank you for reading!