Skip to main content

Terraform Resource

A Terraform resource is a block of HCL configuration that declares one piece of infrastructure — like an EC2 instance, an S3 bucket, or a DNS record. Terraform creates, updates, and deletes the real-world object to match the resource declaration.

What Is a Terraform Resource?

A resource is the most important block in Terraform. Every piece of infrastructure you want to exist — an EC2 instance, an S3 bucket, a Route 53 record, a GitHub repository — is declared as a resource block. Terraform's entire job is to make the real world match what your resource blocks say.

Think of a resource block as a specification: "I want an S3 bucket named swiggy-order-images-prod with versioning enabled and public access blocked." Terraform reads that spec, compares it to what actually exists, and makes the minimum changes needed.

Resource Block Anatomy

HCL
# resource "<TYPE>" "<NAME>" — both parts together form the address
resource "aws_s3_bucket" "order_images" {
# Arguments — specific to this resource type
bucket = "swiggy-order-images-prod" # the S3 bucket name
tags = {
Environment = "production"
Team = "platform"
}
}

The resource address aws_s3_bucket.order_images is how you reference this resource from other parts of your configuration.

Referencing Resource Attributes

After Terraform creates a resource, you can read its attributes in other resource blocks:

HCL
resource "aws_s3_bucket" "order_images" {
bucket = "swiggy-order-images-prod"
}
resource "aws_s3_bucket_versioning" "order_images" {
bucket = aws_s3_bucket.order_images.id # reads the bucket ID after creation
versioning_configuration {
status = "Enabled"
}
}

This reference also creates an implicit dependency — Terraform knows to create the bucket before the versioning config.

Meta-Arguments

Meta-arguments are special arguments that work on any resource type, regardless of provider:

HCL
resource "aws_instance" "app_server" {
ami = "ami-0f5ee92e2d63afc18"
instance_type = "t3.medium"
# count — create multiple identical instances
count = 3 # creates app_server[0], app_server[1], app_server[2]
# depends_on — explicit dependency when there is no attribute reference
depends_on = [aws_iam_instance_profile.app_profile]
# lifecycle — control replace/destroy behaviour
lifecycle {
create_before_destroy = true # spin up replacement before killing old one
prevent_destroy = true # block terraform destroy for this resource
}
# provider — use a non-default provider
provider = aws.us_east
tags = { Name = "app-server-${count.index}" }
}

for_each — Creating Resources from a Map

HCL
# Create one S3 bucket per environment
resource "aws_s3_bucket" "env_buckets" {
for_each = toset(["dev", "staging", "prod"]) # set of strings
bucket = "zerodha-data-${each.key}" # each.key = dev / staging / prod
tags = { Environment = each.key }
}
# Creates: aws_s3_bucket.env_buckets["dev"]
# aws_s3_bucket.env_buckets["staging"]
# aws_s3_bucket.env_buckets["prod"]

Lifecycle Rules

HCL
resource "aws_db_instance" "payments_db" {
identifier = "payments-prod"
engine = "postgres"
instance_class = "db.t3.medium"
username = "admin"
password = var.db_password
lifecycle {
prevent_destroy = true # block accidental terraform destroy
create_before_destroy = true # for zero-downtime replacement
ignore_changes = [password] # don't revert manual password rotations
}
}

Troubleshooting Resources

Error Cause Fix
Error: Reference to undeclared resource Typo in resource address Check resource_type.resource_name spelling
Error: Unsupported argument Wrong argument name for this resource type Check provider documentation for valid arguments
Error: Resource already exists Resource exists in AWS but not in state Use terraform import to bring it into state
Error: timeout while waiting for resource AWS API slow or limits hit Increase timeout in timeouts block
Tip

Use terraform state list to see every resource Terraform is tracking. Use terraform state show aws_s3_bucket.order_images to inspect all attributes of a specific resource.

Common Mistake

Using count when you should use for_each. If you have count = 3 and remove the first item, Terraform renumbers all resources and may destroy and recreate resources you did not intend to touch. Use for_each with a map for any resource you might add or remove from.

Frequently Asked Questions

What's the relationship between a Terraform resource block and the real cloud object it manages?

Each resource block is tracked in the state file by a unique resource address (like `aws_instance.web`), which is mapped internally to the cloud provider's real resource ID (like an EC2 instance ID) — Terraform uses that mapping to know what to check, update, or destroy on each run. Deleting a resource block from your .tf files and running apply will destroy the real object; removing it from state with `terraform state rm` instead just stops Terraform from tracking it, leaving the real resource untouched.

What's a common mistake that causes an unwanted resource replacement instead of an in-place update?

Changing an attribute that a provider marks as `ForceNew` in its schema — such as an EC2 instance's availability zone or an RDS engine version in some configurations — triggers a destroy-and-recreate instead of an in-place update, which is often not what the engineer expects or wants for a stateful resource. Always check `terraform plan` output for `-/+` (replace) versus `~` (update in place) before applying, especially on anything holding data.