@@ -15,6 +15,7 @@ import (
1515 awstypes "github.com/aws/aws-sdk-go-v2/service/datazone/types"
1616 "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
1717 "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
18+ "github.com/hashicorp/terraform-plugin-framework/diag"
1819 "github.com/hashicorp/terraform-plugin-framework/path"
1920 "github.com/hashicorp/terraform-plugin-framework/resource"
2021 "github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -142,6 +143,7 @@ func (r *environmentResource) Schema(ctx context.Context, req resource.SchemaReq
142143 CustomType : fwtypes.NewListNestedObjectTypeOf [resourceUserParametersData ](ctx ),
143144 PlanModifiers : []planmodifier.List {
144145 listplanmodifier .UseStateForUnknown (),
146+ listplanmodifier .RequiresReplace (),
145147 },
146148 NestedObject : schema.NestedBlockObject {
147149 Attributes : map [string ]schema.Attribute {
@@ -219,7 +221,7 @@ func (r *environmentResource) Create(ctx context.Context, req resource.CreateReq
219221 return
220222 }
221223
222- resp . Diagnostics . Append ( fwflex . Flatten ( ctx , output , & state , fwflex . WithIgnoredFieldNames ([] string { "UserParameters" })) ... )
224+ flattenEnvironment ( ctx , output , & state , & resp . Diagnostics )
223225 if resp .Diagnostics .HasError () {
224226 return
225227 }
@@ -252,21 +254,14 @@ func (r *environmentResource) Read(ctx context.Context, req resource.ReadRequest
252254 return
253255 }
254256
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 )
259258 if resp .Diagnostics .HasError () {
260259 return
261260 }
262261
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-
268262 resp .Diagnostics .Append (resp .State .Set (ctx , & state )... )
269263}
264+
270265func (r * environmentResource ) Update (ctx context.Context , req resource.UpdateRequest , resp * resource.UpdateResponse ) {
271266 conn := r .Meta ().DataZoneClient (ctx )
272267
@@ -277,18 +272,35 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
277272 return
278273 }
279274
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+ }
285287
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 () {
287298 return
288299 }
289- in . Identifier = state . Id . ValueStringPointer ()
300+ }
290301
291- out , err := conn .UpdateEnvironment (ctx , in )
302+ if needsUpdate {
303+ out , err := conn .UpdateEnvironment (ctx , & input )
292304 if err != nil {
293305 resp .Diagnostics .AddError (
294306 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
306318 }
307319
308320 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 )
310322
311323 if err != nil {
312324 resp .Diagnostics .AddError (
@@ -315,6 +327,11 @@ func (r *environmentResource) Update(ctx context.Context, req resource.UpdateReq
315327 )
316328 return
317329 }
330+
331+ flattenEnvironment (ctx , output , & plan , & resp .Diagnostics )
332+ if resp .Diagnostics .HasError () {
333+ return
334+ }
318335 }
319336
320337 resp .Diagnostics .Append (resp .State .Set (ctx , & plan )... )
@@ -375,8 +392,8 @@ func (r *environmentResource) ImportState(ctx context.Context, req resource.Impo
375392
376393func waitEnvironmentCreated (ctx context.Context , conn * datazone.Client , domainId string , id string , timeout time.Duration ) (* datazone.GetEnvironmentOutput , error ) {
377394 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 ),
380397 Refresh : statusEnvironment (ctx , conn , domainId , id ),
381398 Timeout : timeout ,
382399 NotFoundChecks : 20 ,
@@ -385,7 +402,7 @@ func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId
385402
386403 outputRaw , err := stateConf .WaitForStateContext (ctx )
387404 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 {
389406 tfresource .SetLastError (err , fmt .Errorf ("%s: %s" , status , aws .ToString (deployment .FailureReason .Message )))
390407 }
391408 return out , err
@@ -396,8 +413,8 @@ func waitEnvironmentCreated(ctx context.Context, conn *datazone.Client, domainId
396413
397414func waitEnvironmentUpdated (ctx context.Context , conn * datazone.Client , domainId string , id string , timeout time.Duration ) (* datazone.GetEnvironmentOutput , error ) {
398415 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 ),
401418 Refresh : statusEnvironment (ctx , conn , domainId , id ),
402419 Timeout : timeout ,
403420 NotFoundChecks : 20 ,
@@ -417,10 +434,12 @@ func waitEnvironmentUpdated(ctx context.Context, conn *datazone.Client, domainId
417434
418435func waitEnvironmentDeleted (ctx context.Context , conn * datazone.Client , domainId string , id string , timeout time.Duration ) (* datazone.GetEnvironmentOutput , error ) {
419436 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 ,
424443 }
425444
426445 outputRaw , err := stateConf .WaitForStateContext (ctx )
@@ -479,7 +498,7 @@ type environmentResourceModel struct {
479498 framework.WithRegionModel
480499 AccountIdentifier types.String `tfsdk:"account_identifier"`
481500 AccountRegion types.String `tfsdk:"account_region"`
482- BlueprintId types.String `tfsdk:"blueprint_identifier"`
501+ BlueprintIdentifier types.String `tfsdk:"blueprint_identifier"`
483502 CreatedAt timetypes.RFC3339 `tfsdk:"created_at"`
484503 CreatedBy types.String `tfsdk:"created_by"`
485504 Description types.String `tfsdk:"description"`
@@ -496,6 +515,23 @@ type environmentResourceModel struct {
496515 UserParameters fwtypes.ListNestedObjectValueOf [resourceUserParametersData ] `tfsdk:"user_parameters"`
497516}
498517
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+
499535type resourceLastDeployment struct {
500536 DeploymentId types.String `tfsdk:"deployment_id"`
501537 DeploymentStatus types.String `tfsdk:"deployment_status"`
@@ -521,3 +557,38 @@ type resourceUserParametersData struct {
521557 Name types.String `tfsdk:"name"`
522558 Value types.String `tfsdk:"value"`
523559}
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