Terraform Installation Guide: A Thorough Walkthrough for Beginners

A comprehensive guide to installing Terraform across different operating systems. From version management with tfenv to initial configuration and verification, this practical guide addresses common beginner stumbling blocks.
代表 / エンジニア
Terraform has become increasingly visible as a leading tool for Infrastructure as Code (IaC). If you've been thinking "I want to get started, but I don't know where to begin," you're not alone. This article walks through Terraform installation, initial configuration, and verification step by step. We'll also cover practical examples of what Terraform can actually do, along with essential commands for real-world use. Whether you're just starting out, this guide aims to give you a solid first step.
What Is Terraform—Fundamentals Before Getting Started
Terraform is an open-source IaC tool developed by HashiCorp. It supports major cloud providers including AWS, Google Cloud, and Azure, letting you define infrastructure resources using a declarative syntax called HCL (HashiCorp Configuration Language).
When I first used Terraform, I was genuinely impressed by the experience of "writing a configuration file and having servers and networks build themselves." Compared to manually clicking through console UIs, the difference in reproducibility and efficiency is night and day.
What Declarative Syntax Means
Understanding "declarative" is essential to grasping Terraform. Declarative means describing only the desired end state—"make it look like this." In contrast, "imperative" approaches like shell scripts or Ansible describe step-by-step procedures—"first do this, then do that."
For example, to create an S3 bucket, you write:
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-example-bucket-2026"
}You don't write "call the API to create a bucket." You just describe the desired state, and Terraform calculates the diff from the current state and performs the necessary operations automatically. This mechanism enables safe management of not just resource creation, but also modifications and deletions.
The Role of the State File
Terraform generates a terraform.tfstate file that records the current state of managed resources. When you run terraform plan, Terraform compares this file against your code to determine "what to create, change, or destroy."
Beginners often proceed without being aware of the state file, but it becomes unavoidable when you start working in teams. I once accidentally committed the .tfstate file to Git, only to realize later that it contained sensitive information. Since the state file records resource attributes verbatim, storing it in a remote backend like S3 or GCS—not locally—is the recommended practice. Keep this in mind even at the initial setup stage.
Rich Provider Ecosystem
One of Terraform's greatest strengths is its rich provider plugin ecosystem. As of April 2026, the Terraform Registry hosts over 4,000 providers. Here's just a sampling of the major ones:
- Cloud: AWS, Google Cloud, Azure, Oracle Cloud, DigitalOcean
- SaaS: GitHub, Datadog, PagerDuty, Cloudflare
- Databases: MySQL, PostgreSQL, MongoDB Atlas
- Containers: Docker, Kubernetes, Helm
- Other: DNS, TLS certificates, random value generation
This means you can manage not just cloud infrastructure, but GitHub repository settings, DNS records, and monitoring tool configurations—all within a single workflow. In my own projects, I manage AWS infrastructure and GitHub repository permissions with the same Terraform code, and the benefits of centralized management are felt daily.
Benefits of Adopting Terraform
Here's a summary of Terraform's advantages:
| Aspect | Manual Provisioning | Terraform Provisioning |
|---|---|---|
| Reproducibility | Depends on runbooks, prone to tribal knowledge | Defined in code, identical results for everyone |
| Change management | Hard to track differences | Git history provides full audit trail |
| Review | Screenshot-based | Code review ensures quality |
| Multi-environment deployment | Manual setup per environment | Efficient deployment via variable switching |
| Cleanup | Risk of forgotten resources inflating costs | terraform destroy for clean teardown |
This comparison makes Terraform's advantages clear when considering team development and production operations.
OS-Specific Installation—macOS, Windows, and Linux
Terraform installation methods vary by OS. Here are the recommended approaches for the three major platforms.
macOS
Homebrew is the simplest option.
brew tap hashicorp/tap
brew install hashicorp/tap/terraformUsing the official HashiCorp tap ensures you always get the latest stable version. If you have an older version installed, upgrade with brew upgrade hashicorp/tap/terraform.
Note that the same command works on Apple Silicon (M1/M2/M3/M4) Macs. Since Homebrew itself supports arm64, there's no need to worry about Rosetta 2.
Windows
The Chocolatey package manager is the convenient choice.
choco install terraformIf you don't have Chocolatey installed, download the ZIP from the official Terraform site and place the extracted terraform.exe in a directory included in your PATH.
For manual PATH configuration, follow these steps:
- Extract the downloaded ZIP to a location like
C:\terraform - Open "Edit system environment variables" (Windows key → search "environment variables")
- Add
C:\terraformto thePathvariable - Open a new Command Prompt or PowerShell window, then run
terraform version
"I changed the environment variable but it's not working" is a common complaint—in most cases, the terminal simply hasn't been reopened. Always verify in a fresh window after changing settings.
Linux (Ubuntu/Debian)
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt-get update && sudo apt-get install terraformFor CentOS/RHEL, use yum:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraformPost-Installation Verification
After installation, verify the version:
terraform versionIf you see Terraform v1.x.x, you're good. Getting stuck here blocks all further progress, so carefully verify your PATH configuration.
If you see command not found, check the following in order:
- Run
which terraform(macOS/Linux) orwhere terraform(Windows) to locate the binary - Verify the binary is in a directory included in your PATH
- Reload your shell configuration file (
.bashrc,.zshrc, etc.)
Version Management with tfenv
Once you start using Terraform in practice, you'll encounter situations where different projects require different versions. I initially underestimated version management, but when "it works on my machine" problems started surfacing within the team, I recognized its importance.
tfenv is a Terraform version management tool that lets you pin versions per project. It's the same concept as nvm for Node.js or rbenv for Ruby.
Installing and Using tfenv
# macOS
brew install tfenv
# Linux (manual installation)
git clone https://github.com/tfutils/tfenv.git ~/.tfenv
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bashrc
# List available versions
tfenv list-remote
# Install a specific version
tfenv install 1.9.8
# Switch active version
tfenv use 1.9.8
# List installed versions
tfenv list
# Pin version at project root
echo "1.9.8" > .terraform-versionIncluding the .terraform-version file in your repository ensures all team members work with the same version. tfenv automatically searches upward from the current directory for .terraform-version, so placing it at the project root is all you need. It's a small touch, but the trouble prevention effect is significant.
When using tfenv, watch for conflicts with directly Homebrew-installed Terraform. If both exist in PATH, an unintended version may be used. To consolidate on tfenv, removing the direct install with brew uninstall terraform is the safe approach.
What You Can Do with Terraform—Cloud Configuration Examples
With the fundamentals covered, let's see "what Terraform can actually do" through concrete code examples. We'll use AWS as the example, but the same approaches work with Google Cloud and Azure.
Example 1: VPC and Subnet Setup
Here's the foundation of any network—VPC and subnet definitions:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "main-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-1a"
}
}The key feature here is resource cross-referencing via aws_vpc.main.id. Manually, you'd "create the VPC, copy the ID, paste it into the subnet config..." With Terraform, code-level references automatically resolve dependencies.
Example 2: EC2 Instance Creation
resource "aws_instance" "web" {
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
tags = {
Name = "web-server"
}
}An EC2 instance defined in just a few lines. Running terraform plan lets you verify "what will happen" before any resources are actually created, preventing unintended changes.
Example 3: S3 Bucket with Access Policy
resource "aws_s3_bucket" "assets" {
bucket = "my-app-assets-2026"
}
resource "aws_s3_bucket_public_access_block" "assets" {
bucket = aws_s3_bucket.assets.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}Here we create an S3 bucket and simultaneously apply public access blocking. Including security settings in code prevents the "created a bucket but forgot access controls" scenario.
Example 4: Environment Switching with Variables
Terraform's variable blocks let you parameterize values, enabling efficient management of multiple environments like development, staging, and production.
variable "environment" {
description = "Target deployment environment"
type = string
default = "dev"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
resource "aws_instance" "app" {
ami = "ami-0abcdef1234567890"
instance_type = var.instance_type
tags = {
Name = "app-${var.environment}"
Environment = var.environment
}
}To switch values at apply time, prepare terraform.tfvars files per environment or pass command-line arguments.
# Specify via command-line arguments
terraform apply -var="environment=prod" -var="instance_type=t3.large"
# Specify a tfvars file
terraform apply -var-file="environments/prod.tfvars"In my projects, I place dev.tfvars, stg.tfvars, and prod.tfvars in an environments/ directory. Environment differences become clear at the file level, making situations like "production has different settings" easy to spot.
Initial Configuration and Verification—Testing with a Minimal Setup
With the environment ready, let's actually run Terraform. Here we'll use a local_file resource that can be tested without cloud credentials.
Create a working directory and create a main.tf file with the following content:
terraform {
required_version = ">= 1.0"
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.0"
}
}
}
resource "local_file" "hello" {
content = "Hello, Terraform!"
filename = "${path.module}/hello.txt"
}Let me clarify what each block does:
terraformblock: Declares version constraints for Terraform itself and the providers to use.required_versionaligns the team's Terraform version, andrequired_providerspins provider source and version.resourceblock: Defines the managed resource."local_file"is the resource type, and"hello"is the local identifier. When defining multiple resources of the same type, this identifier distinguishes them.${path.module}: A built-in variable representing the directory path of the current.tffile.
Basic Workflow: init → plan → apply
The fundamental workflow follows three steps:
# 1. Initialize (download providers)
terraform init
# 2. Review execution plan (what will be created/changed/destroyed)
terraform plan
# 3. Apply (create resources)
terraform applyLet's look at what happens at each step in more detail.
terraform init creates a .terraform directory and downloads provider plugins specified in required_providers. It also generates .terraform.lock.hcl, a lock file recording provider versions and checksums to ensure the entire team uses identical provider versions. This file should be committed to Git.
terraform plan produces output like:
Terraform will perform the following actions:
# local_file.hello will be created
+ resource "local_file" "hello" {
+ content = "Hello, Terraform!"
+ filename = "./hello.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.The + symbol indicates creation. Changes show as ~, and deletions as -. This output lets you verify that only intended changes will occur.
terraform apply displays a confirmation prompt. After reviewing and typing yes, a hello.txt file is generated in the same directory. This init → plan → apply cycle is Terraform's foundation, and the same flow applies to actual cloud resource management.
It might feel like "that's it?" at first, but this small success becomes the stepping stone for what comes next.
Experience Changes and Deletion Too
Beyond creation, try modification and deletion at this stage to deepen your understanding.
Edit the content in main.tf:
resource "local_file" "hello" {
content = "Hello, Terraform! Updated."
filename = "${path.module}/hello.txt"
}Running terraform plan shows the existing resource replacement:
# local_file.hello must be replaced
-/+ resource "local_file" "hello" {
~ content = "Hello, Terraform!" -> "Hello, Terraform! Updated."
filename = "./hello.txt"
~ id = "..." -> (known after apply)
}
Plan: 1 to add, 0 to change, 1 to destroy.-/+ means "destroy then recreate." Running terraform apply confirms the file content is updated.
To remove resources that are no longer needed, use terraform destroy:
terraform destroyType yes at the confirmation prompt, and all Terraform-managed resources are deleted. For cloud resources, this command is crucial for preventing forgotten resources from running up costs. The confidence of being able to reliably clean up test environments is something manual processes can't match.
Essential Terraform Commands for Real-World Use
Beyond the basic init, plan, and apply, there are many commands you'll use regularly. Here's a curated selection of the most frequently needed ones.
Code Validation and Formatting
# Validate syntax (check HCL grammar errors)
terraform validate
# Auto-format code
terraform fmt
# Recursively format including subdirectories
terraform fmt -recursive
# List files needing formatting (for CI)
terraform fmt -check -recursiveterraform fmt is especially valuable in team development. With indentation and line break styles automatically unified, code reviews no longer waste time on formatting comments. I've set up terraform fmt -check as a Git pre-commit hook to prevent unformatted code from being committed.
State Inspection and Manipulation
# List managed resources
terraform state list
# Show detailed information for a specific resource
terraform state show aws_instance.web
# Remove a resource from state (stop managing it with Terraform)
terraform state rm aws_s3_bucket.old_bucket
# Rename a resource within state (for refactoring)
terraform state mv aws_instance.old_name aws_instance.new_nameterraform state list is useful for getting a bird's-eye view of "what is Terraform currently managing." When inheriting Terraform code on a new project, making this command your first step is a good habit.
terraform state mv is used when refactoring resource names. If you change a resource name in code, Terraform interprets it as "delete the old resource + create the new one." By renaming within state first with state mv, you can complete the rename without affecting actual resources.
Output Values and Resource Inspection
# Display values defined in output blocks
terraform output
# Display a specific output in JSON format (for script integration)
terraform output -json
# Display only a specific output value
terraform output instance_ipoutput is the mechanism for passing Terraform execution results to the outside. For example, defining the IP address of a created EC2 instance or a load balancer's DNS name as output makes them accessible from deploy scripts or other tools.
Other Practical Commands
# Apply only a specific resource (partial apply in large environments)
terraform apply -target=aws_instance.web
# Skip confirmation prompt (for CI/CD pipelines)
terraform apply -auto-approve
# Show diff between current state and code (drift detection)
terraform plan -detailed-exitcode
# Clear provider cache and reinitialize
terraform init -upgrade
# Output dependency graph (Graphviz format)
terraform graph | dot -Tpng > graph.pngThe -target option is for quickly applying a subset in large environments, but routine use isn't recommended. Dependencies may not resolve correctly, so consider it for limited scenarios only.
-auto-approve is used for automated application in CI/CD pipelines. It's essential in environments where humans can't respond to confirmation prompts, but for local execution, I recommend against it for safety.
Common Troubles and Solutions
When starting out, you'll inevitably encounter unexpected errors. Here are common issues and solutions, including ones I've personally stumbled on.
Provider Initialization Errors
Error: Failed to query available provider packages
If this error appears during terraform init, it may be a network connectivity issue or incorrect provider source specification. In corporate proxy environments, setting the HTTPS_PROXY environment variable may be necessary.
export HTTPS_PROXY=http://proxy.example.com:8080
terraform initState Lock Conflicts
When sharing the same state file across a team, simultaneous apply operations cause lock conflicts.
Error: Error locking state: Error acquiring the state lock
This usually resolves once the other operation completes. If a lock remains after an abnormal termination, force-unlock it:
terraform force-unlock <LOCK_ID>Always verify that the running operation has truly finished before executing this.
Unexpected Diffs During Plan
If terraform plan shows large diffs even though you haven't changed any code, Terraform or provider versions may have changed. Pinning versions with tfenv and .terraform.lock.hcl prevents these issues.
Diffs also appear when manual changes have been made on the cloud side—known as "drift." Since terraform plan detects this, periodically running plan to check for drift is an effective operational practice.
.gitignore Configuration
Some Terraform-related files should not be committed to Git. Set up .gitignore properly at project inception.
# Local .terraform directory (provider plugins, etc.)
.terraform/
# State files (may contain sensitive information)
*.tfstate
*.tfstate.*
# Variable definition files containing sensitive data
*.tfvars
!example.tfvars
# Crash logs
crash.log
crash.*.log
# Plan output files
*.tfplanConversely, the following files should be committed to Git:
.tffiles (the infrastructure definitions themselves).terraform.lock.hcl(provider version lock).terraform-version(tfenv version specification)
In my experience, getting this .gitignore configuration right early in the project prevents considerable downstream headaches. Accidental state file commits in particular pose secret leakage risks, so ensure they're excluded from the very start.
Conclusion—Next Steps Forward
This article covered Terraform installation through initial configuration, verification, practical examples, and essential commands. Here's a recap of the key takeaways:
- Verify OS-specific installation and confirm with
terraform version - Adopt tfenv for per-project version management
- Experience the
init → plan → applybasic workflow hands-on - Understand variables and providers and apply them to real cloud configurations
- Use
fmt,validate, andstatecommands in your daily workflow - Prepare for team development with
.gitignoreand lock file management
With this foundation, the next step is managing real resources on AWS or Google Cloud. The safe operational flow of reviewing change diffs with terraform plan before applying gives significant peace of mind even in production environments.
Looking further ahead, Terraform's world extends to code reuse with modules, environment isolation with Workspaces, and CI/CD pipeline integration. Start by working through this article's content hands-on to build your feel for Terraform.
If you need help with Terraform-based infrastructure or IaC adoption, feel free to contact us. We provide end-to-end support from cloud-native infrastructure design to operations.