All guides
Practice now
D3 · Terraform Core

Resources, data sources, and dependencies

Resources are Terraform's central element: they define the infrastructure you want to manage. Data sources let you read existing infrastructure. Understanding dependencies and the lifecycle block is key for the exam.

Syntax of the resource block

The resource block has two mandatory labels: the type (determined by the provider) and the local name (identifier within Terraform).

# resource "<TYPE>" "<LOCAL_NAME>" { ... }
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "web-server"
  }
}

# Resource address: aws_instance.web
# Attribute: aws_instance.web.id
# Attribute: aws_instance.web.public_ip

# Root module:       aws_instance.web
# Child module:       module.app.aws_instance.web
# Nested module:    module.app.module.compute.aws_instance.web
ConceptDescriptionExample
TypeDefines which API is called (determined by the provider)aws_instance, azurerm_virtual_machine
Local nameUnique identifier within the module (not in the cloud)web, database, main — letters, numbers, and _ only
AddressType + name = full resource addressaws_instance.web
ArgumentsParameters that configure the resource (defined by the provider)ami, instance_type, tags
AttributesValues computed after creation (exported values).id, .arn, .public_ip

Data sources: reading without creating

Data sources let you query information about existing infrastructure (created by another module, another tool, or manually) without Terraform managing it.

# data "<TYPE>" "<LOCAL_NAME>" { ... }
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]   # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
  }
}

# Use the data source result
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id   # data.<type>.<name>.<attr>
  instance_type = "t3.micro"
}

# Other examples of data sources
data "aws_vpc" "existing" {
  id = "vpc-12345678"                # look up by exact ID
}

data "aws_availability_zones" "available" {
  state = "available"                # all available AZs
}

💡 Data sources are read during terraform plan

Data sources are queried during terraform plan (or at the start of apply). If the queried infrastructure doesn't exist, the plan will fail with an error. Unlike resources, Terraform creates or destroys nothing when using data sources.

Resource vs data source

Characteristicresource {}data {}
Creates infra?✅ Yes — Terraform creates/manages the resource❌ No — only reads existing infra
Appears in state?✅ Yes — tracks its state in tfstate❌ No — does not save state (re-reads every plan)
Can Terraform destroy it?✅ Yes — with destroy❌ No — never destroys it
Reference prefixtype.name.attr (no prefix)data.type.name.attr
When to use itWhen Terraform must own the resourceWhen the infra already exists and you only need its attributes
Syntaxresource "aws_vpc" "main" {}data "aws_vpc" "existing" {}

Implicit and explicit dependencies

Terraform automatically builds a dependency graph based on the references between resources. You can also declare explicit dependencies with depends_on.

# IMPLICIT DEPENDENCY (recommended)
# Terraform detects that aws_subnet.main depends on aws_vpc.main
# because it references its .id attribute
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "main" {
  vpc_id     = aws_vpc.main.id     # <-- creates an implicit dependency
  cidr_block = "10.0.1.0/24"
}

# EXPLICIT DEPENDENCY (use only when there is no direct reference)
# For example: the app depends on the DB existing, but doesn't reference any attr
resource "aws_db_instance" "main" {
  # ... DB configuration
}

resource "aws_instance" "app" {
  ami           = "ami-abc123"
  instance_type = "t3.micro"

  depends_on = [aws_db_instance.main]   # wait for the DB to be ready
}

💡 When to use depends_on

Use depends_on only when there is a real dependency that Terraform cannot detect automatically (for example, when a resource needs another one to be ready but doesn't use any of its attributes). Unnecessary use of depends_on can slow down the plan by adding dependencies that Terraform could otherwise parallelize.

lifecycle meta-arguments

The lifecycle block inside a resource controls how Terraform manages the resource's lifecycle: creation, update, and destruction.

resource "aws_db_instance" "prod" {
  identifier = "prod-db"
  # ... other arguments

  lifecycle {
    # Prevents Terraform from destroying this resource (errors if it tries)
    prevent_destroy = true

    # Creates the new resource BEFORE destroying the old one (zero downtime)
    create_before_destroy = true

    # Ignores changes to these arguments (no diff shown in plan)
    ignore_changes = [
      engine_version,   # the DB can self-update without Terraform reverting it
      tags["LastModified"],
    ]

    # Terraform destroys and recreates the resource if this other resource changes
    replace_triggered_by = [
      aws_launch_template.app.id,
    ]
  }
}
ArgumentTypeEffect
prevent_destroyboolIf true, any plan that destroys the resource will produce an error. Protects critical resources.
create_before_destroyboolIf true, creates the replacement before destroying the current one. Requires the provider to support it.
ignore_changeslist(attr)Ignores changes on the listed attributes. Use "all" to ignore everything (extreme case).
replace_triggered_bylist(ref)Forces replacement of this resource when the referenced resources/attributes change.

⚠️ prevent_destroy does not protect against terraform destroy

prevent_destroy = true prevents Terraform from destroying the resource as part of a configuration change (e.g.: renaming a DB that requires replacement). However, if someone removes the resource block from the code and runs apply, the resource will still be destroyed because the prevent_destroy rule no longer exists.

Did you get this topic?

Test what you just learned

A team has a production database defined with resource 'aws_db_instance' 'prod'. They want Terraform to NOT revert changes if someone changes the engine_version parameter (the DB manages its own engine updates). Which lifecycle configuration is correct?

Ready to test "Resources, Data Sources and Dependencies"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.