Devops

Terraform Resources Explained: From Basics to Real Usage

April 7, 2026
Published
#Automation#AWS#Cloud#DevOps#Infrastructure as Code#Terraform

If you've written even a small Terraform configuration, you've already worked with resources—they’re the core building blocks of everything Terraform does. No resources, no infrastructure. Simple as that.

But once you move beyond basic examples, things get more nuanced: dependencies, lifecycle rules, dynamic creation, and managing drift. That’s where understanding Terraform resources properly starts to pay off.

What is a Terraform Resource?

A Terraform resource represents a piece of infrastructure. It could be a virtual machine, a database, a DNS record, or even something abstract like an IAM policy.

At its simplest, a resource block looks like this:

TEXT
1resource "aws_instance" "web_server" {
2  ami           = "ami-0c55b159cbfafe1f0"
3  instance_type = "t2.micro"
4}
5

There are three parts to pay attention to:

  • Type: aws_instance (defined by the provider)
  • Name: web_server (your local identifier)
  • Configuration: the attributes inside the block

Terraform combines the type and name into a unique identifier: aws_instance.web_server.

Starting with a Real Example

Let’s say you want to spin up an EC2 instance and attach a security group.

TEXT
1resource "aws_security_group" "web_sg" {
2  name        = "web-sg"
3  description = "Allow HTTP traffic"
4
5  ingress {
6    from_port   = 80
7    to_port     = 80
8    protocol    = "tcp"
9    cidr_blocks = ["0.0.0.0/0"]
10  }
11}
12
13resource "aws_instance" "web" {
14  ami           = "ami-0c55b159cbfafe1f0"
15  instance_type = "t2.micro"
16
17  vpc_security_group_ids = [aws_security_group.web_sg.id]
18}
19

Here’s where things get interesting: Terraform automatically understands that the EC2 instance depends on the security group because of the reference aws_security_group.web_sg.id.

No need to explicitly define dependencies in most cases.

Resource Arguments vs Attributes

A common point of confusion: not everything inside a resource block behaves the same way.

  • Arguments: values you define (like instance_type)
  • Attributes: values Terraform exposes after creation (like id, public_ip)

You can reference attributes like this:

TEXT
1output "instance_ip" {
2  value = aws_instance.web.public_ip
3}
4

Creating Multiple Resources Dynamically

Hardcoding multiple resource blocks gets messy quickly. Terraform gives you two main tools: count and for_each.

Using count

TEXT
1resource "aws_instance" "app" {
2  count         = 3
3  ami           = "ami-0c55b159cbfafe1f0"
4  instance_type = "t2.micro"
5}
6

This creates three identical instances. You can reference them using indexes:

TEXT
1aws_instance.app[0].id

Using for_each

More flexible and safer when dealing with dynamic sets:

TEXT
1variable "instance_names" {
2  default = ["api", "worker", "frontend"]
3}
4
5resource "aws_instance" "app" {
6  for_each = toset(var.instance_names)
7
8  ami           = "ami-0c55b159cbfafe1f0"
9  instance_type = "t2.micro"
10
11  tags = {
12    Name = each.value
13  }
14}
15

This avoids index-shifting issues that can happen with count.

Lifecycle Rules: Controlling Resource Behavior

Sometimes the default create/update/destroy behavior isn’t enough. That’s where lifecycle comes in.

TEXT
1resource "aws_instance" "example" {
2  ami           = "ami-123456"
3  instance_type = "t2.micro"
4
5  lifecycle {
6    create_before_destroy = true
7    prevent_destroy       = true
8    ignore_changes        = [tags]
9  }
10}
11

What these do:

  • create_before_destroy: avoids downtime during replacement
  • prevent_destroy: protects critical resources
  • ignore_changes: ignores external modifications

A common mistake developers make is skipping lifecycle rules for production systems—this can lead to accidental downtime or data loss.

Implicit vs Explicit Dependencies

Terraform usually infers dependencies automatically, but sometimes you need to be explicit.

TEXT
1resource "aws_instance" "web" {
2  ami           = "ami-123456"
3  instance_type = "t2.micro"
4
5  depends_on = [aws_s3_bucket.logs]
6}
7

Use depends_on sparingly—overusing it can make your configuration harder to reason about.

Handling Resource Drift

Infrastructure changes outside Terraform (like manual console edits) create drift.

Terraform detects this during plan and tries to reconcile it. But depending on your setup, this can result in:

  • Unexpected updates
  • Resource replacement
  • Failed applies

To stay safe:

  • Run terraform plan frequently
  • Avoid manual changes in production
  • Use ignore_changes selectively

Resource Naming and Organization

Readable Terraform code matters more than people think—especially in teams.

Instead of this:

TEXT
1resource "aws_instance" "x1" {}

Prefer something meaningful:

TEXT
1resource "aws_instance" "backend_api_server" {}

Also consider grouping related resources into modules once things grow beyond a single file.

When Resources Become Complex

As your infrastructure scales, resource definitions often grow with:

  • Nested blocks (e.g., networking rules)
  • Dynamic blocks
  • Conditional expressions

At that point, splitting configurations into modules isn’t just cleaner—it becomes necessary.

A Quick Mental Model

Think of Terraform resources as:

"Declarative blueprints that describe the desired state, not the steps to get there."

You don’t tell Terraform how to create an EC2 instance—you describe what it should look like, and Terraform figures out the execution plan.

Wrapping It Up

Terraform resources are deceptively simple at first glance, but they carry a lot of power once you start layering dependencies, lifecycle rules, and dynamic configurations.

If you get comfortable with:

  • Writing clean resource blocks
  • Managing dependencies correctly
  • Using lifecycle rules wisely

…you’ll avoid most of the common pitfalls teams run into when scaling Terraform.

And honestly, that’s where Terraform starts to feel less like a tool—and more like a reliable system for managing infrastructure at scale.

Comments

Leave a comment on this article with your name, email, and message.

Loading comments...

Similar Articles

More posts from the same category you may want to read next.

Share: