Infrastructure as Code in Microsoft Azure met Terraform

Tristan Boland, October 2021.


*Infrastructure as Code* is een manier om wijzigingen in je infrastrctuur te automatiseren, gebruikmakend van configuratie bestanden in je repository.

Deze blogpost introduceert de beginselen van Infrastructure as Code (IaC) in Microsoft Azure Services en ook de tool Terraform.

De post geeft een korte indruk van enkele zaken, allereerst een korte theoretische introductie van IaC. Daarna duiken we in de voordelen van IaC vergeleken met de "gebruikelijke" manier om infrastructure te beheren in Azure. Tot slot do ik een stap voor stap implementatie van IaC in de Microsoft Azure stack met de tool Terraform, via lab onderzoek (Bonestroo et al., 2018).

Kennis maken met IaC

Om te beginnen: Infrastructure as Code is het proces van het vervangen van handmatige stappen voor het maken en beheren van infrastructure met regels code (Workfall, 2021). Deze noemt met vaak configuratiebestanden of resources. Deze resources kunnen elk stuk infrastructure in a bepaalde omgeving maken, bijvoorbeeld een virtuele machine, een database, of — in de context van Azure — een logic app of service bus.

Infrastructure as code workflow (DigitalOcean, 2021) Infrastructure as code workflow (DigitalOcean, 2021)

Gebruik en voordelen van IaC

Er zijn een aantal voordelen van het gebruik van IaC, waarvan de voornaamste is dat het een efficiente en consistente manier is om infrastructuur te definieren en beheren.

Versiebeheer

Het definieren van infrastructuur resources maakt het mogelijk om wijzigingen te en toevoegingen aan de infrastructuur bij te houden in versiebeheer. Het gevolg is een historie van wijzigingen in je infrastructuur, waarnaar je makkelijk een rollback kunt doen in geval van een fout of niet werkend systeem.

Ook is het mogelijk om de wijzigingen in je infrastructuur te inspecteren en controleren voordat je daadwerkelijke oplevert.

Documentatie

Deze bestanden kunnen ook dienen als documentatie, aangezien de bestanden een specificatie zijn van de state (toestand) en opmaak van de services op een leesbare manier.

Snelheid

Ook is het op deze manier specificeren van je infrastructuur een stuk sneller. Vooral als je te maken hebt met een zeer uitgebreide infrastructuur, is het simpelweg niet mogelijk om alles te behren via een grafische user interface. Niet DevOps teams hebben het nog zwaarder te verduren, aangezien developers moeten wachten op een apart ops team om de benodigde infrastructure in de lucht te brengen (SDxCentral, 2021).

Je kunt makkelijk delen van IaC bestanden hergebruiken om specifieke services op te schalen.

Automatisering

Bovendien kunnen tools zoals Terraformje in staat stellen jr process te automatiseren. Je kan een IaC configuratie wijzigen of toevoegen. Daarna kun je de deployment van de wijzingen integreren in je CI/CD pipeline om deze naar een cloud provider naar keuze op te leveren.

Best practices with IaC

Voordat je IaC toepast is het goed om te een paar best practices te kennen om je aan te houden. Ik heb enkele van deze practices hieronder opgesoms, welke ik het meest waardevol vindt.

Documenting

When using infrastructure as code your configuration files will essentially be your documentation (Chan, 2021). This doesn't mean you shouldn't write any documentation at all!

In complexe landschappen kunnen diagrammen heel waardevol zijn. In sommige gevallen is een snelle blik op een overzicht / diagram meer waard dan de gehele structuur van de configuratie file te bestuderen.

Keep it simple, keep it modular

Het is een best practice om de configuratiebestanden simpel te houden en makkelijk te lezen. Op die manier kunnen andere developers makkelijk up to speed komen met de infrastructuur configuratie.

Dit kun je bijvoorbeeld doen door te modulatiseren. Als je de configuratie modulair houdt, dan kun je elementen hergebruiken, wat voor een consistente file structure zorgt (Xenonstack, 2021).

Mutable of immutable

Een mutable infrastructure betekent dat je wijzigingen kunt toepassen op je infrastructure terwijl deze al up and running is. Het hanteren van mutable infrastructure zorgt ervoor dat individuele wijzigingen aan je cluster kunnen zorgen voor configuration drift, waarbij een cluster stapsgewijs steeds meer afwijkt over de tijd, als gevolg vn de handmatige wijzigingen (Pelletier, 2020).

Sommigen argumenteren dat een betere manier om met wijzigingen om te gaan is met Immutable Infrastructure. Dit betekent de infrastructure die runt weggooien en deze volledig vervangen als je een wijziging deployed. Zo sta je toe om vorige configurations op te slaan als versies, en makkelijk opnieuw te deployen op basis van een versie van de infrastructuur (Vasiljevic, 2021).

Mutable vs immutable (Vasiljevic, 2021) Mutable vs immutable (Vasiljevic, 2021)

Implementing IaC

Er is een heel scale van tools die je kunt gebruiken voor IaC. In dit geval focussen we op Terraform. Aangezien je Terraform kunt integreren met een veelheid aan cloud service providers.

Overview of the Terraform workflow (Hashicorp, 2021) Overzicht van de workflow van Terraform (Hashicorp, 2021)

Implementing IaC with Terraform

Terraform gebruikt .tf bestanden om je infrastructure configuraties in op te slaan. De syntax lijkt op Yaml and JSON. Als gewenst kun je ook een .tf.json file extensie gebruiken, die je toestaat om JSON te schrijven.

Om te beginnen met het implementern van IaC, zal ik eerst een configuratie voor een lokale Docker container maken om bekent te raken met de syntax en verschillende onderdelen van Terraform.

Ik heb een .tf configuratie bestand gemaakt met instructies om Nginx te runnen op een lokale Docker container:

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = ">= 2.13.0"
    }
  }
}

provider "docker" {
  host    = "npipe:////.//pipe//docker_engine"
}

resource "docker_image" "nginx" {
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.latest
  name  = "NginxUsingTerraform"
  ports {
    internal = 80
    external = 8000
  }
}

Dit bestand bestaat uit het volgende:

  • Terraform blok met Terraform settings.
  • Een lijst van vereiste providers binnen het Terraform blok. Dit lijkt op een dependency die is vereist om de infrastructuur te creeren.
  • Een provider, die in dit geval ons doorverwijst naar onze Docker Engine die de infrastructure aanmaakt.
  • Een lijst van resources. Dit zijn de resources om aan te maken. In dit geval is er een Nginx image, en een Nginx container waarop de image gedeployed zal worden.

Terraform commando's

Terraform ondersteunt de volgende basic commando's:

  • Om Terraform te initialiseren en de vereiste dependencies te downloaden gebruik terraform init.
  • Om de configuratie te valideren gebruik je terraform validate.
  • To see a list of changes to be deployed use terraform plan.
  • To apply changes to your infrastructure use terraform apply.
  • To stop your running infrastructure use terraform destroy.

TODO: Rest vertalen Using the init and apply commands the infrastructure is deployed to a local Docker instance. A list of changes is shown and Terraform asks for confirmation before deploying.

Applying changes using Terraform

After a short while you will see the specified Nginx container running in your local Docker instance.

Running in Docker

Scaling up

Say you want to add another Nginx container to your infrastructure. Since the image and Docker provider are listed above for the first resource. Adding a second container is as easy as adding a second resource to the configuration file.

Adding the following resource to the file creates a second Docker container running Nginx in Docker.

resource "docker_container" "nginx_clone" {
  image = docker_image.nginx.latest
  name  = "NginxUsingTerraformClone"
  ports {
    internal = 81
    external = 8001
  }
}

Two containers tunning in Docker Both resources running in a Docker container

Implementing Terraform on Microsoft Azure

Terraform can communicate with a multitude of cloud services. I will use Azure for this example.

A prerequisite to working with Terraform in Azure is being logged in to your Azure account using the Azure cli.

To get started you can want to create a resource group and storage account. This resource group will contain all our resources.

I created the resource group and storage account using the following configuration:

resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_storage_account" "storageaccount" {
  name                     = "tbterraformstorageac"
  resource_group_name      = var.resource_group_name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

You might notice var.[name] items in this example, these are variables.

Variables

Terraform configuration files support variables as well. For example the name of the resource group I created will be re-used in future configuration files. To add variables you can create a new configuration file which can contain the following:

variable "resource_group" {
  default = "TerraformResourceGroup"
}
variable "location" {
  default = "westeurope"
}

Adding a resource to Azure

Next I will create a feature branch, on this branch I will make some changes to our infrastructure. I will be adding a MSSQL Database to azure.

To add cloud specific resources there are a multitude of templates available in the Terraform registry to get started. As there are way too many configuration options for each resource to remember!

I added the following file to create a database server and the MSSQL instance:

resource "azurerm_mssql_server" "tbdatabaseserver" {
  name                         = "tbdatabaseserver"
  resource_group_name          = var.resource_group_name
  location                     = var.location
  version                      = "12.0"
  administrator_login          = "tb4dm1n!"
  administrator_login_password = "SuperSecurePass123!"
}

resource "azurerm_mssql_database" "tbsqldatabase" {
  name           = "tbsqldatabase"
  server_id      = azurerm_mssql_server.tbdatabaseserver.id
  collation      = "SQL_Latin1_General_CP1_CI_AS"
  license_type   = "LicenseIncluded"
  max_size_gb    = 4
  read_scale     = true
  sku_name       = "BC_Gen5_2"
  zone_redundant = true

  extended_auditing_policy {
    storage_endpoint                        = azurerm_storage_account.storageaccount.primary_blob_endpoint
    storage_account_access_key              = azurerm_storage_account.storageaccount.primary_access_key
    storage_account_access_key_is_secondary = true
    retention_in_days                       = 6
  }
}

You can apply these changes directly from the command line using the apply command. But in most cases, you won't be working on your own. Terraform changes can be tracked by your version control system, and thus your changes can be monitored when making a pull request. There will be more info about this below.

Integrating Terraform into your pipeline

When your Terraform infrastructure is added to your version control system you are able to integrate it into your build pipeline. This allows you to see the changes Terraform is about to make to your infrastructure when creating pull requests.

Adding Terraform support is as easy as adding a Terraform extension to Azure DevOps.

You can then add the terraform init, ... validate, ... plan, ... apply commands to your pipeline stages. Allowing you to validate the changes you are going to make to your infrastructure. Show the changes using the output of plan, to eventually merge the changes kicking off an apply pipeline.

Unfortunately I am not able to demonstrate this due to missing privileges in Azure. If you do want to try this for yourself I would highly recommend reading the following blogposts:

In summary

When starting a new project, or when currently working on a project. I would highly recommend to implement IaC as it simplifies creating and managing infrastructure for your project. It allows anyone to create infrastructure without having to navigate a user interface. They can easily get up to date on the infrastructure landscape since the configuration files are readable and serve as documentation.

Versioning of infrastructure is easily done because the infrastructure configuration is checked in to your version control system. Which also allows the ability to review changes before deploying.

Using a tool like Terraform you are able to easily apply your changes to a multitude of cloud services.

Sources

Last change: 2025-01-13