Terraform and Azure

Have you already played around with Terraform and Azure? Until today, I hadn’t and decided to try it out. A couple of weeks ago Corey Sanders announced progress with how Azure and Terraform are integrated; and below you can read on how I tried it out.

Setting up the infrastructure

Terraform is integrated into our Azure cloud shell, but I decided to set it up on a virtual machine (full installation):
First step is to create a VM to run Terraform on:
TF1
Installation is as simple as downloading a zip file, extracting it and adding that path to your $PATH variable. Easy to do:

sudo apt-get install unzip -y
wget https://releases.hashicorp.com/terraform/0.10.8/terraform_0.10.8_linux_amd64.zip
unzip terraform_0.10.8_linux_amd64.zip
rm terraform_0.10.8_linux_amd64.zip
mkdir bin
mv terraform bin/terraform
echo “PATH=$PATH:$HOME/bin” >> .profile
source .profile

Next step is to create a service principal that will have access to the azure APIs. We can create this in the portal, but let’s go ahead and do that via the CLI. First things first: install the CLI.

echo “deb https://packages.microsoft.com/repos/azure-cli/ wheezy main” | \
sudo tee /etc/apt/sources.list.d/azure-cli.list
echo “deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main” | \
sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt-key adv –keyserver packages.microsoft.com –recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893
sudo apt-get install apt-transport-https
sudo apt-get update && sudo apt-get install azure-cli

With the CLI installed, let’s login and create a service principal.

az login
az ad sp create-for-rbac –role=”Contributor” –scopes=”/subscriptions/*SUBCRIPTION ID GOES HERE*”

Something weird happened here for me, with a lot of retries happening on the creation of both the SP as on the role assignment. But it worked and returned me the appID and secret. Keep this secret ‘secret’, as it opens the doors to your Azure subscription!
TF2
Next step is to create the right environment variables for Terraform to use. I’ve added them to a neat little script, you could also make them part of your login scripts (.profile or .bash_rc).

#!/bin/sh
echo “Setting environment variables for Terraform”
export ARM_SUBSCRIPTION_ID=your_subscription_id
export ARM_CLIENT_ID=your_appId
export ARM_CLIENT_SECRET=your_password
export ARM_TENANT_ID=your_tenant_id

TF3

Creating a resource group

Next, we’ll create a resource group via TerraForm. Create an empty directory, with a file in there with the following content:

provider “azurerm” {
}
resource “azurerm_resource_group” “rg” {
name = “TF_created_RG”
location = “westeurope”
}

Next steps are to (1) initialise Terraform (2) test our deployment (3) deploy it.

terraform init
terraform plan
terraform apply

And behold: we have a resource group!
TF4

Creating a virtual machine (VM)

And now, let’s create a VM. I won’t go in all the details, but this documentation article explains more about it: https://docs.microsoft.com/en-us/azure/virtual-machines/linux/terraform-create-complete-vm
I created the following terraform file and followed the same steps as before:

provider “azurerm” {
}
resource “azurerm_resource_group” “rg” {
name = “TF_created_RG”
location = “westeurope”
}
resource “azurerm_virtual_network” “myterraformnetwork” {
name                = “TF_Vnet”
address_space       = [“10.0.0.0/16”]
location            = “${azurerm_resource_group.rg.location}”
resource_group_name = “${azurerm_resource_group.rg.name}”
tags {
environment = “Terraform Demo”
}
}
resource “azurerm_subnet” “myterraformsubnet” {
name                 = “TF_Subnet”
resource_group_name  = “${azurerm_resource_group.rg.name}”
virtual_network_name = “${azurerm_virtual_network.myterraformnetwork.name}”
address_prefix       = “10.0.2.0/24”
}
resource “azurerm_public_ip” “myterraformpublicip” {
name                         = “TF_publicIP”
location                     = “${azurerm_resource_group.rg.location}”
resource_group_name          = “${azurerm_resource_group.rg.name}”
public_ip_address_allocation = “dynamic”
tags {
environment = “Terraform Demo”
}
}
resource “azurerm_network_interface” “myterraformnic” {
name                = “TF_NIC”
location            = “${azurerm_resource_group.rg.location}”
resource_group_name = “${azurerm_resource_group.rg.name}”
ip_configuration {
name                          = “TF_NicConfiguration”
subnet_id                     = “${azurerm_subnet.myterraformsubnet.id}”
private_ip_address_allocation = “dynamic”
public_ip_address_id          = “${azurerm_public_ip.myterraformpublicip.id}”
}
tags {
environment = “Terraform Demo”
}
}
resource “random_id” “randomId” {
keepers = {
# Generate a new ID only when a new resource group is defined
resource_group = “${azurerm_resource_group.rg.name}”
}
byte_length = 8
}
resource “azurerm_storage_account” “mystorageaccount” {
name                = “diag${random_id.randomId.hex}”
resource_group_name = “${azurerm_resource_group.rg.name}”
location            = “${azurerm_resource_group.rg.location}”
account_replication_type = “LRS”
account_tier = “Standard”
tags {
environment = “Terraform Demo”
}
}
resource “azurerm_virtual_machine” “myterraformvm” {
name                  = “myVM”
location              = “${azurerm_resource_group.rg.location}”
resource_group_name   = “${azurerm_resource_group.rg.name}”
network_interface_ids = [“${azurerm_network_interface.myterraformnic.id}”]
vm_size               = “Standard_DS1_v2”
storage_os_disk {
name              = “myOsDisk”
caching           = “ReadWrite”
create_option     = “FromImage”
managed_disk_type = “Premium_LRS”
}
storage_image_reference {
publisher = “Canonical”
offer     = “UbuntuServer”
sku       = “16.04.0-LTS”
version   = “latest”
}
os_profile {
computer_name  = “TFvm”
admin_username = “nilfranadmin”
}
os_profile_linux_config {
disable_password_authentication = true
ssh_keys {
path     = “/home/nilfranadmin/.ssh/authorized_keys”
key_data = “ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAslS5LnoCJlj8OE4VncUK2iP6YhVT/RmeNkvP3VTd/GbiZd384wrD0rzr3MwEgMm4ZkjUQno54x+bpRhIFDha4Kj89cs7LwuPHZSkXLF+aVydxy2nu464TmflnhVVW71wLE9E3bCUxmh5+IZ3sJ8is2XQMuC1IHiIoEMFc+buMTG+kVc3f+VaJ5ZT+bFPjqs816YBPTSZRmUjzfwRcLIRXvlVxlFsMckhSTa7xCCxunsGKITOnqmlk/vIWr/bKfev6RD+qV8DFquM0zxquwcSv5ERXE384m6ESJ/YJ4IN5P14CDWT3pdZtwM1jOaL/zPyMHbamk5iTPLfuPao740plQ==”
}
}
boot_diagnostics {
enabled     = “true”
storage_uri = “${azurerm_storage_account.mystorageaccount.primary_blob_endpoint}”
}
tags {
environment = “Terraform Demo”
}
}

This created my VM (with all the surrounding elements):
TF5

What I learned today:

  • How to use Terraform to create Azure resources.
  • Although Terraform is cloud agnostic, the drivers to each cloud are specific. You can’t ‘simply’ swap a parameter ‘AWS’ to ‘Azure’ and re-use the same template. #I didn’t know that before. I was mistakenly thinking that within Terraform you created a ‘unbranded’ resources; which got translated to the right provider. I was wrong.
  • The syntax is surprisingly simple and straightforward, if you understand what needs to happen under the covers (aka if you know what you need to create on Azure).

Leave a Reply