Infrastructure as code: Using terraform to deploy resources in azure.
On my first introducition to infrastructure as code I was intrigued but confused. I didn’t fully understand the concept and its capabilities, that is, until now. I’ve since toyed with the idea of learning terraform (no particular reason for choosing terraform, this was what I was first introduced to). In this article I’ll take you along my journey of learning the basics of terraforming in azure.
I only vaguely followed the microsoft examples as I eventually just started using them as a guide to deploy my own infrastructure.
Here’s what I did:
- Install the latest version terraform
- Deployed resource groups (closely following microsoft documentation)
- Deployed azure container instances
- Deployed Virtual machines
Installing the latest version of terraform
For all of my experimenting I only used the Azure CLI however I will try installing terraforn locally and using it alongside the azure powershell module in the future
Installing terraform in the azure cli is simple, worth noting that terraform is already installed in the cli however microsoft recommends to intstall the latest terraform packge.
The install is done by using curl to download the appropriate zip file from the terraform website
unzipping the terraform package
moving the contents of the unzipped archive to a directory called bin/terraform
see Microsoft docs
terraform version
curl -O <terraform_download_url>
unzip <zip_file_downloaded_in_previous_step>
mkdir bin
mv terraform bin/
terraform version
Deploying resource groups
We’ll mostly be working with 4 main files, however terraform does create additional files:
provider.tf - used to specific which providers we’re working with; in this case its azurerm
main.tf - used to call the azure resource providers and define the resources we want to deploy
variables.tf - variables can be used in the main.tf file any variable used in the main.tf file must be declared in this file which also hold the arguments for the variables
output.tf - we can use this file to specify which resources/resource properties we want to get information on after deployment. eg A resource group name after deployment
I first started by deploying a single resource group then trying to make sense of the different files and the syntax used by terraform. Closely following this article.
I then moved onto trying to deploy two resource groups, worth noting that I didn’t get this on my first try and this did help me develop my understanding of the azurerm resource providers.
provider.tf
terraform {
required_version = ">=0.12"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>2.0"
}
}
}
provider "azurerm" {
features {}
}
main.tf
# Generating name for resource group
resource "random_pet" "rg-name" {
prefix = var.resource_group_name_prefix
}
# Generating name for resource group
resource "random_pet" "rg-name-two" {
prefix = var.resource_group_name_prefix
}
# Creating resource group
resource "azurerm_resource_group" "rg" {
name = random_pet.rg-name.id
location = var.resource_group_location
tags = var.rg_tags
}
# Creating resource group
resource "azurerm_resource_group" "rg_two" {
name = random_pet.rg-name-two.id
location = var.resource_group_location
tags = var.rg_tags
}
variables.tf
variable "resource_group_name_prefix" {
default = "rg"
description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
}
variable "resource_group_location" {
default = "uksouth"
description = "Location of the resource group."
}
variable "rg_tags" {
default = {Provisioner = "Terraform"}
description = "Test adding tag via terraform"
}
output.tf
output "resource_group_name" {
value = azurerm_resource_group.rg.name
}
output "resource_group_tags" {
value = azurerm_resource_group.rg.tags
}
output "resource_group_name_two" {
value = azurerm_resource_group.rg_two.name
}
To actually deploy in azure we then run the following commands
terraform plan -out main.tfplan
output:
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
create
Terraform will perform the following actions:
# azurerm_resource_group.rg will be created
resource "azurerm_resource_group" "rg" {
id = (known after apply)
location = "uksouth"
name = (known after apply)
tags = {
"Provisioner" = "Terraform"
}
}
# azurerm_resource_group.rg_two will be created
resource "azurerm_resource_group" "rg_two" {
id = (known after apply)
location = "uksouth"
name = (known after apply)
tags = {
"Provisioner" = "Terraform"
}
}
# random_pet.rg-name will be created
resource "random_pet" "rg-name" {
id = (known after apply)
length = 2
prefix = "rg"
separator = "-"
}
# random_pet.rg-name-two will be created
resource "random_pet" "rg-name-two" {
id = (known after apply)
length = 2
prefix = "rg"
separator = "-"
}
Plan: 4 to add, 0 to change, 0 to destroy.
Changes to Outputs:
resource_group_name = (known after apply)
resource_group_name_two = (known after apply)
resource_group_tags = {
"Provisioner" = "Terraform"
}
Saved the plan to: main.tfplan
To perform exactly these actions, run the following command to apply:
terraform apply "main.tfplan"
We then need to apply the terraform plan file we just created.
terraform apply main.tfplan
output:
random_pet.rg-name-two: Creating...
random_pet.rg-name: Creating...
random_pet.rg-name-two: Creation complete after 0s [id=rg-tolerant-pelican]
random_pet.rg-name: Creation complete after 0s [id=rg-peaceful-rattler]
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg_two: Creating...
azurerm_resource_group.rg: Creation complete after 1s [id=/subscriptions/cff2c421-5594-4219-9863-f9566429966a/resourceGroups/rg-peaceful-rattler]
azurerm_resource_group.rg_two: Creation complete after 1s [id=/subscriptions/cff2c421-5594-4219-9863-f9566429966a/resourceGroups/rg-tolerant-pelican]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
resource_group_name = "rg-peaceful-rattler"
resource_group_name_two = "rg-tolerant-pelican"
resource_group_tags = tomap({
"Provisioner" = "Terraform"
})
After executing these commands we can see the output is what we’ve defined in the output.tf file. We can also see the resources appear in our azure portal!
Here’s the two resource groups created by terraform.
We see here that out tags have been also been added!
Cleaning up after ourselves is also very easy
terraform plan -destroy -out main.destroy.tfplan
terraform apply main.destroy.tfplan
Deploying an azure container group
For this I followed the terraform documentation. My knowledge of containerization and docker was a limiting factor in me being able to set up a properly functioning environment.
For my full code implemenation please see my Github
main.tf
resource "random_pet" "rg-name" {
prefix = var.resource_group_name_prefix
}
resource "azurerm_resource_group" "example" {
name = random_pet.rg-name.id
location = var.resource_group_location
tags = var.rg_tags
}
resource "azurerm_container_group" "example" {
name = "example-continst"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
ip_address_type = "public"
dns_name_label = "tf-containerinst-ra"
os_type = "Linux"
container {
name = "testvwcontainer"
image = "vaultwarden/server:latest"
cpu = "0.5"
memory = "1.5"
ports {
port = 80
protocol = "TCP"
}
}
tags = var.rg_tags
}
Resource group with container group
Container group with container Instance
Deploying the virtual machines
Following both microsoft and terraform documentation, I definitely learnt the most from this exercise. Initially starting with deploying a single vm I got a better understanding of the resources that surround an azure vm. It was a simple enough task once I broke down the main.tf file into the individual resources that terraform will be deploying. Also trying to understand every line of code helped a lot with seeing how the different resources are linked and how they work in combination with each other.
That being said there were two different versions of my I environment. The first being where I just duplicated the VM and NIC.
In my second version I tidied up the networking and only associated the subnet to the network security group.
The azure resources remained the same across the two versions
For my full code implemenation please see my Github
Overall this was very exciting for me and I enjoyed learning and using terraform. The real moment of magic sets in after hitting enter on the terraform apply command, then seeing all your infrastructure automagically appear. It was very helpful in developing my understanding of azure on the whole. I understand that I have a long way to go if I want to do any real work with terraform like tidying up my .tf files and using modules to simplify deployment.
Future things to learn:
Storing my state file in a storage account to follow Microsoft’s best practises.
Deployment of management groups
Vnet peering (pre-cursor to deploying hub and spoke topology)
Terraforming across differrent azure subscriptions