Following on from my introductory posts around Terraform, in this post, I’m going to create an Azure Storage account and blob container within Terraform. I’m then going to split the terraform file up so that it can be better managed.
For the purpose of continuity, let’s see what we already have in the file:
# Configure the Azure provider
terraform {
required\_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.26"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm\_resource\_group" "rg" {
name = "myTFResourceGroup"
location = "ukwest"
}
# App Service
resource "azurerm\_app\_service\_plan" "app-service-plan" {
name = "pcm-app-service-plan"
location = azurerm\_resource\_group.rg.location
resource\_group\_name = azurerm\_resource\_group.rg.name
sku {
tier = "Standard"
size = "S1"
}
}
resource "azurerm\_app\_service" "app-service" {
name = "pcm-app-service"
location = azurerm\_resource\_group.rg.location
resource\_group\_name = azurerm\_resource\_group.rg.name
app\_service\_plan\_id = azurerm\_app\_service\_plan.app-service-plan.id
}
In this post, we’re going to create an Azure storage account and container. As before, I had a look here to determine what the resource type would be.
The script to add the new resource is quite straight-forward:
# Storage Account & Container
resource "azurerm\_storage\_account" "storageaccount1" {
name = "pcmstorageaccount1"
resource\_group\_name = azurerm\_resource\_group.rg.name
location = azurerm\_resource\_group.rg.location
account\_tier = "Standard"
account\_replication\_type = "LRS"
}
resource "azurerm\_storage\_container" "blobcontainer" {
name = "content"
storage\_account\_name = azurerm\_storage\_account.storageaccount1.name
container\_access\_type = "private"
}
Let’s now see how we can separate our new block into a separate file.
Modules
What we can do with modules is to split off a portion of the terraform configuration into its own file. Let’s try creating a “create-storage.tf” file. Create it in a new directory (I’ll explain why shortly) called Modules:
The file itself basically contains the part of the main config that relates to storage:
variable "resource-name" {}
variable "resource-location" {}
variable "unique-name" {}
# Storage Account & Container
resource "azurerm\_storage\_account" "storageaccount1" {
name = var.unique-name
resource\_group\_name = var.resource-name
location = var.resource-location
account\_tier = "Standard"
account\_replication\_type = "LRS"
}
resource "azurerm\_storage\_container" "blobcontainer" {
name = "content"
storage\_account\_name = azurerm\_storage\_account.storageaccount1.name
container\_access\_type = "private"
}
There’s two things of note here: firstly, we have some variables defined at the top, and secondly, we’re using them with the syntax: var.[name]:
We can then use that in the root module like this:
module "storage" {
resource-name = azurerm\_resource\_group.rg.name
resource-location = azurerm\_resource\_group.rg.location
unique-name = "pcmstorageaccount2"
source = ".\\\\modules"
}
Notice that we supply only two things to the module: the variables that we defined inside the module itself, and the source, which is simply a pointer for terraform to work out where the module is.
The next step is to re-init.
terraform init
terraform init amongst other things, scans the directory for references to modules; and, where it finds one, it loads it in. If you execute terraform plan without first init, then you’ll get the following error:
Error: Module not installed
on main.tf line 47: 47: module “storage” {
This module is not yet installed. Run “terraform init” to install all modules required by this configuration.
Caveat
I mentioned I would explain why we needed a Modules directory; it took be a few tries before it dawned on me. Terraform supports the following syntax:
module "storage" {
resource-name = azurerm\_resource\_group.rg.name
resource-location = azurerm\_resource\_group.rg.location
unique-name = "pcmstorageaccount2"
source = ".\\\\."
}
That is, you can reference the current directory as the location for the module. However, what Terraform actually does is to scan that directory for all modules - including the root module (in our case main.tf). Meaning that it will find that, scan the file, find a reference to a sub-module, scan the current directory, find the root module, find the reference to the sub-module, scan the current directory… you get the idea.
Output
Although we don’t need it for our case, terraform does support the concept of an output for a module. There’s a block type called output:
output "some-name" {
value = resource.value
}
In the calling code, this would be referred to as module-name.some-name