add deprecation marks to resources based on schema

This commit is contained in:
Daniel Schmidt 2026-01-20 15:50:00 +01:00
parent 02a4ddce1b
commit 7eaf6daf60
8 changed files with 1619 additions and 59 deletions

View file

@ -0,0 +1,5 @@
kind: ENHANCEMENTS
body: improve detection of deprecated resource attributes / blocks
time: 2026-01-20T17:28:31.861321+01:00
custom:
Issue: "38077"

View file

@ -77,27 +77,6 @@ func (b *Block) StaticValidateTraversal(traversal hcl.Traversal) tfdiags.Diagnos
}
if attrS, exists := b.Attributes[name]; exists {
// Check for Deprecated status of this attribute.
// We currently can't provide the user with any useful guidance because
// the deprecation string is not part of the schema, but we can at
// least warn them.
//
// This purposely does not attempt to recurse into nested attribute
// types. Because nested attribute values are often not accessed via a
// direct traversal to the leaf attributes, we cannot reliably detect
// if a nested, deprecated attribute value is actually used from the
// traversal alone. More precise detection of deprecated attributes
// would require adding metadata like marks to the cty value itself, to
// be caught during evaluation.
if attrS.Deprecated {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated attribute`,
Detail: fmt.Sprintf(`The attribute %q is deprecated. Refer to the provider documentation for details.`, name),
Subject: next.SourceRange().Ptr(),
})
}
// For attribute validation we will just apply the rest of the
// traversal to an unknown value of the attribute type and pass
// through HCL's own errors, since we don't want to replicate all

View file

@ -224,10 +224,6 @@ func TestStaticValidateTraversal(t *testing.T) {
`obj.nested_map["key"].optional`,
``,
},
{
`obj.deprecated`,
`Deprecated attribute: The attribute "deprecated" is deprecated. Refer to the provider documentation for details.`,
},
}
for _, test := range tests {

View file

@ -133,3 +133,7 @@ func (d *Deprecations) IsModuleCallDeprecationSuppressed(addr addrs.Module) bool
}
return false
}
func (d *Deprecations) DiagnosticsForValueMarks(valueMarks cty.ValueMarks, module addrs.Module, rng *hcl.Range) tfdiags.Diagnostics {
return d.deprecationMarksToDiagnostics(marks.FilterDeprecationMarks(valueMarks), module, rng)
}

View file

@ -0,0 +1,54 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package deprecation
import (
"fmt"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/zclconf/go-cty/cty"
)
// MarkDeprecatedValues inspects the given cty.Value according to the given
// configschema.Block schema, and marks any deprecated attributes or blocks
// found within the value with deprecation marks.
// It works based on the given cty.Value's structure matching the given schema.
func MarkDeprecatedValues(val cty.Value, schema *configschema.Block, origin string) cty.Value {
if schema == nil {
return val
}
newVal := val
// Check if the block is deprecated
if schema.Deprecated {
newVal = newVal.Mark(marks.NewDeprecation("deprecated resource block used", origin))
}
if !newVal.IsKnown() {
return newVal
}
// Even if the block itself is not deprecated, its attributes might be
// deprecated as well
if val.Type().IsObjectType() || val.Type().IsMapType() || val.Type().IsCollectionType() {
// We ignore the error, so errors are not allowed in the transform function
newVal, _ = cty.Transform(newVal, func(p cty.Path, v cty.Value) (cty.Value, error) {
attr := schema.AttributeByPath(p)
if attr != nil && attr.Deprecated {
v = v.Mark(marks.NewDeprecation("deprecated resource attribute used", fmt.Sprintf("%s.%s", origin, p)))
}
block := schema.BlockByPath(p)
if block != nil && block.Deprecated {
v = v.Mark(marks.NewDeprecation("deprecated resource block used", fmt.Sprintf("%s.%s", origin, p)))
}
return v, nil
})
}
return newVal
}

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
testing_provider "github.com/hashicorp/terraform/internal/providers/testing"
"github.com/hashicorp/terraform/internal/provisioners"
@ -2459,38 +2460,529 @@ resource "aws_instance" "test" {
}
func TestContext2Validate_deprecatedAttr(t *testing.T) {
p := testProvider("aws")
for name, tc := range map[string]struct {
attributeSchema map[string]*configschema.Attribute
blockSchema map[string]*configschema.NestedBlock
module map[string]string
expectedValidationDiags func(*configs.Config) tfdiags.Diagnostics
expectedPlanDiags func(*configs.Config) tfdiags.Diagnostics
expectedApplyDiags func(*configs.Config) tfdiags.Diagnostics
}{
"in locals": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
locals {
deprecated = aws_instance.test.foo
}
`,
},
expectedValidationDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 5, Column: 28, Byte: 108},
End: hcl.Pos{Line: 5, Column: 49, Byte: 129},
},
})
},
},
"in count": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.Number, Required: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
foo = 2
}
resource "aws_instance" "test2" {
count = aws_instance.test.foo
foo = 1
}
`,
},
expectedValidationDiags: func(m *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 6, Column: 23, Byte: 152},
End: hcl.Pos{Line: 6, Column: 44, Byte: 173},
},
})
},
},
"in for_each": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.Set(cty.String), Required: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
foo = ["a", "b"]
}
resource "aws_instance" "test2" {
for_each = aws_instance.test.foo
foo = ["x"]
}
`,
},
expectedValidationDiags: func(m *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 6, Column: 26, Byte: 164},
End: hcl.Pos{Line: 6, Column: 47, Byte: 185},
},
})
},
},
"in output": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
output "deprecated_output" {
value = aws_instance.test.foo
}
`,
},
expectedValidationDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 5, Column: 23, Byte: 123},
End: hcl.Pos{Line: 5, Column: 44, Byte: 144},
},
})
},
// During apply we take the planned value for the output. Since the plan
// does not contain marks we can not detect this usage at apply time.
expectedApplyDiags: func(c *configs.Config) tfdiags.Diagnostics { return tfdiags.Diagnostics{} },
},
"in resource attribute": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
"bar": {Type: cty.String, Optional: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
resource "aws_instance" "test2" {
bar = aws_instance.test.foo
}
`,
},
expectedValidationDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 5, Column: 21, Byte: 126},
End: hcl.Pos{Line: 5, Column: 42, Byte: 147},
},
})
},
},
"in precondition": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Required: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
foo = "bar"
}
resource "aws_instance" "test2" {
foo = "baz"
lifecycle {
precondition {
condition = aws_instance.test.foo != ""
error_message = "foo must not be empty"
}
}
}
`,
},
expectedPlanDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 9, Column: 31, Byte: 245},
End: hcl.Pos{Line: 9, Column: 58, Byte: 272},
},
})
},
},
"in postcondition condition": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Required: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
foo = "bar"
lifecycle {
postcondition {
condition = self.foo != ""
error_message = "foo must not be empty"
}
}
}
`,
},
expectedPlanDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 6, Column: 31, Byte: 160},
End: hcl.Pos{Line: 6, Column: 45, Byte: 174},
},
})
},
},
"in dynamic block": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.Set(cty.String), Computed: true, Deprecated: true},
},
blockSchema: map[string]*configschema.NestedBlock{
"bar": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: false,
Optional: true,
},
},
},
},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
foo = ["a", "b"]
}
resource "aws_instance" "test2" {
dynamic "bar" {
for_each = aws_instance.test.foo
content {
baz = bar.value
}
}
}
`,
},
expectedPlanDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 5, Column: 45, Byte: 135},
End: hcl.Pos{Line: 5, Column: 45, Byte: 135},
},
}).Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 5, Column: 45, Byte: 135},
End: hcl.Pos{Line: 5, Column: 45, Byte: 135},
},
})
},
},
"in check assertion": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Required: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
foo = "bar"
}
check "test_check" {
assert {
condition = aws_instance.test.foo != ""
error_message = "foo must not be empty"
}
}
`,
},
expectedPlanDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 7, Column: 29, Byte: 170},
End: hcl.Pos{Line: 7, Column: 56, Byte: 197},
},
})
},
},
"in module input": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
module "child" {
source = "./child"
input = aws_instance.test.foo
}
`,
"child/main.tf": `
variable "input" {
type = string
}
`,
},
expectedValidationDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 6, Column: 23, Byte: 144},
End: hcl.Pos{Line: 6, Column: 44, Byte: 165},
},
})
},
},
"in provisioner and connection block": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
resource "aws_instance" "test2" {
provisioner "shell" {
test_string = aws_instance.test.foo
connection {
type = "ssh"
host = aws_instance.test.foo
}
}
}
`,
},
expectedValidationDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 6, Column: 36, Byte: 177},
End: hcl.Pos{Line: 6, Column: 57, Byte: 198},
},
}).Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 9, Column: 26, Byte: 284},
End: hcl.Pos{Line: 9, Column: 47, Byte: 305},
},
})
},
},
"in action config": {
attributeSchema: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
},
module: map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
action "aws_register" "example" {
config {
host = aws_instance.test.foo
}
}
`,
},
expectedValidationDiags: func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated value used`,
Detail: `deprecated resource attribute used`,
Subject: &hcl.Range{
Filename: filepath.Join(c.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 6, Column: 24, Byte: 152},
End: hcl.Pos{Line: 6, Column: 45, Byte: 173},
},
})
},
},
} {
t.Run(name, func(t *testing.T) {
// Default values
if tc.expectedValidationDiags == nil {
tc.expectedValidationDiags = func(c *configs.Config) tfdiags.Diagnostics {
return tfdiags.Diagnostics{}
}
}
// By default we want the same validations in plan as in validate
if tc.expectedPlanDiags == nil {
tc.expectedPlanDiags = tc.expectedValidationDiags
}
// And the same validations in apply as in plan
if tc.expectedApplyDiags == nil {
tc.expectedApplyDiags = tc.expectedPlanDiags
}
pr := simpleMockProvisioner()
p := testProvider("aws")
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: tc.attributeSchema,
BlockTypes: tc.blockSchema,
},
},
Actions: map[string]*providers.ActionSchema{
"aws_register": {
ConfigSchema: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"host": {Type: cty.String, Optional: true},
},
},
},
},
})
m := testModuleInline(t, tc.module)
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
},
Provisioners: map[string]provisioners.Factory{
"shell": testProvisionerFuncFixed(pr),
},
})
t.Run("validate", func(t *testing.T) {
validateDiags := ctx.Validate(m, nil)
tfdiags.AssertDiagnosticsMatch(t, validateDiags, tc.expectedValidationDiags(m))
})
var plan *plans.Plan
t.Run("plan", func(t *testing.T) {
var planDiags tfdiags.Diagnostics
plan, planDiags = ctx.Plan(m, nil, SimplePlanOpts(plans.NormalMode, InputValues{}))
tfdiags.AssertDiagnosticsMatch(t, planDiags, tc.expectedPlanDiags(m))
})
t.Run("apply", func(t *testing.T) {
_, applyDiags := ctx.Apply(plan, m, &ApplyOpts{})
tfdiags.AssertDiagnosticsMatch(t, applyDiags, tc.expectedApplyDiags(m))
})
})
}
}
func TestContext2Validate_deprecated_resource(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
resource "test_resource" "test" { # WARNING
attr = "value"
}
output "a" {
value = test_resource.test.attr # WARNING
}
`,
})
p := new(testing_provider.MockProvider)
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
"test_resource": {
Deprecated: true,
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true, Deprecated: true},
"attr": {
Type: cty.String,
Computed: true,
},
},
},
},
})
m := testModuleInline(t, map[string]string{
"main.tf": `
resource "aws_instance" "test" {
}
locals {
deprecated = aws_instance.test.foo
}
`,
})
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
diags := ctx.Validate(m, nil)
warn := diags.ErrWithWarnings().Error()
if !strings.Contains(warn, `The attribute "foo" is deprecated`) {
t.Fatalf("expected deprecated warning, got: %q\n", warn)
}
diags := ctx.Validate(m, &ValidateOpts{})
tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Usage of deprecated resource "test_resource"`,
Detail: `The resource "test_resource" has been marked as deprecated by its provider. Please check the provider documentation for more information.`,
Subject: &hcl.Range{
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 2, Column: 1, Byte: 1},
End: hcl.Pos{Line: 2, Column: 32, Byte: 32},
},
}).Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Deprecated value used",
Detail: `Resource "test_resource" is deprecated`,
Subject: &hcl.Range{
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 7, Column: 13, Byte: 92},
End: hcl.Pos{Line: 7, Column: 36, Byte: 115},
},
}))
}
func TestContext2Validate_unknownForEach(t *testing.T) {

View file

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/deprecation"
"github.com/hashicorp/terraform/internal/didyoumean"
"github.com/hashicorp/terraform/internal/instances"
"github.com/hashicorp/terraform/internal/lang"
@ -649,9 +650,13 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
// resource has (using d.Evaluator.Instances.ResourceInstanceKeys) and
// then retrieving the value for each instance to assemble into the
// result, using some per-resource-mode logic maintained elsewhere.
return d.getEphemeralResource(addr, rng)
val, epehemeralDiags := d.getEphemeralResource(addr, rng)
diags = diags.Append(epehemeralDiags)
return deprecation.MarkDeprecatedValues(val, schema.Body, addr.String()), diags
case addrs.ListResourceMode:
return d.getListResource(config, rng)
val, listDiags := d.getListResource(config, rng)
diags = diags.Append(listDiags)
return deprecation.MarkDeprecatedValues(val, schema.Body, addr.String()), diags
default:
// continue with the rest of the function
}
@ -796,11 +801,21 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
// We should only end up here during the validate walk (or
// console/eval), since later walks should have at least partial
// states populated for all resources in the configuration.
ret := cty.DynamicVal
if schema.Body.Deprecated {
ret = ret.Mark(marks.NewDeprecation(fmt.Sprintf("Resource %q is deprecated", addr.Type), addr.String()))
switch {
case config.Count != nil:
return deprecation.MarkDeprecatedValues(cty.DynamicVal, schema.Body, addr.String()), diags
case config.ForEach != nil:
return deprecation.MarkDeprecatedValues(cty.DynamicVal, schema.Body, addr.String()), diags
default:
// We don't know the values of the single resource instance, but we know the general
// shape these values will take.
content := map[string]cty.Value{}
for attr, attrType := range ty.AttributeTypes() {
content[attr] = cty.UnknownVal(attrType)
}
return deprecation.MarkDeprecatedValues(cty.ObjectVal(content), schema.Body, addr.String()), diags
}
return ret, diags
}
}
@ -830,7 +845,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
continue
}
vals[int(intKey)] = instance
vals[int(intKey)] = deprecation.MarkDeprecatedValues(instance, schema.Body, addr.Instance(key).String())
}
// Insert unknown values where there are any missing instances
@ -852,7 +867,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
// old key that is being dropped and not used for evaluation
continue
}
vals[string(strKey)] = instance
vals[string(strKey)] = deprecation.MarkDeprecatedValues(instance, schema.Body, addr.Instance(key).String())
}
if len(vals) > 0 {
@ -872,11 +887,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
val = cty.UnknownVal(ty)
}
ret = val
}
if schema.Body.Deprecated {
ret = ret.Mark(marks.NewDeprecation(fmt.Sprintf("Resource %q is deprecated", addr.Type), addr.String()))
ret = deprecation.MarkDeprecatedValues(val, schema.Body, addr.String())
}
return ret, diags