This article was translated using AI.

Reference: Terraform Up & Running (O’Reilly)

Infrastructure as Code (IaC) turns cloud resources into versioned, shareable code. Terraform is the most popular cross-cloud IaC tool—it supports AWS, GCP, Azure, and many others.


Setup

  • Terraform 1.2.7
  • AWS
  • PyCharm (editor)

Repository layout:

terraform/
 ├── main.tf
 └── var.tf

main.tf defines resources; var.tf holds variables.

Install Terraform from terraform.io. Verify:

terraform -help
terraform -v

Configure the AWS Provider

# main.tf
provider "aws" {
  region = "ap-northeast-2"
}

Initialize the working directory:

terraform init

This creates .terraform/ and .terraform.lock.hcl, which track provider plugins and resource state.


Launch an EC2 Instance

resource "aws_instance" "ec2" {
  ami           = "ami-0ea5eb4b05645aa8a"  # Ubuntu 20.04 LTS
  instance_type = "t3.nano"

  tags = {
    Name = "terraform-example"
  }
}

Run a dry run:

terraform plan

If it looks good:

terraform apply

Confirm with yes. In the AWS console you should see the new instance.


Add a Security Group & User Data

resource "aws_security_group" "instance" {
  name = "terraform-example-instance"

  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Update the instance to attach the security group and launch a lightweight web server:

resource "aws_instance" "ec2" {
  ami           = "ami-0ea5eb4b05645aa8a"
  instance_type = "t3.nano"

  tags = {
    Name = "terraform-example"
  }

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, world" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF

  vpc_security_group_ids = [aws_security_group.instance.id]
}

Re-run terraform apply. Test the endpoint:

curl http://<public-ip>:8080
# outputs "Hello, world"

Clean Up

Don’t forget to remove resources when finished:

terraform destroy

Alternatively, comment out resources and run terraform apply to delete them.


Notes

When updating an existing instance, I noticed port 8080 sometimes failed until I destroyed and recreated the resource. I’ll dig deeper, but for now, recreating resolved the issue.