TerraformVII · Resources, Data Sources, and count/for_eachProduction Terraform
Resource Attributes and Computed Values
What you'll learn
- Distinguish required arguments from optional arguments in a resource block
- Identify computed, optional-computed, and required-computed attribute kinds
- Recognise "known after apply" and explain why it affects planning
- Use attribute references to compose resources without circular dependencies
Prerequisites
None — start here.
Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13
Every resource block is a set of named fields. Some of them you write; some of them the provider writes back to you. Some you must supply; some you may leave blank. The vocabulary around these fields is inconsistent in the wild — the Terraform documentation uses “argument” and “attribute” interchangeably — and the consequences of confusing them show up at the worst moment: during a production apply. This lesson is the disciplined reading of the resource schema.
Argument versus attribute
The Terraform documentation draws a fine distinction that the wider community ignores:
- Argument — a field the user writes in the configuration. Appears
on the left-hand side of
=inside the resource block. Used as input to the provider API. - Attribute — any named field exposed by a resource. Includes all arguments (since they are also readable after apply) plus the provider-computed fields.
In practice, the distinction matters when you try to write to a field that the provider only writes. The error message tells you:
Error: Invalid value for attribute
on main.tf line 12, in resource "aws_instance" "web":
12: arn = "arn:aws:ec2:eu-west-1:..."
The given attribute is read-only.
arn is an attribute, not an argument. You cannot set it; the
provider populates it.
The schema kinds
A provider declares each attribute with one or two boolean flags in its schema:
| Kind | User can set? | Provider writes? | Default? |
|---|---|---|---|
Required | Yes (must) | No | None — must be supplied |
Optional | Yes (may) | No | Per schema default |
Computed | No | Yes | Provider default |
Optional + Computed | Yes (may) | Yes | Provider default if not set |
Required + Computed | Yes (must, but only after creation) | Yes | Provider default on creation |
A real example using the AWS provider:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0" # Required argument
instance_type = "t3.small" # Required argument
monitoring = false # Optional argument, default false
subnet_id = aws_subnet.public["a"].id # Required argument, cross-resource reference
tags = { # Optional argument
Name = "web-1"
}
# The following are Computed attributes. You can read them; you cannot write them.
# arn, id, private_dns, public_ip, password_data (when applicable).
}
The error above (is read-only) is the hallmark of writing to a
Computed-only attribute. The fix is to remove the assignment and
either reference the attribute elsewhere or use a different resource
attribute that the provider does accept as input.
Known after apply
Some attributes are Computed and the provider cannot determine their
value until the resource is created. The plan reports these as
(known after apply). Example:
# aws_instance.web will be created
+ resource "aws_instance" "web" {
+ ami = "ami-0c55b159cbfafe1f0"
+ arn = (known after apply)
+ id = (known after apply)
+ instance_type = "t3.small"
+ private_dns = (known after apply)
+ public_ip = (known after apply)
# ...
}
A (known after apply) value cannot be referenced in the same apply.
It is resolved at the end of the apply phase, after the provider
returns the live values. This is why a configuration that references
(known after apply) of a resource it has just declared fails with
a self-reference error.
The rule is: an attribute can only be referenced from a resource created earlier in the same apply, or in a separate apply.
Inspecting the schema
The provider ships its schema with the plugin. Inspect it on the command line:
terraform providers schema -json | jq '.provider_schemas."registry.terraform.io/hashicorp/aws".resource_schemas."aws_instance".block.attributes | to_entries[] | {name: .key, type: .value.type, required: .value.required, optional: .value.optional, computed: .value.computed}'
{
"name": "arn",
"type": "string",
"required": false,
"optional": false,
"computed": true
}
{
"name": "instance_type",
"type": "string",
"required": true,
"optional": false,
"computed": false
}
{
"name": "monitoring",
"type": "bool",
"required": false,
"optional": true,
"computed": false
}
The schema is the source of truth for what is required, what is optional, and what is read-only. Read it before writing the configuration, not after.
Reading attributes after apply
The point of having computed attributes is that they let you reference values you did not write — and that you could not have known without creating the resource. A typical pattern:
resource "aws_lb" "web" {
name = "web"
load_balancer_type = "application"
subnets = [for s in aws_subnet.public : s.id]
}
output "lb_dns_name" {
value = aws_lb.web.dns_name
}
aws_lb.web.dns_name is a Computed attribute. The provider assigns
it during apply. The output block surfaces it for downstream
consumers. The same pattern is used for IDs, ARNs, generated
passwords, and any other value the provider owns.
Production failure modes
-
Writing to a computed-only attribute. The apply fails with
Invalid value for attributeoris read-only. Symptom: the plan succeeds (validation only checks syntax) but the apply fails at the first resource that violates the schema. Recovery: remove the assignment, use the attribute as an output or reference it from a different resource. -
Relying on an
Optionaldefault that changed. A new provider version changes the default of an optional argument. The plan shows~for a resource that the team did not modify. Symptom: the diff showsmonitoring = true -> falsefor many resources on the next apply after a provider upgrade. Recovery: pin the provider version in therequired_providersblock, or set the attribute explicitly. -
Self-reference to a
known after applyattribute. A resource block references its own computed attribute. Symptom: the apply fails with “self-dependency” or “cycle”. Recovery: split the apply into two runs, or move the dependent value into a data source lookup rather than an output of the same block. -
Treating an
Optional + Computedattribute as user-set forever. If you set the attribute tonull, the provider keeps itnullfor the lifetime of the resource (it is in state). Symptom: the attribute does not get the value the provider would have chosen. Recovery:terraform state rmthe attribute and let the next refresh repopulate it, or set it to a real value. -
Sensitive value in logs. A computed sensitive attribute (e.g.
password) is written into a tag, an output withoutsensitive, or alocal_fileresource. Symptom: the value shows up in CI logs or a backup bucket. Recovery: redact logs, rotate the credential, mark the outputsensitive = true. -
Assuming the schema is stable across provider versions. Provider upgrades may rename attributes, change defaults, or move attributes into nested blocks. Symptom: a plan after a provider upgrade shows many
~lines for attributes the team did not change. Recovery: read the provider upgrade guide; pin the provider version explicitly.
Security and performance
Security. Computed attributes can be sensitive. Treat the state
file as a sensitive artefact regardless of which attributes are
marked Sensitive. Encrypt the state at rest, restrict access to
the state backend, and never commit state to Git.
Performance. Reading an attribute is free. Computing it may not be — the provider may call an extra API to read the live value back. For attributes that are returned by the create API, the cost is zero. For attributes that require a follow-up read, the cost is the latency of the read API. Check the provider documentation if the apply is performance-sensitive.
What to do in production
- Pin provider versions in
required_providers. Schema drift between provider versions is the most common cause of unexpected attribute changes. - Run
terraform plan -out=tfplanfor every change and read the schema diffs. - Use
terraform providers schema -jsonto inspect what is required and what is computed for any resource type you are about to use. - For sensitive computed attributes, mark the consuming output or
variable
sensitive = true. The value is redacted from plan output but is still in state. - Avoid reading computed attributes from the same apply in which the resource is created; if you must, split the apply.
Verification
# 1. Inspect the schema for a specific resource type
terraform providers schema -json | \
jq '.provider_schemas."registry.terraform.io/hashicorp/aws".resource_schemas."aws_instance".block.attributes'
# 2. Confirm no computed-only attribute is being assigned
grep -E '^\s*(arn|id|dns_name|private_dns)\s*=' main.tf
# 3. Show the known-after-apply attributes in the current plan
terraform plan -out=tfplan
terraform show -json tfplan | jq '.resource_changes[] | select(.change.after_unknown != {}) | .address'
# 4. Confirm sensitive attributes are redacted in the plan
terraform show -json tfplan | jq '.resource_changes[] | select(.change.after_sensitive != {}) | .address'
# 5. Verify state contains the expected computed attributes
terraform state show aws_instance.web
A clean verification looks like:
$ terraform plan -out=tfplan
$ terraform show -json tfplan | jq '.resource_changes[] | select(.change.after_unknown != {}) | .address'
The second command should print only the address of any resource
whose plan contains known after apply values. For a configuration
that references computed attributes from another apply, this list
should be non-empty and explainable.
Knowledge check · 7 questions
Q1. Which of the following best describes the difference between an argument and an attribute?
Q2. An attribute is declared `Optional + Computed` in the provider schema. Which is true?
Q3. A `Computed`-only attribute is read-only: Terraform errors if the configuration tries to set it.
Q4. Which of the following are valid attribute kinds in a Terraform provider schema? (Select all that apply.)
Q5. Why does `terraform plan` show `(known after apply)` for an attribute?
Q6. What command inspects the provider schema on the command line?
Q7. A team upgrades the AWS provider from 5.0 to 5.20. Their next plan shows dozens of `~` lines for attributes they did not change. What is the most likely cause?
Passing score: 75%. Answers are checked in this browser.