Consuming terraform modules with in modules

We are working on creating various terraform modules for Azure cloud in organization. I have a basic doubt on using the modules.

Lets say we have a module for creating resource groups. We are planning to write a module for storage container. Would it be better to use the resource group module inside the storage module or would it be better to let the consumer terraform script handle it like,
module resourcegroup {

}
module storage {
}

Thanks
Hound

Hi endejoli / Hound, and welcome to our community!

First, a disclaimer about Azure. I personally don’t have much experience testing terraform modules that deploy Azure resources. But I can offer some advice based on how we’ve been designing our modules (for AWS).

  1. There are advantages to nesting the resource group module within the storage module because it encourage more code reuse. You already have a module that creates a resource group, as you said. If you decide the storage module creates that resource group as well as the storage_account and storage_container, then the consumer only has to invoke the storage module.
  2. However, it might not be appropriate for the storage module to create the resource group, because you can imagine a resource group containing any number of other resources besides a storage container. It seems like the most future-proof idea would be for the consumer invoke the resource group module, and all other modules that need to be grouped in that resource group should use the outputs from that module.

Example of storage container module: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container


Breaking out that example into separate modules:
./modules/resource_group.tf:

resource "azurerm_resource_group" "example" {
  name     = var.name
  location = var.location
}

Maybe the storage module accepts the resource_group info as variables.
./modules/storage.tf:

resource "azurerm_storage_account" "example" {
  name                     = var.storage_account_name
  resource_group_name      = var.resource_group_name
  location                 = var.resource_group_location
  account_tier             = "Standard"
  account_replication_type = "LRS"

  tags = {
    environment = "staging"
  }
}

resource "azurerm_storage_container" "example" {
  name                  = var.storage_container_name
  storage_account_name  = var.resource_group_name
  container_access_type = "private"
}

Then the consumer terraform would specify both and use the outputs of one for the other:
consumer:

module "resource_group" {
  source   = "../modules/resource_group.tf"
  name     = var.rg_name
  location = var.rg_location
}

module "storage" {
  source                  = "../modules/storage.tf"
  storage_account_name    = "example"
  storage_container_name  = "vhds"
  resource_group_name     = var.resource_group_name
  resource_group_location = var.resource_group_location
}

That means the consumer is actually creating the resource group.

So it really depends on your use case, but not knowing much about what other resources you are deploying, I would lean toward the consumer defining both.

Does this help?

-Rho