Skip to content

Commit 9475049

Browse files
authored
Disallow null resource identities (#1193)
* wip: add a test with null identity * update: disallow null resource identities * create: disallow null resource identities * read: disallow null resource identities * use hashicorp/terraform-plugin-go#541 * use terraform-plugin-go@main * add changelog
1 parent bd26b78 commit 9475049

File tree

7 files changed

+282
-0
lines changed

7 files changed

+282
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: BUG FIXES
2+
body: 'all: Added an additional validation check to ensure the resource identity object is not null.'
3+
time: 2025-09-22T17:53:41.039656-04:00
4+
custom:
5+
Issue: "1193"

internal/fwserver/server_createresource.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ func (s *Server) CreateResource(ctx context.Context, req *CreateResourceRequest,
169169
return
170170
}
171171

172+
if req.IdentitySchema != nil {
173+
if resp.NewIdentity.Raw.IsFullyNull() {
174+
resp.Diagnostics.AddError(
175+
"Missing Resource Identity After Create",
176+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource create. "+
177+
"This is always an issue in the Terraform Provider and should be reported to the provider developers.",
178+
)
179+
return
180+
}
181+
}
182+
172183
semanticEqualityReq := SchemaSemanticEqualityRequest{
173184
PriorData: fwschemadata.Data{
174185
Description: fwschemadata.DataDescriptionPlan,

internal/fwserver/server_createresource_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,106 @@ func TestServerCreateResource(t *testing.T) {
580580
Private: testEmptyPrivate,
581581
},
582582
},
583+
"response-invalid-nil-identity": {
584+
server: &fwserver.Server{
585+
Provider: &testprovider.Provider{},
586+
},
587+
request: &fwserver.CreateResourceRequest{
588+
PlannedState: &tfsdk.Plan{
589+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
590+
"test_computed": tftypes.NewValue(tftypes.String, nil),
591+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
592+
}),
593+
Schema: testSchema,
594+
},
595+
IdentitySchema: testIdentitySchema,
596+
ResourceSchema: testSchema,
597+
Resource: &testprovider.ResourceWithIdentity{
598+
Resource: &testprovider.Resource{
599+
CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
600+
resp.Identity.Raw = tftypes.NewValue(testIdentitySchema.Type().TerraformType(ctx), nil)
601+
// Prevent missing resource state error diagnostic
602+
var data testSchemaData
603+
604+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
605+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
606+
},
607+
},
608+
},
609+
},
610+
expectedResponse: &fwserver.CreateResourceResponse{
611+
Diagnostics: []diag.Diagnostic{
612+
diag.NewErrorDiagnostic(
613+
"Missing Resource Identity After Create",
614+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource create. "+
615+
"This is always an issue in the Terraform Provider and should be reported to the provider developers.",
616+
),
617+
},
618+
NewIdentity: &tfsdk.ResourceIdentity{
619+
Raw: tftypes.NewValue(testIdentityType, nil),
620+
Schema: testIdentitySchema,
621+
},
622+
NewState: &tfsdk.State{
623+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
624+
"test_computed": tftypes.NewValue(tftypes.String, nil),
625+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
626+
}),
627+
Schema: testSchema,
628+
},
629+
Private: testEmptyPrivate,
630+
},
631+
},
632+
"response-invalid-null-identity": {
633+
server: &fwserver.Server{
634+
Provider: &testprovider.Provider{},
635+
},
636+
request: &fwserver.CreateResourceRequest{
637+
PlannedState: &tfsdk.Plan{
638+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
639+
"test_computed": tftypes.NewValue(tftypes.String, nil),
640+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
641+
}),
642+
Schema: testSchema,
643+
},
644+
IdentitySchema: testIdentitySchema,
645+
ResourceSchema: testSchema,
646+
Resource: &testprovider.ResourceWithIdentity{
647+
Resource: &testprovider.Resource{
648+
CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
649+
resp.Diagnostics.Append(resp.Identity.Set(ctx, testIdentitySchemaData{})...)
650+
// Prevent missing resource state error diagnostic
651+
var data testSchemaData
652+
653+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
654+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
655+
},
656+
},
657+
},
658+
},
659+
expectedResponse: &fwserver.CreateResourceResponse{
660+
Diagnostics: []diag.Diagnostic{
661+
diag.NewErrorDiagnostic(
662+
"Missing Resource Identity After Create",
663+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource create. "+
664+
"This is always an issue in the Terraform Provider and should be reported to the provider developers.",
665+
),
666+
},
667+
NewIdentity: &tfsdk.ResourceIdentity{
668+
Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{
669+
"test_id": tftypes.NewValue(tftypes.String, nil),
670+
}),
671+
Schema: testIdentitySchema,
672+
},
673+
NewState: &tfsdk.State{
674+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
675+
"test_computed": tftypes.NewValue(tftypes.String, nil),
676+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
677+
}),
678+
Schema: testSchema,
679+
},
680+
Private: testEmptyPrivate,
681+
},
682+
},
583683
"response-invalid-newidentity": {
584684
server: &fwserver.Server{
585685
Provider: &testprovider.Provider{},

internal/fwserver/server_readresource.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res
198198
}
199199
}
200200

201+
if req.IdentitySchema != nil {
202+
if resp.NewIdentity.Raw.IsFullyNull() {
203+
resp.Diagnostics.AddError(
204+
"Missing Resource Identity After Read",
205+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource read. "+
206+
"This is always an issue in the Terraform Provider and should be reported to the provider developers.",
207+
)
208+
return
209+
}
210+
}
211+
201212
semanticEqualityReq := SchemaSemanticEqualityRequest{
202213
PriorData: fwschemadata.Data{
203214
Description: fwschemadata.DataDescriptionState,

internal/fwserver/server_readresource_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ func TestServerReadResource(t *testing.T) {
177177
Schema: testIdentitySchema,
178178
}
179179

180+
testEmptyIdentity := &tfsdk.ResourceIdentity{
181+
Schema: testIdentitySchema,
182+
Raw: tftypes.NewValue(testIdentitySchema.Type().TerraformType(context.Background()), nil),
183+
}
184+
180185
testNewStateRemoved := &tfsdk.State{
181186
Raw: tftypes.NewValue(testType, nil),
182187
Schema: testSchema,
@@ -649,6 +654,35 @@ func TestServerReadResource(t *testing.T) {
649654
Private: testEmptyPrivate,
650655
},
651656
},
657+
"response-invalid-nil-identity": {
658+
server: &fwserver.Server{
659+
Provider: &testprovider.Provider{},
660+
},
661+
request: &fwserver.ReadResourceRequest{
662+
CurrentState: testCurrentState,
663+
CurrentIdentity: nil,
664+
IdentitySchema: testIdentitySchema,
665+
Resource: &testprovider.ResourceWithIdentity{
666+
Resource: &testprovider.Resource{
667+
ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
668+
resp.Identity = req.Identity
669+
},
670+
},
671+
},
672+
},
673+
expectedResponse: &fwserver.ReadResourceResponse{
674+
Diagnostics: diag.Diagnostics{
675+
diag.NewErrorDiagnostic(
676+
"Missing Resource Identity After Read",
677+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource read. "+
678+
"This is always an issue in the Terraform Provider and should be reported to the provider developers.",
679+
),
680+
},
681+
NewState: testCurrentState,
682+
NewIdentity: testEmptyIdentity,
683+
Private: testEmptyPrivate,
684+
},
685+
},
652686
"response-identity-valid-update-null-currentidentity": {
653687
server: &fwserver.Server{
654688
Provider: &testprovider.Provider{},

internal/fwserver/server_updateresource.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,17 @@ func (s *Server) UpdateResource(ctx context.Context, req *UpdateResourceRequest,
199199
}
200200
}
201201

202+
if req.IdentitySchema != nil {
203+
if resp.NewIdentity.Raw.IsFullyNull() {
204+
resp.Diagnostics.AddError(
205+
"Missing Resource Identity After Update",
206+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource update. "+
207+
"This is always an issue in the Terraform Provider and should be reported to the provider developers.",
208+
)
209+
return
210+
}
211+
}
212+
202213
semanticEqualityReq := SchemaSemanticEqualityRequest{
203214
PriorData: fwschemadata.Data{
204215
Description: fwschemadata.DataDescriptionPlan,

internal/fwserver/server_updateresource_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,116 @@ func TestServerUpdateResource(t *testing.T) {
927927
Private: testEmptyPrivate,
928928
},
929929
},
930+
"response-new-identity-nil": {
931+
server: &fwserver.Server{
932+
Provider: &testprovider.Provider{},
933+
},
934+
request: &fwserver.UpdateResourceRequest{
935+
PlannedState: &tfsdk.Plan{
936+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
937+
"test_computed": tftypes.NewValue(tftypes.String, nil),
938+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
939+
}),
940+
Schema: testSchema,
941+
},
942+
PlannedIdentity: &tfsdk.ResourceIdentity{
943+
Raw: tftypes.NewValue(testIdentityType, nil),
944+
Schema: testIdentitySchema,
945+
},
946+
IdentitySchema: testIdentitySchema,
947+
ResourceSchema: testSchema,
948+
Resource: &testprovider.ResourceWithIdentity{
949+
Resource: &testprovider.Resource{
950+
UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
951+
var data testSchemaData
952+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
953+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
954+
955+
resp.Identity.Raw = tftypes.NewValue(testIdentityType, nil)
956+
957+
},
958+
},
959+
},
960+
},
961+
expectedResponse: &fwserver.UpdateResourceResponse{
962+
Diagnostics: []diag.Diagnostic{
963+
diag.NewErrorDiagnostic(
964+
"Missing Resource Identity After Update",
965+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource update. This is always an issue in the Terraform Provider and should be reported to the provider developers.",
966+
),
967+
},
968+
NewIdentity: &tfsdk.ResourceIdentity{
969+
Raw: tftypes.NewValue(testIdentityType, nil),
970+
Schema: testIdentitySchema,
971+
},
972+
NewState: &tfsdk.State{
973+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
974+
"test_computed": tftypes.NewValue(tftypes.String, nil),
975+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
976+
}),
977+
Schema: testSchema,
978+
},
979+
Private: testEmptyPrivate,
980+
},
981+
},
982+
"response-new-identity-null": {
983+
server: &fwserver.Server{
984+
Provider: &testprovider.Provider{},
985+
},
986+
request: &fwserver.UpdateResourceRequest{
987+
PlannedState: &tfsdk.Plan{
988+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
989+
"test_computed": tftypes.NewValue(tftypes.String, nil),
990+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
991+
}),
992+
Schema: testSchema,
993+
},
994+
PlannedIdentity: &tfsdk.ResourceIdentity{
995+
Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{
996+
"test_id": tftypes.NewValue(tftypes.String, nil),
997+
}),
998+
Schema: testIdentitySchema,
999+
},
1000+
IdentitySchema: testIdentitySchema,
1001+
ResourceSchema: testSchema,
1002+
Resource: &testprovider.ResourceWithIdentity{
1003+
Resource: &testprovider.Resource{
1004+
UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
1005+
var data testSchemaData
1006+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
1007+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
1008+
1009+
var identityData testIdentitySchemaData
1010+
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
1011+
resp.Diagnostics.Append(resp.Identity.Set(ctx, &identityData)...)
1012+
1013+
},
1014+
},
1015+
},
1016+
},
1017+
expectedResponse: &fwserver.UpdateResourceResponse{
1018+
Diagnostics: []diag.Diagnostic{
1019+
diag.NewErrorDiagnostic(
1020+
"Missing Resource Identity After Update",
1021+
"The Terraform Provider unexpectedly returned no resource identity data after having no errors in the resource update. This is always an issue in the Terraform Provider and should be reported to the provider developers.",
1022+
),
1023+
},
1024+
NewIdentity: &tfsdk.ResourceIdentity{
1025+
Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{
1026+
"test_id": tftypes.NewValue(tftypes.String, nil),
1027+
}),
1028+
Schema: testIdentitySchema,
1029+
},
1030+
NewState: &tfsdk.State{
1031+
Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{
1032+
"test_computed": tftypes.NewValue(tftypes.String, nil),
1033+
"test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"),
1034+
}),
1035+
Schema: testSchema,
1036+
},
1037+
Private: testEmptyPrivate,
1038+
},
1039+
},
9301040
"response-newstate-semantic-equality": {
9311041
server: &fwserver.Server{
9321042
Provider: &testprovider.Provider{},

0 commit comments

Comments
 (0)