AskLearn
Loading...
← Back to Terraform Course
BeginnerGetting Started

Build Infrastructure

Create your first infrastructure

Tutorial 3: Build Infrastructure

Learning Objectives

  • Create your first Terraform configuration
  • Understand basic Terraform workflow
  • Learn about providers and resources
  • Build actual cloud infrastructure

Prerequisites

  • Terraform installed and verified
  • Cloud provider account (AWS used in examples)
  • Cloud provider credentials configured

The Terraform Workflow

Core Commands

  1. terraform init - Initialize working directory
  2. terraform plan - Preview changes
  3. terraform apply - Create/update infrastructure
  4. terraform destroy - Remove infrastructure

Setting Up Your First Project

Project Structure

my-first-infrastructure/
ā”œā”€ā”€ main.tf           # Main configuration
ā”œā”€ā”€ variables.tf      # Input variables (optional)
ā”œā”€ā”€ outputs.tf        # Output values (optional)
└── terraform.tfvars  # Variable values (optional)

Create Project Directory

mkdir my-first-infrastructure
cd my-first-infrastructure

Writing Your First Configuration

Basic Configuration Components

1. Provider Configuration

# main.tf
terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Configure the AWS Provider
provider "aws" {
  region = "us-west-2"
}

2. Resource Definition

# Create an EC2 instance
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1d0"  # Amazon Linux 2 AMI
  instance_type = "t2.micro"
  
  tags = {
    Name = "terraform-example"
  }
}

Complete First Configuration

Create main.tf with the following content:

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-west-2"
}

# Data source to get latest Amazon Linux 2 AMI
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# Create a VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name = "terraform-vpc"
  }
}

# Create Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  
  tags = {
    Name = "terraform-igw"
  }
}

# Create a subnet
resource "aws_subnet" "main" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "us-west-2a"
  map_public_ip_on_launch = true
  
  tags = {
    Name = "terraform-subnet"
  }
}

# Create route table
resource "aws_route_table" "main" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
  
  tags = {
    Name = "terraform-rt"
  }
}

# Associate route table with subnet
resource "aws_route_table_association" "main" {
  subnet_id      = aws_subnet.main.id
  route_table_id = aws_route_table.main.id
}

# Create security group
resource "aws_security_group" "web" {
  name        = "terraform-web-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id
  
  # HTTP access
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # SSH access
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]  # Restrict this in production!
  }
  
  # All outbound traffic
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = {
    Name = "terraform-web-sg"
  }
}

# Create EC2 instance
resource "aws_instance" "web" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.main.id
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum install -y httpd
              systemctl start httpd
              systemctl enable httpd
              echo "<h1>Hello from Terraform!</h1>" > /var/www/html/index.html
              EOF
  
  tags = {
    Name = "terraform-web-server"
  }
}

Understanding the Configuration

Resource Blocks

resource "RESOURCE_TYPE" "RESOURCE_NAME" {
  # Resource arguments
  argument1 = "value1"
  argument2 = "value2"
}

Resource Dependencies

Terraform automatically creates dependencies based on resource references:

# This instance depends on the subnet and security group
resource "aws_instance" "web" {
  subnet_id              = aws_subnet.main.id        # Dependency
  vpc_security_group_ids = [aws_security_group.web.id]  # Dependency
}

Data Sources

# Data sources fetch information from existing resources
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  # ... filters
}

Executing the Terraform Workflow

Step 1: Initialize

terraform init

What happens:

  • Downloads AWS provider plugin
  • Creates .terraform directory
  • Initializes backend (local by default)

Expected output:

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.0.1...
Terraform has been successfully initialized!

Step 2: Validate

terraform validate

What happens:

  • Checks configuration syntax
  • Validates resource arguments
  • Ensures required arguments are present

Step 3: Format (Optional)

terraform fmt

What happens:

  • Formats configuration files
  • Ensures consistent style
  • Safe to run repeatedly

Step 4: Plan

terraform plan

What happens:

  • Shows what changes will be made
  • Compares desired state vs current state
  • No actual changes are made

Expected output:

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                     = "ami-0c55b159cbfafe1d0"
      + instance_type           = "t2.micro"
      # ... more attributes
    }

Plan: 6 to add, 0 to change, 0 to destroy.

Step 5: Apply

terraform apply

Interactive approval:

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Auto-approve (use carefully):

terraform apply -auto-approve

Inspecting Your Infrastructure

Show Current State

terraform show

List Resources

terraform state list

Get Specific Resource Info

terraform state show aws_instance.web

AWS Console Verification

  1. Log into AWS Console
  2. Navigate to EC2 service
  3. Verify your instance is running
  4. Check VPC, subnet, security group creation

Adding Outputs

Create outputs.tf:

output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.web.id
}

output "instance_public_ip" {
  description = "Public IP address of the EC2 instance"
  value       = aws_instance.web.public_ip
}

output "website_url" {
  description = "URL to access the website"
  value       = "http://${aws_instance.web.public_ip}"
}

View Outputs

# After apply, view outputs
terraform output

# Get specific output
terraform output instance_public_ip

Modifying Infrastructure

Update Configuration

Modify the instance tags in main.tf:

resource "aws_instance" "web" {
  # ... existing configuration
  
  tags = {
    Name        = "terraform-web-server"
    Environment = "development"
    Project     = "terraform-tutorial"
  }
}

Apply Changes

terraform plan
terraform apply

Terraform will show:

  # aws_instance.web will be updated in-place
  ~ resource "aws_instance" "web" {
      ~ tags     = {
          + "Environment" = "development"
          + "Project"     = "terraform-tutorial"
            "Name"        = "terraform-web-server"
        }
    }

Testing Your Infrastructure

Access Your Web Server

# Get the public IP
terraform output instance_public_ip

# Test HTTP access
curl http://$(terraform output -raw instance_public_ip)

Expected response:

<h1>Hello from Terraform!</h1>

Cleaning Up

Destroy Infrastructure

terraform destroy

Warning prompt:

Terraform will destroy all your managed infrastructure.
There is no undo. Only 'yes' will be accepted to confirm.

Enter a value: yes

What gets destroyed:

  • EC2 instance
  • Security group
  • Subnet
  • Route table and association
  • Internet gateway
  • VPC

Common Issues and Solutions

Authentication Errors

# Check AWS credentials
aws configure list

# Set credentials if needed
aws configure

AMI Not Found

Update the AMI ID or use data source:

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_instance" "web" {
  ami = data.aws_ami.amazon_linux.id
  # ...
}

Resource Already Exists

# Import existing resource
terraform import aws_instance.web i-1234567890abcdef0

Best Practices Introduced

1. Use Data Sources

Instead of hardcoding AMI IDs, use data sources to find the latest AMI.

2. Meaningful Names

Use descriptive names for resources that indicate their purpose.

3. Tags

Always tag your resources for better organization and cost tracking.

4. Security Groups

Create specific security groups rather than using default ones.

5. Comments

Add comments to explain complex configurations.

Key Takeaways

  • Terraform uses declarative configuration to define infrastructure
  • The workflow is: init → plan → apply → destroy
  • Resources have dependencies that Terraform manages automatically
  • Always run terraform plan before terraform apply
  • Use terraform destroy to clean up resources and avoid charges
  • Data sources help make configurations more dynamic and maintainable

Next Steps

  1. Complete Tutorial 4: Change Infrastructure
  2. Learn about variables and outputs
  3. Explore different resource types
  4. Practice with different cloud providers

Additional Resources