Referencing resource attributes
Referencing resource attributes
A Terraform argument rarely stands alone: most of the time it borrows a value another object already produced, and the mechanism is a reference, a dotted address whose first token says what kind of thing you are naming. The most common target is another resource's exported attribute, written <TYPE>.<NAME>.<ATTRIBUTE>. If an aws_vpc resource named main exports an id, then aws_vpc.main.id[1] reads that id wherever you need it. That address takes two variants once a resource is repeated, and those variants are the rest of this section.
A counted resource is a list
Adding count[2] to a resource turns it into a list of objects, one per instance. You select an instance by numeric index, aws_instance.web[0], and read an attribute from it, aws_instance.web[0].id. The bare address aws_instance.web now means the whole list, so aws_instance.web.id with count set is an error: you asked a list for an attribute only a single object has. Inside the block, count.index[2] is this instance's distinct index number starting at 0, which is how you vary an argument across the copies.
A for_each resource is a map
for_each[3] instead accepts a map or a set of strings and turns the resource into a map of objects keyed by those keys. You select an instance by its string key, aws_instance.web["app"], and read aws_instance.web["app"].id. A numeric index like [0] fails here, because the collection is keyed by string, not by position. Inside the block, each.key[3] is the current key and each.value is its value (for a set, the two are identical).
One idea sits under all three cases: a plain resource is a single object, count makes a list you index by number, and for_each makes a map you index by key. The selector follows directly from which collection you created.
References set order and known-after-apply
A reference does more than copy a value: it also fixes the order Terraform works in.
When one resource's argument references another resource's attribute, for example subnet_id = aws_subnet.main.id on an instance, Terraform reads that as a dependency and must create aws_subnet.main before the instance that uses its id. Terraform infers this by studying the attributes used in interpolation expressions[4] across the whole configuration, builds a dependency graph, and walks that graph to decide the create and update order. You write no ordering directive; the reference is the directive.
depends_on is only for hidden links
Because references already imply order, depends_on[5] is reserved for the case where a resource relies on another's behavior but reads none of its data, so no expression reveals the link. Adding depends_on beside a reference that already connects the two is redundant, not safer. HashiCorp recommends expression references over depends_on[5] wherever a value can carry the dependency, because a reference also tells Terraform which value the order depends on.
Values known only after apply
Some attributes are assigned by the provider when it creates the object, an EC2 instance id or an ARN, so their value cannot exist at plan time. Terraform still plans successfully: it prints the not-yet-known value as (known after apply)[6] and fills in the real value during apply. A reference to such an attribute propagates that unknown, so a dependent argument also shows (known after apply) in the plan. That is expected output, not a failure; the value resolves the moment the upstream resource is created.
Named value reference forms
Resource attributes are one family of references; every other value Terraform exposes has its own fixed prefix, and knowing the prefix is knowing the reference.
The named values you reach by prefix:
| Reference form | Names | Example |
|---|---|---|
var.<NAME> | an input variable | var.region |
local.<NAME> | a local value | local.common_tags |
module.<NAME>.<OUTPUT> | a child module's declared output | module.network.vpc_id |
data.<TYPE>.<NAME> | a data source's result | data.aws_ami.ubuntu.id |
path.module, path.root, path.cwd | filesystem paths | path.module |
terraform.workspace | the current workspace name | terraform.workspace |
All of these are documented on the single references[1] page, and path.* and terraform.workspace are usable anywhere in a module.
The locals block: plural keyword, singular reference
A locals block[7] assigns names to expressions so a value computed once can be reused. The keyword is plural, locals, but you reference a single value with the singular local.<NAME>: locals { name = ... } defines it and local.name reads it. Writing locals.name is a frequent slip. A local is internal to the module that defines it; you can access it in that module but not in others[7], and a caller cannot set it. That is the line between a local and an input variable: a variable takes external input, a local only names an internal expression.
Values that exist only inside a block
A few names are valid only in a specific context. count.index, each.key, and each.value live only inside a block that sets count or for_each (the addressing rule from the first section), and self is valid only inside provisioner and connection blocks, where it refers to the resource being provisioned. Outside their block these names resolve to nothing and Terraform rejects the configuration.
How this appears on the exam
This objective is tested by spotting the reference that looks right but is not. Each trap below restates a rule established above.
- Unindexed reference to a counted resource. With
countset,aws_instance.webis a list, soaws_instance.web.idis an error; the correct form indexes first,aws_instance.web[0].id. - Numeric index on a
for_eachinstance. Afor_eachresource is keyed by string, soaws_instance.web[0]fails; use the key,aws_instance.web["app"]. - Bare variable or local name.
regionis not a reference to a variable;var.regionis. Likewise a local islocal.name, never the bare name and never the plurallocals.name. - A redundant
depends_on. If an argument already references the other resource's attribute, the dependency already exists; addingdepends_onfor the same pair is unnecessary.depends_onis the answer only when there is no shared value to reference. - Reaching a module's internals. A parent can read
module.network.vpc_id(a declared output) but not the child's internal resources; "reference the module'saws_vpcdirectly" is always wrong. (known after apply)treated as an error. A computed id shown as(known after apply)in the plan is normal; the value is assigned during apply. An answer that calls this a failure, or that requires adepends_onto fix it, is wrong.- A block-local value out of context.
count.indexoutside a counted block, orselfoutside a provisioner or connection block, is invalid; a stem that uses one of these in a plain resource argument is the distractor.
Addressing a resource and its instances
| Aspect | Single resource | With count | With for_each |
|---|---|---|---|
| Instance collection | One object | List of objects | Map of objects |
| Reference one instance | `aws_instance.web` | `aws_instance.web[0]` | `aws_instance.web["app"]` |
| Bare address gives | The single object | The whole list | The whole map |
| Block-local iterator | None | `count.index` | `each.key` / `each.value` |
Sharp facts the exam loves — give these one last read before exam day.
Cheat sheet
Sharp facts the exam loves — scan these before test day.
- Attribute reference form
Another resource's exported attribute is referenced as
<TYPE>.<NAME>.<ATTRIBUTE>, for exampleaws_vpc.main.id.6 questions test this
- In one configuration an `aws_eip` resource sets `instance = aws_instance.web.id`, and `aws_instance.web` uses neither `count` nor `for_each`. Beyond supplying the instance id value, what else does wri
- In Terraform, when one resource needs a value that another managed resource - one that uses neither count nor for_each - exports, which general form correctly references that exported attribute?
- An engineer declares `resource "aws_instance" "node" { count = 4 ... }` and now needs an output that returns the `id` of only the FIRST instance Terraform created. Given that count instances are addre
- Two resources sit in one Terraform configuration: a VPC declared as `resource "aws_vpc" "main" { ... }` and a security group that needs the VPC's exported `id` for its `vpc_id` argument. Neither resou
- A configuration declares `resource "aws_instance" "api" { for_each = var.nodes ... }`, where `var.nodes` is a map with the keys `"blue"` and `"green"`. Another resource must reference the `id` of the
- During a review, an engineer points at the reference `aws_subnet.public.vpc_id`, used inside another resource block to wire two resources together; neither resource uses `count` or `for_each`. Reading
- Indexing count instances
For a resource using
count, individual instances are addressed by numeric index such asaws_instance.web[0].id; the bare nameaws_instance.webis the whole list of instances.Trap Referencing
aws_instance.web.iddirectly whencountis set, which errors because the value is a list.5 questions test this
- An engineer wrote `resource "aws_instance" "web" { count = 2 ... }` and then referenced `aws_instance.web.id` in an output, expecting a single instance id. Running `terraform plan` returns an error in
- A module author converts a resource from `count` to `for_each = var.instances`, where `var.instances` is a map, but leaves an existing reference `aws_instance.web[0].id` unchanged. After the switch th
- A configuration declares `resource "aws_instance" "web" { count = 5 ... }`. An output must return a single list containing the `id` of every instance the `count` created, in index order. Using the doc
- An engineer declares `resource "aws_instance" "node" { count = 4 ... }` and now needs an output that returns the `id` of only the FIRST instance Terraform created. Given that count instances are addre
- A resource declares `resource "aws_instance" "worker" { count = 3 ... }`. Elsewhere an engineer writes the bare address `aws_instance.worker` with no index, expecting it to behave like a single resour
- Indexing for_each instances
For a resource using
for_each, instances are addressed by their map/set key, such asaws_instance.web["app"].id; the collection is a map keyed byeach.key.Trap Using a numeric index like
[0]to address afor_eachinstance instead of its string key.4 questions test this
- A resource `aws_s3_bucket.logs` is declared with `for_each = { app = "app-logs", db = "db-logs" }`. Another resource must reference the `arn` of the bucket created for the `db` entry. Which statement
- A module author converts a resource from `count` to `for_each = var.instances`, where `var.instances` is a map, but leaves an existing reference `aws_instance.web[0].id` unchanged. After the switch th
- A configuration declares `resource "aws_instance" "web" { for_each = toset(["app", "db"]) ... }`. A teammate needs the `private_ip` of the instance created for the `"app"` key. Because the resource us
- A configuration declares `resource "aws_instance" "api" { for_each = var.nodes ... }`, where `var.nodes` is a map with the keys `"blue"` and `"green"`. Another resource must reference the `id` of the
- References create implicit dependencies
When one resource argument references another resource's attribute (e.g.
subnet_id = aws_subnet.main.id), Terraform automatically infers a dependency and creates or updates the referenced resource first.Trap Adding a manual
depends_onwhen a reference already establishes the ordering.12 questions test this
- A subnet resource is referenced by an instance through `subnet_id = aws_subnet.main.id`, which makes the instance implicitly depend on the subnet. The engineer later runs `terraform destroy` to tear d
- In a configuration, an `aws_nat_gateway` sets its `allocation_id` argument to `aws_eip.nat.id`, referencing an Elastic IP declared in the same configuration. The Elastic IP does not reference the NAT
- An engineer defines a new `aws_subnet` and an `aws_instance` whose `subnet_id` references `aws_subnet.main.id`. The subnet does not exist yet, and its `id` is assigned by the provider only when the su
- After adding a `depends_on` argument to a module that already references an upstream resource, an engineer notices that the next `terraform plan` reports many more of the module's attributes as `(know
- A team is establishing configuration standards. One proposal is to add `depends_on` to every resource block 'to be safe,' regardless of whether the resources already reference one another. According t
- An engineer references a newly created database's `aws_db_instance.main.address` - assigned on create, so `(known after apply)` at plan time - inside an argument of a separate application resource. A
- An engineer's configuration declares a dozen resources across several files, some referencing other resources' attributes and some fully independent. Nothing sets an explicit ordering. When the engine
- A configuration defines an `aws_s3_bucket` for logs and an `aws_key_pair` for SSH access. Neither resource references any attribute of the other, and there is no `depends_on` between them. When the en
- An application running on an `aws_instance` reads objects from an S3 bucket at runtime, but the instance's configuration never references any attribute of the `aws_s3_bucket` resource. The engineer st
- A configuration creates a new resource and then defines an `output` value that references the `arn` attribute of that not-yet-created resource. Because the resource does not exist during planning, the
- A root module declares `aws_vpc.main` and calls a child `network` module, passing `vpc_id = aws_vpc.main.id` as one of the module's input variables. The child module uses that value to create subnets,
- A platform engineer writes an `aws_instance` resource whose `subnet_id` argument is set to `aws_subnet.main.id`, referencing a subnet declared in the same configuration. Neither block includes a `depe
- Unknown referenced values
If a referenced attribute is not yet known at plan time (for example an ID the provider assigns on create), the plan shows the dependent value as
(known after apply).8 questions test this
- An engineer defines a new `aws_subnet` and an `aws_instance` whose `subnet_id` references `aws_subnet.main.id`. The subnet does not exist yet, and its `id` is assigned by the provider only when the su
- After adding a `depends_on` argument to a module that already references an upstream resource, an engineer notices that the next `terraform plan` reports many more of the module's attributes as `(know
- An engineer references a newly created database's `aws_db_instance.main.address` - assigned on create, so `(known after apply)` at plan time - inside an argument of a separate application resource. A
- An engineer configures a resource with `count` set to a value derived from another resource's `id` attribute, but that `id` is assigned by the provider only when the other resource is created. When th
- A `terraform plan` for a brand-new database shows its `endpoint` and `id` as `(known after apply)`. The engineer proceeds with `terraform apply`, and it completes successfully. What is now true of tho
- Reviewing a `terraform plan` for a new deployment, an engineer sees one attribute displayed as `(known after apply)` and a different attribute displayed as `(sensitive value)`. A colleague assumes bot
- A configuration creates a new resource and then defines an `output` value that references the `arn` attribute of that not-yet-created resource. Because the resource does not exist during planning, the
- An engineer plans the creation of a new `aws_instance` whose `ami` and `instance_type` are set to literal strings in the configuration, while `id`, `arn`, and `private_ip` are assigned by the provider
- Named value prefixes
Terraform references named values by prefix: input variables as
var.<NAME>, local values aslocal.<NAME>, data sources asdata.<TYPE>.<NAME>, and child-module outputs asmodule.<NAME>.<OUTPUT>.Trap Referencing a variable by its bare name instead of
var.<name>, or reaching a child module's internal resource instead of its declared output.11 questions test this
- A root configuration calls a child module with the label `network`, and that child module declares `output "vpc_id"` exposing the id of an `aws_vpc` resource it creates. From the root module, which ex
- Module A defines `region_config` in its `locals` block. The author of a separate module B tries to reference `local.region_config` directly, and Terraform reports that the local value is not declared.
- An engineer wants to embed the name of the currently selected CLI workspace into a resource tag so resources are labeled per environment. Which built-in named value returns the current workspace name
- A module author wants the teams that call the module to be able to supply their own value for `environment`, but currently `environment` is defined in a `locals` block and callers report they cannot o
- An engineer defines a `locals` block that computes `name_prefix` from several input variables, and now needs to use that computed value in a resource's `tags` argument. Which expression correctly refe
- An engineer defines a `resource "aws_s3_bucket" "logs"` block and, in a separate `aws_s3_bucket_policy` resource in the same configuration, needs to supply that bucket's ARN. Which expression correctl
- An engineer adds a `data "aws_availability_zones" "available"` block and needs to pass the list of zone names it returns into a resource argument. Which expression correctly references the `names` att
- While reviewing a module, a new team member asks how Terraform tells apart the different kinds of named values it can reference in expressions. Which statement correctly describes the reference prefix
- A module declares `variable "network"` with an object type whose members include a `region` string, and a resource needs the value of that `region` member. Which expression correctly references the `r
- An engineer adds a `data "aws_ami" "ubuntu"` block to look up an image, then needs to pass the resulting image id to the `ami` argument of an `aws_instance` resource. Which expression correctly refere
- An engineer declares `variable "instance_type"` in a module and needs to use its value as the `instance_type` argument of an `aws_instance` resource in the same module. Which expression correctly refe
- Path and workspace values
path.module,path.root, andpath.cwdgive filesystem paths andterraform.workspacegives the current workspace name;self,count.index, andeach.key/each.valueare only valid inside specific block contexts.- locals block for named local values
A locals block assigns names to expressions so a value can be computed once and reused, referenced elsewhere as local. (singular local, even though the block keyword is locals). Local values keep configuration DRY and readable, but unlike input variables they are internal to the module and cannot be set or overridden by callers.
Trap Reference a local with the singular local., not the plural locals.; and because callers cannot set locals, a local is not a substitute for a variable when you need external input.
7 questions test this
- A teammate is confused because a Terraform configuration declares values inside a block written `locals { ... }`, yet every reference in the code uses `local.` without the trailing s. Which explanatio
- Module A defines `region_config` in its `locals` block. The author of a separate module B tries to reference `local.region_config` directly, and Terraform reports that the local value is not declared.
- A module author wants the teams that call the module to be able to supply their own value for `environment`, but currently `environment` is defined in a `locals` block and callers report they cannot o
- During a code review, an engineer replaces the same lengthy expression that appeared in five different resource arguments with a single named value defined in a `locals` block and referenced as `local
- An engineer defines a `locals` block that computes `name_prefix` from several input variables, and now needs to use that computed value in a resource's `tags` argument. Which expression correctly refe
- While reviewing a module, a new team member asks how Terraform tells apart the different kinds of named values it can reference in expressions. Which statement correctly describes the reference prefix
- An engineer keeps an environment prefix in a `locals` block and wants to override it for a single run by passing `terraform apply -var="prefix=staging"` on the command line. What is the correct unders