Learn Terraform – deploy the first VM

After terraform is installed on the computer you use to deploy your first cloud resources – the initial sample in chapter 2 is to deploy a VM. A Linux based VM. You can find all the samples out of the book from Yevgeniy under this repro in GitHub.

So how do we do this in azure?

Deploy your first VM in Azure

One main difference to aws is that in Azure we deploy resources always in a resource group (RG) – so the script in Terraform is not so easy as in aws.

    resource "azurerm_resource_group" "RG" {
        name = "myFirstTerraform-RG"
        location = "westeurope"

So having the resource group deployed we can use it to put the VM in there. that’s not all we need to deploy a VM in Azure. Let’s have a look at the documentation of the Azure provider at HashiCorp for the “azurerm_virtual_machine”. And as you read the introduction you see, there is another resource type in the provider to directly focus on a Linux VM – “azurerm_linux_virtual_machine”.

Going thru the documentation we will find, that we need the following CONFIG arguments for a Linux VM:

  • admin_username
  • location
  • name
  • network_interface_ids
  • os_disk
  • resource_group_name
  • size

and for sure one of these:

  • admin_password
    (if this option is used – be aware that _disable_passwordauthentication must be set to false)
  • admin_ssh_key

So if we compare this with a simple example in chapter 2 of the book – Azure seems to be more complex. For me it looks like that in aws you can a template and aws will add every configuration you needed with some default values (see page 43). So the Azure Resource Manager needs the configuration defined by the admin or better the script.

So let’s get back to our script.

We already have now the RG in it. Now we need to deploy a network:

resource "azurerm_virtual_network" "vnet" {
  name                = "myFirstVnet"
  address_space       = [""]
  location            = "myFirstTerraform-RG"
  resource_group_name = "westeurope"

In the Book, the concept of “DRY” is explained later, but I think it is good to think about it now. “DRY” means “don’t repeat yourself”. It would be no good idea to reference the location and the ressource_group_name by filling in the values defined in the script before. It is better to point to the values.


If we want to reference the name of the RG, we have to use:


and the same with the location used in the definition of the RG:


Taking this into the code leads to the following

resource "azurerm_virtual_network" "vnet" {
  name                = "myFirstVnet"
  address_space       = [""]
  location            = azurerm_resource_group.myFirstTerraform-RG.name
  resource_group_name = azurerm_resource_group.myFirstTerraform-RG.location

What is important to understand that terraform is now able to identify dependencies between the resources and can handle the order of deploying the needed resources (see Chapter 2 in the book page 51). The next steps in your terraform script would now to add all needed Resources including references as well as the VM itself.

The whole script would look like this:

## Deploy your first VM to Azure
## based on an Ubunto Image

## Define the provider
provider "azurerm" {
  version = "~>2.0.0"
  features {}

## define the resource gruop
resource "azurerm_resource_group" "myFirstTerraform" {
  name     = "myFirstTerraform-RG"
  location = "West Europe"

## define the network
resource "azurerm_virtual_network" "myFirstTerraform" {
  name                = "myFirstTerraform-vNet"
  address_space       = [""]
  location            = azurerm_resource_group.myFirstTerraform.location
  resource_group_name = azurerm_resource_group.myFirstTerraform.name

## define the subnet
resource "azurerm_subnet" "myFirstTerraform" {
  name                 = "internal"
  resource_group_name  = azurerm_resource_group.myFirstTerraform.name
  virtual_network_name = azurerm_virtual_network.myFirstTerraform.name
  address_prefix       = ""

## define a public ip for the vm to ssh in
resource "azurerm_public_ip" "myFirstTerraform" {
  name                = "myFirstTerraform-pip"
  location            = azurerm_resource_group.myFirstTerraform.location
  resource_group_name = azurerm_resource_group.myFirstTerraform.name
  allocation_method   = "Dynamic"

## define the networkinterface
resource "azurerm_network_interface" "myFirstTerraform" {
  name                = "myFirstTerraform-nic"
  location            = azurerm_resource_group.myFirstTerraform.location
  resource_group_name = azurerm_resource_group.myFirstTerraform.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.myFirstTerraform.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.myFirstTerraform.id

## define the VM
resource "azurerm_linux_virtual_machine" "myFirstTerraform" {
  name                = "myFirstTerraform-vm"
  resource_group_name = azurerm_resource_group.myFirstTerraform.name
  location            = azurerm_resource_group.myFirstTerraform.location
  size                = "Standard_B2s"
  computer_name = "myFirstLinuxVM"
  admin_username = "adminuser"
  admin_password = "Password1234!"
  disable_password_authentication = false
  network_interface_ids = [

    os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "16.04-LTS"
    version   = "latest"

CAUTION We do not have defined any firewall in front of the VM. In azure we need e.g. a network security group for this, so one more component should be added to your script and should be applied to the network înterface.

To define a network security group in the script:

resource "azurerm_network_security_group" "myFirstTerrafrom" {
    name                = "myFirstTerrafrom-NSG"
    location            = azurerm_resource_group.myFirstTerraform.location
    resource_group_name = azurerm_resource_group.myFirstTerraform.name

    security_rule {
        name                       = "SSH"
        priority                   = 1001
        direction                  = "Inbound"
        access                     = "Allow"
        protocol                   = "Tcp"
        source_port_range          = "*"
        destination_port_range     = "22"
        source_address_prefix      = "*"
        destination_address_prefix = "*"

and we need the assignment to the network interface:

resource "azurerm_network_interface_security_group_association" "myFirstTerraform" {
    network_interface_id      = azurerm_network_interface.myFirstTerraform.id
    network_security_group_id = azurerm_network_security_group.myFirstTerraform.id

As soon as I have finished the whole chapter 2 – I will provide a GitHub Repro with all the information.

Learn Terraform – get started…

After reading Chapter 1 of the book it was time to get my machine ready for using Terraform to script the deployments in Azure. So I search the Microsoft Docs for a short guide and found this short description.

First Step – Install Terraform on my machine :

I decided to install Terraform in the Windows Subsystem for Linux (WSL) on my Windows10 machine. I’m on the fast-ring in the Windows10 insider program – so I’m already able to use WSL2. If you want to learn more about WSL2 visit the Microsoft page https://aka.ms/wsl2. To install Terraform in my WSL2 I opened the bash and entered the following command (use sudo of needed to have privileged rights):

# First - install unzip

apt-get install unzip

# Download the installation for Terraform (check for newest version before...)

wget https://releases.hashicorp.com/terraform/0.12.6/terraform_0.12.6_linux_amd64.zip

# Unzip the Terraform folder

unzip terraform_0.12.6_linux_amd64.zip terraform

# Move the folder to an accessible path for the system

mv terraform /usr/local/bin/

# test the Terraform version on your WSL machine

terraform --version

If everything worked fine, you will now have Terraform on your WSL to start using it.

Second Step – get Terraform connected

As soon as you want Terraform to apply your script in your Azure Tenant the best way is to deploy a service principal in your Azure Active Directory with the needed contributor rights. You can find these guide in the Microsoft Docs.

The last action is to define the environment variables for Terraform to use the just created credentials.

Last Step – deploy your first resource

Now that the environment is set up properly – we can deploy our first Azure resource. At what is the best first resource – yes, a resource group!

We need our first Terraform script. I made a new folder in the WSL and started my Visual Studio Code (VSCode) in this folder by just typing code . to start the UI in that folder and created my first script named main.tf and entered the following code:

# main.tf

provider "azurerm" {
  version = "~>2.0.0"
  features {}

resource "azurerm_resource_group" "rg" {
        name = "myFirstTerraform-RG"
        location = "westeurope"
        tags = {
            source = "Terraform"

After saving the script I opened the terminal in VSCode and run my first Terraform command:

terraform init

If all environment variables are set correctly and we made no other typing error Terraform will interpret the provider statement and will download the needed Azure provider. This done we can apply our script to Azure by typing

terraform apply

We only have to type one final “yes” into the console and our first resource group is deployed in Azure. So let’s open the Azure portal and check:

That’s it – our first Terraform script was successfully been deploy to Azure!

Learn Terraform

How I started…

I just decided to learn more about using Terraform to deploy services in azure. In the past, I deployed most of the time my services in the Azure by using the portal, the azure-CLI or using ARM templates. During a lot of discussions around automation, I heard a lot of people talking about Terraform as their choice for scripting their deployments. Especially thanks to my colleague Arnaud Lheureux – we sit together on the Microsoft Ready in the booth around the Cloud Adoption Framework and he showed me what he already has done with Terraform to deploy a landing zone in Azure. That was the impulse to start to learn more about Terraform…

Please visit Arnaud’s web post about using Terraform in the context of landing zones in the Microsoft Cloud Adoption Framework for Azure! (https://www.arnaudlheureux.io/2020/01/31/understanding-landing-zones-for-azure-cloud-adoption-framework/)

The next step was not to search all over the web to find some articles around that topic. My next step was to search for a book to start my learning. I found the following book (https://www.terraformupandrunning.com/) by Yevgeniy Brikmann:

So I ordered the book and was impatient starting my journey with Terraform. As the book arrived I started directly with the first chapters and quickly find out, that all samples in this book provided by Yevgeniy were related to AWS. So I directly considered – that will be my challenge! Bring all samples to Azure and write them down in this blog.

I wrote Yevgeniy a short message to double-check if this would be alright for him – thanks Yevgeniy for your consent!

So stay tuned to read about my first steps with Terraform and the adoption of the samples to azure.

Part 1 – Get started…

Part 2 – Deploy your first VM

Part 3 – Deploy web service

Part 4 – Deploy a VM Scale Set

Let’s Start…

Maybe some of you knew my German Blog https://www.blogaboutcloud.de. On this page I try to share my learning experiences around Azure and other Microsoft Cloud related topics. During the part month a started to learn a lot of new thinks as well as training others on Azure related topics.

I learn for myself, it is easier to learn something new, if you try to explain it to others. That is the idea behind this site. As often as I start to learn something new, I will try to write about it here.

I hope some of you will like to read…