TL;DR: Terraform 1.15 reports a deprecated attribute you never referenced because deprecation rides inside the value as a cty mark, and exporting a whole resource object carries the mark with it. Three different situations print the identical warning. Run terraform validate -json, compare warning_count to the console, then grep each originates from attribute against your own HCL: hits are real migration work, misses are propagation. Terraform 1.16 fixes the child module case only.

Nobody wrote website_domain. Terraform 1.15.8 printed it anyway, attached to an output block that exports an S3 bucket:

Warning: Deprecated value used

  on bucket.tf line 11, in output "bucket":
  11:   value       = aws_s3_bucket.example

  The deprecation originates from aws_s3_bucket.example.website_domain

website_domain is deprecated. [more deprecation instructions here]

(and 13 more similar warnings elsewhere)

That is hashicorp/terraform issue #38982, and the last line matters as much as the text above it. Issue #38554, filed 2026-05-11 against 1.15.2, counted roughly 30 of these coming out of one S3 module, and the reporter's summary does the diagnosis for you: "no value is passed to the variable, it is never referenced, so no warning should be produced." One warning summary with several unrelated triggers is the same trap as npm's null edgesOut crash. You cannot fix it until you know which trigger you have.

Why Terraform blames an attribute you never wrote

Terraform 1.15 shipped variable and output deprecation in PR #38001, and it is implemented with marks rather than usage analysis. internal/lang/marks defines a DeprecationMark{Message, OriginDescription}, and deprecated provider schema attributes get the same treatment. A mark lives inside the value. Export the resource object and the mark on website_domain goes with it, so when the output node walks the value looking for marks, it finds one and reports the export as a use.

The The deprecation originates from line is literally the mark's OriginDescription. That is the reason it names an attribute that appears nowhere in your configuration, and the reason no amount of editing your own HCL clears it.

The blast radius is a module you did not write. The terraform-aws-eventbridge module tripped this at version 4.3.0 on Terraform 1.15.4 with AWS provider 6.46.0 from a single line, output "eventbridge_rules" { value = aws_cloudwatch_event_rule.this }, as filed in module issue #199. Any module that exports whole resource objects, which is most of them, becomes a warning generator the moment a provider deprecates one nested attribute. And a deprecation is a removal clock, the same kind of clock that Ingress-NGINX retirement put on everyone's ingress layer, so the noise is worth reading rather than muting.

What the 1.16 changelog line actually covers

Terraform 1.16.0-rc2 landed on 2026-08-19 with a changelog entry that reads like the end of this: "Child module outputs with unreferenced deprecated nested attributes no longer return deprecation warnings" (#38778).

The merged diff is eleven lines in internal/terraform/node_output.go (merged 2026-06-25, approved by jbardin), and it branches on IsRoot():

if n.ModulePath().IsRoot() {
    val, deprecationDiags = ctx.Deprecations().ValidateExpressionDeepAndUnmark(val, n.ModulePath(), n.Config.Expr)
} else {
    // If the output is in a child module, only check for deprecations
    // at the "top level". This avoids deprecation warnings when
    // outputting an entire resource with a nested deprecated attribute.
    // (References to said attribute should still incur a warning)
    val, deprecationDiags = ctx.Deprecations().ValidateAndUnmark(val, n.ModulePath(), n.Config.Expr.Range().Ptr())
}

The doc comments in internal/deprecation/deprecation.go name the difference plainly: ValidateExpressionDeepAndUnmark uses GetDeprecationMarksDeep and traverses the value, ValidateAndUnmark uses GetDeprecationMarks and handles top-level marks only. Root outputs keep the deep walk.

The repro in #38982 is a root module output (on bucket.tf line 11, in output "bucket"), and my read of the diff is that this case still warns on 1.16 even though the issue is closed. I would spend ten minutes reproducing your own case on rc2 before you schedule an upgrade around that changelog line.

aws_s3_bucket.exampleprovider marks website_domain deprecatedwhere is theoutput block?ValidateAndUnmarktop-level marks onlyno warning in 1.16ValidateExpressionDeepAndUnmarkwalks nested markswarning still firesnested marks survive into the callercaller re-exports the objectfrom a root output"child module""root module"

Worth noting that the 1.15 line moved the opposite way in the same week. Terraform 1.15.9, released 2026-08-19 alongside rc2, carries "Child module validation has been fixed and will now raise errors or warning diagnostics for invalid blocks." Patch forward on 1.15 and you get more child module diagnostics; move to 1.16 and you get fewer of this specific kind. 1.15.6 had already fixed a terraform console panic when evaluating expressions with deprecated values. Four minor releases in, the marks implementation is still settling.

Three causes hide behind one warning summary

The issue threads run these together, which is why the advice inside them contradicts itself.

CauseWhat the "originates from" path points atFixed in 1.16.0The right move
You actually reference the deprecated attributeAn attribute you can grep in your own HCLNo, and correctly soMigrate the attribute
A child module output exports a whole resource or data source objectAn attribute of a resource inside the moduleYes (#38778)Upgrade Terraform
A root module output or local exports a whole objectAn attribute of a resource in your root configNot per the diffExport named attributes, or accept the noise

The two commands that separate them

Run terraform validate -json and compare its warning_count against what the console showed you. The human renderer consolidates same-summary diagnostics into (and N more similar warnings elsewhere), a behavior still tracked as open issue #32104, so the collapsed remainder is exactly where a genuine deprecation hides behind a dozen propagation artifacts. If the console printed 4 and the JSON says 30, you have been reading 13 percent of your deprecation surface.

Then pull the attribute name out of each originates from path and grep your own HCL for it:

terraform validate -json \
  | jq -r '.diagnostics[] | select(.summary=="Deprecated value used") | .detail' \
  | grep -o 'originates from [^ ]*' | sort -u

Zero grep hits in your configuration means propagation. Hits mean migration work. That split is the only thing that tells you whether a Terraform upgrade helps you at all, and it takes about a minute per workspace inside the same CI job that already runs plan against AWS.

ignore_nested_deprecations deletes the signal, it does not hide it

The workaround circulating in the module threads is ignore_nested_deprecations = true on the module call. The syntax page gives it one sentence: "If the ignore_nested_deprecations argument is set to true, Terraform does not show deprecation warnings in the module call or nested modules." No mention of scope, and nothing about what happens to the underlying data.

The source is more specific than the docs. Suppression runs through IsModuleCallDeprecationSuppressed, checked against a suppressedModules list, and both validation paths remove the marks from the value whether or not suppression is on. This is not a display filter. It strips the deprecation information for that module and everything under it, real deprecations included. Set it to quiet fourteen false positives and you have also disabled the fifteenth, which was the one you needed.

These warnings are your AWS provider v7 backlog

Ignoring them is tempting because they do not change an exit code. Plan stays green, CI stays green.

The AWS provider is currently using deprecations as its v7 removal queue. Provider issue #42468, milestone v7.0.0, is open to delete name from the aws_region data source. Issue #44803 tracks removing deprecated status attributes from the organizations resources. Each of those turns into a hard plan error on the day you bump the major version, and the warnings you are scrolling past are the inventory of that work. It is the same warning-window economics as the npm allowScripts notices before npm 12: free to fix while it is a warning, a broken pipeline once the major lands. Suppress the list at the module boundary in August and you rediscover it as an upgrade-day outage, which is how a Kubernetes minor bump turns into an "unrecognized format int32" scramble.

What to run before your next provider bump

  1. Capture the real count. terraform validate -json | jq '.warning_count' in every workspace, and record the delta against the console number. Anything above roughly 10 means the console has been collapsing most of it (#32104).
  2. Classify with grep, not by reading. Extract each originates from attribute and search your own HCL. Hits go on the migration list; misses are mark propagation and no config edit will clear them.
  3. Fix the ones you own at the source. In your own modules, export named attributes instead of whole resource objects wherever the consumer contract allows. That kills the noise on every Terraform version, including the root module case 1.16 does not cover.
  4. Leave ignore_nested_deprecations alone unless the module is vendored and unchangeable. If you must set it, put the date you will revisit it in a comment next to the argument, because it blinds the entire subtree.
  5. Turn the surviving genuine warnings into a v7 ticket now. Provider issues #42468 and #44803 are open against the v7.0.0 milestone; every attribute on that list is a plan error waiting for your major bump.
  6. If a 1.16 upgrade is on the plan specifically to clear this, reproduce your case on 1.16.0-rc2 first. The changelog says child module outputs, and the eleven-line diff means it: https://github.com/hashicorp/terraform/pull/38778