Published on

Create AKS cluster and ACR with Terraform

Table of Contents

Terraform

Terraform is an open-source infrastructure as code software tool created by HashiCorp. It enables users to define and provision a datacenter infrastructure using a high-level configuration language known as Hashicorp Configuration Language (HCL), or optionally JSON. Terraform supports a number of cloud infrastructure providers such as Amazon Web Services Google Cloud Platform, Linode, Microsoft Azure, Oracle Cloud Infrastructure, Hetzner or VMware vSphere as well as OpenStack.

Install terraform

Download and install terraform from here.

Create a new folder for infrastructure

mkdir infrastructure
cd infrastructure

We will create all the terraform files in this folder. this will be only for infrastructure.

Create main.tf

new-item main.tf

This is where we will create all the resources.

create variables.tf

new-item variables.tf

This is where we will create all the variables. We will use these variables in main.tf file.

Create dev.tfvars

new-item dev.tfvars

This is where we will create all the variables values. We will use these variables values in main.tf file. This file will not be checked in to source control. You can add this file to .gitignore file. you can also use this file to override the variables values. you can create multiple tfvars files for different environments.

Create output.tf

new-item output.tf

This is where we will create all the outputs. We will use these outputs in main.tf file.

Create configs folder

mkdir configs

This is where we will create all the kube config files. We will use these kube config files to connect to AKS cluster.

Create variables and outputs

Add the following code to variables.tf

variable "location" {
  default     = "westeurope"
  description = "The Azure Region in which all resources should be created."
  type        = string
}

variable "resource_group_name" {
  default     = "projectname-dev-rg"
  description = "The name of the resource group in which all resources should be created."
  type        = string
}

variable "resource_group_tags" {
  default = {
    environment = "dev",
    project     = "projectname"
  }
  description = "A map of tags to add to all resources for this environment."
  type        = map(string)

  validation {
    condition     = length(keys(var.resource_group_tags)) == 2
    error_message = "The resource_group_tags map must contain exactly two elements."
  }

  validation {
    condition     = contains(keys(var.resource_group_tags), "environment")
    error_message = "The resource_group_tags map must contain a key named \"environment\"."
  }

  validation {
    condition     = contains(keys(var.resource_group_tags), "project")
    error_message = "The resource_group_tags map must contain a key named \"project\"."
  }
}

Add the following code to output.tf

output "resource_group_id" {
  value = azurerm_resource_group.rg.id
}

output "resource_group_name" {
  value = azurerm_resource_group.rg.name
}

output "resource_group_location" {
  value = azurerm_resource_group.rg.location
}

output "resource_group_tags" {
  value = azurerm_resource_group.rg.tags
}

output "acr_id" {
  value = azurerm_container_registry.acr.id
}

output "acr_name" {
  value = azurerm_container_registry.acr.name
}

output "acr_login_server" {
  value = azurerm_container_registry.acr.login_server
}

output "acr_admin_username" {
  value = azurerm_container_registry.acr.admin_username
}

output "acr_admin_password" {
  value = azurerm_container_registry.acr.admin_password
  sensitive = true
}

output "aks_id" {
  value = azurerm_kubernetes_cluster.aks.id
}

output "aks_name" {
  value = azurerm_kubernetes_cluster.aks.name
}

output "aks_location" {
  value = azurerm_kubernetes_cluster.aks.location
}

output "aks_node_resource_group" {
  value = azurerm_kubernetes_cluster.aks.node_resource_group
}

output "aks_dns_prefix" {
  value = azurerm_kubernetes_cluster.aks.dns_prefix
}

output "aks_kube_admin_config" {
  value = azurerm_kubernetes_cluster.aks.kube_admin_config_raw
  sensitive = true
}

output "aks_kube_config" {
  value = azurerm_kubernetes_cluster.aks.kube_config_raw
  sensitive = true
}

output "aks_identity" {
  value = azurerm_kubernetes_cluster.aks.identity
}

Add the following code to main.tf

# Configure the Azure provider
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0.2"
    }

    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = ">= 2.0.0"
    }

    random = {
      source  = "hashicorp/random"
      version = "~> 3.0.0"
    }

    local = {
      source  = "hashicorp/local"
      version = "~> 2.0.0"
    }
  }
  required_version = ">= 1.1.0"
}

provider "azurerm" {
  features {}
}

locals {
  name_suffix = "${var.resource_group_tags["project"]}-${var.resource_group_tags["environment"]}"
}

resource "azurerm_resource_group" "rg" {
  name     = "${local.name_suffix}-rg"
  location = var.location
  tags     = var.resource_group_tags
}

// create acr

resource "azurerm_container_registry" "acr" {
  name                = "${replace(local.name_suffix, "-", "")}acr"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  sku                 = "Basic"
  admin_enabled       = true
  tags                = var.resource_group_tags
}

provider "kubernetes" {
  host                   = azurerm_kubernetes_cluster.aks.kube_config.0.host
  client_certificate     = base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.client_certificate)
  client_key             = base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.client_key)
  cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.cluster_ca_certificate)
}

provider "local" {
}


resource "local_file" "kube_config" {
  filename = "${path.module}/configs/${local.name_suffix}.config"
  content  = <<EOT
${azurerm_kubernetes_cluster.aks.kube_config_raw}
EOT
}

// attach acr to aks

resource "azurerm_role_assignment" "aks_acr_role_assignment" {
  depends_on           = [azurerm_kubernetes_cluster.aks]
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "AcrPull"
  principal_id         = azurerm_kubernetes_cluster.aks.kubelet_identity[0].object_id
}

Create dev.tfvars

location = "westeurope"
resource_group_name = "projectname-dev-rg"
resource_group_tags = {
  environment = "dev"
  project     = "projectname"
}

Upgrade terraform

terraform init -upgrade

This will upgrade the terraform to latest version and download the latest provider. This will also update the lock file.

Note: terraform use azure cli to authenticate to azure. Make sure you have azure cli installed and logged in to azure.

Plan terraform with tfvars file

terraform plan -var-file="dev.tfvars"

Always plan before applying terraform. This will show you what resources will be created. This will also show you if there are any errors. This will not create any resources so it is safe to run this command.

Apply terraform with tfvars file with auto approve

terraform apply -var-file="dev.tfvars" -auto-approve

This will create all the resources. This will also create a kube config file in configs folder. This file will be used to connect to AKS cluster.

Destroy terraform with tfvars file with auto approve

terraform destroy -var-file="dev.tfvars" -auto-approve

This will destroy all the resources. I recommend you create and destroy the resources few times to and get familiar with terraform.

Conclusion

If you are new to terraform I recommend you to go through the official documentation. This will give you a good understanding of terraform. In the next post we prepare the aks cluster for deployment.