diff --git a/internal/fwschema/write_only_nested_attribute_validation.go b/internal/fwschema/write_only_nested_attribute_validation.go
index 4cba87413..bb4812cb2 100644
--- a/internal/fwschema/write_only_nested_attribute_validation.go
+++ b/internal/fwschema/write_only_nested_attribute_validation.go
@@ -52,6 +52,34 @@ func ContainsAnyWriteOnlyChildAttributes(nestedAttr NestedAttribute) bool {
return false
}
+// BlockContainsAnyWriteOnlyChildAttributes will return true if any child attribute for the
+// given nested block has WriteOnly set to true.
+func BlockContainsAnyWriteOnlyChildAttributes(block Block) bool {
+ nestedObjAttrs := block.GetNestedObject().GetAttributes()
+ nestedObjBlocks := block.GetNestedObject().GetBlocks()
+
+ for _, childAttr := range nestedObjAttrs {
+ if childAttr.IsWriteOnly() {
+ return true
+ }
+
+ nestedAttribute, ok := childAttr.(NestedAttribute)
+ if ok {
+ if ContainsAnyWriteOnlyChildAttributes(nestedAttribute) {
+ return true
+ }
+ }
+ }
+
+ for _, childBlock := range nestedObjBlocks {
+ if BlockContainsAnyWriteOnlyChildAttributes(childBlock) {
+ return true
+ }
+ }
+
+ return false
+}
+
func InvalidWriteOnlyNestedAttributeDiag(attributePath path.Path) diag.Diagnostic {
return diag.NewErrorDiagnostic(
"Invalid Schema Implementation",
@@ -62,6 +90,26 @@ func InvalidWriteOnlyNestedAttributeDiag(attributePath path.Path) diag.Diagnosti
)
}
+func InvalidSetNestedAttributeWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic {
+ return diag.NewErrorDiagnostic(
+ "Invalid Schema Implementation",
+ "When validating the schema, an implementation issue was found. "+
+ "This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+ fmt.Sprintf("%q is a set nested attribute that contains a WriteOnly child attribute.\n\n", attributePath)+
+ "Every child attribute of a set nested attribute must have WriteOnly set to false.",
+ )
+}
+
+func SetBlockCollectionWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic {
+ return diag.NewErrorDiagnostic(
+ "Invalid Schema Implementation",
+ "When validating the schema, an implementation issue was found. "+
+ "This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+ fmt.Sprintf("%q is a set nested block that contains a WriteOnly child attribute.\n\n", attributePath)+
+ "Every child attribute within a set nested block must have WriteOnly set to false.",
+ )
+}
+
func InvalidComputedNestedAttributeWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic {
return diag.NewErrorDiagnostic(
"Invalid Schema Implementation",
diff --git a/internal/fwserver/write_only_nullification_test.go b/internal/fwserver/write_only_nullification_test.go
index c73851772..47ca9780f 100644
--- a/internal/fwserver/write_only_nullification_test.go
+++ b/internal/fwserver/write_only_nullification_test.go
@@ -539,93 +539,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) {
Optional: true,
WriteOnly: true,
},
- "set-nested-attribute": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- "nested-set-nested-attribute": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- },
- "nested-set-nested-attribute-wo": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- },
- "set-nested-attribute-wo": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- "nested-set-nested-attribute": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- },
- "nested-set-nested-attribute-wo": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- WriteOnly: true,
- },
},
Blocks: map[string]schema.Block{
"single-nested-block": schema.SingleNestedBlock{
@@ -788,92 +701,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) {
},
},
},
- "set-nested-block": schema.SetNestedBlock{
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- "nested-set-nested-attribute": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- },
- "nested-set-nested-attribute-wo": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- WriteOnly: true,
- },
- },
- Blocks: map[string]schema.Block{
- "nested-set-nested-block": schema.SetNestedBlock{
- NestedObject: schema.NestedBlockObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- "nested-set-nested-attribute": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- },
- "nested-set-nested-attribute-wo": schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "nested-string": schema.StringAttribute{
- Optional: true,
- },
- "nested-string-wo": schema.StringAttribute{
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- Optional: true,
- WriteOnly: true,
- },
- },
- },
- },
- },
- },
- },
},
}
input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{
@@ -1053,74 +880,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) {
}),
}),
}),
- "set-nested-attribute": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- }),
- }),
- "set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- }),
- }),
"single-nested-block": tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"nested-string": tftypes.String,
@@ -1257,94 +1016,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) {
}),
}),
}),
- "set-nested-block": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-block": tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- },
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-block": tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- },
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- "nested-set-nested-block": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"),
- }),
- }),
- }),
- }),
- }),
- }),
})
expected := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{
"single-nested-attribute": tftypes.NewValue(tftypes.Object{
@@ -1450,45 +1121,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) {
},
},
}, nil),
- "set-nested-attribute": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, nil),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, nil),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil),
- }),
- }),
- "set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- }, nil),
"single-nested-block": tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"nested-string": tftypes.String,
@@ -1609,84 +1241,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) {
}),
}),
}),
- "set-nested-block": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-block": tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- },
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-block": tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- },
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, nil),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, nil),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil),
- "nested-set-nested-block": tftypes.NewValue(tftypes.Set{
- ElementType: tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- },
- }, []tftypes.Value{
- tftypes.NewValue(tftypes.Object{
- AttributeTypes: map[string]tftypes.Type{
- "nested-string": tftypes.String,
- "nested-string-wo": tftypes.String,
- "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType},
- "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType},
- },
- }, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, nil),
- "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{
- tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{
- "nested-string": tftypes.NewValue(tftypes.String, "foo"),
- "nested-string-wo": tftypes.NewValue(tftypes.String, nil),
- }),
- }),
- "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil),
- }),
- }),
- }),
- }),
})
got, err := tftypes.Transform(input, NullifyWriteOnlyAttributes(context.Background(), s))
if err != nil {
diff --git a/resource/schema/set_attribute.go b/resource/schema/set_attribute.go
index 843f58fb3..b65656dac 100644
--- a/resource/schema/set_attribute.go
+++ b/resource/schema/set_attribute.go
@@ -166,16 +166,6 @@ type SetAttribute struct {
// computed and the value could be altered by other changes then a default
// should be avoided and a plan modifier should be used instead.
Default defaults.Set
-
- // WriteOnly indicates that Terraform will not store this attribute value
- // in the plan or state artifacts.
- // If WriteOnly is true, either Optional or Required must also be true.
- // WriteOnly cannot be set with Computed.
- //
- // This functionality is only supported in Terraform 1.11 and later.
- // Practitioners that choose a value for this attribute with older
- // versions of Terraform will receive an error.
- WriteOnly bool
}
// ApplyTerraform5AttributePathStep returns the result of stepping into a set
@@ -240,9 +230,9 @@ func (a SetAttribute) IsSensitive() bool {
return a.Sensitive
}
-// IsWriteOnly returns the WriteOnly field value.
+// IsWriteOnly returns false as write-only attributes are not supported for sets and set-based data.
func (a SetAttribute) IsWriteOnly() bool {
- return a.WriteOnly
+ return false
}
// SetDefaultValue returns the Default field value.
diff --git a/resource/schema/set_attribute_test.go b/resource/schema/set_attribute_test.go
index ebc7f291b..2795fbde8 100644
--- a/resource/schema/set_attribute_test.go
+++ b/resource/schema/set_attribute_test.go
@@ -394,12 +394,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) {
attribute: schema.SetAttribute{},
expected: false,
},
- "writeOnly": {
- attribute: schema.SetAttribute{
- WriteOnly: true,
- },
- expected: true,
- },
}
for name, testCase := range testCases {
diff --git a/resource/schema/set_nested_attribute.go b/resource/schema/set_nested_attribute.go
index 56c449307..5d408cf1a 100644
--- a/resource/schema/set_nested_attribute.go
+++ b/resource/schema/set_nested_attribute.go
@@ -173,19 +173,6 @@ type SetNestedAttribute struct {
// computed and the value could be altered by other changes then a default
// should be avoided and a plan modifier should be used instead.
Default defaults.Set
-
- // WriteOnly indicates that Terraform will not store this attribute value
- // in the plan or state artifacts.
- // If WriteOnly is true, either Optional or Required must also be true.
- // WriteOnly cannot be set with Computed.
- //
- // If WriteOnly is true for a nested attribute, all of its child attributes
- // must also set WriteOnly to true and no child attribute can be Computed.
- //
- // This functionality is only supported in Terraform 1.11 and later.
- // Practitioners that choose a value for this attribute with older
- // versions of Terraform will receive an error.
- WriteOnly bool
}
// ApplyTerraform5AttributePathStep returns the Attributes field value if step
@@ -268,9 +255,9 @@ func (a SetNestedAttribute) IsSensitive() bool {
return a.Sensitive
}
-// IsWriteOnly returns the WriteOnly field value.
+// IsWriteOnly returns false as write-only attributes are not supported for sets and set-based data.
func (a SetNestedAttribute) IsWriteOnly() bool {
- return a.WriteOnly
+ return false
}
// SetDefaultValue returns the Default field value.
@@ -297,12 +284,8 @@ func (a SetNestedAttribute) ValidateImplementation(ctx context.Context, req fwsc
resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path))
}
- if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) {
- resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path))
- }
-
- if a.IsComputed() && fwschema.ContainsAnyWriteOnlyChildAttributes(a) {
- resp.Diagnostics.Append(fwschema.InvalidComputedNestedAttributeWithWriteOnlyDiag(req.Path))
+ if fwschema.ContainsAnyWriteOnlyChildAttributes(a) {
+ resp.Diagnostics.Append(fwschema.InvalidSetNestedAttributeWithWriteOnlyDiag(req.Path))
}
if a.SetDefaultValue() != nil {
diff --git a/resource/schema/set_nested_attribute_test.go b/resource/schema/set_nested_attribute_test.go
index 0e24d3f72..a0210f410 100644
--- a/resource/schema/set_nested_attribute_test.go
+++ b/resource/schema/set_nested_attribute_test.go
@@ -563,12 +563,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) {
attribute: schema.SetNestedAttribute{},
expected: false,
},
- "writeOnly": {
- attribute: schema.SetNestedAttribute{
- WriteOnly: true,
- },
- expected: true,
- },
}
for name, testCase := range testCases {
@@ -913,9 +907,9 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) {
},
},
},
- "writeOnly-with-child-writeOnly-no-error-diagnostic": {
+ "child-writeOnly-attribute-error-diagnostic": {
attribute: schema.SetNestedAttribute{
- WriteOnly: true,
+ Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"test_attr": schema.StringAttribute{
@@ -928,59 +922,32 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) {
Name: "test",
Path: path.Root("test"),
},
- expected: &fwschema.ValidateImplementationResponse{},
- },
- "writeOnly-without-child-writeOnly-error-diagnostic": {
- attribute: schema.SetNestedAttribute{
- WriteOnly: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "test_attr": schema.StringAttribute{
- Computed: true,
- },
- },
- },
- },
- request: fwschema.ValidateImplementationRequest{
- Name: "test",
- Path: path.Root("test"),
- },
expected: &fwschema.ValidateImplementationResponse{
Diagnostics: diag.Diagnostics{
diag.NewErrorDiagnostic(
"Invalid Schema Implementation",
"When validating the schema, an implementation issue was found. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
- "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+
- "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.",
+ "\"test\" is a set nested attribute that contains a WriteOnly child attribute.\n\n"+
+ "Every child attribute of a set nested attribute must have WriteOnly set to false.",
),
},
},
},
- "computed-without-child-writeOnly-no-error-diagnostic": {
+ "nested-attribute-with-child-writeOnly-attribute-error-diagnostic": {
attribute: schema.SetNestedAttribute{
- Computed: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "test_attr": schema.StringAttribute{
- Computed: true,
- },
- },
- },
- },
- request: fwschema.ValidateImplementationRequest{
- Name: "test",
- Path: path.Root("test"),
- },
- expected: &fwschema.ValidateImplementationResponse{},
- },
- "computed-with-child-writeOnly-error-diagnostic": {
- attribute: schema.SetNestedAttribute{
- Computed: true,
+ Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
- "test_attr": schema.StringAttribute{
- WriteOnly: true,
+ "test_nested_set": schema.SetNestedAttribute{
+ Optional: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "test_attr": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ },
+ },
},
},
},
@@ -995,8 +962,8 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) {
"Invalid Schema Implementation",
"When validating the schema, an implementation issue was found. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
- "\"test\" is a Computed nested attribute that contains a WriteOnly child attribute.\n\n"+
- "Every child attribute of a Computed nested attribute must have WriteOnly set to false.",
+ "\"test\" is a set nested attribute that contains a WriteOnly child attribute.\n\n"+
+ "Every child attribute of a set nested attribute must have WriteOnly set to false.",
),
},
},
diff --git a/resource/schema/set_nested_block.go b/resource/schema/set_nested_block.go
index 78de8afb1..6b4b728bc 100644
--- a/resource/schema/set_nested_block.go
+++ b/resource/schema/set_nested_block.go
@@ -7,6 +7,8 @@ import (
"context"
"fmt"
+ "github.com/hashicorp/terraform-plugin-go/tftypes"
+
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
@@ -15,7 +17,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
- "github.com/hashicorp/terraform-plugin-go/tftypes"
)
// Ensure the implementation satisifies the desired interfaces.
@@ -223,6 +224,10 @@ func (b SetNestedBlock) Type() attr.Type {
// errors or panics. This logic runs during the GetProviderSchema RPC and
// should never include false positives.
func (b SetNestedBlock) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) {
+ if fwschema.BlockContainsAnyWriteOnlyChildAttributes(b) {
+ resp.Diagnostics.Append(fwschema.SetBlockCollectionWithWriteOnlyDiag(req.Path))
+ }
+
if b.CustomType == nil && fwtype.ContainsCollectionWithDynamic(b.Type()) {
resp.Diagnostics.Append(fwtype.BlockCollectionWithDynamicTypeDiag(req.Path))
}
diff --git a/resource/schema/set_nested_block_test.go b/resource/schema/set_nested_block_test.go
index 4aee18302..409e5d0e5 100644
--- a/resource/schema/set_nested_block_test.go
+++ b/resource/schema/set_nested_block_test.go
@@ -10,6 +10,8 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
+ "github.com/hashicorp/terraform-plugin-go/tftypes"
+
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
@@ -20,7 +22,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-go/tftypes"
)
func TestSetNestedBlockApplyTerraform5AttributePathStep(t *testing.T) {
@@ -548,6 +549,32 @@ func TestSetNestedBlockValidateImplementation(t *testing.T) {
},
},
},
+ "nestedobject-write-only": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "test_attr": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ request: fwschema.ValidateImplementationRequest{
+ Name: "test",
+ Path: path.Root("test"),
+ },
+ expected: &fwschema.ValidateImplementationResponse{
+ Diagnostics: diag.Diagnostics{
+ diag.NewErrorDiagnostic(
+ "Invalid Schema Implementation",
+ "When validating the schema, an implementation issue was found. "+
+ "This is always an issue with the provider and should be reported to the provider developers.\n\n"+
+ "\"test\" is a set nested block that contains a WriteOnly child attribute.\n\n"+
+ "Every child attribute within a set nested block must have WriteOnly set to false.",
+ ),
+ },
+ },
+ },
}
for name, testCase := range testCases {
diff --git a/resource/schema/write_only_nested_attribute_validation_test.go b/resource/schema/write_only_nested_attribute_validation_test.go
index acc2ca1de..b14b97931 100644
--- a/resource/schema/write_only_nested_attribute_validation_test.go
+++ b/resource/schema/write_only_nested_attribute_validation_test.go
@@ -7,14 +7,13 @@ import (
"testing"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
- "github.com/hashicorp/terraform-plugin-framework/provider/metaschema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
)
func TestContainsAllWriteOnlyChildAttributes(t *testing.T) {
t.Parallel()
tests := map[string]struct {
- nestedAttr metaschema.NestedAttribute
+ nestedAttr schema.NestedAttribute
expected bool
}{
"empty nested attribute returns true": {
@@ -138,7 +137,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) {
},
},
"set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: false,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"string_attribute": schema.StringAttribute{
@@ -203,7 +201,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) {
},
"set nested attribute with multiple writeOnly child attributes returns true": {
nestedAttr: schema.SetNestedAttribute{
- WriteOnly: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"string_attribute": schema.StringAttribute{
@@ -219,7 +216,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) {
},
"set nested attribute with one non-writeOnly child attribute returns false": {
nestedAttr: schema.SetNestedAttribute{
- WriteOnly: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"string_attribute": schema.StringAttribute{
@@ -233,107 +229,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) {
},
expected: false,
},
- "set nested attribute with writeOnly child nested attributes returns true": {
- nestedAttr: schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: true,
- },
- },
- },
- },
- },
- },
- },
- expected: true,
- },
- "set nested attribute with non-writeOnly child nested attribute returns false": {
- nestedAttr: schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: false,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: true,
- },
- },
- },
- },
- },
- },
- },
- expected: false,
- },
- "set nested attribute with one non-writeOnly child nested attribute returns false": {
- nestedAttr: schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: true,
- },
- },
- },
- },
- "list_nested_attribute": schema.ListNestedAttribute{
- WriteOnly: false,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: true,
- },
- },
- },
- },
- },
- },
- },
- expected: false,
- },
- "set nested attribute with one non-writeOnly nested child attribute returns false": {
- nestedAttr: schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: false,
- },
- },
- },
- },
- },
- },
- },
- expected: false,
- },
"map nested attribute with writeOnly child attribute returns true": {
nestedAttr: schema.MapNestedAttribute{
NestedObject: schema.NestedAttributeObject{
@@ -634,7 +529,7 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) {
func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) {
t.Parallel()
tests := map[string]struct {
- nestedAttr metaschema.NestedAttribute
+ nestedAttr schema.NestedAttribute
expected bool
}{
"empty nested attribute returns false": {
@@ -763,7 +658,6 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) {
},
},
"set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: false,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"string_attribute": schema.StringAttribute{
@@ -863,16 +757,15 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) {
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"string_attribute": schema.StringAttribute{
- WriteOnly: false,
- Computed: true,
+ WriteOnly: true,
+ Optional: true,
},
"float32_attribute": schema.Float32Attribute{
- WriteOnly: false,
- Computed: true,
+ WriteOnly: true,
+ Optional: true,
},
},
},
@@ -882,7 +775,7 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) {
},
expected: true,
},
- "set nested attribute with non-writeOnly child nested attribute returns false": {
+ "set nested attribute with no writeOnly child nested attributes returns false": {
nestedAttr: schema.SetNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
@@ -905,51 +798,11 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) {
},
expected: false,
},
- "set nested attribute with one non-writeOnly child nested attribute returns true": {
- nestedAttr: schema.SetNestedAttribute{
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: false,
- Computed: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: false,
- Computed: true,
- },
- },
- },
- },
- "list_nested_attribute": schema.ListNestedAttribute{
- WriteOnly: false,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "string_attribute": schema.StringAttribute{
- WriteOnly: false,
- Computed: true,
- },
- "float32_attribute": schema.Float32Attribute{
- WriteOnly: false,
- Computed: true,
- },
- },
- },
- },
- },
- },
- },
- expected: true,
- },
- "set nested attribute with one non-writeOnly nested child attribute returns true": {
+ "set nested attribute with one writeOnly nested child attribute returns true": {
nestedAttr: schema.SetNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"set_nested_attribute": schema.SetNestedAttribute{
- WriteOnly: false,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"string_attribute": schema.StringAttribute{
@@ -1279,3 +1132,670 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) {
})
}
}
+
+func TestBlockContainsAnyWriteOnlyChildAttributes(t *testing.T) {
+ t.Parallel()
+ tests := map[string]struct {
+ block schema.Block
+ expected bool
+ }{
+ "empty nested block returns false": {
+ block: schema.ListNestedBlock{},
+ expected: false,
+ },
+ "list nested block with writeOnly child attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list nested block with non-writeOnly child attribute returns false": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ expected: false,
+ },
+ "list nested block with multiple writeOnly child attributes returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list nested block with one non-writeOnly child attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list nested block with writeOnly child nested attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list nested block with non-writeOnly child nested attribute returns false": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: false,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: false,
+ },
+ "list nested block with one non-writeOnly child nested attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list nested block with one non-writeOnly nested child attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: false,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list double-nested block with top-level writeOnly nested child attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: false,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "double_list_nested_block": schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "list double-nested block with one non-writeOnly nested child attribute returns true": {
+ block: schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: false,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "double_list_nested_block": schema.ListNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: false,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set nested block with non-writeOnly child attribute returns false": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ expected: false,
+ },
+ "set nested block with multiple writeOnly child attributes returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set nested block with one non-writeOnly child attribute returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set nested block with writeOnly child nested attribute returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set nested block with non-writeOnly child nested attribute returns false": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: false,
+ },
+ "set nested block with one non-writeOnly child nested attribute returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set nested block with one non-writeOnly nested child attribute returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set double-nested block with top-level writeOnly nested child attribute returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "double_set_nested_block": schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "set double-nested block with one non-writeOnly nested child attribute returns true": {
+ block: schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "double_set_nested_block": schema.SetNestedBlock{
+ NestedObject: schema.NestedBlockObject{
+ Attributes: map[string]schema.Attribute{
+ "set_nested_attribute": schema.SetNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "single nested block with non-writeOnly child attribute returns false": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ expected: false,
+ },
+ "single nested block with multiple writeOnly child attributes returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ expected: true,
+ },
+ "single nested block with one non-writeOnly child attribute returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ expected: true,
+ },
+ "single nested block with writeOnly child nested attribute returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "single nested block with non-writeOnly child nested attribute returns false": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "single_nested_attribute": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ expected: false,
+ },
+ "single nested block with one non-writeOnly child nested attribute returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "single_nested_attribute": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ "list_nested_attribute": schema.ListNestedAttribute{
+ WriteOnly: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "single nested block with one non-writeOnly nested child attribute returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "single_nested_attribute": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "single double-nested block with top-level writeOnly nested child attribute returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "single_nested_attribute": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "double_single_nested_block": schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ "single double-nested block with one non-writeOnly nested child attribute returns true": {
+ block: schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "single_nested_attribute": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: false,
+ },
+ },
+ },
+ },
+ Blocks: map[string]schema.Block{
+ "double_single_nested_block": schema.SingleNestedBlock{
+ Attributes: map[string]schema.Attribute{
+ "single_nested_attribute": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "string_attribute": schema.StringAttribute{
+ WriteOnly: false,
+ },
+ "float32_attribute": schema.Float32Attribute{
+ WriteOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ }
+ for name, tt := range tests {
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+ if got := fwschema.BlockContainsAnyWriteOnlyChildAttributes(tt.block); got != tt.expected {
+ t.Errorf("BlockContainsAllWriteOnlyChildAttributes() = %v, want %v", got, tt.expected)
+ }
+ })
+ }
+}
diff --git a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx
index 9e4f8afe9..01ce7e51f 100644
--- a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx
+++ b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx
@@ -159,20 +159,6 @@ The [`setplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin
Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state.
-### WriteOnly
-
-
-
- Only managed resources implement this concept.
-
-
-
-Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments).
-Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral)
-and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later.
-
-If a nested attribute has the `WriteOnly` field set, all child attributes must also have `WriteOnly` set.
-
### Validation
Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types).
diff --git a/website/docs/plugin/framework/handling-data/attributes/set.mdx b/website/docs/plugin/framework/handling-data/attributes/set.mdx
index 9f3d7dcd3..512489aad 100644
--- a/website/docs/plugin/framework/handling-data/attributes/set.mdx
+++ b/website/docs/plugin/framework/handling-data/attributes/set.mdx
@@ -128,18 +128,6 @@ The [`setplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin
Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state.
-### WriteOnly
-
-
-
- Only managed resources implement this concept.
-
-
-
-Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments).
-Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral)
-and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later.
-
### Validation
Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types).
diff --git a/website/docs/plugin/framework/resources/write-only-arguments.mdx b/website/docs/plugin/framework/resources/write-only-arguments.mdx
index 79549b9cb..ee61e2b99 100644
--- a/website/docs/plugin/framework/resources/write-only-arguments.mdx
+++ b/website/docs/plugin/framework/resources/write-only-arguments.mdx
@@ -15,7 +15,9 @@ which should either use the value by making the appropriate change to the API or
The following are high level differences between `Required`/`Optional` arguments and write-only arguments:
-- Write-only arguments can accept ephemeral and non-ephemeral values
+- Write-only arguments can accept ephemeral and non-ephemeral values.
+
+- Write-only arguments cannot be used with set attributes, set nested attributes, and set nested blocks.
- Write-only argument values are only available in the configuration. The prior state, planned state, and final state values for
write-only arguments should always be `null`.
@@ -25,15 +27,15 @@ write-only arguments should always be `null`.
- Write-only argument values cannot produce a Terraform plan difference.
- This is because the prior state value for a write-only argument will always be `null` and the planned/final state value will also be `null`, therefore, it cannot produce a diff on its own.
- - The one exception to this case is if the write-only argument is added to `requires_replace` during Plan Modification (i.e., using the [`RequiresReplace()`](/terraform/plugin/framework/resources/plan-modification#requiresreplace) plan modifier), in that case, the write-only argument will always cause a diff/trigger a resource recreation
+ - The one exception to this case is if the write-only argument is added to `requires_replace` during Plan Modification (i.e., using the [`RequiresReplace()`](/terraform/plugin/framework/resources/plan-modification#requiresreplace) plan modifier), in that case, the write-only argument will always cause a diff/trigger a resource recreation.
- Since write-only arguments can accept ephemeral values, write-only argument configuration values are not expected to be consistent between plan and apply.
## Schema
An attribute can be made write-only by setting the `WriteOnly` field to `true` in the schema. Attributes with `WriteOnly` set to `true` must also have
-one of `Required` or `Optional` set to `true`. If a nested attribute has `WriteOnly` set to `true`, all child attributes must also have `WriteOnly` set to `true`.
-`Computed` cannot be set to true for write-only arguments.
+one of `Required` or `Optional` set to `true`. If a list nested, map nested, or single nested attribute has `WriteOnly` set to `true`, all child attributes must also have `WriteOnly` set to `true`.
+A set nested block cannot have any child attributes with `WriteOnly` set to `true`. `Computed` cannot be set to true for write-only arguments.
**Schema example:**