Terraform and Azure (Infrastructure as Code) – Part 2

Changing Azure Subscriptions

In part 1 I discussed logging into Azure using Azure CLI and noting the default subscription, as that is where Terraform will deploy resources to. However, you may have multiple Azure subscriptions and want to deploy to one that is not the default. This can be accomplished using the Azure CLI:

az account list

This will list the accounts / subscriptions that you have access to.

az account show

This lists details about the current account / subscription that you are currently using.

az account set --subscription "subscription_name_here"

Changes you to the subscription you have named. If you run “az account” show pre and post the “az account set” command you will be able to see the subscription change has taken effect.

If changing the account / subscription via the Azure CLI seems a bit messy then Terraform also includes an option to define which subscription the build should take place within. Open the main.tf file and edit:

provider "azurerm" {
    features{}
}

To include a new line indicating the subscription ID of the subscription you want to build in:

provider "azurerm" {
    subscription_id = "subscription_ID_here"
    features {}
}

The subscription ID can be found using the Azure CLI commands listed above. Terraform will now deploy to this subscription, even if another subscription is set within the Azure CLI.

azurerm_virtual_machive Vs azurerm_linux_virtual_machine

In part 1 I used the resource “azurerm_virutal_machine” which can be used to create a Windows virtual machine or a Linux virtual machine. However, as I know I want a Linux virtual machine and I want the options that come with using a Linux virtual machine I can change this to use the “azurerm_linux_virtual_machine” resource instead.

The azurerm_linux_virtual_machine has some changes to settings:

SettingChanged?Now Known As
namenon/a
locationnon/a
resource_group_namenon/a
network_interface_idsnon/a
vm_sizeyessize
storage_os_diskyesos_disk
storage_image_referenceyessource_image_reference
os_profileyesoptions now sit on their own (i.e. computer_name is now outside of os_profile)
os_profile_linux_configyesoptions now sit on their own (i.e. disable_passworth_authentication is now outside of os_profile_linux_config
A table of some fo the changes between azurerm_virtual_machine and azurerm_linux_virtual_machine

Using the virtual machine example from part 1 it would now look like:

resource "azurerm_linux_virutal_machine" "vm" {
    name = var.servername
    location = var.location
    resource_group_name = azurerm_resource_group.rg.name
    network_interface_ids = [azurerm_network_interface.nic.id]
    size = var.vm_size

    os_disk {
        name = "os-${var.servname}-disk"
        caching = "ReadWrite"
    }

    source_image_reference {
        publisher = var.os.publisher
        offer = var.os.offer
        sku = var.os.sku
        version = var.os.version
    }

    
        computer_name = var.servername
        admin_username = var.admin_username
        admin_password = var.admin_password
        disable_password_authentication = false
    
    tags = {
        ...
    }

SSH Key

Using a password with SSH, even SSH locked down to certain IP addresses is not recommended in a production environment so we need to add an SSH key to the virtual machine during the Terraform process. To add a public key to the virtual machine add the following lines to azurerm_linux_virtual_machine resource in main.tf:

admin_ssh_key {
    username = var.admin_username
    public_key = file("path_to_file_here")
    }

With these lines added the “disable_password_authentication” can be set to true and the private key (partner to the public key) can be used to connect via SSH to the virtual machine.

Automating / Scripting

Terraform building a virtual machine (and appropriate Azure resources) is pretty cool, but having to log in to add applications or configuration seems a little poor. Thankfully Azure has a “custom data” option for virtual machines (like AWS’ EC2 User Data) that can run scripts as the machine starts for the first time, and as its an option in Azure it is also available for use in Terraform.

To utilise this option create a bash script e.g. here’s one for installing Apache on an Ubuntu based virtual machine:

#! /bin/bash
sudo apt-get update
sudo apt-get install -y apache2

Save the script (e.g. as apache_install.sh) in a location you can navigate to. Add a new section to the main.tf Terraform file, I generally put this above the “resource azurerm_linux_virtual_machine” section:

data "template_file" "apache_install" {
    template = file("path_to_file")
}

Then within the “resource azurerm_linux_virtual_machine” section add a new line around the computer_name details saying:

custom_data = base64encode(data.template_file.apache_install.rendered)

This encodes the bash script as base64 so that it can be passed into the custom data field. The final main.tf Terraform file should now include something similar to:

data "template_file" "apache_install" {
    template = file("path_to_bash_script")
    }

resource "azurerm_linux_virutal_machine" "vm" {
    name = var.servername
    location = var.location
    resource_group_name = azurerm_resource_group.rg.name
    network_interface_ids = [azurerm_network_interface.nic.id]
    size = var.vm_size

    os_disk {
        name = "os-${var.servname}-disk"
        caching = "ReadWrite"
    }

    source_image_reference {
        publisher = var.os.publisher
        offer = var.os.offer
        sku = var.os.sku
        version = var.os.version
    }

    
        computer_name = var.servername
        admin_username = var.admin_username
        admin_password = var.admin_password
        disable_password_authentication = true
        custom_data = base64encode(data.template_file.apache_install.rendered)

    admin_ssh_key {
        username = var.admin_username
        public_key = file("path_to_public_key")
}    
    tags = {
        ...
    }

If multiple applications and configuration changes need to be carried out it may be more efficient to use Ansible and a playbook.

One thought on “Terraform and Azure (Infrastructure as Code) – Part 2

Comments are closed.