Tools: Terraform Basics: Manage Your Infrastructure as Code in 30 Minutes

Tools: Terraform Basics: Manage Your Infrastructure as Code in 30 Minutes

Terraform Basics: Manage Your Infrastructure as Code in 30 Minutes

Why Infrastructure as Code

Install Terraform

Your First Terraform Config (DigitalOcean Droplet)

State Management

Practical Example: Full Stack (Droplet + Firewall + Domain)

Essential Terraform Commands If you're still managing servers by clicking through cloud consoles — this is for you. Terraform lets you define your entire infrastructure in code. Reproducible, version-controlled, reviewable. Terraform tracks what it's created in a state file. For teams, store state remotely: I built ARIA to solve exactly this.

Try it free at step2dev.com — no credit card needed. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# Mac -weight: 500;">brew tap hashicorp/tap && -weight: 500;">brew -weight: 500;">install hashicorp/tap/terraform # Ubuntu/Debian -weight: 500;">wget -O- https://-weight: 500;">apt.releases.hashicorp.com/gpg | -weight: 600;">sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://-weight: 500;">apt.releases.hashicorp.com $(lsb_release -cs) main" | -weight: 600;">sudo tee /etc/-weight: 500;">apt/sources.list.d/hashicorp.list -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install terraform terraform --version # Mac -weight: 500;">brew tap hashicorp/tap && -weight: 500;">brew -weight: 500;">install hashicorp/tap/terraform # Ubuntu/Debian -weight: 500;">wget -O- https://-weight: 500;">apt.releases.hashicorp.com/gpg | -weight: 600;">sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://-weight: 500;">apt.releases.hashicorp.com $(lsb_release -cs) main" | -weight: 600;">sudo tee /etc/-weight: 500;">apt/sources.list.d/hashicorp.list -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install terraform terraform --version # Mac -weight: 500;">brew tap hashicorp/tap && -weight: 500;">brew -weight: 500;">install hashicorp/tap/terraform # Ubuntu/Debian -weight: 500;">wget -O- https://-weight: 500;">apt.releases.hashicorp.com/gpg | -weight: 600;">sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://-weight: 500;">apt.releases.hashicorp.com $(lsb_release -cs) main" | -weight: 600;">sudo tee /etc/-weight: 500;">apt/sources.list.d/hashicorp.list -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install terraform terraform --version # main.tf terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } } provider "digitalocean" { token = var.do_token } variable "do_token" { description = "DigitalOcean API token" sensitive = true } resource "digitalocean_droplet" "web" { name = "web-server-1" region = "nyc3" size = "s-1vcpu-1gb" image = "ubuntu-22-04-x64" ssh_keys = [digitalocean_ssh_key.default.fingerprint] tags = ["web", "production"] } resource "digitalocean_ssh_key" "default" { name = "my-key" public_key = file("~/.ssh/id_ed25519.pub") } output "droplet_ip" { value = digitalocean_droplet.web.ipv4_address } # main.tf terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } } provider "digitalocean" { token = var.do_token } variable "do_token" { description = "DigitalOcean API token" sensitive = true } resource "digitalocean_droplet" "web" { name = "web-server-1" region = "nyc3" size = "s-1vcpu-1gb" image = "ubuntu-22-04-x64" ssh_keys = [digitalocean_ssh_key.default.fingerprint] tags = ["web", "production"] } resource "digitalocean_ssh_key" "default" { name = "my-key" public_key = file("~/.ssh/id_ed25519.pub") } output "droplet_ip" { value = digitalocean_droplet.web.ipv4_address } # main.tf terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } } provider "digitalocean" { token = var.do_token } variable "do_token" { description = "DigitalOcean API token" sensitive = true } resource "digitalocean_droplet" "web" { name = "web-server-1" region = "nyc3" size = "s-1vcpu-1gb" image = "ubuntu-22-04-x64" ssh_keys = [digitalocean_ssh_key.default.fingerprint] tags = ["web", "production"] } resource "digitalocean_ssh_key" "default" { name = "my-key" public_key = file("~/.ssh/id_ed25519.pub") } output "droplet_ip" { value = digitalocean_droplet.web.ipv4_address } # Initialize (downloads provider plugins) terraform init # Preview what will be created terraform plan # Apply (creates the resources) terraform apply # Destroy when done terraform destroy # Initialize (downloads provider plugins) terraform init # Preview what will be created terraform plan # Apply (creates the resources) terraform apply # Destroy when done terraform destroy # Initialize (downloads provider plugins) terraform init # Preview what will be created terraform plan # Apply (creates the resources) terraform apply # Destroy when done terraform destroy terraform { backend "s3" { bucket = "my-terraform-state" key = "production/terraform.tfstate" region = "us-east-1" # Enable state locking dynamodb_table = "terraform-locks" encrypt = true } } terraform { backend "s3" { bucket = "my-terraform-state" key = "production/terraform.tfstate" region = "us-east-1" # Enable state locking dynamodb_table = "terraform-locks" encrypt = true } } terraform { backend "s3" { bucket = "my-terraform-state" key = "production/terraform.tfstate" region = "us-east-1" # Enable state locking dynamodb_table = "terraform-locks" encrypt = true } } resource "digitalocean_droplet" "app" { name = "app-server" region = "nyc3" size = "s-2vcpu-2gb" image = "ubuntu-22-04-x64" ssh_keys = [digitalocean_ssh_key.default.fingerprint] } resource "digitalocean_firewall" "app" { name = "app-firewall" droplet_ids = [digitalocean_droplet.app.id] inbound_rule { protocol = "tcp" port_range = "22" source_addresses = ["YOUR_IP/32"] # SSH only from your IP } inbound_rule { protocol = "tcp" port_range = "80" source_addresses = ["0.0.0.0/0", "::/0"] } inbound_rule { protocol = "tcp" port_range = "443" source_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "tcp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } } resource "digitalocean_domain" "app" { name = "yourdomain.com" ip_address = digitalocean_droplet.app.ipv4_address } resource "digitalocean_droplet" "app" { name = "app-server" region = "nyc3" size = "s-2vcpu-2gb" image = "ubuntu-22-04-x64" ssh_keys = [digitalocean_ssh_key.default.fingerprint] } resource "digitalocean_firewall" "app" { name = "app-firewall" droplet_ids = [digitalocean_droplet.app.id] inbound_rule { protocol = "tcp" port_range = "22" source_addresses = ["YOUR_IP/32"] # SSH only from your IP } inbound_rule { protocol = "tcp" port_range = "80" source_addresses = ["0.0.0.0/0", "::/0"] } inbound_rule { protocol = "tcp" port_range = "443" source_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "tcp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } } resource "digitalocean_domain" "app" { name = "yourdomain.com" ip_address = digitalocean_droplet.app.ipv4_address } resource "digitalocean_droplet" "app" { name = "app-server" region = "nyc3" size = "s-2vcpu-2gb" image = "ubuntu-22-04-x64" ssh_keys = [digitalocean_ssh_key.default.fingerprint] } resource "digitalocean_firewall" "app" { name = "app-firewall" droplet_ids = [digitalocean_droplet.app.id] inbound_rule { protocol = "tcp" port_range = "22" source_addresses = ["YOUR_IP/32"] # SSH only from your IP } inbound_rule { protocol = "tcp" port_range = "80" source_addresses = ["0.0.0.0/0", "::/0"] } inbound_rule { protocol = "tcp" port_range = "443" source_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "tcp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } } resource "digitalocean_domain" "app" { name = "yourdomain.com" ip_address = digitalocean_droplet.app.ipv4_address } terraform init # Initialize project terraform plan # Preview changes terraform apply # Apply changes terraform destroy # Destroy all resources terraform show # Show current state terraform output # Show outputs terraform fmt # Format .tf files terraform validate # Validate config syntax terraform init # Initialize project terraform plan # Preview changes terraform apply # Apply changes terraform destroy # Destroy all resources terraform show # Show current state terraform output # Show outputs terraform fmt # Format .tf files terraform validate # Validate config syntax terraform init # Initialize project terraform plan # Preview changes terraform apply # Apply changes terraform destroy # Destroy all resources terraform show # Show current state terraform output # Show outputs terraform fmt # Format .tf files terraform validate # Validate config syntax - "I'm not sure what settings I used on the production server" - Can't recreate your setup if the server dies - No audit trail for infrastructure changes - Your entire infra is a -weight: 500;">git repo - Recreate production in 10 minutes - Review infrastructure changes like code reviews