Automate Infra Setup and Configuration with Terraform Provisioners

Automate Infra Setup and Configuration with Terraform Provisioners

For many years, Terraform has been the go-to tool for setting up infrastructure. It allows us to define cloud resources like VPCs, subnets, and EC2 instances using code, making infrastructure-as-code (IaC) a reality. However, there was always a missing piece: configuring the software and tools on the infrastructure itself. Traditionally, tools like Ansible were used alongside Terraform to handle this configuration.

But times have changed. Terraform's provisioners have evolved to fill this gap, allowing us to configure and manage software directly on the infrastructure. Today, I want to share my journey of setting up a basic VPC, launching an EC2 instance, and installing NGINX—all with Terraform.

What Are Provisioners in Terraform?

Provisioners are Terraform’s way of performing actions on a local or remote machine once a resource is created. They allow us to:

  • Copy files to remote machines.

  • Execute shell commands.

  • Configure infrastructure directly from Terraform.

This eliminates the need for additional tools like Ansible for basic setups, streamlining the automation process.

My Terraform Experiment: Setting Up NGINX

As someone learning Terraform, I decided to try using provisioners to set up a simple infrastructure. My goal was to:

  1. Create a VPC with a public subnet.

  2. Launch an EC2 instance in the subnet.

  3. Use provisioners to install and configure NGINX on the EC2 instance.

Here’s how it went.

The Terraform Code

The following is the key part of my main.tf file that made this possible:

provider "aws" {
    profile = "test"
    region = "ap-south-1"
}

variable "cidr" {
  default = "10.0.0.0/16"
}

resource "aws_key_pair" "example" {
  key_name   = "terraform-sagar"  # Replace with your desired key name
  public_key = file("~/.ssh/id_rsa.pub")  # Replace with the path to your public key file
}

resource "aws_vpc" "myvpc" {
  cidr_block = var.cidr
}

resource "aws_subnet" "sub1" {
  vpc_id                  = aws_vpc.myvpc.id
  cidr_block              = "10.0.0.0/24"
  availability_zone       = "ap-south-1a"
  map_public_ip_on_launch = true
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.myvpc.id
}

resource "aws_route_table" "RT" {
  vpc_id = aws_vpc.myvpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
}

resource "aws_route_table_association" "rta1" {
  subnet_id      = aws_subnet.sub1.id
  route_table_id = aws_route_table.RT.id
}

resource "aws_security_group" "webSg" {
  name   = "web"
  vpc_id = aws_vpc.myvpc.id

  ingress {
    description = "HTTP from VPC"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Web-sg"
  }
}

resource "aws_instance" "server" {
  ami                    = "ami-053b12d3152c0cc71"
  instance_type          = "t2.micro"
  key_name      = aws_key_pair.example.key_name
  vpc_security_group_ids = [aws_security_group.webSg.id]
  subnet_id              = aws_subnet.sub1.id

  connection {
    type        = "ssh"
    user        = "ubuntu"  # Replace with the appropriate username for your EC2 instance
    private_key = file("~/.ssh/id_rsa")  # Replace with the path to your private key
    host        = self.public_ip
  }


  provisioner "remote-exec" {
    inline = [
      "echo 'Hello from the remote instance'",
      "sudo apt update -y",  # Update package lists (for ubuntu)
      "cd /home/ubuntu",
      "sudo apt-get install -y nginx",
      "sudo systemctl start nginx",
      "sudo systemctl enable nginx" , # Example package installation
      "echo '<h1>Welcome SAGAR</h1>' | sudo tee /var/www/html/index.html",  # Replace default page
    ]
  }
}

What This Code Does

  1. Sets Up the Infrastructure:

    • Creates a VPC with a public subnet.

    • Launches an EC2 instance with a security group allowing HTTP and SSH traffic.

  2. Configures the Instance:

    • The remote-exec provisioner updates the package lists, installs NGINX, and configures it to start on boot.

    • It also customizes the default NGINX landing page.

Running the Code

To deploy this setup, I ran the following commands:

terraform init
terraform plan
terraform apply

Once the EC2 instance was up and running, I could visit its public IP in a browser and see my custom NGINX page:

Why This Is Exciting

This experiment showed me how powerful Terraform has become. Previously, I would have set up the infrastructure with Terraform but used Ansible to install and configure NGINX. Now, I can do it all in one place.

Future Plans

While this was a basic demo, I have bigger plans for the future. My goal is to automate the setup of a complete infrastructure, including:

  1. VPC with Multiple Subnets: I plan to create a more complex network with public and private subnets to handle different workloads.

  2. Master EC2 for Kubernetes Management: I want to set up a dedicated EC2 instance to run Kubernetes commands using tools like kubectl.

  3. Worker Nodes and Add-ons: This includes launching additional EC2 instances for Kubernetes worker nodes and configuring them automatically.

  4. Additional Resources: Other components like load balancers, databases, and monitoring tools will also be part of the setup.

The idea is to create a fully automated environment that can handle all the needs of a modern cloud application. Using Terraform’s provisioners, I can not only create the infrastructure but also configure it to be ready for production use.

Final Thoughts

Terraform provisioners open up new possibilities for automating infrastructure and configuration. They’re especially helpful for small to medium-sized setups or when you’re just starting with IaC.

If you’re like me and learning Terraform, I highly recommend experimenting with provisioners. They make it easy to not only create infrastructure but also configure it exactly how you need. As I continue on this journey, I’m excited to see how much I can automate and simplify my work.

Happy automating!