IaC Core
Variables, outputs & modules
Lesson 4 of 5
What you'll learn
- Parameterize configuration with typed input variables
- Expose computed values to callers with outputs
- Package resources into a reusable module with a clean interface
Hard-coded values don't scale across teams or environments. Variables turn a config into a function of its inputs; outputs are its return values.
variable "instance_count" {
type = number
default = 2
}
resource "aws_instance" "web" {
count = var.instance_count
instance_type = "t3.small"
ami = "ami-0abc123"
}
output "instance_ids" {
value = aws_instance.web[*].id
}
A module is a function over infrastructure
A module is a directory of .tf files with declared variable inputs and output returns. Calling it is like calling a function: pass inputs, get named resources and outputs back. This is how you stop copy-pasting a VPC across ten projects.
module "network" {
source = "./modules/network"
cidr_block = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
}
resource "aws_instance" "api" {
subnet_id = module.network.subnet_ids[0] # consume an output
}
The interface is the contract
Callers depend only on a module's inputs and outputs, never its internals. That boundary lets you refactor what's inside — swap resources, add defaults — without breaking consumers, exactly like a well-typed function signature.
Version your shared modules
Pin modules from a registry or Git with a version constraint (version = "~> 3.1"). An unpinned source means a colleague's terraform init can silently pull a breaking change into your plan.
Run it. The module takes inputs and returns named resources plus outputs, with defaults applied.
Why can you safely refactor the resources inside a module without breaking its callers?
Saved on this device. Sign in to sync your progress everywhere.