From 4bc5ff414c97592c485f38a20c89ec864c66d09f Mon Sep 17 00:00:00 2001 From: Glenn Sarti Date: Thu, 5 May 2022 10:53:14 +0800 Subject: [PATCH] Add run_tasks permission to Team Access Run Tasks has added a new `run_tasks` custom permission to Team Workspace access. This commit adds this new permission to the Team Access object (both resource and data) and adds acceptance tests for both objects. --- CHANGELOG.md | 15 +++-- tfe/data_source_team_access.go | 5 ++ tfe/data_source_team_access_test.go | 2 + tfe/resource_tfe_team_access.go | 28 ++++++++- tfe/resource_tfe_team_access_test.go | 78 ++++++++++++++++++++++++ website/docs/d/team_access.html.markdown | 1 + website/docs/r/team_access.html.markdown | 1 + 7 files changed, 121 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfeb9daa3..3c63f4e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ BREAKING CHANGES: * **Removed Authentication Method**: Host-specific TF_TOKEN_... environment variable can no longer be used for token authentication. This method of authentication is incompatible with the Terraform Cloud remote execution model. Please use the TFE_TOKEN environment variable. +* r/tfe_workspace: Default value of the `file_triggers_enabled` field is changed to `false`. This will align the + `file_triggers_enabled` field default value with the default value for the same field in the + [TFC API](https://www.terraform.io/cloud-docs/api-docs/workspaces). + If the value of the `file_triggers_enabled` field was not explicitly set and either of the fields `working_directory` + (not an empty string) or `trigger_prefixes` was used - to keep the behavior unchanged, the `file_trigger_enabled` + field should now explicitly be set to `true`. ([#510](https://github.com/hashicorp/terraform-provider-tfe/pull/510/files)) +* r/tfe_team_access: The `permissions` attribute requires `run_tasks` in the block. ([#487](https://github.com/hashicorp/terraform-provider-tfe/pull/487)) BUG FIXES: * Prevent overwriting `vcs_repo` attributes in `r/tfe_workspace` when update API call fails ([#498](https://github.com/hashicorp/terraform-provider-tfe/pull/498)) @@ -14,14 +21,6 @@ FEATURES: * **New Data Source**: d/tfe_workspace_run_task ([#488](https://github.com/hashicorp/terraform-provider-tfe/pull/488)) * r/tfe_notification_configuration: Add Microsoft Teams notification type ([#484](https://github.com/hashicorp/terraform-provider-tfe/pull/484)) -BREAKING CHANGES: -* r/tfe_workspace: Default value of the `file_triggers_enabled` field is changed to `false`. This will align the - `file_triggers_enabled` field default value with the default value for the same field in the - [TFC API](https://www.terraform.io/cloud-docs/api-docs/workspaces). - If the value of the `file_triggers_enabled` field was not explicitly set and either of the fields `working_directory` - (not an empty string) or `trigger_prefixes` was used - to keep the behavior unchanged, the `file_trigger_enabled` - field should now explicitly be set to `true`. ([#510](https://github.com/hashicorp/terraform-provider-tfe/pull/510/files)) - ## 0.31.0 (April 21, 2022) BUG FIXES: diff --git a/tfe/data_source_team_access.go b/tfe/data_source_team_access.go index 9c293fff2..79d09da36 100644 --- a/tfe/data_source_team_access.go +++ b/tfe/data_source_team_access.go @@ -46,6 +46,11 @@ func dataSourceTFETeamAccess() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + + "run_tasks": { + Type: schema.TypeBool, + Computed: true, + }, }, }, }, diff --git a/tfe/data_source_team_access_test.go b/tfe/data_source_team_access_test.go index efce93836..9185d5939 100644 --- a/tfe/data_source_team_access_test.go +++ b/tfe/data_source_team_access_test.go @@ -33,6 +33,8 @@ func TestAccTFETeamAccessDataSource_basic(t *testing.T) { "data.tfe_team_access.foobar", "permissions.0.sentinel_mocks", "read"), resource.TestCheckResourceAttr( "data.tfe_team_access.foobar", "permissions.0.workspace_locking", "true"), + resource.TestCheckResourceAttr( + "data.tfe_team_access.foobar", "permissions.0.run_tasks", "false"), resource.TestCheckResourceAttrSet("data.tfe_team_access.foobar", "id"), resource.TestCheckResourceAttrSet("data.tfe_team_access.foobar", "team_id"), resource.TestCheckResourceAttrSet("data.tfe_team_access.foobar", "workspace_id"), diff --git a/tfe/resource_tfe_team_access.go b/tfe/resource_tfe_team_access.go index 16fe4c0a4..5da993ea2 100644 --- a/tfe/resource_tfe_team_access.go +++ b/tfe/resource_tfe_team_access.go @@ -112,6 +112,11 @@ func resourceTFETeamAccess() *schema.Resource { Type: schema.TypeBool, Required: true, }, + + "run_tasks": { + Type: schema.TypeBool, + Required: true, + }, }, }, }, @@ -193,6 +198,12 @@ func resourceTFETeamAccessCreate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("permissions.0.run_tasks") { + if v, ok := d.GetOkExists("permissions.0.run_tasks"); ok { + options.RunTasks = tfe.Bool(v.(bool)) + } + } + log.Printf("[DEBUG] Give team %s %s access to workspace: %s", tm.Name, access, ws.Name) tmAccess, err := tfeClient.TeamAccess.Add(ctx, options) if err != nil { @@ -227,6 +238,7 @@ func resourceTFETeamAccessRead(d *schema.ResourceData, meta interface{}) error { "state_versions": tmAccess.StateVersions, "sentinel_mocks": tmAccess.SentinelMocks, "workspace_locking": tmAccess.WorkspaceLocking, + "run_tasks": tmAccess.RunTasks, }} if err := d.Set("permissions", permissions); err != nil { return fmt.Errorf("error setting permissions for team access %s: %w", d.Id(), err) @@ -281,6 +293,12 @@ func resourceTFETeamAccessUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("permissions.0.run_tasks") { + if v, ok := d.GetOkExists("permissions.0.run_tasks"); ok { + options.RunTasks = tfe.Bool(v.(bool)) + } + } + log.Printf("[DEBUG] Update team access: %s", d.Id()) tmAccess, err := tfeClient.TeamAccess.Update(ctx, d.Id(), options) if err != nil { @@ -295,6 +313,7 @@ func resourceTFETeamAccessUpdate(d *schema.ResourceData, meta interface{}) error "state_versions": tmAccess.StateVersions, "sentinel_mocks": tmAccess.SentinelMocks, "workspace_locking": tmAccess.WorkspaceLocking, + "run_tasks": tmAccess.RunTasks, }} if err := d.Set("permissions", permissions); err != nil { return fmt.Errorf("error setting permissions for team access %s: %w", d.Id(), err) @@ -397,7 +416,14 @@ func setCustomAccess(d *schema.ResourceDiff) error { // Interpolated values not known at plan time are not allowed because we cannot re-check // for a change in permissions later - when the plan is expanded for new values learned during // an apply. This creates an inconsistent final plan and causes an error. - for _, permission := range []string{"permissions.0.runs", "permissions.0.variables", "permissions.0.state_versions", "permissions.0.sentinel_mocks", "permissions.0.workspace_locking"} { + for _, permission := range []string{ + "permissions.0.runs", + "permissions.0.variables", + "permissions.0.state_versions", + "permissions.0.sentinel_mocks", + "permissions.0.workspace_locking", + "permissions.0.run_tasks", + } { if !d.NewValueKnown(permission) { return fmt.Errorf("'%q' cannot be derived from a value that is unknown during planning", permission) } diff --git a/tfe/resource_tfe_team_access_test.go b/tfe/resource_tfe_team_access_test.go index 2a1c76d5e..8c4d5f72a 100644 --- a/tfe/resource_tfe_team_access_test.go +++ b/tfe/resource_tfe_team_access_test.go @@ -11,6 +11,44 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +func TestAccTFETeamAccess_admin(t *testing.T) { + tmAccess := &tfe.TeamAccess{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + expectedPermissions := map[string]interface{}{ + "runs": tfe.RunsPermissionApply, + "variables": tfe.VariablesPermissionWrite, + "state_versions": tfe.StateVersionsPermissionWrite, + "sentinel_mocks": tfe.SentinelMocksPermissionRead, + "workspace_locking": true, + "run_tasks": true, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTFETeamAccessDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETeamAccess_admin(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETeamAccessExists( + "tfe_team_access.foobar", tmAccess), + testAccCheckTFETeamAccessAttributesAccessIs(tmAccess, tfe.AccessAdmin), + testAccCheckTFETeamAccessAttributesPermissionsAre(tmAccess, expectedPermissions), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "access", "admin"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.runs", "apply"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.variables", "write"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "write"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "read"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "true"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "true"), + ), + }, + }, + }) +} + func TestAccTFETeamAccess_write(t *testing.T) { tmAccess := &tfe.TeamAccess{} rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() @@ -21,6 +59,7 @@ func TestAccTFETeamAccess_write(t *testing.T) { "state_versions": tfe.StateVersionsPermissionWrite, "sentinel_mocks": tfe.SentinelMocksPermissionRead, "workspace_locking": true, + "run_tasks": false, } resource.Test(t, resource.TestCase{ @@ -41,6 +80,7 @@ func TestAccTFETeamAccess_write(t *testing.T) { resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "write"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "read"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "true"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "false"), ), }, }, @@ -70,6 +110,7 @@ func TestAccTFETeamAccess_custom(t *testing.T) { "state_versions": tfe.StateVersionsPermissionReadOutputs, "sentinel_mocks": tfe.SentinelMocksPermissionNone, "workspace_locking": false, + "run_tasks": false, }, ), resource.TestCheckResourceAttr("tfe_team_access.foobar", "access", "custom"), @@ -78,6 +119,7 @@ func TestAccTFETeamAccess_custom(t *testing.T) { resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "read-outputs"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "none"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "false"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "false"), ), }, }, @@ -107,6 +149,7 @@ func TestAccTFETeamAccess_updateToCustom(t *testing.T) { "state_versions": tfe.StateVersionsPermissionWrite, "sentinel_mocks": tfe.SentinelMocksPermissionRead, "workspace_locking": true, + "run_tasks": false, }, ), resource.TestCheckResourceAttr("tfe_team_access.foobar", "access", "write"), @@ -115,6 +158,7 @@ func TestAccTFETeamAccess_updateToCustom(t *testing.T) { resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "write"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "read"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "true"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "false"), ), }, { @@ -131,6 +175,7 @@ func TestAccTFETeamAccess_updateToCustom(t *testing.T) { "state_versions": tfe.StateVersionsPermissionReadOutputs, "sentinel_mocks": tfe.SentinelMocksPermissionNone, "workspace_locking": false, + "run_tasks": false, }, ), resource.TestCheckResourceAttr("tfe_team_access.foobar", "access", "custom"), @@ -139,6 +184,7 @@ func TestAccTFETeamAccess_updateToCustom(t *testing.T) { resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "read-outputs"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "none"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "false"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "false"), ), }, }, @@ -168,6 +214,7 @@ func TestAccTFETeamAccess_updateFromCustom(t *testing.T) { "state_versions": tfe.StateVersionsPermissionReadOutputs, "sentinel_mocks": tfe.SentinelMocksPermissionNone, "workspace_locking": false, + "run_tasks": false, }, ), resource.TestCheckResourceAttr("tfe_team_access.foobar", "access", "custom"), @@ -176,6 +223,7 @@ func TestAccTFETeamAccess_updateFromCustom(t *testing.T) { resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "read-outputs"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "none"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "false"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "false"), ), }, { @@ -192,6 +240,7 @@ func TestAccTFETeamAccess_updateFromCustom(t *testing.T) { "state_versions": tfe.StateVersionsPermissionRead, "sentinel_mocks": tfe.SentinelMocksPermissionNone, "workspace_locking": false, + "run_tasks": false, }, ), resource.TestCheckResourceAttr("tfe_team_access.foobar", "access", "plan"), @@ -200,6 +249,7 @@ func TestAccTFETeamAccess_updateFromCustom(t *testing.T) { resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.state_versions", "read"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.sentinel_mocks", "none"), resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.workspace_locking", "false"), + resource.TestCheckResourceAttr("tfe_team_access.foobar", "permissions.0.run_tasks", "false"), ), }, }, @@ -283,6 +333,9 @@ func testAccCheckTFETeamAccessAttributesPermissionsAre(tmAccess *tfe.TeamAccess, if tmAccess.WorkspaceLocking != expectedPermissions["workspace_locking"].(bool) { return fmt.Errorf("Bad workspace-locking permission: Expected %s, Received %t", expectedPermissions["workspace_locking"], tmAccess.WorkspaceLocking) } + if tmAccess.RunTasks != expectedPermissions["run_tasks"].(bool) { + return fmt.Errorf("Bad run_tasks permission: Expected %s, Received %t", expectedPermissions["run_tasks"], tmAccess.RunTasks) + } return nil } } @@ -308,6 +361,30 @@ func testAccCheckTFETeamAccessDestroy(s *terraform.State) error { return nil } +func testAccTFETeamAccess_admin(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_team" "foobar" { + name = "team-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_workspace" "foobar" { + name = "workspace-test" + organization = tfe_organization.foobar.id +} + +resource "tfe_team_access" "foobar" { + access = "admin" + team_id = tfe_team.foobar.id + workspace_id = tfe_workspace.foobar.id +}`, rInt) +} + func testAccTFETeamAccess_write(rInt int) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { @@ -380,6 +457,7 @@ resource "tfe_team_access" "foobar" { state_versions = "read-outputs" sentinel_mocks = "none" workspace_locking = false + run_tasks = false } team_id = tfe_team.foobar.id workspace_id = tfe_workspace.foobar.id diff --git a/website/docs/d/team_access.html.markdown b/website/docs/d/team_access.html.markdown index ae340bfe4..aec54df3e 100644 --- a/website/docs/d/team_access.html.markdown +++ b/website/docs/d/team_access.html.markdown @@ -41,3 +41,4 @@ The `permissions` block contains: * `state_versions` - The permissions granted to state versions. Valid values are `none`, `read-outputs`, `read`, or `write` * `sentinel_mocks` - The permissions granted to Sentinel mocks. Valid values are `none` or `read` * `workspace_locking` - Whether permission is granted to manually lock the workspace or not. +* `run_tasks` - Whether permission is granted to manage workspace run tasks or not. diff --git a/website/docs/r/team_access.html.markdown b/website/docs/r/team_access.html.markdown index a468e81f6..552378601 100644 --- a/website/docs/r/team_access.html.markdown +++ b/website/docs/r/team_access.html.markdown @@ -48,6 +48,7 @@ The `permissions` block supports: * `state_versions` - (Required) The permission to grant the team on the workspace's state versions. Valid values are `none`, `read`, `read-outputs`, or `write`. * `sentinel_mocks` - (Required) The permission to grant the team on the workspace's generated Sentinel mocks, Valid values are `none` or `read`. * `workspace_locking` - (Required) Boolean determining whether or not to grant the team permission to manually lock/unlock the workspace. +* `run_tasks` - (Required) Whether permission is granted to manage workspace run tasks or not. -> **Note:** At least one of `access` or `permissions` _must_ be provided, but not both. Whichever is omitted will automatically reflect the state of the other.