Provisioning Resources in Azure using Terraform

26/10/2019

Overview

Azure is a public cloud provided by Microsoft and is similar to other clouds you may be familiar with such as Amazon Web Services (AWS) and the Google Cloud Platform (GCP) . All of these clouds allow you to host your web apps and services without having to maintain your own hardware. In recent years there has been a huge increase in cloud adoption and as a result a number of supporting technologies have been developed. One example of these technologies is Terraformwhich allows you to describe your Infrastructure as Code (IaC) and automate the provisioning or destructuion of it. This post will show you how to get started with provisioning resources using Terraform with Azure. It should also be noted that Terraform isn't cloud specific and can be used with a range of cloud providers - making it a great language to learn if you're interested in devops!

Setup an Account with Microsoft Azure

  • Go to the Azure Website and setup a free account. You'll need this so that you can later use terraform to authenticate with your account to provision resources.

Configure Dev Environment for Terraform

  • Download the latest version of Terraform from theTerraform Website and follow the installation instructions depending on your OS.
  • Once Terraform's installed make sure it's on your PATH. To check this run terraform -vfrom a shell of your choice. If you see Command not found you'll need to add the location of the Terraform binary to your PATH.
  • You'll also need a text editor. I'd recommend VS Code due to it having a range of extensions for Terraform which provide syntax highlighting and code snippets, but it's completely up to you.

Authenticate Shell to link to Azure Subscription

  • Install the latest Azure CLI from here and make sure it is on your PATH. The Azure CLI provides an API to interact with your Azure account. We'll be using it to authenticate your shell to allow Terraform to create and destroy your Azure account's resources.
  • Copy the following bash script (courtesy of @TheYorkshireDev) and save it in a file called azure-login.sh. Next run the script from the shell session you will be using with Terraform. The script will open a browser tab for you to login to your account. After logging in follow the instructions in the shell to set the Azure subscription you want Terraform to provision resources in. Make sure you keep the shell open to run your Terraform commands from later on.
echo -e "************************************"
echo -e "Authenticating Shell using Azure CLI"
echo -e "************************************\n"

subscriptions="$(az.cmd login -o tsv | awk 'BEGIN {'{ FS = "t" }'} ; {'{ print $2 " | " $4 }'}')"

echo "Subscription ID | Subscription Name"
echo "------------------------------------ | ------------------------------------"
echo "$subscriptions"

echo -e "\nEnter Subscription ID:"
read azure_subscription

echo -e "Setting account subscription"
az.cmd account set --subscription="${azure_subscription}"

Provision Resources using Terraform

  • Copy the following code into a main.tf file in a directory of your choice.
variable "prefix" {
  default = "test-dgio"
}

resource "azurerm_resource_group" "test_rg" {
  name     = "${var.prefix}-rg"
  location = "East US"
}

resource "azurerm_virtual_network" "test_vnet" {
  name                = "${var.prefix}-vnet"
  address_space       = ["10.0.0.0/24"]
  location            = azurerm_resource_group.test_rg.location
  resource_group_name = azurerm_resource_group.test_rg.name
}

resource "azurerm_subnet" "test_subnet" {
  name                 = "${var.prefix}-vnet"
  resource_group_name  = azurerm_resource_group.test_rg.name
  virtual_network_name = azurerm_virtual_network.test_rg.name
  address_prefix       = "10.0.0.0/27"
}

resource "azurerm_public_ip" "test_pip" {
  name                = "${var.prefix}-pip"
  location            = azurerm_resource_group.test_rg.location
  resource_group_name = azurerm_resource_group.test_rg.name
  allocation_method   = "Static"
}

resource "azurerm_network_interface" "test_nic" {
  name                = "${var.prefix}-nic"
  location            = azurerm_resource_group.test_rg.location
  resource_group_name = azurerm_resource_group.test_rg.name

  ip_configuration {
    name                          = "test-ipconfig"
    subnet_id                     = azurerm_subnet.test_subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.test_pip.id
  }
}

resource "azurerm_network_security_group" "test_nsg" {
  name                = "${var.prefix}-nsg"
  location            = azurerm_resource_group.test_rg.location
  resource_group_name = azurerm_resource_group.test_rg.name
}
    
resource "azurerm_network_security_rule" "test_nsr" {
  name                        = "Allow_All_SSH_Inbound_VM"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "TCP"
  source_port_range           = "*"
  destination_port_range      = "22"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.test_rg.name
  network_security_group_name = azurerm_network_security_group.test_nsg.name
}

resource "azurerm_virtual_machine" "test_vm" {
  name                  = "${var.prefix}-vm"
  location              = azurerm_resource_group.test_rg.location
  resource_group_name   = azurerm_resource_group.test_rg.name
  network_interface_ids = [azurerm_network_interface.test_rg.id]
  vm_size               = "Standard_DS1_v2"

  delete_os_disk_on_termination    = true
  delete_data_disks_on_termination = true
  network_security_group_id        = azurerm_network_security_group.test_nsg.id

  storage_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }

  storage_os_disk {
    name              = "${var.prefix}-osdisk"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }

  os_profile {
    computer_name  = "${var.prefix}-vm"
    admin_username = "testadmin"
    admin_password = "Password1234!"
  }

  os_profile_linux_config {
    disable_password_authentication = false
  }
}
  • The code above will create a Virtual Machine that you'll be able to SSH in to.
  • Using the shell you ran azure-login.sh from cd into the directory where you savedmain.tf. From here run terraform plan where you will see the resources Terraform has interpreted you want from reading the main.tf file.
  • Next run terraform apply. This will provision the resources outlined from the plan and you can see the resources by visiting the Azure Portal.
  • Using an SSH client of your choice connect to the Virtual Machine you've just provisioned. You'll find the Public IP of your VM in the Azure Portal.