Skip to main content

Command Palette

Search for a command to run...

Terraform Day 7: Terraform Functions Part: 2

Updated
4 min read
Terraform Day 7: Terraform Functions Part: 2
K

"Hello, I'm Kiran Pawar, a passionate Cloud and Devops Engineer with a strong background in cloud automation, configuration, and deployment. My journey in the world of technology has been a thrilling adventure, where I've had the privilege to work with cutting-edge tools and practices.

🚀 As a DevOps Engineer:

I specialize in automating, configuring, and deploying instances in cloud environments and data centers. My expertise extends to DevOps, GitOps, CI/CD pipeline management, HashiCorp Terraform, and containerization. I'm proficient in AWS and Linux/Unix administration, ensuring robust infrastructure and application performance.

🔧 My Tech Stack:

Front-end skills: HTML, CSS, SCSS, Tailwind CSS, Bootstrap, React, Material-UI, JavaScript DevOps toolbox: GIT, OWASP,Nexus,Trivy, Github, Gitlab, Terraform, Ansible, Docker, Kubernetes, Helm, Jenkins, Prometheus, Grafana, Argo CD, AWS EKS.

🌐 My Cloud Expertise:

I have hands-on experience managing AWS services, including EC2, S3, EBS, VPC, ELB, RDS, IAM, Route53, and more.

🔒 Networking and Security:

My skills include managing networking concepts such as TCP/IP protocols, security policies, and subnet interfacing. I have a strong understanding of infrastructure and networking, covering topics like firewalls, IP addressing, DNS, and more.

💡 What Sets Me Apart:

I bring a positive attitude, a strong work ethic, and a collaborative spirit to every project. I'm a self-starter, a fast learner, and an effective team player with strong interpersonal skills. In addition to my DevOps skills, I've developed shell scripts (Bash) for automating tasks and have proficiency in Python scripting. My ability to communicate and manage projects, along with a track record of resolving client issues, adds value to every team I work with. If you're looking for a DevOps engineer who is also well-versed in front-end technologies, feel free to connect with me. Let's explore new possibilities and create exceptional technical solutions together!"

This Blog demonstrates the usage of various Terraform functions such as lookup, count, and condition, along with implementing file provisioners (remote-exec, local-exec). The goal is to dynamically manage infrastructure using variables, conditional logic, and provisioning tasks.

clone repository https://github.com/imkiran13/Mastering-Terraform.git

Project Structure

  • ec2.tf: Main file to create EC2 instances.

  • variables.tf: Define variables such as AMIs, instance type, keyname, and environment.

  • terraform.tfvars: Assign values to variables such as AMI IDs for different regions and the environment.

  • null.tf: Implements null_resource to run scripts without recreating instances.

  • userdata.sh: Script to install software on EC2 instances after they are created.

Terraform Functions Overview

1. AMI Lookup

The lookup function helps dynamically retrieve AMI IDs based on the region.

Example:

variable "amis" {
  type = map(string)
}

# In terraform.tfvars
amis = {
  us-east-1 = "ami-0abcd1234efgh5678"
  us-east-2 = "ami-0wxyz1234mnop5678"
}

# In ec2.tf
ami = lookup(var.amis, var.aws_region)

This setup allows us to deploy EC2 instances using region-specific AMIs. For example, AMIs in us-east-1 may not work in us-east-2.

2. Instance Count with Subnet Mapping

We declare three subnets, and each subnet must map to one EC2 instance. By using count, we can define how many instances to create based on the length of subnets.

count = length(var.public_cidr_block)

subnet_id = element(var.subnets, count.index)

3. Conditional Deployment

Using a condition, we can decide how many instances to create based on the environment.

count = var.environment == "prod" ? 3 : 1

This means if the environment is prod, 3 instances are created; otherwise, 1 instance is created.

Provisioners

File Provisioning with remote-exec

We use provisioners to apply scripts after EC2 instances are created without recreating the instances.

  • User Data: Initially, the user data script is passed during instance creation.

  • Provisioners: To avoid recreating instances for every change, we use null_resource to run scripts or commands on existing instances.

Verify the Private Key File

  • Ensure the devops.pem file exists at the path specified and has the correct permissions (read-only, typically chmod 400 devops.pem).

  • Ensure the private key in devops.pem corresponds to the public key added to the instances during their creation (via the AWS key pair or manually).

Example: create null.tf file and create file provisioner

resource "null_resource" "cluster" {
  count = length(var.private_cird_block)

  provisioner "file" {
    source      = "user-data.sh"
    destination = "/tmp/user-data.sh"

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = file("devops.pem")
      host        = element(aws_instance.public-server.*.public_ip, count.index)
    }
  }

}

Run the terraform command terraform apply —auto-approve

SSH into public instance 1

before adding null.tf resource

After adding null.tf resource

We have successfully copied the user-data.sh file from local to remote.

Tainting Resources

If we need to recreate a resource, we can use Terraform's taint feature. Marking a resource as "tainted" forces Terraform to recreate it during the next apply.

Open the user-data.sh file, make your changes, and then apply them.

Apply the changes and check if they are reflected.

To make sure the changes are applied, we need to taint the resource. This way, only the script is updated on our EC2 server, and the entire EC2 instance is not restarted.

Taint Example

terraform taint null_resource.cluster[0]
terraform apply

Check if the changes are applied now without restarting the entire EC2 instance.

ssh into public ec2 server

Create a remote-exec provisioner to install all the packages on the EC2 server without restarting it.
update null.tf file

resource "null_resource" "cluster" {
  count = length(var.private_cird_block)

  provisioner "file" {
    source      = "user-data.sh"
    destination = "/tmp/user-data.sh"

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = file("devops.pem")
      host        = element(aws_instance.public-server.*.public_ip, count.index)
    }
  }
  provisioner "remote-exec" {
    inline = [
      "sudo chmod 400 /tmp/user-data.sh",
      "sudo /tmp/user-data.sh",
      "sudo apt update",
      "sudo apt install jq unzip -y",
    ]

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = file("devops.pem")
      host        = element(aws_instance.public-server.*.public_ip, count.index)
    }
  }
}

This marks the resource as needing recreation, allowing the new script to be applied without affecting the rest of the infrastructure

.

Copy the EC2 public IP http://ec2-public-ip/ and paste it into your browser.

Destroy resources using terraform destroy --auto-approve

Next Steps

  • Explore Terraform Modules for better structuring and reuse of code.

Interview Tips

What is taint in Terraform? Taint marks a resource for recreation. You can manually taint a resource using the terraform taint command, causing Terraform to destroy and recreate it during the next apply. Conversely, you can "untaint" a resource to prevent it from being recreated.

Mastering Terraform

Part 3 of 9

This series dives deep into mastering Infrastructure as Code (IaC) with Terraform, starting from the basics and advancing to real-world implementations. Whether you're a beginner or looking to refine your skills.

Up next

Terraform Day 6: Terraform Functions Part: 1

This Blog demonstrates the efficient use of Terraform functions to manage infrastructure as code without duplicating resources. The focus is on creating modular, scalable, and maintainable Terraform configurations. Overview In this project, we will u...

More from this blog

Kiran Pawar's Blog

122 posts