Module
The module resource allows you to reuse and organize HCL configuration by importing external configuration files. Modules enable code reusability, maintainability, and collaboration by packaging common lab patterns into reusable components that can be shared across different lab configurations.
Use Cases
Section titled “Use Cases”As a lab author, you can use modules to create more organized and reusable configurations:
- Configuration Reusability: Package common lab patterns (e.g., LAMP stack, Kubernetes cluster) into modules that can be reused across multiple labs
- Team Collaboration: Share standardized lab components with team members through module registries or repositories
- Complex Lab Organization: Break large lab configurations into smaller, manageable modules organized by functionality
- Environment Variations: Create modules that can be configured with different variables for development, staging, and production environments
- Third-Party Integration: Use community modules or vendor-provided modules for common infrastructure patterns
- Version Management: Use different versions of modules to maintain compatibility and control updates
Modules promote code reuse and help maintain consistency across lab configurations while reducing duplication and complexity.
HCL Syntax
Section titled “HCL Syntax”Basic Syntax
Section titled “Basic Syntax”module "name" { source = "./path/to/module"}
Full Syntax
Section titled “Full Syntax”module "web_stack" { source = "github.com/example/lab-modules//web-stack" version = "v1.2.0"
variables = { environment = "production" cpu_limit = 2048 replicas = 3 }
depends_on = ["resource.network.main"]}
Fields
Section titled “Fields”Field | Required | Type | Description |
---|---|---|---|
source | ✓ | string | Location of the module (local path, GitHub URL, or registry reference) |
version | string | Version of the module to use. Defaults to “latest”. | |
variables | any | Variables to pass to the module (can be object, list, or primitive values) | |
depends_ | list(string) | List of resource dependencies (inherited from ResourceBase) | |
disabled | bool | Whether this module is disabled. Defaults to false. |
Module Sources
Section titled “Module Sources”Modules can be sourced from different locations:
Source Type | Format | Example |
---|---|---|
Local Filesystem | Relative path | source = "./modules/web-server" |
GitHub Repository | GitHub URL with optional subdirectory | source = "github.com/org/repo//modules/web" |
Module Registry | registry/namespace/module format | source = "instruqt.com/hashicorp/consul" |
Module Reference
Section titled “Module Reference”Resources within modules are referenced using the syntax:
module.module_name.resource.resource_type.resource_name
Module outputs are referenced using:
module.module_name.output.output_name
Examples
Section titled “Examples”Local Module
Section titled “Local Module”module "database" { source = "./modules/postgres"
variables = { database_name = "myapp" username = "admin" port = 5432 }}
# Reference module resourcesresource "container" "app" { image { name = "myapp:latest" }
environment = { DB_HOST = module.database.resource.container.postgres.meta.name DB_PORT = "5432" }}
Remote GitHub Module
Section titled “Remote GitHub Module”module "k8s_cluster" { source = "github.com/instruqt/lab-modules//kubernetes/basic-cluster" version = "v2.1.0"
variables = { cluster_name = "demo-cluster" node_count = 3 cpu_per_node = 2048 }}
# Use module outputsoutput "cluster_kubeconfig" { value = module.k8s_cluster.output.kubeconfig_path}
Module Registry Source
Section titled “Module Registry Source”module "web_stack" { source = "instruqt.com/examples/lamp-stack"
variables = { environment = variable.env php_version = "8.1" mysql_root_pass = "secure_password" }}
Complex Module with Dependencies
Section titled “Complex Module with Dependencies”# Base networking modulemodule "network" { source = "./modules/network"
variables = { subnet_cidr = "10.0.0.0/16" }}
# Web tier module that depends on networkmodule "web_tier" { source = "./modules/web-tier"
depends_on = ["module.network"]
variables = { network_id = module.network.output.network_id instance_type = "standard" replica_count = 2 }}
# Database tier modulemodule "db_tier" { source = "./modules/database"
depends_on = ["module.network"]
variables = { network_id = module.network.output.network_id database_size = "large" backup_enabled = true }}
Module with Different Variable Types
Section titled “Module with Different Variable Types”module "microservices" { source = "./modules/microservices"
variables = { # String variables environment = "staging" namespace = "my-app"
# Number variables cpu_limit = 1024 memory_limit = 2048
# Boolean variables monitoring_enabled = true debug_mode = false
# List variables services = ["api", "web", "worker"] ports = [8080, 8081, 8082]
# Object variables database_config = { host = "postgres.local" port = 5432 database = "myapp" ssl = true }
# Complex nested structure service_configs = { api = { replicas = 3 cpu = 512 memory = 1024 } web = { replicas = 2 cpu = 256 memory = 512 } } }}
Conditional Module Usage
Section titled “Conditional Module Usage”module "monitoring" { source = "./modules/monitoring"
disabled = !variable.enable_monitoring
variables = { environment = variable.environment services = ["web", "api", "database"] }}
Module Chaining
Section titled “Module Chaining”# Base infrastructure modulemodule "infrastructure" { source = "./modules/infrastructure"
variables = { environment = variable.environment }}
# Application module using infrastructure outputsmodule "application" { source = "./modules/application"
depends_on = ["module.infrastructure"]
variables = { network_id = module.infrastructure.output.network_id subnet_id = module.infrastructure.output.subnet_id app_name = variable.app_name }}
# Monitoring module using both previous modulesmodule "monitoring" { source = "./modules/monitoring"
depends_on = ["module.infrastructure", "module.application"]
variables = { network_id = module.infrastructure.output.network_id app_container = module.application.output.app_container_name monitoring_port = 9090 }}
Module Structure
Section titled “Module Structure”A typical module directory structure:
modules/└── web-server/ ├── main.hcl # Main module configuration ├── variables.hcl # Variable definitions ├── outputs.hcl # Output definitions └── templates/ # Template files (if needed) └── nginx.conf.tpl
Example Module Implementation
Section titled “Example Module Implementation”modules/web-server/variables.hcl:
variable "server_name" { default = "web-server" description = "Name of the web server"}
variable "port" { default = 80 description = "Port for the web server"}
variable "image_tag" { default = "latest" description = "Docker image tag to use"}
modules/web-server/main.hcl:
resource "container" "web" { image { name = "nginx:${variable.image_tag}" }
container_name = variable.server_name
port { local = variable.port remote = 80 }}
resource "template" "config" { source = "templates/nginx.conf.tpl" destination = "./nginx.conf"
vars = { server_name = variable.server_name listen_port = variable.port }}
modules/web-server/outputs.hcl:
output "container_name" { value = resource.container.web.meta.name description = "Name of the web server container"}
output "service_url" { value = format("http://localhost:%d", variable.port) description = "URL to access the web server"}
Best Practices
Section titled “Best Practices”- Clear Interface: Define clear input variables and outputs for your modules
- Documentation: Include descriptions for all variables and outputs
- Version Control: Use version tags for modules shared across teams
- Default Values: Provide sensible defaults for optional variables
- Modular Design: Keep modules focused on specific functionality
- Dependency Management: Use explicit dependencies when modules need specific ordering
Common Patterns
Section titled “Common Patterns”Environment-Specific Modules
Section titled “Environment-Specific Modules”module "app_environment" { source = "./modules/environment"
variables = { environment = variable.environment config = { dev = { cpu = 512 memory = 1024 replicas = 1 debug = true } prod = { cpu = 2048 memory = 4096 replicas = 3 debug = false } }[variable.environment] }}
Shared Service Modules
Section titled “Shared Service Modules”# Shared database modulemodule "shared_database" { source = "./modules/postgres"
variables = { instance_type = "high-performance" databases = ["app1", "app2", "app3"] }}
# Multiple apps using the shared databasemodule "app1" { source = "./modules/web-app"
variables = { database_host = module.shared_database.output.host database_name = "app1" }}
module "app2" { source = "./modules/web-app"
variables = { database_host = module.shared_database.output.host database_name = "app2" }}
Validation Rules
Section titled “Validation Rules”- Source Required: The
source
field must be specified - Valid Module Name: Module names must follow valid identifier rules
- Dependency References: Dependencies in
depends_on
must reference existing resources - Variable Types: Variables passed to modules must match expected types in the module
- Single Label: Module blocks must have exactly one label (the module name)
Modules provide a powerful way to organize, reuse, and share lab configuration patterns, enabling you to build complex lab environments from well-tested, reusable components.