Terraform is an infrastructure as code tool for creating, modifying and versioning infrastructure securely and efficiently across all cloud providers.
Installation
| Platform | Command |
|---|
| Ubuntu/Debian | wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg |
| CentOS/RHEL | sudo yum install -y yum-utils && sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo |
| macOS | brew tap hashicorp/tap && brew install terraform |
| Windows | choco install terraform |
| Direct binary | Download from releases.hashicorp.com |
Essential commands
| Action | Command | Description |
|---|
| Initialize | terraform init | Initialize project and download providers |
| Plan | terraform plan | Show changes that will be applied |
| Apply | terraform apply | Apply changes to infrastructure |
| Destroy | terraform destroy | Delete all managed resources |
| Validate | terraform validate | Check file syntax |
| State | terraform show | Display current state |
| Refresh | terraform refresh | Update state with reality |
State management
| Action | Command |
|---|
| List resources | terraform state list |
| View a resource | terraform state show aws_instance.web |
| Import resource | terraform import aws_instance.web i-1234567890abcdef0 |
| Remove from state | terraform state rm aws_instance.web |
| Move resource | terraform state mv aws_instance.web aws_instance.web2 |
| Pull state | terraform state pull |
| Push state | terraform state push terraform.tfstate |
Useful options
| Option | Example | Usage |
|---|
| Target | terraform apply -target=aws_instance.web | Apply only to one resource |
| Auto-approve | terraform apply -auto-approve | No interactive confirmation |
| Var-file | terraform apply -var-file="prod.tfvars" | Use a variables file |
| Var | terraform apply -var="instance_count=3" | Pass a variable |
| State | terraform apply -state=custom.tfstate | Use a custom state file |
| Plan file | terraform plan -out=plan.out | Save the plan |
File structure
Recommended organization
terraform-project/
├── main.tf # Main configuration
├── variables.tf # Variable declarations
├── outputs.tf # Outputs
├── terraform.tfvars # Variable values
├── versions.tf # Provider versions
└── modules/ # Reusable modules
├── vpc/
├── ec2/
└── rds/
Example main.tf file
terraform {
required_version = ">= 1.0"
required_providers {
vsphere = {
source = "hashicorp/vsphere"
version = "~> 2.0"
}
}
}
provider "vsphere" {
user = var.vsphere_user
password = var.vsphere_password
vsphere_server = var.vsphere_server
# If self-signed certificate
allow_unverified_ssl = true
}
# Datasources to retrieve vSphere info
data "vsphere_datacenter" "datacenter" {
name = "Datacenter"
}
data "vsphere_datastore" "datastore" {
name = "datastore1"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
data "vsphere_compute_cluster" "cluster" {
name = "cluster1"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
data "vsphere_network" "network" {
name = "VM Network"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
data "vsphere_virtual_machine" "template" {
name = "ubuntu-20.04-template"
datacenter_id = data.vsphere_datacenter.datacenter.id
}
# VM
resource "vsphere_virtual_machine" "web" {
name = "${var.vm_name}-web"
resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
datastore_id = data.vsphere_datastore.datastore.id
num_cpus = 2
memory = 4096
guest_id = data.vsphere_virtual_machine.template.guest_id
scsi_type = data.vsphere_virtual_machine.template.scsi_type
network_interface {
network_id = data.vsphere_network.network.id
adapter_type = data.vsphere_virtual_machine.template.network_interface_types[0]
}
disk {
label = "disk0"
size = data.vsphere_virtual_machine.template.disks.0.size
eagerly_scrub = data.vsphere_virtual_machine.template.disks.0.eagerly_scrub
thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned
}
clone {
template_uuid = data.vsphere_virtual_machine.template.id
customize {
linux_options {
host_name = "${var.vm_name}-web"
domain = "example.com"
}
network_interface {
ipv4_address = "192.168.1.100"
ipv4_netmask = 24
}
ipv4_gateway = "192.168.1.1"
dns_server_list = ["8.8.8.8", "8.8.4.4"]
}
}
}
Variables (variables.tf)
variable "vsphere_server" {
description = "vCenter server"
type = string
default = "vcenter.example.com"
}
variable "vsphere_user" {
description = "vSphere user"
type = string
}
variable "vsphere_password" {
description = "vSphere password"
type = string
sensitive = true
}
variable "vm_name" {
description = "Base VM name"
type = string
default = "terraform-vm"
}
variable "vm_count" {
description = "Number of VMs to create"
type = number
default = 1
}
Outputs (outputs.tf)
output "vm_name" {
description = "Created VM name"
value = vsphere_virtual_machine.web.name
}
output "vm_ip" {
description = "VM IP address"
value = vsphere_virtual_machine.web.default_ip_address
}
output "vm_uuid" {
description = "VM UUID"
value = vsphere_virtual_machine.web.uuid
sensitive = false
}
Modules
Create a module
# modules/vm/main.tf
resource "vsphere_virtual_machine" "this" {
name = var.vm_name
resource_pool_id = var.resource_pool_id
datastore_id = var.datastore_id
num_cpus = var.num_cpus
memory = var.memory
guest_id = var.guest_id
network_interface {
network_id = var.network_id
}
disk {
label = "disk0"
size = var.disk_size
}
clone {
template_uuid = var.template_uuid
customize {
linux_options {
host_name = var.vm_name
domain = var.domain
}
network_interface {
ipv4_address = var.ip_address
ipv4_netmask = 24
}
ipv4_gateway = var.gateway
}
}
}
# modules/vm/variables.tf
variable "vm_name" {
description = "VM name"
type = string
}
variable "num_cpus" {
description = "Number of CPUs"
type = number
default = 2
}
variable "memory" {
description = "Memory in MB"
type = number
default = 4096
}
Use a module
module "web_server" {
source = "./modules/vm"
vm_name = "web-01"
resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
datastore_id = data.vsphere_datastore.datastore.id
network_id = data.vsphere_network.network.id
template_uuid = data.vsphere_virtual_machine.template.id
guest_id = data.vsphere_virtual_machine.template.guest_id
num_cpus = 4
memory = 8192
ip_address = "192.168.1.101"
gateway = "192.168.1.1"
domain = "example.com"
}
# Reference a module output
output "web_server_ip" {
value = module.web_server.ip_address
}
Best practices
| Practice | Description |
|---|
| Organization | Separate by environment and services |
| Tagging | Use consistent tags everywhere |
| Remote state | Always use a remote backend |
| Secrets | Never hardcode secrets |
| Documentation | Comment and document the code |
| Validation | Test with terraform plan |
| Modules | Reuse with modules |
| Versioning | Lock provider versions |
Debugging & Troubleshooting
| Debug | Command |
|---|
| Debug mode | TF_LOG=DEBUG terraform apply |
| Detailed logs | TF_LOG=TRACE terraform plan |
| Graph | terraform graph | dot -Tpng > graph.png |
| Format check | terraform fmt -check -diff |
| Auto format | terraform fmt -recursive |