Managing Module Composition Patterns
1. Using Root Modules
| Property | Description | Example |
|---|---|---|
| source | Path to the module source | ./modules/web-server |
| version | Version of the module to use | v1.2.0 |
| providers | Map of provider aliases to provider configurations | { aws = aws } |
Example: Root Module Configuration
module "web_server" {
source = "./modules/web-server"
version = "v1.2.0"
instance_type = "t3.micro"
ami_id = "ami-12345678"
providers = {
aws = aws
}
}
Note: Root modules are the entry point for Terraform configurations and can reference other modules to compose complex infrastructure.
2. Creating Child Modules
| Property | Description | Example |
|---|---|---|
| source | Path to the module source code | ./child-module |
| version | Version of the module to use | v1.0.0 |
| providers | Map of provider aliases to provider configurations | { aws = aws } |
Example: Creating a child module
module "web_server" {
source = "./web-server-module"
instance_type = "t3.micro"
ami_id = "ami-12345678"
}
Note: Child modules are defined in the
module block and can be reused across different configurations.
3. Implementing Module Nesting
Module nesting in Terraform allows you to organize and reuse configurations by embedding modules within other modules. This pattern promotes modularity, reusability, and maintainability of infrastructure code.
| Property | Description | Example |
|---|---|---|
| source | Specifies the source of the module to be used. | source = "terraform-aws-modules/vpc/aws" |
| version | Defines the version of the module to use (optional but recommended). | version = "3.0.0" |
| variables | Variables passed to the nested module. | cidr_block = "10.0.0.0/16" |
Example: Using Nested Modules
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"]
}
module "web_server" {
source = "./modules/web-server"
vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.private_subnets[0]
}
Note: When nesting modules, ensure that variables and outputs are properly defined to allow communication between parent and child modules.
4. Using Module Dependencies
| Property | Description | Example |
|---|---|---|
| source | Location of the module (local path, Git repo, registry) | ./modules/web-server |
| version | Version constraint for registry modules | ~> 1.0 |
| providers | Mapping of module providers to local aliases | { aws = aws } |
Example: Module with Dependencies
module "web_server" {
source = "./modules/web-server"
count = 2
vpc_id = module.vpc.vpc_id
instance_type = "t3.micro"
}
Example: Registry Module with Version
module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "~> 7.0"
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
}
Note: Module dependencies are resolved at
terraform init time. Changes to dependencies require re-running terraform init.
5. Sharing Data Between Modules
| Source | Data Type | Description |
|---|---|---|
| output | string |
Value exported from a module |
| module | map |
Map of module outputs |
Example: Referencing Module Outputs
module "web_server" {
source = "./web_server"
}
module "database" {
source = "./database"
db_name = module.web_server.db_name
}
Example: Using Output Values in Variables
variable "web_server_port" {
description = "Port for web server"
default = module.web_server.port
}
Note: Outputs must be declared in the module that defines them and referenced in the consuming module.
Warning: Avoid circular dependencies between modules when sharing data.
6. Implementing Module Abstraction
| Pattern | Description | Usage |
|---|---|---|
| Input Variables | Define inputs to modules using variable blocks. |
|
| Output Variables | Expose values from a module using output blocks. |
|
| Module Nesting | Use modules to compose complex infrastructure. | |
Example: Abstracting EC2 Instance Creation
module "web_instance" {
source = "./ec2_module"
instance_type = "t3.micro"
ami_id = "ami-12345678"
}
Note: Abstraction allows reusability and simplifies complex configurations.
7. Creating Module Wrappers
Example: Basic Module Wrapper
module "wrapper" {
source = "./my-module"
version = "1.0.0"
# Pass through variables
instance_type = var.instance_type
vpc_id = var.vpc_id
}
| Property | Description | Usage |
|---|---|---|
| source | Path or URL to the wrapped module | Required for module reference |
| version | Version constraint for the wrapped module | Optional, but recommended |
| depends_on | Explicit dependency declaration | Optional, for complex dependencies |
Note: Module wrappers provide a stable interface for consumers while allowing internal module changes.
Wrapper vs Direct Module Usage
- Wrapper: Provides abstraction layer, version control, and parameter normalization
- Direct: Simpler but less flexible for future changes
8. Using Module Factory Patterns
Module factory patterns in Terraform allow you to create reusable, parameterized modules that can generate multiple instances of similar infrastructure with different configurations.
| Pattern | Description | Use Case |
|---|---|---|
| factory module | A module that accepts a list of configurations and creates multiple instances of a resource or set of resources | Creating multiple similar resources with different parameters |
Example: Factory Module for EC2 Instances
module "ec2_factory" {
source = "./modules/ec2-factory"
instances = [
{
name = "web-server-1"
instance_type = "t3.micro"
ami = "ami-12345678"
},
{
name = "web-server-2"
instance_type = "t3.small"
ami = "ami-87654321"
}
]
}
Example: Factory Module Implementation
# modules/ec2-factory/main.tf
resource "aws_instance" "instance" {
count = length(var.instances)
ami = var.instances[count.index].ami
instance_type = var.instances[count.index].instance_type
tags = {
Name = var.instances[count.index].name
}
}
Note: The factory pattern is particularly useful for managing infrastructure that follows a similar structure but requires different configurations, such as multiple web servers or database instances with varying sizes.
9. Implementing Module Versioning Strategies
| Strategy | Description | Usage | Notes |
|---|---|---|---|
| Semantic Versioning | Follows MAJOR.MINOR.PATCH format (e.g., 1.2.3) | version = "1.2.3" |
Recommended for stable modules |
| Git Tags | Use Git tags to mark module versions (e.g., v1.0.0) | source = "github.com/owner/repo?ref=v1.0.0" |
Supports any Git-compatible source |
| Branch References | Point to specific branches (e.g., main, develop) | source = "github.com/owner/repo?ref=main" |
Not recommended for production due to instability |
| Registry Versioning | Use Terraform Registry for versioned modules | source = "registry.terraform.io/owner/module" |
Enables version pinning and discovery |
Example: Using Registry Module with Version Pinning
module "vpc" {
source = "registry.terraform.io/terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
}
Example: Using Git Tag for Module Version
module "alb" {
source = "github.com/terraform-aws-modules/terraform-aws-alb?ref=v2.15.0"
name = "my-alb"
}
Note: Always pin module versions in production to ensure reproducibility and prevent unexpected changes.
Warning: Using
ref=main or other branch names can lead to breaking changes if the module is updated without bumping the version.
10. Managing Cross-Module State
| Property | Description | Usage |
|---|---|---|
| module.<name>.outputs | References outputs from a child module | |
| data.<type>.<name> | Fetches external data for use in configuration | |
Example: Using Module Outputs
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
}
module "web" {
source = "./modules/web"
vpc_id = module.vpc.vpc_id
}
Note: Terraform does not allow circular dependencies between modules. Ensure that module outputs are used in a way that avoids cycles.
Warning: Avoid tightly coupling modules by referencing outputs directly. Prefer passing inputs to modules rather than relying on internal state.