Estimated reading time: 8 minutes
Designing infrastructure as code (IaC) for Azure using Terraform brings significant benefits, but also recurring challenges. One of the most consistent pain points I’ve observed across various projects comes from the granularity and interdependence of modules.
In this article, we will examine the two common traps Terraform modules fall into — being overly simplistic or overly specific. But pointing out the potential challenges is not enough! We will focus on how to address them and design a robust and maintainable Azure infrastructure using Terraform’s Azure Verified Modules (AVM).
The Power and Pitfalls of Terraform Modules
Terraform modules are the building blocks of scalable and maintainable Infrastructure as Code (IaC). By encapsulating logic into reusable, composable components, they enable engineering teams to enforce consistency, reduce duplication, and accelerate infrastructure delivery.
But as powerful as modules are, they can just as easily become a source of fragility and inefficiency when misused, especially when teams fall into two common traps – simplistic modules and overly specific modules.
1. Simplistic Modules: No Abstraction, No Value
These modules are often thin wrappers around a single Terraform resource with little to no added logic. Here is an example:
module "basic_storage" {
source = "./modules/storage-account"
name = var.name
}
Where the module merely wraps:
resource "azurerm_storage_account" "this" {
name = var.name
resource_group_name = var.resource_group_name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
}
Why This Is a Problem:
- No real encapsulation: You’re not abstracting complexity or enforcing best practices.
- No added value: It’s easier and clearer to use the resource directly.
- Code scattering: The logic gets fragmented across unnecessary files, making debugging harder.
In these cases, modules serve as overhead instead of abstraction. They increase indirection and offer no return on that investment.
2. Specific Modules: Locked In by Design
At the other extreme, we find modules that try to do too much for a specific use case.
For example:
module "app_network" {
source = "./modules/vnet"
environment = "production"
location = "centralus"
app_type = "web-app"
enable_redis = true
enable_sql = true
}
Inside, the module creates a full virtual network plus subnets for Redis, SQL, web apps, all tied to naming conventions and settings that work only for one project.
Why This Is a Problem:
- Low reusability: Designed for a single scenario, unusable elsewhere without forking.
- High coupling: Business logic and infrastructure concerns are mixed, making refactoring painful.
- Maintenance nightmare: Small changes for one consumer risk breaking others.
These types of modules often come up from a “build once, use forever” mindset, but in reality, they create infrastructure silos.
The Result: Technical Debt in Infrastructure Code
When modules swing too far in either direction, they introduce a debt that compounds over time:

Ironically, modules built to accelerate delivery end up slowing teams down, as they become difficult to maintain, extend, or confidently reuse.
4 Principles for Designing Effective Terraform Modules
To unlock the power of modules without falling into these traps, consider these design principles:
- Design for composition, not inheritance: Modules should be small, composable units that can be combined to form patterns, not do everything themselves.
- Focus on interface clarity: A module should expose a clean, minimal set of inputs and outputs. Avoid exposing every possible variable from the underlying resources.
- Encapsulate opinionated defaults, allow overrides: Let modules encode secure defaults (e.g., encryption, TLS), but allow flexibility via optional parameters.
- Separate concerns: Don’t mix networking, identity, compute, and storage in the same module. Let consumers orchestrate these via higher-order pattern modules.
Solution: Introducing Azure Verified Modules (AVM)
To tackle these issues, Microsoft introduced Azure Verified Modules (AVM) a collection of rigorously reviewed Terraform modules that embody best practices for building secure, compliant, and production-grade Azure infrastructure.

Benefits of AVM
- Security-first defaults: Built-in policies, naming conventions, and security settings.
- Standardisation: Ensures consistency across teams and projects.
- Interoperability: Modules are designed to work well together and across environments.
- Validation by Microsoft and the community: Each module undergoes thorough quality and security checks.
AVM Module Types
Azure Verified Modules (AVM) follow a clear classification model, designed to promote reusability, standardisation, and secure-by-default practices. Each module type serves a distinct purpose, allowing platform teams to compose infrastructure more effectively.

- Resource Modules
These wrap one or more related Azure resources, providing abstraction, sensible defaults, and security best practices. Ideal for delivering reusable, standalone building blocks (e.g., storage accounts, resource groups, VNets).
Use case: Provisioning a resource group, storage account, or key vault with secure configurations baked in.Use case: Provisioning a resource group, storage account, or key vault with secure configurations baked in.
- Pattern Modules
These combine multiple resource modules to implement common architectural patterns. They codify platform-level design principles and reduce duplication across projects.
Use case: A hub-and-spoke network topology, or a web application pattern including networking, compute, and monitoring.
- Utility Modules
These modules do not provision Azure resources but support broader automation and standardisation. They often handle logic like naming conventions, tag policies, configuration processing, or telemetry.
Use case: Generating standard resource names according to your org’s naming strategy, or merging default tags with environment-specific tags.
Real-World Example: AVM Resource + Pattern Modules in Action
This example demonstrates how Azure Verified Modules (AVM) can be leveraged to deploy a hub-and-spoke network topology with a shared resource group, adhering to enterprise-ready design principles. In this scenario, we focus exclusively on Pattern and Resource Modules, as Utility Modules typically serve more straightforward, supporting purposes and have limited complexity in their usage.
module "resourcegroup" {
source = "Azure/avm-res-resources-resourcegroup/azurerm"
version = "0.2.1"
name = var.resource_group.name
location = var.regions[var.primary_region]
tags = {
alz = "connectivity"
}
}module "hub-spoke" {
source = "Azure/avm-ptn-alz-connectivity-hub-and-spoke-vnet/azurerm"
version = "0.1.0"enable_telemetry = false
hub_virtual_networks = { primary = {
hub_virtual_network = {
name = "vnet-hub-primary"
address_space = ["10.0.0.0/22"]
location = local.regions.primary
resource_group_name = module.resourcegroup.name
resource_group_creation_enabled = false
subnets = {
"default" = {
name = "default"
address_prefixes = ["10.0.0.0/24"]
}
}
}
spoke_virtual_networks = {
spoke1 = {
name = "vnet-spoke1"
address_space = ["10.1.0.0/24"]
location = local.regions.primary
}
}
}
}
}
The example above demonstrates how Azure Verified Modules (AVM) enable real-world, scalable infrastructure design using Terraform:
- A resource module ensures consistent, secure creation of foundational components like resource groups.
- A pattern module implements best-practice architectures, such as the hub-and-spoke network topology aligned with Azure Landing Zone (ALZ) principles.
- A clean separation of concerns enhances maintainability, auditability, and reusability across projects.
This modular, composable approach doesn’t just simplify provisioning; it also supports the evolution of cloud infrastructure toward self-service delivery models. In these models, platform teams build reusable components, and internal developers can deploy secure, compliant environments without deep infrastructure knowledge.
As organisations scale, Infrastructure as Code (IaC) becomes critical—but it also becomes complex.
Poorly scoped Terraform modules can lead to:
- Fragile, hard-to-maintain codebases
- Inefficient onboarding and handoffs
- Inflexible module logic that inhibits change
- Technical debt that undermines automation efforts
AVM addresses these challenges by offering a curated, enterprise-ready module ecosystem, built around clear design principles:
- Secure, opinionated defaults baked in
- Standardised structure for consistency and interoperability
- Granularity designed for reuse, not rigidity
- Strong alignment with Azure governance and landing zone frameworks
In essence, AVM helps organisations avoid the pitfalls of module misuse, embrace platform engineering practices, and deliver infrastructure that is scalable, secure, and built for the future.
Going a step further: Cloud Enablers by Devoteam
At Devoteam, we know that managing complex cloud environments goes far beyond just writing Terraform. It requires structure, security, and speed at scale. That’s why we created Cloud Enablers: a fully automated, production-ready framework for accelerating the delivery of cloud foundations on Azure.
Built on industry best practices and deeply aligned with Microsoft Cloud Adoption Framework, Cloud Enablers empowers organisations to move fast without compromising on governance or security. It enables platform teams to deliver a standardised, enterprise-grade foundation, so developers and application teams can consume infrastructure as a service, safely and independently.
See our Cloud Enabler explained in 60 seconds!
Whether you rely on GitHub or Azure DevOps, Cloud Enablers provides a secure, opinionated starting point with built-in automation, policy enforcement, and modular extensibility. It’s how we help our clients adopt and manage Cloud Environments correctly from the start.
With Cloud Enablers, your Azure platform becomes more than compliant, it becomes truly cloud-native, scalable, and ready for the future.
3x faster cloud migration and adoption

We’ve built it, so you don’t have to! Our Azure Cloud Enabler Framework accelerates your migration by 3 times and guarantees a secure and compliant outcome.

