Terraform’s power lies in its ability to provision real cloud infrastructure — virtual machines, networks, databases, DNS records, load balancers, and Kubernetes clusters — using declarative configuration. A Terraform configuration describes the desired end state, and Terraform calculates and executes the actions needed to reach that state. Key concepts in practice are: resources (infrastructure objects to create), data sources (read existing infrastructure without managing it), variables (parameterised configuration), outputs (values to display or pass to other modules), modules (reusable infrastructure components), and remote state (shared state for team environments). This guide demonstrates provisioning a complete multi-tier infrastructure (VPC, EC2 instances, RDS database, and security groups) on AWS from a RHEL 9 workstation using Terraform.
Prerequisites
- Terraform installed on RHEL 9
- AWS CLI configured with access credentials (
aws configure)
Step 1 — variables.tf and terraform.tfvars
# variables.tf
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "instance_type" {
type = string
default = "t3.micro"
}
variable "db_password" {
type = string
sensitive = true # Redacted from plan output and logs
}
# terraform.tfvars — never commit this file to Git
region = "us-east-1"
instance_type = "t3.small"
db_password = "SecureRDSPass123!"
Step 2 — main.tf (VPC, EC2, RDS)
# main.tf
provider "aws" {
region = var.region
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "main-vpc" }
}
# Public subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "${var.region}a"
tags = { Name = "public-subnet" }
}
# EC2 web server
data "aws_ami" "rhel9" {
most_recent = true
owners = ["309956199498"] # Red Hat's AWS account
filter {
name = "name"
values = ["RHEL-9*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.rhel9.id
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
tags = { Name = "web-server" }
}
# RDS PostgreSQL
resource "aws_db_instance" "db" {
engine = "postgres"
engine_version = "16"
instance_class = "db.t3.micro"
allocated_storage = 20
db_name = "appdb"
username = "appuser"
password = var.db_password
skip_final_snapshot = true
tags = { Name = "app-database" }
}
Step 3 — Outputs and Remote State
# outputs.tf
output "web_public_ip" { value = aws_instance.web.public_ip }
output "db_endpoint" { value = aws_db_instance.db.endpoint }
# Remote state in S3 (add to versions.tf)
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}
Step 4 — Apply and Manage
terraform init # Initialise with S3 backend
terraform plan -out=tfplan # Save plan for review
terraform apply tfplan # Apply saved plan
# After changes:
terraform show # Show current state
terraform state list # List all managed resources
terraform output # Show output values
Conclusion
Terraform infrastructure provisioning on RHEL 9 enables reproducible, version-controlled cloud environments. The sensitive = true attribute on variable declarations prevents credentials from appearing in plan output and logs — a critical security practice when Terraform runs in CI/CD pipelines where logs are often accessible to the entire team. Always use remote state backends in S3 with DynamoDB state locking for team environments to prevent concurrent terraform apply runs from corrupting the state file.
Next steps: How to Install Terraform on RHEL 9, How to Install Ansible on RHEL 9, and How to Register a GitHub Actions Runner on RHEL 9.