Day-14&15-Terraform-AWS
Terraform Functions:
- Count & Index, Splat Expressions
- List
- Element
- Count
- For_each
- Locals
- Dynamic Blocks
- Lookup
- Terraform Conditions
- Using Null Resources
- Terraform Taint
Count & Index:
Count is used to create multiple public-subnet, private-subnet of a resource. It tells Terraform how many copies of a resource you want to create. This is useful when you need more than one of something (like virtual machines, security groups, etc.).
Index is used to refer to a specific item in a list or array. It helps you access individual elements by their position. In Terraform, the index starts at 0
for the first item.
- Count: Specifies how many copies of a resource to create.
- Index: Refers to the position of an item in a list, starting from
0
.
resource “aws_subnet” “public-subnet” {
count = 3 #012
vpc_id = “${aws_vpc.default.id}”
cidr_block = “${element(var.public_cird_block, count.index)}”
availability_zone = “${element(var.azs, count.index)}”
tags = {
Name = “${var.vpc_name}-public-subnet”
Owner = local.Owner
CostCenter = local.CostCenter
TeamDL = local.TeamDL
environment = “${var.environment}”
}
}
resource “aws_subnet” “private-subnet” {
count = 3 #012
vpc_id = “${aws_vpc.default.id}”
cidr_block = “${element(var.private_cird_block, count.index)}”
availability_zone = “${element(var.azs, count.index)}”
tags = {
Name = “${var.vpc_name}-private-subnet”
Owner = local.Owner
CostCenter = local.CostCenter
TeamDL = local.TeamDL
environment = “${var.environment}”
}
}
Splat Expressions:
Splat expressions in Terraform are a convenient way to extract values from a list or a map. They help you get a specific attribute from each item in a collection without needing to write a loop for it.
- Splat Expressions: Use
[*]
to extract a specific attribute from all items in a list or a specific value from a map. - Purpose: They make it easier and cleaner to get multiple values without writing complex loops.
resource “aws_route_table_association” “public-RTA” {
count=3
subnet_id = “${element(aws_subnet.public-subnet.*.id, count.index+1)}”
route_table_id = “${aws_route_table.public-route-table.id}”
}
resource “aws_route_table_association” “private-RTA” {
count=3
subnet_id = “${element(aws_subnet.private-subnet.*.id, count.index+1)}”
route_table_id = “${aws_route_table.private-route-table.id}”
}
length Function:
The length
function in Terraform is used to find out how many items are in a list or how many characters are in a string.
vpc_cidr = “172.18.0.0/16”
public_cird_block = [“172.18.1.0/24”, “172.18.2.0/24”, “172.18.3.0/24”,”172.18.4.0/24"]
private_cird_block = [“172.18.10.0/24”, “172.18.20.0/24”, “172.18.30.0/24”, “172.18.40.0/24”]
resource “aws_subnet” “public-subnet” {
# count = 3 #012
count = “${length(var.public_cird_block)}”
vpc_id = “${aws_vpc.default.id}”
cidr_block = “${element(var.public_cird_block, count.index+1)}”
availability_zone = “${element(var.azs, count.index)}”
tags = {
Name = “${var.vpc_name}-public-subnet-${count.index+1}”
Owner = local.Owner
CostCenter = local.CostCenter
TeamDL = local.TeamDL
environment = “${var.environment}”
}
}
resource “aws_subnet” “private-subnet” {
#count = 3 #012
count = “${length(var.private_cird_block)}”
vpc_id = “${aws_vpc.default.id}”
cidr_block = “${element(var.private_cird_block, count.index+1)}”
availability_zone = “${element(var.azs, count.index)}”
tags = {
Name = “${var.vpc_name}-private-subnet-${count.index+1}”
Owner = local.Owner
CostCenter = local.CostCenter
TeamDL = local.TeamDL
environment = “${var.environment}”
}
}
resource “aws_route_table_association” “public-RTA” {
#count=3
count = “${length(var.public_cird_block)}”
subnet_id = “${element(aws_subnet.public-subnet.*.id, count.index)}”
route_table_id = “${aws_route_table.public-route-table.id}”
}
resource “aws_route_table_association” “private-RTA” {
#count=3
count = “${length(var.private_cird_block)}”
subnet_id = “${element(aws_subnet.private-subnet.*.id, count.index)}”
route_table_id = “${aws_route_table.private-route-table.id}”
}
Dynamic Blocks& For_each:
- Dynamic Blocks: Used to create multiple similar configurations without repeating code.
- For_each: Loops through a list or map to apply configurations to each item.
How It Works:
- for_each = var.ports: This tells Terraform to loop through each item in the
ports
list. - content {…}: Inside this block, you define what to do for each port.
ingress.value
refers to the current item in the loop (the current port).
ingress_value = [“80”, “8080”, “443”, “8443”, “22”, “3306”, “1990”, “1443”]
resource “aws_security_group” “allow_all” {
name = “${var.vpc_name}-allow_all”
description = “Allow all inbound traffic”
vpc_id = aws_vpc.default.id
dynamic “ingress” {
for_each = var.ingress_value
content {
to_port = ingress.value
from_port = ingress.value
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 = “${var.vpc_name}-allow_all”
Owner = local.Owner
CostCenter = local.CostCenter
TeamDL = local.TeamDL
environment = var.environment
}
}
Lookup:
Terraform lookup is used to find values in a map. For example, if I want to deploy AMIs in different regions like us-east-1
and us-east-2
, I can use the lookup function. It looks like this: lookup(map, key, default)
. This way, I can easily deploy instances in those two regions by using the lookup function to get the correct AMI for each region.
amis = {
us-east-1 = “ami-0866a3c8686eaeeba”
us-east-2 = “ami-0ea3c35c5c3284d82”
}
ami = lookup(var.amis, var.aws_region)
Terraform Conditions:
Terraform conditions are used to deploy resources based on specific needs. For example, if I want to deploy resources in a Prod environment and a Dev environment, I can set a condition. If the requirement is for the Prod environment, I can create 3 instances. If it’s for the Dev environment, I create only 1 instance. This way, I can manage resources according to the environment needed.
resource “aws_instance” “private_server” {
count = “${var.environment == “Prod” ? 3 : 1 }”
}
Terraform Provisioners & Null Resources:
Terraform provisioners and null resources are used to run scripts in a Terraform environment. For example, if I want to deploy a small static website on an AWS EC2 instance without using the AWS console, I can do this with Terraform. I can use provisioners to connect via SSH and run commands to set up my website.
Using a null resource, I can trigger these commands to run automatically when I create the instance. This way, I can deploy my website easily by just running Terraform, using my key pair for the SSH connection.
resource “null_resource” “cluster” {
count = length(var.public_cird_block) # Adjust as needed for your use case
provisioner “file” {
source = “user-data.sh”
destination = “/tmp/user-data.sh”
connection {
type = “ssh”
user = “ubuntu” # Adjust based on your AMI
private_key = file(“DevSecOps_Key.pem”)
host = element(aws_instance.public_server.*.public_ip, count.index)
}
}
provisioner “remote-exec” {
inline = [
“sudo chmod +x /tmp/user-data.sh”,
“sudo /tmp/user-data.sh”,
“sudo apt update”,
“sudo apt install jq unzip -y”,
]
connection {
type = “ssh”
user = “ubuntu” # Adjust based on your AMI
private_key = file(“DevSecOps_Key.pem”)
host = element(aws_instance.public_server.*.public_ip, count.index)
}
}
depends_on = [aws_instance.public_server] # Ensure the instance is created first
}
Terraform Taint:
I use Terraform taint to mark a resource that needs to be changed. When I update my data.sh
file, I run terraform taint null_resource[0]
to tell Terraform to recreate it. Then, I run terraform apply
to make the changes. This helps ensure my updates appear on the AWS EC2 instance.