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.