@@ -63,6 +63,10 @@ func resourceTFEWorkspace() *schema.Resource {
6363 return err
6464 }
6565
66+ if err := customizeDiffAutoDestroyAt (c , d ); err != nil {
67+ return err
68+ }
69+
6670 return nil
6771 },
6872
@@ -112,9 +116,17 @@ func resourceTFEWorkspace() *schema.Resource {
112116
113117 "auto_destroy_at" : {
114118 Type : schema .TypeString ,
119+ Computed : true ,
115120 Optional : true ,
116121 },
117122
123+ "auto_destroy_activity_duration" : {
124+ Type : schema .TypeString ,
125+ Optional : true ,
126+ ConflictsWith : []string {"auto_destroy_at" },
127+ ValidateFunc : validation .StringMatch (regexp .MustCompile (`^\d{1,4}[dh]$` ), "must be 1-4 digits followed by d or h" ),
128+ },
129+
118130 "execution_mode" : {
119131 Type : schema .TypeString ,
120132 Optional : true ,
@@ -354,6 +366,10 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error
354366 options .AutoDestroyAt = autoDestroyAt
355367 }
356368
369+ if v , ok := d .GetOk ("auto_destroy_activity_duration" ); ok {
370+ options .AutoDestroyActivityDuration = jsonapi .NewNullableAttrWithValue (v .(string ))
371+ }
372+
357373 if v , ok := d .GetOk ("execution_mode" ); ok {
358374 executionMode := tfe .String (v .(string ))
359375 options .SettingOverwrites = & tfe.WorkspaceSettingOverwritesOptions {
@@ -553,6 +569,15 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error {
553569 }
554570 d .Set ("auto_destroy_at" , autoDestroyAt )
555571
572+ if workspace .AutoDestroyActivityDuration .IsSpecified () {
573+ v , err := workspace .AutoDestroyActivityDuration .Get ()
574+ if err != nil {
575+ return fmt .Errorf ("Error reading auto destroy activity duration: %w" , err )
576+ }
577+
578+ d .Set ("auto_destroy_activity_duration" , v )
579+ }
580+
556581 var tagNames []interface {}
557582 managedTags := d .Get ("tag_names" ).(* schema.Set )
558583 for _ , tagName := range workspace .TagNames {
@@ -605,7 +630,8 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error
605630 d .HasChange ("operations" ) || d .HasChange ("execution_mode" ) ||
606631 d .HasChange ("description" ) || d .HasChange ("agent_pool_id" ) ||
607632 d .HasChange ("global_remote_state" ) || d .HasChange ("structured_run_output_enabled" ) ||
608- d .HasChange ("assessments_enabled" ) || d .HasChange ("project_id" ) || d .HasChange ("auto_destroy_at" ) {
633+ d .HasChange ("assessments_enabled" ) || d .HasChange ("project_id" ) ||
634+ hasAutoDestroyAtChange (d ) || d .HasChange ("auto_destroy_activity_duration" ) {
609635 // Create a new options struct.
610636 options := tfe.WorkspaceUpdateOptions {
611637 Name : tfe .String (d .Get ("name" ).(string )),
@@ -658,14 +684,23 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error
658684 }
659685 }
660686
661- if d . HasChange ( "auto_destroy_at" ) {
687+ if hasAutoDestroyAtChange ( d ) {
662688 autoDestroyAt , err := expandAutoDestroyAt (d )
663689 if err != nil {
664690 return fmt .Errorf ("Error expanding auto destroy during update: %w" , err )
665691 }
666692 options .AutoDestroyAt = autoDestroyAt
667693 }
668694
695+ if d .HasChange ("auto_destroy_activity_duration" ) {
696+ duration , ok := d .GetOk ("auto_destroy_activity_duration" )
697+ if ! ok {
698+ options .AutoDestroyActivityDuration = jsonapi .NewNullNullableAttr [string ]()
699+ } else {
700+ options .AutoDestroyActivityDuration = jsonapi .NewNullableAttrWithValue (duration .(string ))
701+ }
702+ }
703+
669704 if d .HasChange ("execution_mode" ) {
670705 if v , ok := d .GetOk ("execution_mode" ); ok {
671706 options .ExecutionMode = tfe .String (v .(string ))
@@ -961,35 +996,6 @@ func validateAgentExecution(_ context.Context, d *schema.ResourceDiff) error {
961996 return nil
962997}
963998
964- func expandAutoDestroyAt (d * schema.ResourceData ) (jsonapi.NullableAttr [time.Time ], error ) {
965- v , ok := d .GetOk ("auto_destroy_at" )
966-
967- if ! ok {
968- return jsonapi .NewNullNullableAttr [time.Time ](), nil
969- }
970-
971- autoDestroyAt , err := time .Parse (time .RFC3339 , v .(string ))
972- if err != nil {
973- return nil , err
974- }
975-
976- return jsonapi .NewNullableAttrWithValue (autoDestroyAt ), nil
977- }
978-
979- func flattenAutoDestroyAt (a jsonapi.NullableAttr [time.Time ]) (* string , error ) {
980- if ! a .IsSpecified () {
981- return nil , nil
982- }
983-
984- autoDestroyTime , err := a .Get ()
985- if err != nil {
986- return nil , err
987- }
988-
989- autoDestroyAt := autoDestroyTime .Format (time .RFC3339 )
990- return & autoDestroyAt , nil
991- }
992-
993999func validTagName (tag string ) bool {
9941000 // Tags are re-validated here because the API will accept uppercase letters and automatically
9951001 // downcase them, causing resource drift. It's better to catch this issue during the plan phase
@@ -1076,3 +1082,64 @@ func errWorkspaceResourceCountCheck(workspaceID string, resourceCount int) error
10761082 }
10771083 return nil
10781084}
1085+
1086+ func customizeDiffAutoDestroyAt (_ context.Context , d * schema.ResourceDiff ) error {
1087+ config := d .GetRawConfig ()
1088+
1089+ // check if auto_destroy_activity_duration is set in config
1090+ if ! config .GetAttr ("auto_destroy_activity_duration" ).IsNull () {
1091+ return nil
1092+ }
1093+
1094+ // if config auto_destroy_at is unset but it exists in state, clear it out
1095+ // required because auto_destroy_at is computed and we want to set it to null
1096+ if _ , ok := d .GetOk ("auto_destroy_at" ); ok && config .GetAttr ("auto_destroy_at" ).IsNull () {
1097+ return d .SetNew ("auto_destroy_at" , nil )
1098+ }
1099+
1100+ return nil
1101+ }
1102+
1103+ func expandAutoDestroyAt (d * schema.ResourceData ) (jsonapi.NullableAttr [time.Time ], error ) {
1104+ v := d .GetRawConfig ().GetAttr ("auto_destroy_at" )
1105+
1106+ if v .IsNull () {
1107+ return jsonapi .NewNullNullableAttr [time.Time ](), nil
1108+ }
1109+
1110+ autoDestroyAt , err := time .Parse (time .RFC3339 , v .AsString ())
1111+ if err != nil {
1112+ return nil , err
1113+ }
1114+
1115+ return jsonapi .NewNullableAttrWithValue (autoDestroyAt ), nil
1116+ }
1117+
1118+ func flattenAutoDestroyAt (a jsonapi.NullableAttr [time.Time ]) (* string , error ) {
1119+ if ! a .IsSpecified () {
1120+ return nil , nil
1121+ }
1122+
1123+ autoDestroyTime , err := a .Get ()
1124+ if err != nil {
1125+ return nil , err
1126+ }
1127+
1128+ autoDestroyAt := autoDestroyTime .Format (time .RFC3339 )
1129+ return & autoDestroyAt , nil
1130+ }
1131+
1132+ func hasAutoDestroyAtChange (d * schema.ResourceData ) bool {
1133+ state := d .GetRawState ()
1134+ if state .IsNull () {
1135+ return d .HasChange ("auto_destroy_at" )
1136+ }
1137+
1138+ config := d .GetRawConfig ()
1139+ autoDestroyAt := config .GetAttr ("auto_destroy_at" )
1140+ if ! autoDestroyAt .IsNull () {
1141+ return d .HasChange ("auto_destroy_at" )
1142+ }
1143+
1144+ return config .GetAttr ("auto_destroy_at" ) != state .GetAttr ("auto_destroy_at" )
1145+ }
0 commit comments