What do you know about Terraform modules?

What do you know about Terraform modules?

18 March 2021

Céline Verwilligen

Terraform is a basic programming language and there aren’t a lot of functions you can use in this particular language. What it does have, however, is Modules. Try to see these as some kind of method you can use. The real definition of Terraform Modules is actually a container for multiple resources that are used together.

Let’s code!

The first way to use Terraform Modules is to search for modules that have already been created on the internet. HashiCorp has a registry where you can find Terraform Providers and Modules. The site ishttps://registry.terraform.io/. You can find the right configuration and providers you need on this site. If you would like to use the Azure provider version 2.36.0 in your terraform code for example, you will need to use the next configuration in your .tf files. 

Terraform 0.13

terraform { 
  required_providers { 
    azurerm = { 
      source = "hashicorp/azurerm" 
      version = "2.36.0" 
    } 
  } 
}

Terraform 0.12

provider "azurerm" { 
version = "2.36.0" 
}

Regarding the modules, you can choose for which provider you would like a module and then you can search the resource you want. If you would like for example an AKS in your code, you will have to look for AKS with the provider azurerm. The page that you will find for this resource explains how you can use the module in the different versions of Terraform. On the tab Inputs you will find the required and optional inputs to use for that specific module. These inputs are important because you can create the right config for your resource this way. 

If you would like to make an AKS with a network attached to it, you will need this code. 

provider "azurerm" { 
   features {} 
}resource "azurerm_resource_group" "example" { 
   name     = "example" 
   location = "westeurope" 
}module "network" { 
    source        = "Azure/network/azurerm" 
    resource_group_name = azurerm_resource_group.example.name 
    address_space       = "10.0.0.0/16" 
    subnet_prefixes     = ["10.0.1.0/24"] 
    subnet_names        = ["subnet1"] 
    depends_on          = [azurerm_resource_group.example] 
}module "aks" { 
   source                   = "Azure/aks/azurerm" 
   resource_group_name      = azurerm_resource_group.example.name 
   client_id                = "your-service-principal-client-appid" 
   client_secret            = "your-service-principal-client-password" 
   prefix                           = "prefix" 
   vnet_subnet_id                   = module.network.vnet_subnets[0] 
   os_disk_size_gb                  = 50 
   enable_azure_policy              = true 
   sku_tier                         = "Paid" # defaults to Free 
   enable_role_based_access_control = true 
   rbac_aad_admin_group_object_ids  = [data.azuread_group.aks_cluster_admins.id] 
   rbac_aad_managed                 = true 
   depends_on = [module.network] 
}

Now we will explain the code more in depth. The first resource is a normal resource group at the location westeurope, and it doesn’t use modules. 

resource "azurerm_resource_group" "example" { 
   name     = "example" 
   location = "westeurope" 
}

Our next step is to create a network that will be attached to the AKS in a later stage. ‘Source’ is a link to the Azure module network terraform and the provider will automatically get the module we need. Next, we need to define a resource group for the module, so we need to assign the resource group named ‘example’. Now we will need to create the address, subnet prefixes and subnet names, which will be custom for our resource. We need to include ‘depends_on’, which is important in order to provide the resources which need to be created before we can create this resource. This will create a hierarchical order in our resources. 

module "network" { 
   source              = "Azure/network/azurerm" 
   resource_group_name = azurerm_resource_group.example.name 
   address_space       = "10.0.0.0/16" 
   subnet_prefixes     = ["10.0.1.0/24"] 
   subnet_names        = ["subnet1"] 
   depends_on          = [azurerm_resource_group.example] 
}

After we have created the vnet, we can create the Kubernetes cluster with the module ‘AKS’. Not all inputs will be required for this module, but for best practice purposes we should give as much input as possible for our module. In the attribute vnet_subnet_id we need to enter the value of  the subnet that we have created with the vnet. The ‘[0]’ after the vnet_subnet means that there could be more subnets created in one vnet. If we want to configure more details of our Kubernetes setup, we can see all the possible inputs in the documentation on the registry website of Terraform. 

module "aks" { 
   source                           = "Azure/aks/azurerm" 
   resource_group_name              = azurerm_resource_group.example.name 
   client_id                        = "your-service-principal-client-appid" 
   client_secret                    = "your-service-principal-client-password" 
   prefix                           = "prefix" 
   vnet_subnet_id                   = module.network.vnet_subnets[0] 
   os_disk_size_gb                  = 50 
   enable_azure_policy              = true 
   sku_tier                         = "Paid" # defaults to Free 
   enable_role_based_access_control = true 
   rbac_aad_admin_group_object_ids  = [data.azuread_group.aks_cluster_admins.id] 
   rbac_aad_managed                 = true 
   depends_on = [module.network] 
}

So now we have created a vnet and an AKS in Azure using terraform modules. This is an option if you would like an environment or resources up and running fast. If you want custom modules which meet the needs of your configuration requirements, however, it’s better to create modules yourself. 

Creating an AKS module 

First you need the correct folder structure to ensure your module is accessible from your other terraform code. 

aksModule (folder)
— main.tf (file)
main.tf (file) 

The /aksModule/main.tf file will contain the code that creates the module and the main.tf will call the module with some input.  So the /aksModule/main.tf will contain the next code: 

variable "location" { 
  Description = "where do you want your resources to be" 
  Default = "westeurope" 
}variable "name" { 
  Description = "what will be the name of your resources" 
}variable "client_id" { 
  Description = "what is de client_id of the Kubernetes cluster" 
}variable "client_secret" { 
  Description = "what is de client_secret of the Kubernetes cluster" 
}variable "vm_size" { 
  Description = "what will be the vm_size of your default node pool" 
  Default = "Standard_D2_v2" 
}variable "address_space" { 
  Description = "what is the address_space your vnet should be" 
  default = ["10.1.2.0/24"] 
  type = list(string) 
}variable "address_prefix" { 
  Description = "what is the address_prefix your subnet should be" 
  default = "10.1.2.0/25" 
  type = string 
}resource "azurerm_resource_group" "rg" { 
  name     = "rg-${var.name}" 
  location = var.location 
}resource "azure_virtual_network" "vnet" { 
  name          = "vnet-${var.name}" 
  address_space = var.address_space 
  location      = var.location  subnet { 
    name           = "subnet${var.name}" 
    address_prefix = var.address_prefix 
  } 
}resource "azurerm_kubernetes_cluster" "aks" { 
  name                = "aks-${var.name}" 
  location            = azurerm_resource_group.rg.location 
  resource_group_name = azurerm_resource_group.rg.name 
  dns_prefix          = "aks-${var.name}"  default_node_pool { 
    name       = "default" 
    node_count = 1 
    vm_size    = var.vm_size 
  }  service_principal { 
    client_id     = var.client_id 
    client_secret = var.client_id 
  } 
}

This code block creates a resource group, a vnet and an AKS. In this block there are 7 variables, where some of these have a default value. These variables with a default value don’t need to be reinitialized, only the ones without a default value require initialization when using the module you’ve created. In the next code block, you just need to trigger the module (in the file main.tf) and the only variables that are required are name, client_id and client_secret. There is also a variable named ‘source’, and that is where you need to call the module you would like to use. 

module "aks-westeurope" { 
  source = "./aksModule" 
  name = "test1" 
  client_id = "00000000-0000-0000-0000-000000000000" 
  client_secret = "00000000000000000000000000000000" 
}

This module will create an AKS with all the default variables. You can also recreate this module, but with for example another region. In the next code block, we will create the same module, but with another location. The module will take the location which has been initialised. The aks-northeurope will be in northeurope instead of westeurope. 

module "aks-northeurope" { 
  source = "./aksModule" 
  name = "test1" 
  client_id = "00000000-0000-0000-0000-000000000000" 
  client_secret = "00000000000000000000000000000000" 
  location = "northeurope" 
}

Conclusion 

 Creating modules is a great way to customize your terraform code and to avoid a lot of repetitive code. If you have a block of code that is often repeated or if you have the same block of resources for each implementation, this is a great solution. Creating your own Terraform modules is the way to go! 

At FlowFactor, Terraform is one of our favorite tools of the trade because configuring and maintaining managed services infrastructure is pretty much our bread and butter. So be sure to stay tuned for more Terraform-related content in the near future 

Have any IT-infrastructure or DevOps-related questions? 

Related posts
No Comments

Sorry, the comment form is closed at this time.