skip to content

Search

Syspirit
EN

Terraform

Deploy your resources with the IaC tool Terraform!

DevOps
Published on

Terraform is an infrastructure as code tool for creating, modifying and versioning infrastructure securely and efficiently across all cloud providers.

Installation

PlatformCommand
Ubuntu/Debianwget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
CentOS/RHELsudo yum install -y yum-utils && sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
macOSbrew tap hashicorp/tap && brew install terraform
Windowschoco install terraform
Direct binaryDownload from releases.hashicorp.com

Essential commands

ActionCommandDescription
Initializeterraform initInitialize project and download providers
Planterraform planShow changes that will be applied
Applyterraform applyApply changes to infrastructure
Destroyterraform destroyDelete all managed resources
Validateterraform validateCheck file syntax
Stateterraform showDisplay current state
Refreshterraform refreshUpdate state with reality

State management

ActionCommand
List resourcesterraform state list
View a resourceterraform state show aws_instance.web
Import resourceterraform import aws_instance.web i-1234567890abcdef0
Remove from stateterraform state rm aws_instance.web
Move resourceterraform state mv aws_instance.web aws_instance.web2
Pull stateterraform state pull
Push stateterraform state push terraform.tfstate

Useful options

OptionExampleUsage
Targetterraform apply -target=aws_instance.webApply only to one resource
Auto-approveterraform apply -auto-approveNo interactive confirmation
Var-fileterraform apply -var-file="prod.tfvars"Use a variables file
Varterraform apply -var="instance_count=3"Pass a variable
Stateterraform apply -state=custom.tfstateUse a custom state file
Plan fileterraform plan -out=plan.outSave the plan

File structure

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

PracticeDescription
OrganizationSeparate by environment and services
TaggingUse consistent tags everywhere
Remote stateAlways use a remote backend
SecretsNever hardcode secrets
DocumentationComment and document the code
ValidationTest with terraform plan
ModulesReuse with modules
VersioningLock provider versions

Debugging & Troubleshooting

DebugCommand
Debug modeTF_LOG=DEBUG terraform apply
Detailed logsTF_LOG=TRACE terraform plan
Graphterraform graph | dot -Tpng > graph.png
Format checkterraform fmt -check -diff
Auto formatterraform fmt -recursive