@@ -63,6 +63,10 @@ func resourceTFEWorkspace() *schema.Resource {
63
63
return err
64
64
}
65
65
66
+ if err := customizeDiffAutoDestroyAt (c , d ); err != nil {
67
+ return err
68
+ }
69
+
66
70
return nil
67
71
},
68
72
@@ -112,9 +116,17 @@ func resourceTFEWorkspace() *schema.Resource {
112
116
113
117
"auto_destroy_at" : {
114
118
Type : schema .TypeString ,
119
+ Computed : true ,
115
120
Optional : true ,
116
121
},
117
122
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
+
118
130
"execution_mode" : {
119
131
Type : schema .TypeString ,
120
132
Optional : true ,
@@ -354,6 +366,10 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error
354
366
options .AutoDestroyAt = autoDestroyAt
355
367
}
356
368
369
+ if v , ok := d .GetOk ("auto_destroy_activity_duration" ); ok {
370
+ options .AutoDestroyActivityDuration = jsonapi .NewNullableAttrWithValue (v .(string ))
371
+ }
372
+
357
373
if v , ok := d .GetOk ("execution_mode" ); ok {
358
374
executionMode := tfe .String (v .(string ))
359
375
options .SettingOverwrites = & tfe.WorkspaceSettingOverwritesOptions {
@@ -553,6 +569,15 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error {
553
569
}
554
570
d .Set ("auto_destroy_at" , autoDestroyAt )
555
571
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
+
556
581
var tagNames []interface {}
557
582
managedTags := d .Get ("tag_names" ).(* schema.Set )
558
583
for _ , tagName := range workspace .TagNames {
@@ -605,7 +630,8 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error
605
630
d .HasChange ("operations" ) || d .HasChange ("execution_mode" ) ||
606
631
d .HasChange ("description" ) || d .HasChange ("agent_pool_id" ) ||
607
632
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" ) {
609
635
// Create a new options struct.
610
636
options := tfe.WorkspaceUpdateOptions {
611
637
Name : tfe .String (d .Get ("name" ).(string )),
@@ -658,14 +684,23 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error
658
684
}
659
685
}
660
686
661
- if d . HasChange ( "auto_destroy_at" ) {
687
+ if hasAutoDestroyAtChange ( d ) {
662
688
autoDestroyAt , err := expandAutoDestroyAt (d )
663
689
if err != nil {
664
690
return fmt .Errorf ("Error expanding auto destroy during update: %w" , err )
665
691
}
666
692
options .AutoDestroyAt = autoDestroyAt
667
693
}
668
694
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
+
669
704
if d .HasChange ("execution_mode" ) {
670
705
if v , ok := d .GetOk ("execution_mode" ); ok {
671
706
options .ExecutionMode = tfe .String (v .(string ))
@@ -961,35 +996,6 @@ func validateAgentExecution(_ context.Context, d *schema.ResourceDiff) error {
961
996
return nil
962
997
}
963
998
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
-
993
999
func validTagName (tag string ) bool {
994
1000
// Tags are re-validated here because the API will accept uppercase letters and automatically
995
1001
// 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
1076
1082
}
1077
1083
return nil
1078
1084
}
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