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:
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!
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
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!
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):
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).