OpenTofu: A Beginners Guide to a Terraform Fork Including Migration From Terraform
OpenTofu Modules
What are Modules
A module in OpenTofu (a Terraform fork) is any directory that contains configuration files. When you run OpenTofu commands inside that directory, it becomes the root module, orchestrating resources defined within.
Root Module Example
Suppose your workspace looks like this:
$ ls /root/opentofu-projects/aws-instance
main.tf variables.tf
– main.tf
# /root/opentofu-projects/aws-instance/main.tf
resource "aws_instance" "webserver" {
ami = var.ami
instance_type = var.instance_type
key_name = var.key
}
– variables.tf
# /root/opentofu-projects/aws-instance/variables.tf
variable "ami" {
type = string
default = "ami-0edab43b6fa892279"
description = "Ubuntu AMI ID in the ca-central-1 region"
}
Note
Running tofu init
, tofu plan
, or tofu apply
inside aws-instance
treats it as the root module.
Calling Child Modules
To avoid duplicating infrastructure code, package a directory as a child module and invoke it:
$ mkdir -p /root/opentofu-projects/development
Create a main.tf
in development
:
# /root/opentofu-projects/development/main.tf
module "dev-webserver" {
source = "../aws-instance"
}
module "dev-webserver"
assigns a logical name.source = "../aws-instance"
points to the child module’s path.
Now development
is the root module, calling the ../aws-instance
child module.
Building a Reusable Payroll App Module
FlexIT Consulting needs the same payroll stack in multiple regions. The architecture uses:
- One EC2 instance (custom AMI)
- One DynamoDB table
- One S3 bucket
All resources live in the default VPC:
Define the Module
Organize reusable code under modules/payroll-app
:
$ mkdir -p /root/opentofu-projects/modules/payroll-app
$ ls /root/opentofu-projects/modules/payroll-app
app_server.tf dynamodb_table.tf s3_bucket.tf variables.tf
app_server.tf
# modules/payroll-app/app_server.tf
resource "aws_instance" "app_server" {
ami = var.ami
instance_type = "t2.medium"
tags = {
Name = "${var.app_region}-app-server"
}
depends_on = [
aws_dynamodb_table.payroll_db,
aws_s3_bucket.payroll_data
]
}
s3_bucket.tf
# modules/payroll-app/s3_bucket.tf
resource "aws_s3_bucket" "payroll_data" {
bucket = "${var.app_region}-${var.bucket}"
}
dynamodb_table.tf
# modules/payroll-app/dynamodb_table.tf
resource "aws_dynamodb_table" "payroll_db" {
name = "user_data"
billing_mode = "PAY_PER_REQUEST"
hash_key = "EmployeeID"
attribute {
name = "EmployeeID"
type = "S"
}
}
variables.tf
# modules/payroll-app/variables.tf
variable "app_region" {
type = string
}
variable "bucket" {
type = string
default = "flexit-payroll-alpha-22001c"
}
variable "ami" {
type = string
}
- Hardcoded: instance type, DynamoDB table name, and hash key.
- Configurable: AMI, region, bucket via variables.
Deploy in US East (us-east-1)
Create a root module for the US deployment:
$ mkdir /root/opentofu-projects/us-payroll-app
– provider.tf
# /root/opentofu-projects/us-payroll-app/provider.tf
provider "aws" {
region = "us-east-1"
}
– main.tf
# /root/opentofu-projects/us-payroll-app/main.tf
module "us_payroll" {
source = "../modules/payroll-app"
app_region = "us-east-1"
ami = "ami-24e140119877avm"
}
Initialize and apply:
$ cd /root/opentofu-projects/us-payroll-app
$ tofu init
$ tofu apply
You’ll see:
module.us_payroll.aws_dynamodb_table.payroll_db will be created
module.us_payroll.aws_instance.app_server will be created
module.us_payroll.aws_s3_bucket.payroll_data will be created
Note
The S3 bucket name combines the region prefix with the default bucket variable.
Deploy in London (eu-west-2)
Repeat for the UK region:
$ mkdir /root/opentofu-projects/uk-payroll-app
– provider.tf
# /root/opentofu-projects/uk-payroll-app/provider.tf
provider "aws" {
region = "eu-west-2"
}
– main.tf
# /root/opentofu-projects/uk-payroll-app/main.tf
module "uk_payroll" {
source = "../modules/payroll-app"
app_region = "eu-west-2"
ami = "ami-35e140119877avm"
}
$ cd /root/opentofu-projects/uk-payroll-app
$ tofu init && tofu apply
Resources provisioned under:
module.uk_payroll.aws_instance.app_server
module.uk_payroll.aws_s3_bucket.payroll_data
module.uk_payroll.aws_dynamodb_table.payroll_db
OpenTofu can source community or verified modules from the registry, just like Terraform. For example, to provision a security group:
module "security_group_ssh" {
source = "terraform-aws-modules/security-group/aws/modules/ssh"
version = "3.16.0"
vpc_id = "vpc-7d8d215"
ingress_cidr_blocks = ["10.10.0.0/16"]
name = "ssh-access"
}
Warning
Always pin the version
to prevent unexpected module changes. Use tofu get
or tofu init
to fetch registry modules.
Benefit | Description |
---|---|
Simpler configs | Keep root modules concise for easier maintenance |
Reusability | Share the same module across multiple projects |
Stability | Enforce default settings and reduce configuration drift |
Reduced errors | Leverage tested modules from your team or the community registry |
Links and References
Watch Video
Watch video content