Skip to content

Commit 1a0b1eb

Browse files
authored
Merge branch 'main' into sebasslash/enable-onprem-ci
2 parents eba7bdd + 5219dbf commit 1a0b1eb

File tree

7 files changed

+195
-1
lines changed

7 files changed

+195
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
ENHANCEMENTS:
44
* `r/tfe_oauth_client`: Add Bitbucket Data Center support with the `bitbucket_data_center` option for `service_provider` by @zainq11 [#1303](https://github.com/hashicorp/terraform-provider-tfe/pull/1304)
5+
* `r/tfe_workspace`: Add an `auto_destroy_at` attribute for scheduling an auto-destroy run in the future, by @notchairmk [1354](https://github.com/hashicorp/terraform-provider-tfe/pull/1354)
6+
* `d/tfe_workspace`: Add an `auto_destroy_at` attribute for reading a scheduled auto-destroy, by @notchairmk [1354](https://github.com/hashicorp/terraform-provider-tfe/pull/1354)
57

68
## v0.55.0
79

internal/provider/data_source_workspace.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ func dataSourceTFEWorkspace() *schema.Resource {
5252
Computed: true,
5353
},
5454

55+
"auto_destroy_at": {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
},
59+
5560
"file_triggers_enabled": {
5661
Type: schema.TypeBool,
5762
Computed: true,
@@ -240,6 +245,12 @@ func dataSourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error
240245
d.Set("operations", workspace.Operations)
241246
d.Set("policy_check_failures", workspace.PolicyCheckFailures)
242247

248+
autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt)
249+
if err != nil {
250+
return fmt.Errorf("Error flattening auto destroy during read: %w", err)
251+
}
252+
d.Set("auto_destroy_at", autoDestroyAt)
253+
243254
// If target tfe instance predates projects, then workspace.Project will be nil
244255
if workspace.Project != nil {
245256
d.Set("project_id", workspace.Project.ID)

internal/provider/data_source_workspace_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,25 @@ func TestAccTFEWorkspaceDataSourceWithTriggerPatterns(t *testing.T) {
171171
})
172172
}
173173

174+
func TestAccTFEWorkspaceDataSource_readAutoDestroyAt(t *testing.T) {
175+
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
176+
177+
resource.Test(t, resource.TestCase{
178+
PreCheck: func() { testAccPreCheck(t) },
179+
Providers: testAccProviders,
180+
Steps: []resource.TestStep{
181+
{
182+
Config: testAccTFEWorkspaceDataSourceConfig_basic(rInt),
183+
Check: resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "auto_destroy_at", ""),
184+
},
185+
{
186+
Config: testAccTFEWorkspaceDataSourceConfig_basicWithAutoDestroy(rInt),
187+
Check: resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"),
188+
},
189+
},
190+
})
191+
}
192+
174193
func TestAccTFEWorkspaceDataSource_readProjectIDDefault(t *testing.T) {
175194
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
176195

@@ -243,6 +262,44 @@ data "tfe_workspace" "foobar" {
243262
}`, rInt, rInt, aart)
244263
}
245264

265+
func testAccTFEWorkspaceDataSourceConfig_basic(rInt int) string {
266+
return fmt.Sprintf(`
267+
resource "tfe_organization" "foobar" {
268+
name = "tst-terraform-%d"
269+
270+
}
271+
272+
resource "tfe_workspace" "foobar" {
273+
name = "workspace-test-%d"
274+
organization = tfe_organization.foobar.id
275+
description = "provider-testing"
276+
}
277+
278+
data "tfe_workspace" "foobar" {
279+
name = tfe_workspace.foobar.name
280+
organization = tfe_workspace.foobar.organization
281+
}`, rInt, rInt)
282+
}
283+
284+
func testAccTFEWorkspaceDataSourceConfig_basicWithAutoDestroy(rInt int) string {
285+
return fmt.Sprintf(`
286+
resource "tfe_organization" "foobar" {
287+
name = "tst-terraform-%d"
288+
289+
}
290+
291+
resource "tfe_workspace" "foobar" {
292+
name = "workspace-test-%d"
293+
organization = tfe_organization.foobar.id
294+
description = "provider-testing"
295+
auto_destroy_at = "2100-01-01T00:00:00Z"
296+
}
297+
298+
data "tfe_workspace" "foobar" {
299+
name = tfe_workspace.foobar.name
300+
organization = tfe_workspace.foobar.organization
301+
}`, rInt, rInt)
302+
}
246303
func testAccTFEWorkspaceDataSourceConfigWithTriggerPatterns(workspaceName string, organizationName string) string {
247304
return fmt.Sprintf(`
248305
data "tfe_workspace" "foobar" {

internal/provider/resource_tfe_workspace.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"time"
2020

2121
tfe "github.com/hashicorp/go-tfe"
22+
"github.com/hashicorp/jsonapi"
2223
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
2324
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2425
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -109,6 +110,11 @@ func resourceTFEWorkspace() *schema.Resource {
109110
Default: false,
110111
},
111112

113+
"auto_destroy_at": {
114+
Type: schema.TypeString,
115+
Optional: true,
116+
},
117+
112118
"execution_mode": {
113119
Type: schema.TypeString,
114120
Optional: true,
@@ -340,6 +346,14 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error
340346
}
341347
}
342348

349+
if _, ok := d.GetOk("auto_destroy_at"); ok {
350+
autoDestroyAt, err := expandAutoDestroyAt(d)
351+
if err != nil {
352+
return fmt.Errorf("Error expanding auto destroy during create: %w", err)
353+
}
354+
options.AutoDestroyAt = autoDestroyAt
355+
}
356+
343357
if v, ok := d.GetOk("execution_mode"); ok {
344358
executionMode := tfe.String(v.(string))
345359
options.SettingOverwrites = &tfe.WorkspaceSettingOverwritesOptions{
@@ -533,6 +547,12 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error {
533547
}
534548
d.Set("agent_pool_id", agentPoolID)
535549

550+
autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt)
551+
if err != nil {
552+
return fmt.Errorf("Error flattening auto destroy during read: %w", err)
553+
}
554+
d.Set("auto_destroy_at", autoDestroyAt)
555+
536556
var tagNames []interface{}
537557
managedTags := d.Get("tag_names").(*schema.Set)
538558
for _, tagName := range workspace.TagNames {
@@ -585,7 +605,7 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error
585605
d.HasChange("operations") || d.HasChange("execution_mode") ||
586606
d.HasChange("description") || d.HasChange("agent_pool_id") ||
587607
d.HasChange("global_remote_state") || d.HasChange("structured_run_output_enabled") ||
588-
d.HasChange("assessments_enabled") || d.HasChange("project_id") {
608+
d.HasChange("assessments_enabled") || d.HasChange("project_id") || d.HasChange("auto_destroy_at") {
589609
// Create a new options struct.
590610
options := tfe.WorkspaceUpdateOptions{
591611
Name: tfe.String(d.Get("name").(string)),
@@ -638,6 +658,14 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error
638658
}
639659
}
640660

661+
if d.HasChange("auto_destroy_at") {
662+
autoDestroyAt, err := expandAutoDestroyAt(d)
663+
if err != nil {
664+
return fmt.Errorf("Error expanding auto destroy during update: %w", err)
665+
}
666+
options.AutoDestroyAt = autoDestroyAt
667+
}
668+
641669
if d.HasChange("execution_mode") {
642670
if v, ok := d.GetOk("execution_mode"); ok {
643671
options.ExecutionMode = tfe.String(v.(string))
@@ -933,6 +961,35 @@ func validateAgentExecution(_ context.Context, d *schema.ResourceDiff) error {
933961
return nil
934962
}
935963

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+
936993
func validTagName(tag string) bool {
937994
// Tags are re-validated here because the API will accept uppercase letters and automatically
938995
// downcase them, causing resource drift. It's better to catch this issue during the plan phase

internal/provider/resource_tfe_workspace_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,6 +2648,52 @@ func TestAccTFEWorkspace_basicAssessmentsEnabled(t *testing.T) {
26482648
})
26492649
}
26502650

2651+
func TestAccTFEWorkspace_createWithAutoDestroyAt(t *testing.T) {
2652+
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
2653+
2654+
resource.Test(t, resource.TestCase{
2655+
PreCheck: func() { testAccPreCheck(t) },
2656+
Providers: testAccProviders,
2657+
CheckDestroy: testAccCheckTFEWorkspaceDestroy,
2658+
Steps: []resource.TestStep{
2659+
{
2660+
Config: testAccTFEWorkspace_basicWithAutoDestroyAt(rInt),
2661+
Check: resource.ComposeTestCheckFunc(
2662+
testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider),
2663+
resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"),
2664+
),
2665+
},
2666+
},
2667+
})
2668+
}
2669+
2670+
func TestAccTFEWorkspace_updateWithAutoDestroyAt(t *testing.T) {
2671+
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
2672+
2673+
resource.Test(t, resource.TestCase{
2674+
PreCheck: func() { testAccPreCheck(t) },
2675+
Providers: testAccProviders,
2676+
CheckDestroy: testAccCheckTFEWorkspaceDestroy,
2677+
Steps: []resource.TestStep{
2678+
{
2679+
Config: testAccTFEWorkspace_basic(rInt),
2680+
Check: resource.ComposeTestCheckFunc(
2681+
testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider),
2682+
resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", ""),
2683+
),
2684+
},
2685+
{
2686+
Config: testAccTFEWorkspace_basicWithAutoDestroyAt(rInt),
2687+
Check: resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"),
2688+
},
2689+
{
2690+
Config: testAccTFEWorkspace_basic(rInt),
2691+
Check: resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", ""),
2692+
},
2693+
},
2694+
})
2695+
}
2696+
26512697
func TestAccTFEWorkspace_createWithSourceURL(t *testing.T) {
26522698
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
26532699

@@ -2924,6 +2970,22 @@ resource "tfe_workspace" "foobar" {
29242970
}`, rInt)
29252971
}
29262972

2973+
func testAccTFEWorkspace_basicWithAutoDestroyAt(rInt int) string {
2974+
return fmt.Sprintf(`
2975+
resource "tfe_organization" "foobar" {
2976+
name = "tst-terraform-%d"
2977+
2978+
}
2979+
2980+
resource "tfe_workspace" "foobar" {
2981+
name = "workspace-test"
2982+
organization = tfe_organization.foobar.id
2983+
auto_apply = true
2984+
file_triggers_enabled = false
2985+
auto_destroy_at = "2100-01-01T00:00:00Z"
2986+
}`, rInt)
2987+
}
2988+
29272989
func testAccTFEWorkspace_operationsTrue(organization string) string {
29282990
return fmt.Sprintf(`
29292991
resource "tfe_workspace" "foobar" {

website/docs/d/workspace.html.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ In addition to all arguments above, the following attributes are exported:
3535
* `allow_destroy_plan` - Indicates whether destroy plans can be queued on the workspace.
3636
* `auto_apply` - Indicates whether to automatically apply changes when a Terraform plan is successful.
3737
* `auto_apply_run_trigger` - Whether the workspace will automatically apply changes for runs that were created by run triggers from another workspace.
38+
* `auto_destroy_at` - Future date/time string at which point all resources in a workspace will be scheduled to be deleted.
3839
* `assessments_enabled` - (Available only in HCP Terraform) Indicates whether health assessments such as drift detection are enabled for the workspace.
3940
* `file_triggers_enabled` - Indicates whether runs are triggered based on the changed files in a VCS push (if `true`) or always triggered on every push (if `false`).
4041
* `global_remote_state` - (Optional) Whether the workspace should allow all workspaces in the organization to access its state data during runs. If false, then only specifically approved workspaces can access its state (determined by the `remote_state_consumer_ids` argument).

website/docs/r/workspace.html.markdown

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ The following arguments are supported:
6868
* `assessments_enabled` - (Optional) Whether to regularly run health assessments such as drift detection on the workspace. Defaults to `false`.
6969
* `auto_apply` - (Optional) Whether to automatically apply changes when a Terraform plan is successful. Defaults to `false`.
7070
* `auto_apply_run_trigger` - (Optional) Whether to automatically apply changes for runs that were created by run triggers from another workspace. Defaults to `false`.
71+
* `auto_destroy_at` - (Optional) A future date/time string at which point all resources in a workspace will be scheduled for deletion. Must be a string in RFC3339 format (e.g. "2100-01-01T00:00:00Z").
72+
73+
~> **NOTE:** `auto_destroy_at` is not intended for workspaces containing production resources or long-lived workspaces. Since this attribute is in-part managed by HCP Terraform, using `ignore_changes` for this attribute may be preferred.
74+
7175
* `description` - (Optional) A description for the workspace.
7276
* `execution_mode` - (Optional) **Deprecated** Which [execution mode](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#execution-mode) to use. Use [tfe_workspace_settings](workspace_settings) instead.
7377
* `file_triggers_enabled` - (Optional) Whether to filter runs based on the changed files

0 commit comments

Comments
 (0)