Writing Terraform; like any other IaC toolset, over time you may be repeating the same process for common resources such as an Azure Virtual network, Container registry, Postgres Database etc – instead of copying the same resource multiple times, you can create what is called a Terraform module to assist you with this repetition allowing you to create reusable Terraform.
What is a Terraform module?
There is various thoughts/views on what a Terraform module is; to me, a Terraform module is a single or a grouping of Terraform resources that you can use to deploy multiple times within your infrastructure estate.
Think of a terraform module as a piece of your infrastructure puzzle – the puzzle is to deploy a full environment, a module will deploy part of that puzzle!
How are Terraform modules structured?
As mentioned above, as a Terraform module is a single or grouping of Terraform resources, a “module” is stored outside of your main Terraform deployment. This is usually in a separate folder or in another repository.
What is inside this folder/repository?
- Terraform Resources – That deploy whenever you reference your module within Terraform
- Terraform Inputs – From your main Terraform deployment you will input various values and configurations that will be referenced within your Terraform module
- Terraform Outputs – Outputs that can be used once the module is deployed, for example the resource ID
- Whatever else you want to include π – What else you want to include can be decided by you and each module is certainly different!
I will chat about two examples to begin the thought of why to use a Terraform Module.
Firstly, thinking of a simple Azure resource that I can deploy – lets look at deploying an Azure container register for my first module
What will I need to deploy an Azure Container Registry?
Lets think, what Azure Resources do I need to deploy an Azure Container Registry?
- Resource Group to deploy Azure Container Register into
- Azure Container Registry resource
A sample Terraform showing both
# Example from https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_registry
resource "azurerm_resource_group" "rg" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_container_registry" "acr" {
name = "containerRegistry1"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
sku = "Premium"
admin_enabled = false
}
How could we create a module for this? Lets list some points/thoughts
- Two resources required, azurerm_resource_group & azurerm_container_registry
- Name for each resource
- Location
- ACR SKU
- Tags? Costing/billing segregation – tags are awesome for this!
Creating a Terraform module
Now that I have discussed, how a module is structured, lets look at creating a module for an Azure Container Registry
Folder structure that will be used
Terraform
βββ modules
βββ acr
βββ acr.tf
βββ variables.tf
βββ outputs.tf
βββ main.tf
Lets look at each of the files inside the folder acr
acr.tf
Notice the user of variables? These are ingested from the main.tf file which I will show further down
resource "azurerm_resource_group" "acr_resource_group" {
name = "${var.name}-rg"
location = var.location
tags = {
Environment = var.environment
}
}
resource "azurerm_container_registry" "acr" {
name = "${var.name}acr"
resource_group_name = azurerm_resource_group.acr_resource_group.name
location = azurerm_resource_group.acr_resource_group.location
sku = "Premium"
admin_enabled = false
tags = {
Environment = var.environment
}
}
variables.tf
variable "name" {
}
variable "location" {
default = "uksouth"
}
variable "environment" {
}
outputs.tf
Outputs of a module can be used in other parts of your Terraform deployment, in this I am outputting the resource group ID to be used as part of another resource I may want to deploy.
output "resource_group_id" {
value = azurerm_resource_group.acr_resource_group.id
}
Using a module as part of your Terraform deployment
A very simple main.tf below to show how you can reference the acr module above
provider "azurerm" {
version = "~> 2.0"
features {}
}
terraform {
backend "local" {
}
}
module "acr" {
source = "./modules/acr"
name = "tamopsblog"
environment = "Production"
}
If I was to run the Terraform above, it would deploy the module acr , which would create
- Resource group: tamopsblog-rg in region: UK South (no need to input region as I have set to as default in my acr module to UK South)
- Azure Container Registry: tamopsblogacr in region: UK South
- Both resources will be tagged with Environment = Production
Lets see a Terraform Plan for the above
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:
# module.acr.azurerm_container_registry.acr will be created
+ resource "azurerm_container_registry" "acr" {
+ admin_enabled = false
+ admin_password = (sensitive value)
+ admin_username = (known after apply)
+ encryption = (known after apply)
+ georeplication_locations = (known after apply)
+ georeplications = (known after apply)
+ id = (known after apply)
+ location = "uksouth"
+ login_server = (known after apply)
+ name = "tamopsblogacr"
+ network_rule_set = (known after apply)
+ public_network_access_enabled = true
+ resource_group_name = "tamopsblog-rg"
+ retention_policy = (known after apply)
+ sku = "Premium"
+ tags = {
+ "Environment" = "Production"
}
+ trust_policy = (known after apply)
+ zone_redundancy_enabled = false
+ identity {
+ identity_ids = (known after apply)
+ principal_id = (known after apply)
+ tenant_id = (known after apply)
+ type = (known after apply)
}
}
# module.acr.azurerm_resource_group.acr_resource_group will be created
+ resource "azurerm_resource_group" "acr_resource_group" {
+ id = (known after apply)
+ location = "uksouth"
+ name = "tamopsblog-rg"
+ tags = {
+ "Environment" = "Production"
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
Notice it references the two Terraform resources within the acr module?
- module.acr.azurerm_resource_group.acr_resource_group
- module.acr.azurerm_container_registry.acr
Awesome! If I ran Terraform Apply – it would then deploy the module, lets do that now!
module.acr.azurerm_resource_group.acr_resource_group: Creating...
module.acr.azurerm_resource_group.acr_resource_group: Creation complete after 1s [id=/subscriptions/XXXXX/resourceGroups/tamopsblog-rg]
module.acr.azurerm_container_registry.acr: Creating...
module.acr.azurerm_container_registry.acr: Creation complete after 7s [id=/subscriptions/XXXXX/resourceGroups/tamopsblog-rg/providers/Microsoft.ContainerRegistry/registries/tamopsblogacr]
I maybe want another two ACR’s to be deployed, how can I do this? Easily now as you have a module created to deploy an ACR!
module "acr" {
source = "./modules/acr"
name = "tamopsblog"
environment = "Production"
}
module "acr2" {
source = "./modules/acr"
name = "tamops2"
environment = "Production"
}
module "acr3" {
source = "./modules/acr"
name = "tamops3"
environment = "Production"
}
Now to apply the additional modules via terraform, you can see the additions
module.acr.azurerm_resource_group.acr_resource_group: Creating...
module.acr.azurerm_resource_group.acr_resource_group: Creation complete after 1s [id=/subscriptions/XXXXX/resourceGroups/tamopsblog-rg]
module.acr.azurerm_container_registry.acr: Creating...
module.acr.azurerm_container_registry.acr: Creation complete after 7s [id=/subscriptions/XXXXX/resourceGroups/tamopsblog-rg/providers/Microsoft.ContainerRegistry/registries/tamopsblogacr]
module.acr.azurerm_resource_group.acr_resource_group: Creating...
module.acr.azurerm_resource_group.acr_resource_group: Creation complete after 1s [id=/subscriptions/XXXXX/resourceGroups/tamops2blog-rg]
module.acr.azurerm_container_registry.acr: Creating...
module.acr.azurerm_container_registry.acr: Creation complete after 7s [id=/subscriptions/XXXXX/resourceGroups/tamops2blog-rg/providers/Microsoft.ContainerRegistry/registries/tamops2blogacr]
module.acr.azurerm_resource_group.acr_resource_group: Creating...
module.acr.azurerm_resource_group.acr_resource_group: Creation complete after 1s [id=/subscriptions/XXXXX/resourceGroups/tamops3blog-rg]
module.acr.azurerm_container_registry.acr: Creating...
module.acr.azurerm_container_registry.acr: Creation complete after 7s [id=/subscriptions/XXXXX/resourceGroups/tamops3blog-rg/providers/Microsoft.ContainerRegistry/registries/tamops3blogacr]
Awesome, hopefully this blog has given you the insight into what is a Terraform module and how to use them within your Terraform codebase! I will create a follow up blog soon to detail more advanced use-cases for creating Terraform modules!
GitHub Repo containing Terraform code used in this blog post
3 comments