DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on • Edited on

certification Terraform part #2

πŸ” Firewall Basics & AWS Security Groups

1. What is a Port? (Very Important Foundation)

Simple definition

A port is like a door on a server that allows a specific application to communicate.

  • Server = House
  • IP address = House address
  • Port = Door number
  • Application = Person inside the house

Each application listens on a specific port.

Common ports

Service Port Purpose
SSH 22 Login to server
HTTP 80 Website
HTTPS 443 Secure website
FTP 21 File transfer

2. Why Ports Exist (Real Example)

Suppose a server has:

  • SSH β†’ Port 22
  • NGINX (Website) β†’ Port 80
  • FTP β†’ Port 21

All services are running on one IP address (example: 1.2.3.4), but ports tell the OS which application should receive the traffic.

Example:

http://1.2.3.4:80  β†’ Website
ssh 1.2.3.4        β†’ SSH (port 22 by default)
Enter fullscreen mode Exit fullscreen mode

If ports didn’t exist, the server wouldn’t know which application should answer.


3. Checking Open Ports on a Server

netstat -ntlp
Enter fullscreen mode Exit fullscreen mode

This shows:

  • Which ports are open
  • Which application is using the port

Example output meaning:

  • Port 80 β†’ NGINX
  • Port 22 β†’ SSH
  • Port 21 β†’ FTP (vsftpd)

πŸ‘‰ Installing software usually automatically binds it to a port using its config file.


4. What is a Firewall?

High-level definition

A firewall is a security system that controls:

  • βœ… Who can connect to your server (inbound)
  • βœ… Where your server can connect to (outbound)

It works using rules.


5. Why Firewalls Are Needed

Your server may have multiple open ports, but you should not expose all of them.

Example:

  • βœ… Allow users to access the website (port 80)
  • ❌ Block users from accessing SSH (port 22)

Without firewall:

  • Anyone could try to log in via SSH
  • Huge security risk

6. How Firewall Logic Works (Mentally Picture This)

User β†’ Firewall β†’ Server β†’ Application
Enter fullscreen mode Exit fullscreen mode

Firewall checks rules before traffic reaches the server.

Sample rules:

  • ❌ Deny port 22
  • βœ… Allow port 80

Result:

  • Website works
  • SSH blocked from the internet

7. Firewall in AWS = Security Group

Very important AWS concept

AWS Security Group = Firewall

Official definition:

A security group acts as a virtual firewall for your EC2 instance, controlling inbound and outbound traffic.


8. AWS Security Group – Inbound Rules

Inbound rules control:
πŸ‘‰ Who can connect to your EC2

Example inbound rules:

Port Source Meaning
80 0.0.0.0/0 Anyone can access website
22 Your IP only Only you can SSH

0.0.0.0/0 means:

βœ… All IP addresses (entire internet)


9. What Happens If You Remove All Inbound Rules?

  • No traffic allowed
  • Website stops loading
  • SSH stops working

This is exactly what happened in the demo when:

  • β€œAllow all traffic” rule was removed
  • Website became inaccessible

10. Allowing Only the Website (Best Practice)

Correct setup for public website:
βœ… Inbound:

  • Allow TCP 80 from 0.0.0.0/0

❌ Do NOT allow SSH from everyone

Result:

  • Website works
  • Server login protected

11. Outbound Rules (Often Forgotten but Important)

Outbound rules control:
πŸ‘‰ Where your server can connect

Examples:

  • Can EC2 access the internet?
  • Can EC2 talk only to a database?
  • Can EC2 download updates?

By default:
βœ… AWS allows all outbound traffic

But security teams often restrict outbound traffic in production.


12. Firewalls Are the Same Everywhere (AWS, Azure, DigitalOcean)

Concept is identical across providers:

  • AWS β†’ Security Groups
  • DigitalOcean β†’ Firewall
  • Azure β†’ NSG (Network Security Group)

All use:

  • Ports
  • Allow/Deny rules
  • Source & destination IPs

13. Key Takeaways (Very Important for Interviews)

βœ… Ports identify applications
βœ… Applications bind to ports
βœ… Firewalls control traffic
βœ… AWS firewall = Security Group
βœ… Inbound = who can connect to me
βœ… Outbound = where I can connect
βœ… Never open SSH (22) to the world


14. One-Line Interview Answer

Q: What is a firewall in AWS?
A:

A firewall in AWS is implemented using Security Groups, which control inbound and outbound traffic at the EC2 instance level based on port, protocol, and source/destination rules.

AWS Security Groups – Console + Terraform

Part 1: Creating a Security Group from AWS Console

1. Security Group = Firewall

In AWS:

  • Firewall is called a Security Group
  • Every EC2 must have a security group attached

When launching an EC2 instance, AWS always asks:

  • Key pair
  • Firewall (Security Group)

2. Default Security Group (Auto-created)

If you don’t create one manually:

  • AWS creates a default security group
  • It contains some default inbound & outbound rules

But in real projects:

  • You always create your own security groups

3. Creating a Security Group from Scratch (Console)

Steps:

  1. Go to EC2 β†’ Network & Security β†’ Security Groups
  2. Click Create security group
  3. Example:
  • Name: myfirst.sg
  • Description: Firewall
  • VPC: Select your VPC

4. Inbound Rules (Who can connect TO your server)

Example rule:

  • Type: SSH
  • Port: 22
  • Source: 0.0.0.0/0

⚠️ Important (Production Rule):

Never allow SSH (22) from 0.0.0.0/0 in production
Allow only:

  • Office IP
  • VPN IP

5. Outbound Rules (Where server can connect TO)

Default outbound rule:

  • Allow All
  • Destination: 0.0.0.0/0

This allows:

  • OS updates
  • API calls
  • Internet access

6. Attaching Security Group to EC2

Steps:

  1. EC2 β†’ Instance β†’ Actions β†’ Security β†’ Change security groups
  2. Remove default SG
  3. Attach myfirst.sg

βœ… Now firewall rules apply to this EC2


7. Why Ping Didn’t Work (ICMP Explained)

  • ping uses ICMP protocol
  • ICMP is not TCP
  • Security groups block ICMP by default

ICMP rule was added to:
❌ Outbound

Correct is:
βœ… Inbound


Correct ICMP Rule

  • Type: ICMP – Echo Request
  • Source: 0.0.0.0/0

Now ping <EC2-IP> works.


Part 2: Creating Security Group Using Terraform


8. Architecture to Implement (Terraform)

We want:

  • Security Group name: terraform-firewall
  • Inbound:

    • Allow port 80 from 0.0.0.0/0
  • Outbound:

    • Allow all traffic

9. Terraform Resource Types (Very Important)

There are two separate things:

1️⃣ Security Group (container)

resource "aws_security_group" "firewall" {
  name = "terraform-firewall"
}
Enter fullscreen mode Exit fullscreen mode

This creates:
βœ… Security group
❌ No rules yet


2️⃣ Security Group Rules (real firewall logic)

Terraform uses separate resources:

Purpose Terraform Resource
Inbound aws_vpc_security_group_ingress_rule
Outbound aws_vpc_security_group_egress_rule

Ingress = inbound
Egress = outbound


10. Provider Configuration

provider "aws" {
  region = "us-east-1"
}
Enter fullscreen mode Exit fullscreen mode

11. Full Terraform Code (Clean Version)

πŸ”Ή firewall.tf

provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "firewall" {
  name        = "terraform-firewall"
  description = "Managed from Terraform"
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Inbound Rule – Allow HTTP

resource "aws_vpc_security_group_ingress_rule" "allow_http" {
  security_group_id = aws_security_group.firewall.id
  from_port         = 80
  to_port           = 80
  ip_protocol       = "tcp"
  cidr_ipv4         = "0.0.0.0/0"
  description       = "Allow HTTP from Internet"
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Outbound Rule – Allow All

resource "aws_vpc_security_group_egress_rule" "allow_all_outbound" {
  security_group_id = aws_security_group.firewall.id
  ip_protocol       = "-1"
  cidr_ipv4         = "0.0.0.0/0"
  description       = "Allow all outbound traffic"
}
Enter fullscreen mode Exit fullscreen mode

12. What Does ip_protocol = "-1" Mean?

-1 means:
βœ… ALL protocols
βœ… ALL ports

Equivalent to:

  • TCP
  • UDP
  • ICMP
  • Any port

Used mainly in outbound rules.


13. Terraform Commands

terraform init
terraform plan
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

Expected result:

  • Security group created
  • Inbound rule: port 80
  • Outbound rule: allow all

14. from_port & to_port (Port Range)

Single port:

from_port = 80
to_port   = 80
Enter fullscreen mode Exit fullscreen mode

Port range:

from_port = 80
to_port   = 100
Enter fullscreen mode Exit fullscreen mode

βœ… Used when:

  • Application uses multiple ports
  • Load balancers
  • Custom apps

15. security_group_id (Critical Concept)

security_group_id = aws_security_group.firewall.id
Enter fullscreen mode Exit fullscreen mode

This means:

Attach this rule to that security group

Rules do not live alone
They always belong to one security group


16. Reassigning Rules to Another Security Group

If you change:

security_group_id = "sg-DEFAULT_ID"
Enter fullscreen mode Exit fullscreen mode

Terraform will:

  • ❌ Remove rule from old SG
  • βœ… Add rule to new SG

Terraform output:

1 to add, 1 to destroy
Enter fullscreen mode Exit fullscreen mode

βœ… This is expected behavior


17. Final Mental Model (Very Important)

Security Group
 β”œβ”€β”€ Ingress rules (Inbound)
 └── Egress rules (Outbound)
Enter fullscreen mode Exit fullscreen mode

Terraform treats them as:

  • Separate resources
  • Connected using security_group_id

18. Interview-Ready Summary

Q: How do you create AWS security groups using Terraform?
A:

First, create an aws_security_group resource. Then manage inbound and outbound rules separately using aws_vpc_security_group_ingress_rule and aws_vpc_security_group_egress_rule, linking them using the security group ID.

How to Deal with Terraform Documentation & Code Updates

When you work with Terraform long enough, you will notice:

  • βœ… Terraform documentation changes
  • βœ… Example code changes
  • βœ… New β€œrecommended” approaches appear
  • βœ… Old working code no longer matches docs

This does NOT mean old Terraform code is broken.


2. Security Group Example: Old vs New Approach

βœ… New (Recommended) Approach

Terraform separates responsibilities:

  1. Security group
  2. Security group rules
aws_security_group
aws_vpc_security_group_ingress_rule
aws_vpc_security_group_egress_rule
Enter fullscreen mode Exit fullscreen mode

This is:

  • Cleaner
  • More explicit
  • Easier to manage at scale
  • Current best practice

βœ… Old (Legacy) Approach

Everything defined in one resource:

resource "aws_security_group" "example" {
  ingress { ... }
  egress  { ... }
}
Enter fullscreen mode Exit fullscreen mode

Important:

  • βœ… Still works
  • βœ… Supported by current providers
  • ❌ No longer emphasized in latest docs

3. Key Learning: Docs β‰  Only Valid Way

Very important mindset shift:

Terraform documentation shows the recommended way,
not the only working way.

  • HashiCorp adds new patterns
  • They rarely remove working ones
  • Backward compatibility is preserved

This is exactly what you saw:

  • Provider version 5.38 still accepts old security group syntax
  • terraform apply works without errors

4. Why This Matters in Real Jobs

In real companies:

  • You will read Terraform code written 2–5 years ago
  • It may not match current documentation
  • The engineer who wrote it may be gone
  • The code still works perfectly

πŸ‘‰ Your job is to understand it, not blindly rewrite it.


5. Terraform Versioning Strategy (Enterprise Reality)

Enterprises usually:

  • Pin Terraform versions
  • Pin provider versions
  • Avoid frequent upgrades

Why?

  • Stability
  • Predictability
  • Reduced risk

If Windows 10 works perfectly, why upgrade to Windows 11?

Same logic applies to Terraform.


6. How to Handle Documentation Changes Correctly

βœ… Step 1: Identify Provider Version

Look at:

required_providers
Enter fullscreen mode Exit fullscreen mode

or .terraform.lock.hcl


βœ… Step 2: Switch Documentation Version

Terraform docs allow:

  • Version selector (top-right)
  • Older provider versions (e.g., 5.31.0)

Result:

  • Examples match legacy code
  • Less confusion
  • Faster understanding

βœ… Step 3: Compare Approaches (Don’t Panic)

If you see old code:

  • βœ… Check older docs
  • βœ… Validate with terraform plan
  • βœ… Don’t rewrite unless required

7. HashiCorp Did This on Purpose (Good Design)

Why Terraform docs are good:

  • Provider version selector
  • Clear version history
  • Stable APIs
  • Gradual improvements, not breaking changes

Many tools do not offer this level of backward compatibility.


8. When Should You Actually Upgrade?

Upgrade Terraform / providers only when:

  • New AWS feature required
  • Security fix needed
  • Bug fix required
  • Organization-wide migration planned

Never upgrade just because:
❌ Docs changed
❌ New syntax looks cleaner


9. Course & Learning Reality (Important for Students)

Sometimes you will see:

  • Older syntax in videos
  • New syntax in docs

This does NOT mean:

  • Video is wrong
  • Code is broken

It just means:

  • Docs moved forward
  • Code stayed compatible

Now you know how to resolve that confusion.


10. Interview-Ready Summary (Very Important)

Q: How do you handle Terraform documentation changes?

Answer:

I check the provider version used in the codebase and refer to the corresponding documentation version. Terraform maintains backward compatibility, so older syntax usually continues to work even when newer recommended approaches are introduced.


Part 1: Elastic IP (EIP) in AWS Using Terraform

1. What is an Elastic IP?

At a high level:

  • Elastic IP (EIP) is a static public IPv4 address in AWS.
  • It does not change even if:

    • EC2 stops/starts
    • EC2 gets recreated (if re-associated)

Why it exists

Normal EC2 public IPs:

  • Change on stop/start
  • Are temporary

Elastic IP:

  • Fixed
  • Can be reassigned to another instance
  • Useful for:

    • Bastion hosts
    • Production endpoints
    • NAT instances
    • Legacy systems needing static IPs

2. Elastic IP Lifecycle (Very Important)

  1. Create Elastic IP
  2. (Optional) Associate it with EC2
  3. Use the same public IP permanently

⚠️ Important cost note
AWS charges if:

  • EIP is allocated but not attached to a running resource

Always delete unused EIPs.


3. Creating Elastic IP from AWS Console (Concept)

Steps:

  • EC2 β†’ Elastic IPs
  • Allocate Elastic IP
  • AWS immediately assigns a public IP
  • You may associate it with an EC2 instance

No complexity here β€” Terraform works the same way.


4. Creating Elastic IP Using Terraform

Terraform Resource

resource "aws_eip" "example" {
  domain = "vpc"
}
Enter fullscreen mode Exit fullscreen mode

That’s it β€” Elastic IP created.


5. Terraform Code (Clean & Minimal)

eip.tf

provider "aws" {
  region = "us-east-1"
}

resource "aws_eip" "example" {
  domain = "vpc"
}
Enter fullscreen mode Exit fullscreen mode

Key points:

  • instance argument is optional
  • Without instance, EIP is just allocated
  • Can be associated later

6. Terraform Commands

terraform plan
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

Result:

  • Elastic IP created
  • Visible in AWS console
  • Stored in Terraform state

7. Terraform State & Elastic IP

In terraform.tfstate, you’ll see:

"attributes": {
  "public_ip": "100.xxx.xxx.xxx",
  "id": "eipalloc-xxxx"
}
Enter fullscreen mode Exit fullscreen mode

βœ… You can get:

  • Elastic IP address
  • Allocation ID without visiting AWS Console

8. Cleanup (Very Important)

terraform destroy -auto-approve
Enter fullscreen mode Exit fullscreen mode

Always:

  • Destroy test EIPs
  • Verify from AWS console

Part 2: Terraform Attributes (Critical Concept)

9. What Are Attributes in Terraform?

Definition (simple):

Attributes are values generated after a resource is created, and they are stored in the Terraform state file.

They represent:

  • Resource IDs
  • IP addresses
  • DNS names
  • ARNs
  • Status information

10. Why Attributes Matter (Real Use Case)

Attributes allow:

  • One resource to depend on another
  • Passing values between resources
  • Dynamic infrastructure wiring

Example:

  • Use EC2 public IP in:

    • Outputs
    • Load balancers
    • Security groups
    • DNS records

11. Attributes vs Arguments (Must Know Difference)

Type Meaning
Arguments What you give Terraform
Attributes What Terraform gives back

Example:

ami = "ami-123"
Enter fullscreen mode Exit fullscreen mode

βœ… Argument (input)

public_ip
Enter fullscreen mode Exit fullscreen mode

βœ… Attribute (output)


12. Where to Find Attributes?

In Terraform documentation:

  • Scroll to Attribute Reference
  • Listed per resource

Example:

  • aws_instance
  • aws_eip

13. Example: EC2 Attributes

After EC2 creation, state file contains:

Attribute Meaning
id Instance ID
public_ip Public IPv4
private_ip Internal IP
private_dns Internal DNS
arn AWS ARN

These are auto-populated.


14. Reading Attributes from State File

Example from terraform.tfstate:

"public_ip": "3.xxx.xxx.xxx"
Enter fullscreen mode Exit fullscreen mode

βœ… Matches AWS console
βœ… Terraform is the source of truth


15. Why Attributes Are Used Everywhere

Attributes are used for:

  • Outputs
  • Inter-resource connections
  • Dynamic references

Example:

aws_instance.web.public_ip
Enter fullscreen mode Exit fullscreen mode

You’ll use this constantly in real projects.


16. Very Important Mental Model

Terraform Code  β†’  Resource Created  β†’  Attributes Generated  β†’  Stored in tfstate
Enter fullscreen mode Exit fullscreen mode

Terraform state is:

  • Not just tracking existence
  • Also storing real infrastructure values

17. Interview-Ready Summary

Q: What are attributes in Terraform?

Answer:

Attributes are values automatically generated after a resource is created, such as IDs, IP addresses, and ARNs. They are stored in the Terraform state file and used to connect resources together.


18. Key Takeaways (Remember This)

βœ… Elastic IP = static public IPv4
βœ… Terraform creates EIP with aws_eip
βœ… Unused EIPs cost money
βœ… Attributes are resource outputs
βœ… Attributes live in state file
βœ… Attributes are used for integration

Cross-Resource Attribute Reference in Terraform

1. Why This Concept Is Critical

In real Terraform projects:

  • You never create isolated resources
  • Resources depend on values from other resources
  • Those values do not exist before apply

Example reality:

  • EIP is created β†’ public IP generated
  • Security group must whitelist that exact IP
  • You cannot hardcode it

πŸ‘‰ This problem is solved using cross-resource attribute references


2. The Problem We Are Solving

We want this workflow:

  1. Terraform creates an Elastic IP
  2. AWS assigns a public IP
  3. Terraform automatically:
  • Reads that public IP
  • Adds it to a Security Group rule
  • Allows port 443 only from that IP

βœ… No manual copying
βœ… No hardcoding
βœ… Fully automated


3. Key Terraform Feature Used

Cross-Resource Attribute Reference

General syntax:

resource_type.resource_name.attribute
Enter fullscreen mode Exit fullscreen mode

Example:

aws_eip.lb.public_ip
Enter fullscreen mode Exit fullscreen mode

Meaning:

  • aws_eip β†’ resource type
  • lb β†’ local resource name
  • public_ip β†’ attribute generated after creation

4. Resources Involved

We need three resources:

  1. Elastic IP
  2. Security Group
  3. Security Group Ingress Rule

5. Step-by-Step Practical Code

βœ… Provider

provider "aws" {
  region = "us-east-1"
}
Enter fullscreen mode Exit fullscreen mode

βœ… Elastic IP Resource

resource "aws_eip" "lb" {
  domain = "vpc"
}
Enter fullscreen mode Exit fullscreen mode

After creation, Terraform knows:

aws_eip.lb.public_ip
Enter fullscreen mode Exit fullscreen mode

βœ… Security Group

resource "aws_security_group" "attribute_sg" {
  name        = "attribute-sg"
  description = "Security group using cross-resource attributes"
}
Enter fullscreen mode Exit fullscreen mode

βœ… Security Group Ingress Rule (Important Part)

resource "aws_vpc_security_group_ingress_rule" "allow_https" {
  security_group_id = aws_security_group.attribute_sg.id

  from_port   = 443
  to_port     = 443
  ip_protocol = "tcp"

  cidr_ipv4 = "${aws_eip.lb.public_ip}/32"
}
Enter fullscreen mode Exit fullscreen mode

βœ… This is the core learning


6. Why /32 Is Required (Very Important)

AWS security groups expect:

  • CIDR notation
  • NOT just an IP address

Examples:

  • βœ… Single IP β†’ 35.154.233.58/32
  • βœ… Network β†’ 10.0.0.0/24
  • ❌ 35.154.233.58 β†’ INVALID

Even if the IP is correct, AWS will reject it without CIDR.


7. Why Simple Reference Failed Before

This ❌ does NOT work:

cidr_ipv4 = aws_eip.lb.public_ip
Enter fullscreen mode Exit fullscreen mode

Why?

  • That produces only an IP
  • AWS expects CIDR

8. String Interpolation (Solution)

cidr_ipv4 = "${aws_eip.lb.public_ip}/32"
Enter fullscreen mode Exit fullscreen mode

What Terraform does internally:

  1. Creates Elastic IP
  2. Gets public_ip
  3. Appends /32
  4. Passes final CIDR to AWS

βœ… This is string interpolation


9. Why security_group_id Also Uses Attributes

security_group_id = aws_security_group.attribute_sg.id
Enter fullscreen mode Exit fullscreen mode
  • Security group ID does not exist before apply
  • Terraform:

    • Creates security group
    • Reads its id attribute
    • Injects it into rule resource

This creates an implicit dependency


10. Terraform Dependency Handling (Important)

Terraform creates resources in this order automatically:

  1. Elastic IP
  2. Security Group
  3. Security Group Rule

Because:

  • Rule depends on EIP public_ip
  • Rule depends on SG id

No depends_on needed β€” Terraform is smart.


11. Why Terraform Apply Failed First Time

Earlier:

  • EIP created βœ…
  • SG created βœ…
  • Rule failed ❌ (invalid CIDR)

Result:

  • Partial infrastructure

Terraform behavior:

  • This is normal
  • Terraform is idempotent

12. Best Practice (Very Important)

βœ… Infrastructure should succeed in one apply

Good workflow:

  1. Destroy everything
  2. Fix code
  3. Apply again cleanly
terraform destroy -auto-approve
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

13. Where String Interpolation Is Required vs Not Required

Case Interpolation Needed
security_group_id = aws_security_group.sg.id ❌ No
cidr_ipv4 = aws_eip.lb.public_ip ❌ No
cidr_ipv4 = aws_eip.lb.public_ip/32 ❌ Invalid
cidr_ipv4 = "${aws_eip.lb.public_ip}/32" βœ… Yes

Rule:

  • If combining values + static text, use interpolation

14. Mental Model (Memorize This)

Resource A creates value β†’ attribute
Resource B consumes attribute β†’ reference
Terraform resolves dependency automatically
Enter fullscreen mode Exit fullscreen mode

15. Interview-Ready Answer

Q: What is cross-resource attribute reference in Terraform?

Answer:

Cross-resource attribute reference allows one Terraform resource to use attributes generated by another resource, such as IDs or IP addresses, enabling dynamic dependencies and automated infrastructure wiring.


16. Key Takeaways

βœ… Attributes are outputs of resources
βœ… References use resource.type.name.attribute
βœ… Terraform computes dependencies automatically
βœ… CIDR requires /32 for single IPs
βœ… String interpolation is used when modifying values

⭐ PART 1: Output Values in Terraform

1. What Are Output Values?

Simple definition:

Terraform output values allow you to print important information to your CLI after applying your infrastructure.

Examples of things you may want as output:

  • EC2 public IP
  • RDS endpoint
  • Load balancer DNS
  • EKS cluster name
  • VPC ID

Outputs allow you to:

  • Not open AWS Console
  • Quickly copy the value
  • Pass values to other Terraform projects

2. Why Do We Need Outputs?

Without outputs:

  1. You run terraform apply
  2. Terraform creates EC2 / EIP / LB
  3. You must go to AWS Console β†’ find IP or DNS manually (time-consuming, annoying)

With outputs:

Terraform prints directly in CLI:

Outputs:

public_ip = "3.91.122.180"
Enter fullscreen mode Exit fullscreen mode

You copy-paste instantly.


3. How to Create an Output?

Example resource

resource "aws_eip" "lb" {
  domain = "vpc"
}
Enter fullscreen mode Exit fullscreen mode

Output block

output "public_ip" {
  value = aws_eip.lb.public_ip
}
Enter fullscreen mode Exit fullscreen mode

Result

After terraform apply:

public_ip = "100.24.55.33"
Enter fullscreen mode Exit fullscreen mode

4. Customize Output Strings

You can combine strings:

output "https_url" {
  value = "https://${aws_eip.lb.public_ip}:8080"
}
Enter fullscreen mode Exit fullscreen mode

CLI will show:

https_url = "https://100.24.55.33:8080"
Enter fullscreen mode Exit fullscreen mode

This is VERY useful for:

  • Testing services
  • Giving URLs to developers
  • Automation scripts

5. Output All Attributes of a Resource

output "everything_for_eip" {
  value = aws_eip.lb
}
Enter fullscreen mode Exit fullscreen mode

CLI will show:

  • public_ip
  • private_ip
  • arn
  • id
  • domain

Very good for debugging.


6. Why Outputs Matter for Large Organizations

Outputs allow cross-project data exchange.

Example:

Project A:

Creates EIP + outputs:

public_ip = "100.24.55.33"
Enter fullscreen mode Exit fullscreen mode

Project B:

Reads Project A’s state file
β†’ Uses that IP automatically.

This is used heavily in:

  • Multi-team Terraform setups
  • Multi-module systems
  • Shared infrastructure components

⭐ PART 2: Terraform Variables

1. Why Do We Need Variables?

Imagine you hardcode values inside 100 resources:

cidr_ipv4 = "34.87.112.9/32"
Enter fullscreen mode Exit fullscreen mode

If the VPN IP changes tomorrow:

  • You must update 100 places manually
  • You WILL make mistakes
  • Your apply will break

Variables solve this forever.


2. What Are Variables?

Simple definition:

Variables allow you to store important values in one place and reuse them anywhere in your Terraform code.

Example:

variable "vpn_ip" {
  type = string
  default = "34.87.112.9/32"
}
Enter fullscreen mode Exit fullscreen mode

Use it:

cidr_ipv4 = var.vpn_ip
Enter fullscreen mode Exit fullscreen mode

Now when VPN IP changes:

  • Edit it once
  • Everything updates automatically

3. Example Without Variables (Bad)

cidr_ipv4 = "34.87.112.9/32"
cidr_ipv4 = "34.87.112.9/32"
cidr_ipv4 = "34.87.112.9/32"
...
(100 more)
Enter fullscreen mode Exit fullscreen mode

This is how people break production.


4. Example With Variables (Correct)

Define variables

variable "vpn_ip" {
  default = "34.87.112.9/32"
}

variable "app_port" {
  default = 8080
}
Enter fullscreen mode Exit fullscreen mode

Use variables

cidr_ipv4 = var.vpn_ip
from_port = var.app_port
to_port   = var.app_port
Enter fullscreen mode Exit fullscreen mode

Now:

  • 1 change = updated everywhere
  • Zero manual edits
  • Safer code
  • Professional Terraform practice

5. Demo Behavior (Important)

If you change:

app_port = 22
Enter fullscreen mode Exit fullscreen mode

Terraform plan will show:

~ from_port: 8080 β†’ 22
~ to_port:   8080 β†’ 22
Enter fullscreen mode Exit fullscreen mode

Terraform automatically picks new values.


6. Benefits of Variables (Remember This)

βœ” Centralized control
βœ” No repeated static values
βœ” No manual mistakes
βœ” Faster code editing
βœ” Cleaner Terraform code
βœ” Essential for large organizations


⭐ Final Summary (Interview Answer Style)

Q: What are Terraform output values?

Terraform outputs allow you to expose resource attributes (such as IPs or DNS) on the CLI and make them available to other Terraform configurations.


Q: What are Terraform variables?

Terraform variables let you define values in a central place instead of hardcoding them everywhere. This makes code reusable, cleaner, and saferβ€”especially in large environments.

1. Terraform Variables – Practical Understanding

Why Variables Exist (Real Problem)

Hard-coding repeated values causes problems:

  • VPN IP appears in many security group rules
  • Ports appear repeatedly
  • Change = manual edits everywhere
  • High chance of mistakes in production

βœ… Variables solve this by centralizing values.


Identifying What Should Be a Variable

Rule of thumb:

Any value repeated more than once β†’ make it a variable

Examples:

  • VPN IP
  • Ports (22, 21, 8080)
  • AMI IDs
  • Instance types
  • Region-specific values

2. Creating Variables (Practical Example)

Step 1: Main Terraform code (resources)

Example:

cidr_ipv4 = var.vpn_ip
from_port = var.app_port
to_port   = var.app_port
Enter fullscreen mode Exit fullscreen mode

No hardcoded values.


Step 2: Define variables (recommended: variables.tf)

variable "vpn_ip" {
  description = "VPN server IP address"
  type        = string
}

variable "app_port" {
  description = "Application port"
  type        = number
}
Enter fullscreen mode Exit fullscreen mode

βœ… No values yet
βœ… Just definitions


Step 3: Provide values centrally

You now decide where the values live.

That’s where .tfvars comes in.


3. .tfvars – Variable Definition File (VERY IMPORTANT)

Purpose of .tfvars

  • Holds values, not variable definitions
  • Keeps environment-specific configuration separate
  • Makes code reusable and safe

Standard Files (Best Practice)

main.tf
variables.tf
terraform.tfvars
Enter fullscreen mode Exit fullscreen mode

Example terraform.tfvars:

vpn_ip    = "10.10.10.10/32"
app_port = 8080
ssh_port = 22
ftp_port = 21
Enter fullscreen mode Exit fullscreen mode

βœ… Terraform automatically loads terraform.tfvars


Multiple Environments (Real Production Use)

dev.tfvars
stage.tfvars
prod.tfvars
Enter fullscreen mode Exit fullscreen mode

Example:

# dev.tfvars
instance_type = "t3.micro"

# prod.tfvars
instance_type = "m5.large"
Enter fullscreen mode Exit fullscreen mode

Use with:

terraform plan -var-file=prod.tfvars
Enter fullscreen mode Exit fullscreen mode

4. Defaults vs .tfvars – Who Wins?

Variable with default:

variable "instance_type" {
  default = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

If value exists in .tfvars:

βœ… .tfvars overrides default

Terraform precedence rule:

Explicit values always override defaults


5. Variable Naming Best Practices

βœ… Use variables.tf for variable definitions
βœ… Use .tfvars for values
βœ… Add descriptions
βœ… Avoid touching core resource files in production

Example:

variable "vpn_ip" {
  description = "Corporate VPN IP allowed in security groups"
}
Enter fullscreen mode Exit fullscreen mode

6. What Happens If No Value Is Provided?

If:

  • Variable defined
  • No default
  • No .tfvars
  • No CLI / env value

πŸ‘‰ Terraform prompts for input:

var.instance_type
Enter a value:
Enter fullscreen mode Exit fullscreen mode

This works, but not ideal for automation.


7. Ways to Assign Variable Values (IMPORTANT)

Terraform supports 4 main methods.

1️⃣ Default Value

variable "instance_type" {
  default = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

βœ… Lowest priority


2️⃣ .tfvars File (Most Common)

instance_type = "m5.large"
Enter fullscreen mode Exit fullscreen mode

βœ… Recommended for environments


3️⃣ Command Line -var

terraform plan -var="instance_type=m5.large"
Enter fullscreen mode Exit fullscreen mode

βœ… Overrides defaults
βœ… Useful for quick tests


4️⃣ Environment Variables (CI/CD Friendly)

Naming rule (case-insensitive):

TF_VAR_<variable_name>
Enter fullscreen mode Exit fullscreen mode

Example:

TF_VAR_instance_type=m5.large
Enter fullscreen mode Exit fullscreen mode

βœ… Great for pipelines
βœ… No files needed


8. Environment Variables – Windows

Create variable

TF_VAR_instance_type = t2.large
Enter fullscreen mode Exit fullscreen mode

⚠️ Important:

  • You must restart terminal
  • Existing terminals won’t see new variables

Verify:

echo %TF_VAR_instance_type%
Enter fullscreen mode Exit fullscreen mode

9. Environment Variables – Linux / Mac

Set variable

export TF_VAR_instance_type=m5.large
Enter fullscreen mode Exit fullscreen mode

Verify:

echo $TF_VAR_instance_type
Enter fullscreen mode Exit fullscreen mode

Terraform:

terraform plan
Enter fullscreen mode Exit fullscreen mode

βœ… Terraform picks it automatically


10. Variable Assignment Precedence (MEMORIZE)

From highest β†’ lowest priority:

  1. CLI -var
  2. Environment variables TF_VAR_*
  3. .tfvars file
  4. Default value
  5. Prompt (if nothing provided)

11. Production Guidelines (Very Important)

βœ… Never hardcode repeated values
βœ… Keep resource files unchanged across environments
βœ… Store values in .tfvars
βœ… Use env vars in CI/CD
βœ… Always add variable descriptions

This minimizes:

  • Human errors
  • Risky edits
  • Broken infrastructure

12. Interview-Ready Summary

Q: How can you assign values to Terraform variables?

Answer:

Terraform variables can be assigned using default values, .tfvars files, command-line -var flags, environment variables prefixed with TF_VAR_, or interactive prompts if no value is provided.

βœ… PART 1 β€” VARIABLE DEFINITION PRECEDENCE (MOST IMPORTANT)

Terraform allows setting variable values in MANY places:

  • Default
  • terraform.tfvars
  • environment variables
  • auto.tfvars
  • CLI -var
  • Prompts

If values DIFFER, which one wins?

⭐ FINAL PRECEDENCE ORDER (from lowest β†’ highest)

Priority Source Example
1️⃣ Lowest Default default = "t2.micro"
2️⃣ Environment variables TF_VAR_instance_type=t2.small
3️⃣ terraform.tfvars instance_type="t2.large"
4️⃣ terraform.tfvars.json Same as above but JSON
5️⃣ auto.tfvars dev.auto.tfvars
6️⃣ Highest CLI -var or -var-file -var "instance_type=m5.large"

⭐ The LAST one wins

Terraform processes sources in order.
If multiple locations provide values β†’ last one overrides earlier values.


πŸ”₯ EXAMPLES TO MAKE IT EASY

Example 1:

default = "t2.micro"
TF_VAR_instance_type = "t2.small"
terraform.tfvars β†’ "t2.large"
Enter fullscreen mode Exit fullscreen mode

Result: Terraform uses "t2.large" β†’ tfvars overrides env + default.


Example 2:

default = "t2.micro"
terraform.tfvars β†’ "t2.large"
CLI β†’ -var="instance_type=m5.large"
Enter fullscreen mode Exit fullscreen mode

Result: "m5.large" (CLI always wins)


πŸ”§ PRACTICAL DEMO LOGIC (how lecturer showed it)

  1. Default = "t2.micro"
  2. Add environment variable:
TF_VAR_instance_type = "t2.small"
Enter fullscreen mode Exit fullscreen mode

Terraform plan β†’ takes t2.small

  1. Add terraform.tfvars:
instance_type = "t2.large"
Enter fullscreen mode Exit fullscreen mode

Terraform plan β†’ takes t2.large

  1. Use CLI:
terraform plan -var "instance_type=m5.large"
Enter fullscreen mode Exit fullscreen mode

Terraform plan β†’ takes m5.large

βœ” Perfect understanding.


βœ… PART 2 β€” LIST DATA TYPE (VERY COMMON WITH AWS)

⭐ What a LIST is:

  • A sequence of values
  • Written inside square brackets
  • Comma-separated

Example:

["Mumbai", "Bangalore", "Delhi"]
Enter fullscreen mode Exit fullscreen mode

Terraform understands this as a list of strings.


Why list is needed?

Because some AWS arguments expect multiple values.

Example:
EC2 β†’ vpc_security_group_ids

Documentation says:

(Required) list of security group IDs
Enter fullscreen mode Exit fullscreen mode

Meaning Terraform MUST receive:

vpc_security_group_ids = [
  "sg-001",
  "sg-002"
]
Enter fullscreen mode Exit fullscreen mode

If you write incorrect format:

vpc_security_group_ids = "sg-001"
Enter fullscreen mode Exit fullscreen mode

Terraform throws:

Incorrect attribute value type
Enter fullscreen mode Exit fullscreen mode

⭐ List can contain only ONE value too such as:

vpc_security_group_ids = ["sg-001"]
Enter fullscreen mode Exit fullscreen mode

You STILL must keep list brackets because argument type is list, even if list length = 1.


βœ… PART 3 β€” LIST WITH TYPE CONSTRAINTS

You can restrict what type goes inside a list.

Example:

variable "ports" {
  type = list(number)
}
Enter fullscreen mode Exit fullscreen mode

βœ” Accepts: [22, 8080]
❌ Rejects: ["ssh", "http"]

If user enters incorrect type β†’ Terraform errors:

Invalid value for input variable
Number required
Enter fullscreen mode Exit fullscreen mode

Very useful for validation.


βœ… PART 4 β€” Terraform BASIC DATA TYPES

Terraform supports:

1. string

"hello"
Enter fullscreen mode Exit fullscreen mode

2. number

45
10.5
Enter fullscreen mode Exit fullscreen mode

3. bool

true
false
Enter fullscreen mode Exit fullscreen mode

4. list

Sequence of values:

["a", "b", "c"]
Enter fullscreen mode Exit fullscreen mode

5. set

Unique unordered values:

set(string)
Enter fullscreen mode Exit fullscreen mode

6. map

Key-value pairs:

{
  Name = "web"
  Team = "devops"
}
Enter fullscreen mode Exit fullscreen mode

7. object

Structured schema:

object({
  name   = string
  memory = number
})
Enter fullscreen mode Exit fullscreen mode

8. tuple

List with fixed types:

tuple([string, number, bool])
Enter fullscreen mode Exit fullscreen mode

⭐ Practical AWS Examples for Each Data Type

String

instance_type = "t3.micro"
Enter fullscreen mode Exit fullscreen mode

Number

volume_size = 20
Enter fullscreen mode Exit fullscreen mode

List

availability_zones = ["us-east-1a", "us-east-1b"]
Enter fullscreen mode Exit fullscreen mode

Map

tags = {
  Name = "web"
  Env  = "dev"
}
Enter fullscreen mode Exit fullscreen mode

⭐ FINAL INTERVIEW SUMMARY

If the interviewer asks:

β€œExplain Terraform variable precedence.”

Your answer:

Terraform loads variables from several sources. If the same variable is defined in multiple places, Terraform uses the value from the source with the highest precedence. The order from lowest to highest is: default values, environment variables, terraform.tfvars, terraform.tfvars.json, auto.tfvars files, and finally CLI options using -var or -var-file. CLI always wins.


β€œWhat is a list in Terraform?”

A list is a Terraform data type used to store multiple values in order, inside square brackets. Some AWS arguments, like vpc_security_group_ids, require a list even when you have only one value. Example: ["sg-123"]. Lists can also be type-restricted, like list(string) or list(number).


β€œWhat is the difference between data types like list, map, number, string?”

List is an ordered collection, map is key-value pairs, string is text, number is numeric value. Terraform determines the required type for each argument from documentation.

βœ… PART 1 β€” MAP DATA TYPE (KEY–VALUE PAIRS)

1. What is a Map in Terraform?

A map is a collection of key–value pairs.

variable "instance_tags" {
  type = map(string)

  default = {
    Name        = "app-server"
    Environment = "dev"
    Team        = "payments"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Each key is unique
  • Each key has exactly one value
  • Values are accessed by key name

2. When to Use Map (Very Important)

You use map when:

  • Data is in key β†’ value format
  • Keys have meaning (not just position)

πŸ”₯ MOST COMMON USE CASE β€” AWS TAGS

AWS Tags are not lists.
They are maps.

AWS Console example:

Key: Name        β†’ Value: web
Key: Environment β†’ Value: dev
Key: Team        β†’ Value: payments
Enter fullscreen mode Exit fullscreen mode

Terraform:

tags = {
  Name = "web"
  Env  = "dev"
}
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ That’s why tags expects:

Map of tags to assign to the resource
Enter fullscreen mode Exit fullscreen mode

3. Practical Map Demo Behavior

❌ Wrong (list instead of map)

["team=payments"]
Enter fullscreen mode Exit fullscreen mode

Terraform error ❌

βœ… Correct

{
  team     = "payments"
  location = "us"
}
Enter fullscreen mode Exit fullscreen mode

4. Default Map Values

variable "my_map" {
  type = map(string)

  default = {
    name = "Alice"
    team = "payments"
  }
}
Enter fullscreen mode Exit fullscreen mode

Terraform will not prompt for values.


βœ… PART 2 β€” REFERENCING VALUES FROM MAPS & LISTS

This is VERY IMPORTANT for exams and real production code.


1. Referencing a Map Value

Map definition:

variable "types" {
  type = map(string)

  default = {
    us-west-2  = "t2.nano"
    ap-south-1 = "t2.small"
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage:

instance_type = var.types["us-west-2"]
Enter fullscreen mode Exit fullscreen mode

βœ… Output:

instance_type = t2.nano
Enter fullscreen mode Exit fullscreen mode

Change key β†’ value changes automatically.


2. Referencing a List Value

List definition:

variable "sizes" {
  type = list(string)
  default = ["m5.large", "m5.xlarge", "m5.2xlarge"]
}
Enter fullscreen mode Exit fullscreen mode

Indexing rules:

Index Value
0 m5.large
1 m5.xlarge
2 m5.2xlarge

Usage:

instance_type = var.sizes[1]
Enter fullscreen mode Exit fullscreen mode

βœ… Output:

m5.xlarge
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Lists use positions, maps use keys


βœ… PART 3 β€” COUNT META-ARGUMENT

1. What is count?

By default:

resource "aws_instance" "example" { }
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Creates ONE resource

With count:

count = 3
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Creates THREE identical resources


2. Why count Exists?

Without count:

  • You must copy the same resource block many times
  • Code becomes huge and unmaintainable

With count:

  • One block
  • Clean code
  • Dynamic scaling

3. Count Resource Addressing

If:

resource "aws_instance" "my_ec2" {
  count = 3
}
Enter fullscreen mode Exit fullscreen mode

Terraform creates:

aws_instance.my_ec2[0]
aws_instance.my_ec2[1]
aws_instance.my_ec2[2]
Enter fullscreen mode Exit fullscreen mode

Indexes always start from 0.


4. Major Problem with count (Critical)

count creates IDENTICAL COPIES.

Example problem:

resource "aws_iam_user" "user" {
  name  = "payments-user"
  count = 3
}
Enter fullscreen mode Exit fullscreen mode

AWS requires unique usernames.

Result:

  • First user βœ… created
  • Second ❌ fails
  • Third ❌ fails

βœ… PART 4 β€” COUNT.INDEX (SOLUTION TO REAL PROBLEMS)

1. What is count.index?

count.index:

  • Starts from 0
  • Increments per resource
  • Unique per instance

2. Using count.index for EC2 Names

Problem:

All EC2s have same Name tag

Solution:

tags = {
  Name = "payments-system-${count.index}"
}
Enter fullscreen mode Exit fullscreen mode

Results:

payments-system-0
payments-system-1
payments-system-2
Enter fullscreen mode Exit fullscreen mode

βœ… Human-friendly
βœ… Debug-friendly


3. Fixing IAM User Uniqueness

resource "aws_iam_user" "user" {
  count = 3
  name  = "payments-user-${count.index}"
}
Enter fullscreen mode Exit fullscreen mode

Creates:

payments-user-0
payments-user-1
payments-user-2
Enter fullscreen mode Exit fullscreen mode

βœ… Works perfectly


4. Advanced Pattern β€” List + Count Index (REAL WORLD)

Variable:

variable "users" {
  type    = list(string)
  default = ["Alice", "Bob", "John"]
}
Enter fullscreen mode Exit fullscreen mode

Resource:

resource "aws_iam_user" "users" {
  count = length(var.users)
  name  = var.users[count.index]
}
Enter fullscreen mode Exit fullscreen mode

Creates:

Alice
Bob
John
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Very common in:

  • IAM users
  • EC2 hostnames
  • Batch resources

5. Important Behavior to Remember

  • count decides how many
  • count.index decides which one
  • If list has more items than count β†’ extra ignored
  • If count > list length β†’ Terraform errors

βœ… FINAL EXAM-READY SUMMARY

Map vs List

Type Used for
map Key–value pairs (tags, configs)
list Ordered values (SGs, AZs)

Referencing

var.map["key"]
var.list[index]
Enter fullscreen mode Exit fullscreen mode

count

  • Creates multiple identical resources
  • Index starts at 0

count.index

  • Makes each instance unique
  • Used for naming & customization

βœ… PART 1 β€” CONDITIONAL EXPRESSIONS IN TERRAFORM

1. What is a Conditional Expression?

A conditional expression lets Terraform choose between two values based on a condition.

General Syntax

condition ? true_value : false_value
Enter fullscreen mode Exit fullscreen mode
  • If condition is true β†’ true_value is used
  • If condition is false β†’ false_value is used

2. Real-World Example: Choosing Instance Type by Environment

Variable:

variable "environment" {
  default = "development"
}
Enter fullscreen mode Exit fullscreen mode

Conditional Expression inside EC2:

instance_type = var.environment == "development" ?
                "t2.micro" :
                "m5.large"
Enter fullscreen mode Exit fullscreen mode

Result:

environment instance_type
development t2.micro
production / anything else m5.large

3. Testing the Behavior

Case 1 β€” Default β€œdevelopment”

terraform plan
β†’ instance_type = t2.micro
Enter fullscreen mode Exit fullscreen mode

Case 2 β€” Change to "production"

terraform plan
β†’ instance_type = m5.large
Enter fullscreen mode Exit fullscreen mode

4. Using "not equal" (!=)

instance_type = var.environment != "development" ?
                "t2.micro" :
                "m5.large"
Enter fullscreen mode Exit fullscreen mode

Meaning:

  • If environment is NOT development β†’ t2.micro
  • Otherwise β†’ m5.large

5. Condition on Missing / Empty Values

If default is empty:

variable "environment" {}
Enter fullscreen mode Exit fullscreen mode

Then:

instance_type = var.environment == "" ?
                "t2.micro" :
                "m5.large"
Enter fullscreen mode Exit fullscreen mode

6. Using Multiple Conditions (Logical AND)

You can combine conditions:

instance_type = (var.environment == "production" &&
                 var.region == "us-east-1") ?
                 "m5.large" :
                 "t2.micro"
Enter fullscreen mode Exit fullscreen mode

True only if:

  • environment = production AND
  • region = us-east-1

Otherwise β†’ t2.micro


🧠 EXAM TIP

Conditional expressions are used heavily in module customization:

  • region-specific defaults
  • environment-based sizing
  • enabling or disabling resources

Keep the syntax memorized:

condition ? true : false
Enter fullscreen mode Exit fullscreen mode

βœ… PART 2 β€” TERRAFORM FUNCTIONS

1. What is a Function?

A function in Terraform:

  • accepts input
  • returns output
  • performs a specific task

This is the same concept you see in Python.


2. Example β€” max() Function

max(10, 30, 20)  β†’ 30
Enter fullscreen mode Exit fullscreen mode

Terraform decides the largest number.


3. Example β€” file() Function

file("random-file.txt")
Enter fullscreen mode Exit fullscreen mode

Returns the entire content of the file as a string.

This is extremely useful for:

  • IAM policies
  • JSON configurations
  • certificates
  • shell scripts

βœ… PART 3 β€” TERRAFORM CONSOLE (Testing Functions)

Run:

terraform console
Enter fullscreen mode Exit fullscreen mode

Then test any function:

max()

> max(10, 50, 20)
50
Enter fullscreen mode Exit fullscreen mode

file()

> file("random-file.txt")
"This is a test file"
Enter fullscreen mode Exit fullscreen mode

Terraform console is powerful:

  • test expressions
  • test variables
  • test functions
  • avoid trial-and-error inside real plan/apply

βœ… PART 4 β€” WHY FUNCTIONS MATTER (REAL WORLD)

1. The Problem (without functions)

A policy embedded directly inside .tf file:

policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    ...
  ]
}
EOF
Enter fullscreen mode Exit fullscreen mode

Issues:

  • makes Terraform file huge
  • editing JSON inside HCL is error-prone
  • no code reuse
  • poor readability

2. The Solution β€” Use file() Function

Move JSON to a separate file:

iam-user-policy.json
Enter fullscreen mode Exit fullscreen mode

Then reference:

policy = file("iam-user-policy.json")
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • cleaner Terraform code
  • easier editing
  • reusable
  • reduces risk of JSON formatting errors
  • separates logic from content

This is used everywhere:

  • IAM JSON policies
  • security configs
  • Lambda zip handling
  • template rendering

🧠 EXAM TIP

You cannot create custom functions.
Terraform only supports built-in functions.

Quote:

Terraform language does not support user-defined functions.

Important categories to remember:

  • numeric functions (max, min, ceil, floor)
  • string functions (upper, lower, trimspace)
  • collection functions (length, keys, values, contains, lookup)
  • filesystem functions (file, templatefile)
  • encoding functions (jsonencode, base64encode)

🧠 MOST IMPORTANT FUNCTIONS FOR EXAM & REAL JOB

jsonencode()

Convert HCL map β†’ JSON string
Used for IAM policies and Kubernetes manifests.

file()

Load external file.

templatefile()

Load file + replace variables.

lookup()

Safe read from maps.

length()

Length of list or map.

keys() / values()

Extract keys/values.

element()

Get list element with wrap-around.

coalesce()

Return first non-null value.


🎯 FINAL SUMMARY (Quick Revision)

Conditional Expressions

condition ? true_value : false_value
Enter fullscreen mode Exit fullscreen mode

Used for:

  • choosing instance types
  • enabling/disabling resources
  • environment-based logic

Terraform Functions

  • Built-in helpers for logic, math, strings, file loading
  • Cannot define your own
  • Massive time-saver and code reducer

file() Function

Loads external files β†’ essential for:

  • policies
  • JSON configs
  • templates

βœ… 1. Terraform Functions β€” What They Are & How to Use Them

Definition

A function in Terraform:

  • takes some input
  • performs a specific calculation or operation
  • returns output

Examples:

max(10, 20, 30)         # β†’ 30
file("demo.txt")       # β†’ returns content of file
length(["a","b","c"])  # β†’ 3
Enter fullscreen mode Exit fullscreen mode

Where to use functions

Functions appear inside:

  • resource arguments
  • variables
  • locals
  • outputs

Example:

ami = lookup(var.ami, var.region)
Enter fullscreen mode Exit fullscreen mode

Key Functions in the Challenge Code

1. lookup()

Purpose: Get a value from a map using a key.

lookup(var.ami, var.region)
Enter fullscreen mode Exit fullscreen mode

If:

ami = {
  "us-east-1" = "ami-123"
  "us-west-2" = "ami-456"
}
region = "us-east-1"
Enter fullscreen mode Exit fullscreen mode

Then:

lookup(var.ami, var.region) β†’ "ami-123"
Enter fullscreen mode Exit fullscreen mode

2. length()

Returns the number of items in a list, string, or map.

length(var.tags)   # If tags = ["a", "b"], result = 2
Enter fullscreen mode Exit fullscreen mode

Often used with count:

count = length(var.tags)
Enter fullscreen mode Exit fullscreen mode

3. element()

Returns item at index from a list.

element(var.tags, count.index)
Enter fullscreen mode Exit fullscreen mode

If:

tags = ["first EC2", "second EC2"]
Enter fullscreen mode Exit fullscreen mode

Then during creation:

  • first EC2 instance β†’ first EC2
  • second EC2 instance β†’ second EC2

4. timestamp()

Returns the current time in RFC3339 format.

Example:

2024-06-17T10:52:01Z
Enter fullscreen mode Exit fullscreen mode

5. formatdate()

Converts timestamp to readable format:

formatdate("DD/MM/YYYY", timestamp())
Enter fullscreen mode Exit fullscreen mode

Output example:

17/06/2024
Enter fullscreen mode Exit fullscreen mode

βœ… 2. Local Values (locals)

What are locals?

Locals let you store reusable expressions, similar to variables, but:

  • cannot be overridden (unlike variables)
  • often store computed values
  • allow functions inside

Example:

locals {
  common_tags = {
    Team = "security-team"
    CreationDate = formatdate("YYYY-MM-DD", timestamp())
  }
}
Enter fullscreen mode Exit fullscreen mode

Use in resources:

tags = local.common_tags
Enter fullscreen mode Exit fullscreen mode

Why locals instead of variables?

Use variables when:

  • users or pipelines need to overwrite values
  • value is an input

Use locals when:

  • you want to compute a value
  • the value should NOT be overridden
  • repeated expressions must be centralized

πŸ”₯ Important Rule

Definition uses locals (plural), but reference uses local (singular).

Example:

locals { common_tags = {...} }

tags = local.common_tags
Enter fullscreen mode Exit fullscreen mode

βœ… 3. Data Sources β€” The Most Important Concept

Definition

Data sources let Terraform read information that already exists outside Terraform.

Terraform does NOT create anything with data sources.

Uses:

  • fetch existing EC2 IDs
  • get AMIs
  • get VPC IDs
  • read files
  • fetch Azure/DigitalOcean metadata

Basic Format

data "<provider>_<type>" "<name>" {
  # optional filters
}
Enter fullscreen mode Exit fullscreen mode

Example:

data "aws_instances" "all" {}
Enter fullscreen mode Exit fullscreen mode

Data is stored in terraform.tfstate.


πŸ” Examples Explained

1. DigitalOcean Account Data Source

data "digitalocean_account" "info" {}
Enter fullscreen mode Exit fullscreen mode

Fetches:

  • email
  • droplet limits
  • floating IP limits

Stored in state, not printed unless you output it.


2. Read Local File

data "local_file" "demo" {
  filename = "${path.module}/demo.txt"
}
Enter fullscreen mode Exit fullscreen mode

Outputs:

data.local_file.demo.content
Enter fullscreen mode Exit fullscreen mode

What is path.module?

It returns the directory where the current .tf file is located.


3. AWS EC2 Instances

Multiple instances

data "aws_instances" "all" {}
Enter fullscreen mode Exit fullscreen mode

Retrieves:

  • private_ips
  • public_ips
  • instance_ids

Single instance

data "aws_instance" "example" {
  instance_id = "i-123..."
}
Enter fullscreen mode Exit fullscreen mode

⚠️ If more than one instance matches, Terraform errors until you filter.


βœ… 4. Filters in Data Sources

Filters are key to narrowing down resources.

Example: Find EC2 instance by tag

data "aws_instance" "prod" {
  filter {
    name   = "tag:team"
    values = ["production"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now Terraform will fetch only:

instance where tag team = production
Enter fullscreen mode Exit fullscreen mode

If more instances match, Terraform still throws an error β€” this data source supports only one match.

For multiple EC2s β†’ use:

data "aws_instances" "prod" {
  filter {
    name   = "tag:team"
    values = ["production"]
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸš€ FINAL SUMMARY (fast revision)

Functions

  • lookup() β†’ read from map
  • length() β†’ count items
  • element() β†’ pick from list
  • timestamp() β†’ current time
  • formatdate() β†’ readable date

Local Values

  • reusable computed expressions
  • allow functions
  • cannot be overridden

Data Sources

  • fetch information outside Terraform
  • do NOT create resources
  • stored in tfstate
  • use filters for precision

Filters

Allow you to fetch:

  • by name
  • by AMI
  • by tags
  • by attributes

βœ… 1. Understanding Terraform Functions (Lookup / Length / Element / Formatdate / Timestamp)

Terraform functions are used to calculate values dynamically instead of hardcoding them.

Example Code

Inside an EC2 resource we may see:

ami           = lookup(var.ami, var.region)
count         = length(var.tags)
tags = {
  Name         = element(var.tags, count.index)
  CreationDate = formatdate("DD MMM YYYY", timestamp())
}
Enter fullscreen mode Exit fullscreen mode

Now break each function down.


πŸ”Ή lookup(map, key, default)

Purpose: Get a value from a map.

Example:

variable "ami" {
  type = map(string)
  default = {
    "us-east-1" = "ami-1111"
    "us-west-2" = "ami-2222"
  }
}

lookup(var.ami, "us-east-1")  β†’  "ami-1111"
Enter fullscreen mode Exit fullscreen mode

If key doesn’t exist β†’ return default.


πŸ”Ή length(list/map/string)

Returns number of items.

length(["a","b"]) β†’ 2
length("hello") β†’ 5
Enter fullscreen mode Exit fullscreen mode

Used for count = length(var.tags).


πŸ”Ή element(list, index)

Retrieve item from list by index.

element(["a", "b", "c"], 1) β†’ "b"
Enter fullscreen mode Exit fullscreen mode

When combined with count.index, it labels resources properly:

first EC2
second EC2
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή timestamp()

Returns UTC timestamp (not human friendly).

Example:

2024-07-11T14:17:30Z
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή formatdate()

Converts timestamp into readable format.

formatdate("DD MM YYYY", timestamp())
Enter fullscreen mode Exit fullscreen mode

This becomes something like:

11 07 2024
Enter fullscreen mode Exit fullscreen mode

βœ… 2. Local values (β€œlocals”)

Locals allow you to centralize repeated values.

Example

locals {
  common_tags = {
    Team = "security-team"
    CreationDate = formatdate("YYYY-MM-DD", timestamp())
  }
}
Enter fullscreen mode Exit fullscreen mode

Then in resources:

tags = local.common_tags
Enter fullscreen mode Exit fullscreen mode

Difference between variables and locals

Feature Variables Locals
Can be overwritten? YES (tfvars, CLI, env) NO
Useful for inputs YES NO
Useful for computed expressions OK BEST

Use locals when value must not change outside code.


βœ… 3. Data sources

Data sources fetch external information that Terraform does not create.

Examples:

  • fetch AMI IDs
  • fetch existing VPCs
  • read files
  • get instance details

πŸ”Ή Types of data sources

1. DigitalOcean Account

data "digitalocean_account" "do" {}
Enter fullscreen mode Exit fullscreen mode

This fetches details about your DO account.


2. Local File

data "local_file" "foo" {
  filename = "${path.module}/demo.txt"
}
Enter fullscreen mode Exit fullscreen mode

Reads content of a file.


3. AWS Instances

data "aws_instances" "all" {}
Enter fullscreen mode Exit fullscreen mode

Returns IDs of all EC2s in region.


Data source output stored in:

terraform.tfstate
Enter fullscreen mode Exit fullscreen mode

βœ… 4. Using Data Source to Get Latest AMI (MOST IMPORTANT PRACTICAL USE CASE)

This is the most real-world task.
Goal: Never hardcode AMI IDs. They differ per region.


πŸ”Ή Wrong / static approach

ami = "ami-12345678"
Enter fullscreen mode Exit fullscreen mode

Fails if region changes.


πŸ”Ή Correct approach using aws_ami data source

data "aws_ami" "ubuntu" {
  most_recent = true

  owners = ["amazon"]

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Use in EC2:

resource "aws_instance" "server" {
  ami           = data.aws_ami.ubuntu.image_id
  instance_type = "t3.micro"
}
Enter fullscreen mode Exit fullscreen mode

This works in any region.


βœ… 5. Debugging Terraform

Debug logs useful when things fail.

Enable debug logs

Linux / Mac

export TF_LOG=TRACE
export TF_LOG_PATH=terraform.log
Enter fullscreen mode Exit fullscreen mode

Windows

set TF_LOG=TRACE
set TF_LOG_PATH=terraform.log
Enter fullscreen mode Exit fullscreen mode

Now run:

terraform plan
Enter fullscreen mode Exit fullscreen mode

Logs will appear in terraform.log.


βœ… 6. Troubleshooting Model

4 types of Terraform errors:

  1. Language errors
  • syntax errors
  • undeclared variables
  • unsupported arguments
  1. State errors
  • lock issues
  • drift
  • corruption
  1. Core Terraform bugs
  • in Terraform binary
  • check GitHub issues
  1. Provider plugin bugs
  • AWS provider bugs
  • must update provider

βœ… 7. Terraform fmt (formatting)

Fixes messy code indentation.

terraform fmt
Enter fullscreen mode Exit fullscreen mode

βœ… 8. Load Order & File Organization

Terraform loads all .tf files in the folder.

Best practice:

provider.tf
variables.tf
outputs.tf
ec2.tf
iam_user.tf
vpc.tf
Enter fullscreen mode Exit fullscreen mode

βœ… 9. Dynamic Blocks

Used when a nested block repeats many times.

Without dynamic block (bad):

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

ingress {
  from_port = 8201
  to_port   = 8201
  protocol  = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
}
Enter fullscreen mode Exit fullscreen mode

With dynamic block (best):

variable "sg_ports" {
  type    = list(number)
  default = [8200, 8201, 8300, 9500]
}

resource "aws_security_group" "dynamic_sg" {
  dynamic "ingress" {
    for_each = var.sg_ports

    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

You can also use:

iterator = port
from_port = port.value
Enter fullscreen mode Exit fullscreen mode

βœ… 10. terraform validate

Check syntax without deploying:

terraform validate
Enter fullscreen mode Exit fullscreen mode

It catches:

  • undeclared variables
  • unsupported arguments
  • syntax issues

βœ… 11. terraform taint / replace

Old command:

terraform taint aws_instance.web
Enter fullscreen mode Exit fullscreen mode

New recommended command:

terraform apply -replace="aws_instance.web"
Enter fullscreen mode Exit fullscreen mode

Purpose:

  • force recreate a resource (when manually changed, corrupted, or broken).

Below is a clean, structured, beginner-friendly rewrite of everything you shared, with clear explanations, correct terminology, and practical examples, exactly aligned with Terraform best practices.

I kept it teachable, interview-ready, and production-oriented.


βœ… 1. Terraform Splat Expression

What is a Splat Expression?

A splat expression is a shortcut that lets you collect the same attribute from multiple resources into a list.

This is most commonly used when:

  • a resource is created using count
  • you want output for all instances, not just one

Example: IAM Users with count

resource "aws_iam_user" "lb" {
  count = 3
  name  = "user-${count.index}"
}
Enter fullscreen mode Exit fullscreen mode

Terraform creates:

  • aws_iam_user.lb[0]
  • aws_iam_user.lb[1]
  • aws_iam_user.lb[2]

Without splat (single resource)

output "first_user_arn" {
  value = aws_iam_user.lb[0].arn
}
Enter fullscreen mode Exit fullscreen mode

Outputs ARN of only one user.


With splat (all resources)

output "all_user_arns" {
  value = aws_iam_user.lb[*].arn
}
Enter fullscreen mode Exit fullscreen mode

βœ… Returns list of all ARNs, no matter how many users exist.


βœ… Why splat is powerful

  • Works for 3 resources or 300
  • No manual indexing
  • Perfect for outputs and cross-module usage

βœ… 2. Terraform Graph

What is terraform graph?

It generates a dependency graph showing how resources depend on each other.

Terraform uses this dependency tree internally to:

  • decide creation order
  • decide destruction order
  • parallelize safely

Example Dependencies

Provider (AWS)
β”‚
β”œβ”€β”€ EC2 instances
β”‚
β”œβ”€β”€ Load Balancer β†’ depends on EC2
β”‚
└── Route53 β†’ depends on Load Balancer
Enter fullscreen mode Exit fullscreen mode

Generate Graph Output

terraform graph
Enter fullscreen mode Exit fullscreen mode

This outputs DOT language, which looks unreadable.


Visualize Graph (Online)

  • Search: graphviz online
  • Paste DOT output
  • See dependency diagram

⚠️ Not recommended for sensitive infra


Visualize Graph (Offline – Recommended)

Install Graphviz

Ubuntu / Debian

sudo apt install graphviz
Enter fullscreen mode Exit fullscreen mode

Mac

brew install graphviz
Enter fullscreen mode Exit fullscreen mode

Generate image

terraform graph | dot -Tpng graph.png
terraform graph | dot -Tsvg graph.svg
Enter fullscreen mode Exit fullscreen mode

Open image locally β†’ βœ… safe & secure.


Why Terraform Graph matters

  • Understand complex dependencies
  • Debug destroy/apply issues
  • Review infra architecture visually

βœ… 3. Saving Terraform Plan to a File

Standard Workflow (Risky in Production)

terraform plan
terraform apply
Enter fullscreen mode Exit fullscreen mode

Problem:

  • Code may change between plan & apply
  • Results may differ

βœ… Recommended Production Workflow

terraform plan -out=infra.plan
terraform apply infra.plan
Enter fullscreen mode Exit fullscreen mode

βœ… Apply uses exact plan, regardless of code changes.


Example: Why it matters

  1. Create plan:
terraform plan -out=infra.plan
Enter fullscreen mode Exit fullscreen mode
  1. Modify resource afterward
  2. Apply saved plan:
terraform apply infra.plan
Enter fullscreen mode Exit fullscreen mode

βœ… Terraform ignores new changes
βœ… Infrastructure matches approved plan


Inspect Saved Plan

Human-readable

terraform show infra.plan
Enter fullscreen mode Exit fullscreen mode

JSON (automation / auditing)

terraform show -json infra.plan | jq
Enter fullscreen mode Exit fullscreen mode

βœ… Required for:

  • CI/CD pipelines
  • Change management
  • Security reviews

βœ… 4. terraform output Command

Purpose

Extract output values from state file without running apply.


Example Output Block

output "iam_names" {
  value = aws_iam_user.lb[*].name
}
Enter fullscreen mode Exit fullscreen mode

Retrieve Outputs Later

terraform output
terraform output iam_names
terraform output iam_arn
Enter fullscreen mode Exit fullscreen mode

βœ… No apply
βœ… No re-creation
βœ… Reads state safely


βœ… 5. Terraform Settings Block

The terraform block controls project behavior, not resources.


Example Terraform Block

terraform {
  required_version = "= 1.9.1"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.54.1"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Why this matters

Without settings:

  • Terraform uses latest provider (may break)
  • Version mismatch risk

With settings:

  • Predictable builds
  • Safe upgrades
  • Production stability

Lock file update

If provider version changes:

terraform init -upgrade
Enter fullscreen mode Exit fullscreen mode

βœ… 6. Resource Targeting (-target)

Default Terraform Behavior

Applies all resources in folder.


Target single resource

terraform plan -target=local_file.foo
terraform apply -target=local_file.foo
terraform destroy -target=local_file.foo
Enter fullscreen mode Exit fullscreen mode

βœ… Only that resource is affected
βœ… Others untouched


When to use -target

βœ… Emergency fixes
βœ… Debugging state issues
βœ… Partial infra recovery

🚫 Not for daily workflows


βœ… 7. Terraform at Scale – API Throttling Problem

Real Problem in Large Infra

  • Hundreds of resources
  • Every terraform plan calls APIs
  • Cloud provider rate limits exceeded
  • Production impact

Example Scenario

  • CIS hardening rules
  • Hundreds of AWS API calls
  • Terraform refresh = excessive load

βœ… 8. Solutions to Terraform Scale Issues


βœ… Solution 1: Split Projects

Instead of one giant repo:

vpc/
iam/
security-groups/
ec2/
Enter fullscreen mode Exit fullscreen mode

βœ… Less API calls
βœ… Faster plans
βœ… Independent deployment


βœ… Solution 2: Resource Targeting

Apply resources one-by-one:

terraform apply -target=aws_security_group.web
Enter fullscreen mode Exit fullscreen mode

βœ… Controlled API usage


βœ… Solution 3: Disable Refresh (Advanced)

terraform plan -refresh=false
Enter fullscreen mode Exit fullscreen mode

βœ… Skips state refresh
βœ… Reduces API calls
⚠️ Use only if state is trusted


When to avoid -refresh=false

  • State drift likely
  • Manual console changes
  • Non-production environments

βœ… FINAL SUMMARY

You now understand:

βœ… Splat expressions
βœ… Terraform graph visualization
βœ… Saving & applying plans safely
βœ… terraform output usage
βœ… Terraform project settings
βœ… Resource targeting
βœ… Handling large infra & API throttling

Terraform zipmap() Function

What is zipmap()?

zipmap() is a Terraform function that creates a map by pairing two lists:

  • one list of keys
  • one list of values

Each key is matched with the value at the same index.


Basic Syntax

zipmap(keys, values)
Enter fullscreen mode Exit fullscreen mode
  • keys β†’ list of strings
  • values β†’ list of values
  • Both lists must have the same length

Simple Example

zipmap(
  ["pineapple", "orange", "strawberry"],
  ["yellow", "orange", "red"]
)
Enter fullscreen mode Exit fullscreen mode

Output:

{
  pineapple  = "yellow"
  orange     = "orange"
  strawberry = "red"
}
Enter fullscreen mode Exit fullscreen mode

Terraform pairs elements by index:

  • pineapple β†’ yellow
  • orange β†’ orange
  • strawberry β†’ red

Trying in Terraform Console

terraform console
Enter fullscreen mode Exit fullscreen mode
zipmap(["a", "b"], ["1", "2"])
Enter fullscreen mode Exit fullscreen mode

Output:

{
  "a" = "1"
  "b" = "2"
}
Enter fullscreen mode Exit fullscreen mode

Practical Use Case: IAM Users

Problem

You create multiple IAM users using count and get:

  • one output for names
  • one output for ARNs

This becomes hard to read and correlate.


IAM Resource

resource "aws_iam_user" "iamuser" {
  count = 3
  name  = "iamuser.${count.index}"
}
Enter fullscreen mode Exit fullscreen mode

Separate Outputs (Hard to Read)

output "iam_names" {
  value = aws_iam_user.iamuser[*].name
}

output "iam_arns" {
  value = aws_iam_user.iamuser[*].arn
}
Enter fullscreen mode Exit fullscreen mode

Result:

  • Names list
  • ARNs list ❌ No clear mapping

βœ… Better Output Using zipmap

output "iam_name_to_arn" {
  value = zipmap(
    aws_iam_user.iamuser[*].name,
    aws_iam_user.iamuser[*].arn
  )
}
Enter fullscreen mode Exit fullscreen mode

Output:

{
  iamuser.0 = "arn:aws:iam::123456789:user/iamuser.0"
  iamuser.1 = "arn:aws:iam::123456789:user/iamuser.1"
  iamuser.2 = "arn:aws:iam::123456789:user/iamuser.2"
}
Enter fullscreen mode Exit fullscreen mode

βœ… Easy to read
βœ… Easy to consume in modules
βœ… Very useful in large projects


Comments in Terraform

Terraform supports three comment styles.


1. Hash (#) – Recommended

# This creates an EC2 instance
resource "aws_instance" "example" {
  instance_type = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

βœ… Most commonly used
βœ… Clean and readable


2. Double Slash (//)

// This is also a single-line comment
Enter fullscreen mode Exit fullscreen mode

βœ… Works like #
❌ Less commonly used


3. Multi-line Comments (/* */)

/*
This is a multi-line comment.
Used for explanations or temporarily disabling code.
*/
Enter fullscreen mode Exit fullscreen mode

Commenting Out a Resource Temporarily

/*
resource "aws_instance" "temp" {
  ami           = "ami-123"
  instance_type = "t2.micro"
}
*/
Enter fullscreen mode Exit fullscreen mode

βœ… Useful during testing
βœ… Resource will NOT be created

⚠️ Remember to close */ or Terraform will comment everything below it.


Terraform Meta-Arguments – Introduction

What is a Meta-Argument?

Meta-arguments change how Terraform behaves, not what it creates.

They are added inside resource blocks.


Default Terraform Resource Behavior

Terraform:

  1. Creates resources present in config but missing in state
  2. Destroys resources in state but missing from config
  3. Updates resources in-place if possible
  4. Destroys & recreates resources if in-place update is not possible

Examples

In-place Update

Changing a tag:

tags = {
  Name = "HelloWorld"
}
Enter fullscreen mode Exit fullscreen mode

β†’ Change to "HelloEarth"
βœ… No destroy, just update


Force Replacement

Changing AMI:

ami = "linux-ami"
Enter fullscreen mode Exit fullscreen mode

β†’ change to Windows AMI
❌ EC2 instance is destroyed and recreated


Problem in Real Life

Someone manually updates a resource in AWS Console.

Example:

  • Adds a tag: Env=production

Terraform behavior:

  • Detects drift
  • Removes the tag during next terraform apply

❌ Sometimes this is not what you want.


Lifecycle Meta-Argument

Purpose

Allows you to customize Terraform’s default behavior.


Example: Ignore Manual Tag Changes

resource "aws_instance" "example" {
  ami           = "ami-123"
  instance_type = "t2.micro"

  tags = {
    Name = "HelloWorld"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}
Enter fullscreen mode Exit fullscreen mode

What This Does

  • Terraform will ignore any manual tag changes
  • Tags added in AWS Console remain untouched
  • Terraform will not try to revert them

βœ… Very common in production


Remove lifecycle Block

Terraform goes back to default behavior:

  • Detects drift
  • Fixes it

Lifecycle Meta-Argument – All Options (Overview)

Inside lifecycle {} you can use:


1. ignore_changes

Ignore specific attribute changes.

βœ… Most commonly used


2. create_before_destroy

Create new resource first, then destroy old one.

βœ… Prevents downtime


3. prevent_destroy

Blocks accidental deletion.

lifecycle {
  prevent_destroy = true
}
Enter fullscreen mode Exit fullscreen mode

βœ… Very useful for:

  • Databases
  • Production storage
  • Critical infrastructure

4. replace_triggered_by

Forces replacement when a referenced resource changes.

βœ… Useful for hard dependencies


Other Important Meta-Arguments (Overview)

Terraform supports:

  • count
  • for_each
  • depends_on
  • provider
  • lifecycle

⚠️ provider meta-argument is not the same as provider block
It overrides which provider is used per resource.


Final Summary

You now understand:

βœ… zipmap() and when to use it
βœ… How to write clean Terraform comments
βœ… Default Terraform resource behavior
βœ… What meta-arguments are
βœ… Why lifecycle meta-argument is critical
βœ… All lifecycle options at a high level

Terraform Lifecycle + Meta-Arguments (Simple Explanations + Examples)


1. create_before_destroy

Purpose

Make Terraform create the new resource first, and destroy the old one later.

Why needed?

In production, you do not want downtime.
Default Terraform behavior is:

  1. Destroy old
  2. Create new

This causes downtime.

How to use

resource "aws_instance" "demo" {
  ami           = var.ami
  instance_type = "t2.micro"

  lifecycle {
    create_before_destroy = true
  }
}
Enter fullscreen mode Exit fullscreen mode

Effect

Terraform will:

  1. Create new instance
  2. Terminate old instance

2. prevent_destroy

Purpose

Stop Terraform from destroying a resource at all.

Use case

  • Prevent accidental deletion of:

    • Production databases
    • KMS keys
    • VPCs
    • S3 buckets with important data

How to use

lifecycle {
  prevent_destroy = true
}
Enter fullscreen mode Exit fullscreen mode

Important

If you delete the entire resource block from tf code β†’ Terraform WILL destroy it.
Prevent_destroy only works if the block is still in configuration.


3. ignore_changes

Purpose

Tell Terraform to ignore manual changes done outside Terraform.

Use case

  • DevOps team changed EC2 instance type manually
  • Someone added tags manually
  • Auto-scaling updated some attributes

Examples

Ignore only Tags

lifecycle {
  ignore_changes = [tags]
}
Enter fullscreen mode Exit fullscreen mode

Ignore Tags + Instance Type

lifecycle {
  ignore_changes = [tags, instance_type]
}
Enter fullscreen mode Exit fullscreen mode

Ignore ALL changes

lifecycle {
  ignore_changes = [all]
}
Enter fullscreen mode Exit fullscreen mode

When using [all], Terraform will never update the resource even if you modify the .tf file.


4. Resource Dependencies (depends_on)

There are two types:


A) Explicit dependency (depends_on)

Use when Terraform cannot automatically understand the relationship.

resource "aws_instance" "app" {
  depends_on = [aws_s3_bucket.data]

  ami           = var.ami
  instance_type = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

Effect

  • Terraform creates S3 bucket first
  • Then creates EC2 instance

Use case

  • S3 bucket must exist before EC2 starts
  • IAM role must exist before Lambda
  • SG must exist before RDS

B) Implicit dependency

Terraform automatically understands dependency when a resource references another using attributes.

Example:

vpc_security_group_ids = [aws_security_group.prod.id]
Enter fullscreen mode Exit fullscreen mode

Because you referenced .id, Terraform will:

  1. Create security group first
  2. Create EC2 instance second

This is implicit ordering.

Use case

Whenever one resource needs an output from another.


5. count vs for_each


A) count

When to use

  • When all resources are identical
  • No differences in configuration

Example

resource "aws_instance" "web" {
  count         = 3
  ami           = var.ami
  instance_type = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

Problems with count

If you change the order of list β†’ Terraform will recreate resources.
Bad for production.


B) for_each (better for production)

When to use

  • When every item needs a unique config
  • When order must not break resources
  • When input is:

    • set
    • map

Example 1: For each with Set

variable "users" {
  type = set(string)
  default = ["alice", "bob", "john"]
}

resource "aws_iam_user" "u" {
  for_each = var.users
  name     = each.value
}
Enter fullscreen mode Exit fullscreen mode

Example 2: For each with Map

variable "amis" {
  type = map(string)
  default = {
    dev  = "ami-111"
    prod = "ami-222"
  }
}

resource "aws_instance" "vm" {
  for_each = var.amis
  ami      = each.value
  tags = {
    Name = each.key
  }
}
Enter fullscreen mode Exit fullscreen mode

6. List vs Set (Very Simple)

Feature List Set
Order matters Yes No
Can have duplicates Yes No
Indexing Yes (0,1,2) No
Best used for ordered data unique items

Example

List:

["a", "b", "b"]
Enter fullscreen mode Exit fullscreen mode

Set:

["a", "b"]
Enter fullscreen mode Exit fullscreen mode

Terraform will remove duplicate "b".


7. Object Data Type

Purpose

Advanced structure with different types for each attribute.

Example

variable "user" {
  type = object({
    name    = string
    age     = number
    country = string
  })
}
Enter fullscreen mode Exit fullscreen mode

Value example

user = {
  name    = "Alice"
  age     = 30
  country = "USA"
}
Enter fullscreen mode Exit fullscreen mode

Extra attributes are ignored

If user gives more fields β†’ Terraform discards them.


8. Map Data Type

Purpose

Key/value data. All values must have same type.

Example

variable "details" {
  type = map(number)
}
Enter fullscreen mode Exit fullscreen mode

Valid:

{ age = 30, salary = 5000 }
Enter fullscreen mode Exit fullscreen mode

Not valid:

{ age = 30, name = "bob" }  # Wrong (string + number)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)