@@ -15,6 +15,7 @@ import (
15
15
awstypes "github.com/aws/aws-sdk-go-v2/service/datazone/types"
16
16
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
17
17
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
18
+ "github.com/hashicorp/terraform-plugin-framework/diag"
18
19
"github.com/hashicorp/terraform-plugin-framework/path"
19
20
"github.com/hashicorp/terraform-plugin-framework/resource"
20
21
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -142,6 +143,7 @@ func (r *environmentResource) Schema(ctx context.Context, req resource.SchemaReq
142
143
CustomType : fwtypes.NewListNestedObjectTypeOf [resourceUserParametersData ](ctx ),
143
144
PlanModifiers : []planmodifier.List {
144
145
listplanmodifier .UseStateForUnknown (),
146
+ listplanmodifier .RequiresReplace (),
145
147
},
146
148
NestedObject : schema.NestedBlockObject {
147
149
Attributes : map [string ]schema.Attribute {
@@ -219,7 +221,7 @@ func (r *environmentResource) Create(ctx context.Context, req resource.CreateReq
219
221
return
220
222
}
221
223
222
- resp . Diagnostics . Append ( fwflex . Flatten ( ctx , output , & state , fwflex . WithIgnoredFieldNames ([] string { "UserParameters" })) ... )
224
+ flattenEnvironment ( ctx , output , & state , & resp . Diagnostics )
223
225
if resp .Diagnostics .HasError () {
224
226
return
225
227
}
@@ -252,21 +254,14 @@ func (r *environmentResource) Read(ctx context.Context, req resource.ReadRequest
252
254
return
253
255
}
254
256
255
- resp .Diagnostics .Append (fwflex .Flatten (ctx , out , & state , fwflex .WithIgnoredFieldNamesAppend ("UserParameters" ),
256
- fwflex .WithFieldNamePrefix ("Environment" ),
257
- )... )
258
-
257
+ flattenEnvironment (ctx , out , & state , & resp .Diagnostics )
259
258
if resp .Diagnostics .HasError () {
260
259
return
261
260
}
262
261
263
- state .AccountIdentifier = fwflex .StringToFramework (ctx , out .AwsAccountId )
264
- state .AccountRegion = fwflex .StringToFramework (ctx , out .AwsAccountRegion )
265
- state .ProjectIdentifier = fwflex .StringToFramework (ctx , out .ProjectId )
266
- state .ProfileIdentifier = fwflex .StringToFramework (ctx , out .EnvironmentProfileId )
267
-
268
262
resp .Diagnostics .Append (resp .State .Set (ctx , & state )... )
269
263
}
264
+
270
265
func (r * environmentResource ) Update (ctx context.Context , req resource.UpdateRequest , resp * resource.UpdateResponse ) {
271
266
conn := r .Meta ().DataZoneClient (ctx )
272
267
@@ -277,18 +272,35 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
277
272
return
278
273
}
279
274
280
- if ! plan .Name .Equal (state .Name ) ||
281
- ! plan .Description .Equal (state .Description ) ||
282
- ! plan .GlossaryTerms .Equal (state .GlossaryTerms ) {
283
- in := & datazone.UpdateEnvironmentInput {}
284
- resp .Diagnostics .Append (fwflex .Expand (ctx , plan , in )... )
275
+ var (
276
+ needsUpdate bool
277
+ )
278
+ input := datazone.UpdateEnvironmentInput {
279
+ DomainIdentifier : plan .DomainIdentifier .ValueStringPointer (),
280
+ Identifier : plan .Id .ValueStringPointer (),
281
+ }
282
+
283
+ if ! plan .Name .Equal (state .Name ) {
284
+ needsUpdate = true
285
+ input .Name = plan .Name .ValueStringPointer ()
286
+ }
285
287
286
- if resp .Diagnostics .HasError () {
288
+ if ! plan .Description .Equal (state .Description ) {
289
+ needsUpdate = true
290
+ input .Description = plan .Description .ValueStringPointer ()
291
+ }
292
+
293
+ if ! plan .GlossaryTerms .Equal (state .GlossaryTerms ) {
294
+ needsUpdate = true
295
+ d := fwflex .Expand (ctx , & plan .GlossaryTerms , & input .GlossaryTerms )
296
+ resp .Diagnostics .Append (d ... )
297
+ if d .HasError () {
287
298
return
288
299
}
289
- in . Identifier = state . Id . ValueStringPointer ()
300
+ }
290
301
291
- out , err := conn .UpdateEnvironment (ctx , in )
302
+ if needsUpdate {
303
+ out , err := conn .UpdateEnvironment (ctx , & input )
292
304
if err != nil {
293
305
resp .Diagnostics .AddError (
294
306
create .ProblemStandardMessage (names .DataZone , create .ErrActionUpdating , ResNameEnvironment , plan .Id .String (), err ),
@@ -306,7 +318,7 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
306
318
}
307
319
308
320
updateTimeout := r .UpdateTimeout (ctx , plan .Timeouts )
309
- _ , err = waitEnvironmentUpdated (ctx , conn , plan .DomainIdentifier .ValueString (), plan .Id .ValueString (), updateTimeout )
321
+ output , err : = waitEnvironmentUpdated (ctx , conn , plan .DomainIdentifier .ValueString (), plan .Id .ValueString (), updateTimeout )
310
322
311
323
if err != nil {
312
324
resp .Diagnostics .AddError (
@@ -315,6 +327,11 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
315
327
)
316
328
return
317
329
}
330
+
331
+ flattenEnvironment (ctx , output , & plan , & resp .Diagnostics )
332
+ if resp .Diagnostics .HasError () {
333
+ return
334
+ }
318
335
}
319
336
320
337
resp .Diagnostics .Append (resp .State .Set (ctx , & plan )... )
@@ -375,8 +392,8 @@ func (r *environmentResource) ImportState(ctx context.Context, req resource.Impo
375
392
376
393
func waitEnvironmentCreated (ctx context.Context , conn * datazone.Client , domainId string , id string , timeout time.Duration ) (* datazone.GetEnvironmentOutput , error ) {
377
394
stateConf := & retry.StateChangeConf {
378
- Pending : enum.Slice [awstypes. EnvironmentStatus ] (awstypes .EnvironmentStatusCreating ),
379
- Target : enum.Slice [awstypes. EnvironmentStatus ] (awstypes .EnvironmentStatusActive ),
395
+ Pending : enum .Slice (awstypes .EnvironmentStatusCreating ),
396
+ Target : enum .Slice (awstypes .EnvironmentStatusActive ),
380
397
Refresh : statusEnvironment (ctx , conn , domainId , id ),
381
398
Timeout : timeout ,
382
399
NotFoundChecks : 20 ,
@@ -385,7 +402,7 @@ func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId
385
402
386
403
outputRaw , err := stateConf .WaitForStateContext (ctx )
387
404
if out , ok := outputRaw .(* datazone.GetEnvironmentOutput ); ok {
388
- if status , deployment := out .Status , out .LastDeployment ; status == awstypes .EnvironmentStatusCreateFailed && deployment != nil {
405
+ if status , deployment := out .Status , out .LastDeployment ; ( status == awstypes .EnvironmentStatusCreateFailed || status == awstypes . EnvironmentStatusValidationFailed ) && deployment != nil {
389
406
tfresource .SetLastError (err , fmt .Errorf ("%s: %s" , status , aws .ToString (deployment .FailureReason .Message )))
390
407
}
391
408
return out , err
@@ -396,8 +413,8 @@ func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId
396
413
397
414
func waitEnvironmentUpdated (ctx context.Context , conn * datazone.Client , domainId string , id string , timeout time.Duration ) (* datazone.GetEnvironmentOutput , error ) {
398
415
stateConf := & retry.StateChangeConf {
399
- Pending : enum.Slice [awstypes. EnvironmentStatus ] (awstypes .EnvironmentStatusUpdating ),
400
- Target : enum.Slice [awstypes. EnvironmentStatus ] (awstypes .EnvironmentStatusActive ),
416
+ Pending : enum .Slice (awstypes .EnvironmentStatusUpdating ),
417
+ Target : enum .Slice (awstypes .EnvironmentStatusActive ),
401
418
Refresh : statusEnvironment (ctx , conn , domainId , id ),
402
419
Timeout : timeout ,
403
420
NotFoundChecks : 20 ,
@@ -417,10 +434,12 @@ func waitEnvironmentUpdated(ctx context.Context, conn *datazone.Client, domainId
417
434
418
435
func waitEnvironmentDeleted (ctx context.Context , conn * datazone.Client , domainId string , id string , timeout time.Duration ) (* datazone.GetEnvironmentOutput , error ) {
419
436
stateConf := & retry.StateChangeConf {
420
- Pending : enum .Slice (awstypes .EnvironmentStatusDeleting , awstypes .EnvironmentStatusActive ),
421
- Target : []string {},
422
- Refresh : statusEnvironment (ctx , conn , domainId , id ),
423
- Timeout : timeout ,
437
+ Pending : enum .Slice (awstypes .EnvironmentStatusActive , awstypes .EnvironmentStatusDeleting , awstypes .EnvironmentStatusDeleted ),
438
+ Target : []string {},
439
+ Refresh : statusEnvironment (ctx , conn , domainId , id ),
440
+ Timeout : timeout ,
441
+ Delay : 10 * time .Second ,
442
+ PollInterval : 5 * time .Second ,
424
443
}
425
444
426
445
outputRaw , err := stateConf .WaitForStateContext (ctx )
@@ -479,7 +498,7 @@ type environmentResourceModel struct {
479
498
framework.WithRegionModel
480
499
AccountIdentifier types.String `tfsdk:"account_identifier"`
481
500
AccountRegion types.String `tfsdk:"account_region"`
482
- BlueprintId types.String `tfsdk:"blueprint_identifier"`
501
+ BlueprintIdentifier types.String `tfsdk:"blueprint_identifier"`
483
502
CreatedAt timetypes.RFC3339 `tfsdk:"created_at"`
484
503
CreatedBy types.String `tfsdk:"created_by"`
485
504
Description types.String `tfsdk:"description"`
@@ -496,6 +515,23 @@ type environmentResourceModel struct {
496
515
UserParameters fwtypes.ListNestedObjectValueOf [resourceUserParametersData ] `tfsdk:"user_parameters"`
497
516
}
498
517
518
+ // nosemgrep:ci.semgrep.framework.manual-flattener-functions
519
+ func flattenEnvironment (ctx context.Context , apiObject * datazone.GetEnvironmentOutput , model * environmentResourceModel , diags * diag.Diagnostics ) {
520
+ diags .Append (fwflex .Flatten (ctx , apiObject , model , fwflex .WithIgnoredFieldNamesAppend ("UserParameters" ))... )
521
+
522
+ model .AccountIdentifier = fwflex .StringToFramework (ctx , apiObject .AwsAccountId )
523
+ model .AccountRegion = fwflex .StringToFramework (ctx , apiObject .AwsAccountRegion )
524
+ model .BlueprintIdentifier = fwflex .StringToFramework (ctx , apiObject .EnvironmentBlueprintId )
525
+ model .ProfileIdentifier = fwflex .StringToFramework (ctx , apiObject .EnvironmentProfileId )
526
+ model .ProjectIdentifier = fwflex .StringToFramework (ctx , apiObject .ProjectId )
527
+
528
+ if model .UserParameters .IsNull () { // Import
529
+ importUserParameters (ctx , & model .UserParameters , apiObject .UserParameters , diags )
530
+ } else {
531
+ populateUserParameters (ctx , & model .UserParameters , apiObject .UserParameters , diags )
532
+ }
533
+ }
534
+
499
535
type resourceLastDeployment struct {
500
536
DeploymentId types.String `tfsdk:"deployment_id"`
501
537
DeploymentStatus types.String `tfsdk:"deployment_status"`
@@ -521,3 +557,38 @@ type resourceUserParametersData struct {
521
557
Name types.String `tfsdk:"name"`
522
558
Value types.String `tfsdk:"value"`
523
559
}
560
+
561
+ func importUserParameters (ctx context.Context , stateUserParams * fwtypes.ListNestedObjectValueOf [resourceUserParametersData ], userParameters []awstypes.CustomParameter , diags * diag.Diagnostics ) {
562
+ params := make ([]resourceUserParametersData , 0 , len (userParameters ))
563
+ for _ , param := range userParameters {
564
+ // If `DefaultValue` is nil, no value has been set
565
+ if param .DefaultValue != nil {
566
+ params = append (params , resourceUserParametersData {
567
+ Name : fwflex .StringToFramework (ctx , param .KeyName ),
568
+ Value : fwflex .StringToFramework (ctx , param .DefaultValue ),
569
+ })
570
+ }
571
+ }
572
+ s , d := fwtypes .NewListNestedObjectValueOfValueSlice (ctx , params )
573
+ diags .Append (d ... )
574
+ if d .HasError () {
575
+ return
576
+ }
577
+ * stateUserParams = s
578
+ }
579
+
580
+ func populateUserParameters (ctx context.Context , stateUserParams * fwtypes.ListNestedObjectValueOf [resourceUserParametersData ], userParameters []awstypes.CustomParameter , diags * diag.Diagnostics ) {
581
+ params , d := stateUserParams .ToSlice (ctx )
582
+ diags .Append (d ... )
583
+ if d .HasError () {
584
+ return
585
+ }
586
+ for _ , p := range params {
587
+ for _ , up := range userParameters {
588
+ if p .Name .ValueString () == aws .ToString (up .KeyName ) {
589
+ p .Value = fwflex .StringToFramework (ctx , up .DefaultValue )
590
+ break
591
+ }
592
+ }
593
+ }
594
+ }
0 commit comments