Skip to content

Commit

Permalink
Merge pull request #1522 from hashicorp/mkam/TF-20798/project-owned-v…
Browse files Browse the repository at this point in the history
…arsets

Add support for project-owned variable sets
  • Loading branch information
mkam authored Dec 17, 2024
2 parents 88c7362 + 53a3f84 commit 11b74d2
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Unreleased

FEATURES:
* `r/tfe_variable_set`: Add `parent_project_id` attribute, by @mkam [#1522](https://github.com/hashicorp/terraform-provider-tfe/pull/1522)

## v0.61.0

DEPRECATIONS:
Expand Down
10 changes: 10 additions & 0 deletions internal/provider/data_source_variable_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ func dataSourceTFEVariableSet() *schema.Resource {
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"parent_project_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}
Expand Down Expand Up @@ -100,6 +106,10 @@ func dataSourceTFEVariableSetRead(d *schema.ResourceData, meta interface{}) erro
d.Set("global", vs.Global)
d.Set("priority", vs.Priority)

if vs.Parent != nil && vs.Parent.Project != nil {
d.Set("parent_project_id", vs.Parent.Project.ID)
}

// Only now include vars and workspaces to cut down on request load.
readOptions := tfe.VariableSetReadOptions{
Include: &[]tfe.VariableSetIncludeOpt{tfe.VariableSetWorkspaces, tfe.VariableSetVars},
Expand Down
49 changes: 49 additions & 0 deletions internal/provider/data_source_variable_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@ func TestAccTFEVariableSetsDataSource_full(t *testing.T) {
)
}

func TestAccTFEVariableSetsDataSource_ProjectOwned(t *testing.T) {
skipUnlessBeta(t)

rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
orgName := fmt.Sprintf("org-%d", rInt)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV5ProviderFactories: testAccMuxedProviders,
Steps: []resource.TestStep{
{
Config: testAccTFEVariableSetsDataSourceConfig_ProjectOwned(rInt),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.tfe_variable_set.project_owned", "id"),
resource.TestCheckResourceAttr(
"data.tfe_variable_set.project_owned", "organization", orgName),
resource.TestCheckResourceAttrPair(
"data.tfe_variable_set.project_owned", "parent_project_id", "tfe_project.foobar", "id"),
),
},
},
},
)
}

func testAccTFEVariableSetsDataSourceConfig_basic(rInt int) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
Expand Down Expand Up @@ -130,3 +155,27 @@ func testAccTFEVariableSetsDataSourceConfig_full(rInt int) string {
depends_on = [tfe_variable.envfoo, tfe_project_variable_set.foobar]
}`, rInt, rInt, rInt, rInt)
}

func testAccTFEVariableSetsDataSourceConfig_ProjectOwned(rInt int) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "org-%d"
email = "[email protected]"
}
resource "tfe_project" "foobar" {
organization = tfe_organization.foobar.id
name = "project-%d"
}
resource "tfe_variable_set" "project_owned" {
name = "project_owned_variable_set_test"
organization = tfe_organization.foobar.id
parent_project_id = tfe_project.foobar.id
}
data "tfe_variable_set" "project_owned" {
name = tfe_variable_set.project_owned.name
organization = tfe_variable_set.project_owned.organization
}
`, rInt, rInt)
}
49 changes: 47 additions & 2 deletions internal/provider/resource_tfe_variable_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package provider

import (
"context"
"fmt"
"log"
"regexp"
Expand All @@ -29,7 +30,16 @@ func resourceTFEVariableSet() *schema.Resource {
StateContext: schema.ImportStatePassthroughContext,
},

CustomizeDiff: customizeDiffIfProviderDefaultOrganizationChanged,
CustomizeDiff: func(c context.Context, d *schema.ResourceDiff, meta interface{}) error {
if err := customizeDiffIfProviderDefaultOrganizationChanged(c, d, meta); err != nil {
return err
}

if err := validateParentProjectID(d); err != nil {
return err
}
return nil
},

Schema: map[string]*schema.Schema{
"name": {
Expand Down Expand Up @@ -68,6 +78,13 @@ func resourceTFEVariableSet() *schema.Resource {
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"parent_project_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
}
}
Expand All @@ -89,6 +106,14 @@ func resourceTFEVariableSetCreate(d *schema.ResourceData, meta interface{}) erro
Priority: tfe.Bool(d.Get("priority").(bool)),
}

if parentProject, ok := d.GetOk("parent_project_id"); ok {
options.Parent = &tfe.Parent{
Project: &tfe.Project{
ID: parentProject.(string),
},
}
}

if description, descriptionSet := d.GetOk("description"); descriptionSet {
options.Description = tfe.String(description.(string))
}
Expand Down Expand Up @@ -151,6 +176,10 @@ func resourceTFEVariableSetRead(d *schema.ResourceData, meta interface{}) error
}
d.Set("workspace_ids", wids)

if variableSet.Parent != nil && variableSet.Parent.Project != nil {
d.Set("parent_project_id", variableSet.Parent.Project.ID)
}

return nil
}

Expand All @@ -168,7 +197,7 @@ func resourceTFEVariableSetUpdate(d *schema.ResourceData, meta interface{}) erro
log.Printf("[DEBUG] Update variable set: %s", d.Id())
_, err := config.Client.VariableSets.Update(ctx, d.Id(), &options)
if err != nil {
return fmt.Errorf("Error updateing variable %s: %w", d.Id(), err)
return fmt.Errorf("Error updating variable %s: %w", d.Id(), err)
}
}

Expand Down Expand Up @@ -212,3 +241,19 @@ func resourceTFEVariableSetDelete(d *schema.ResourceData, meta interface{}) erro
func warnWorkspaceIdsDeprecation() {
log.Printf("[WARN] The workspace_ids field of tfe_variable_set is deprecated as of release 0.33.0 and may be removed in a future version. The preferred method of associating a variable set to a workspace is by using the tfe_workspace_variable_set resource.")
}

func validateParentProjectID(d *schema.ResourceDiff) error {
_, ok := d.GetOk("parent_project_id")
if !ok {
return nil
}

// If parent_project_id is set, global must be false
if global, ok := d.GetOk("global"); ok {
if global.(bool) {
return fmt.Errorf("global must be 'false' when setting parent_project_id")
}
}

return nil
}
74 changes: 74 additions & 0 deletions internal/provider/resource_tfe_variable_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,34 @@ func TestAccTFEVariableSet_import(t *testing.T) {
})
}

func TestAccTFEVariableSet_project_owned(t *testing.T) {
skipUnlessBeta(t)
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckTFEVariableSetDestroy,
Steps: []resource.TestStep{
{
Config: testACCTFEVariableSet_ProjectOwned(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(
"tfe_variable_set.project_owned", "parent_project_id", "tfe_project.foobar", "id"),
),
},

{
Config: testACCTFEVariableSet_UpdateProjectOwned(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(
"tfe_variable_set.project_owned", "parent_project_id", "tfe_project.updated", "id"),
),
},
},
})
}

func testAccCheckTFEVariableSetExists(
n string, variableSet *tfe.VariableSet) resource.TestCheckFunc {
return func(s *terraform.State) error {
Expand Down Expand Up @@ -325,3 +353,49 @@ func testAccTFEVariableSet_update(rInt int) string {
organization = tfe_organization.foobar.id
}`, rInt)
}

func testACCTFEVariableSet_ProjectOwned(rInt int) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "tst-terraform-%d"
email = "[email protected]"
}
resource "tfe_project" "foobar" {
organization = tfe_organization.foobar.id
name = "tst-terraform-%d"
}
resource "tfe_variable_set" "project_owned" {
name = "project_owned_variable_set_test"
description = "a project-owned test variable set"
organization = tfe_organization.foobar.id
parent_project_id = tfe_project.foobar.id
}`, rInt, rInt)
}

func testACCTFEVariableSet_UpdateProjectOwned(rInt int) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "tst-terraform-%d"
email = "[email protected]"
}
resource "tfe_project" "foobar" {
organization = tfe_organization.foobar.id
name = "tst-terraform-%d"
}
resource "tfe_project" "updated" {
organization = tfe_organization.foobar.id
name = "updated-%d"
}
resource "tfe_variable_set" "project_owned" {
name = "project_owned_variable_set_test"
description = "a project-owned test variable set"
organization = tfe_organization.foobar.id
global = false
parent_project_id = tfe_project.updated.id
}`, rInt, rInt, rInt)
}
1 change: 1 addition & 0 deletions website/docs/d/variable_set.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ The following arguments are supported:
* `workspace_ids` - IDs of the workspaces that use the variable set.
* `variable_ids` - IDs of the variables attached to the variable set.
* `project_ids` - IDs of the projects that use the variable set.
* `parent_project_id` - ID of the project that owns the variable set.
6 changes: 5 additions & 1 deletion website/docs/r/project_variable_set.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ description: |-

# tfe_project_variable_set

Adds and removes variable sets from a project
Adds and removes a project from a variable set's scope.

-> **Note:** This resource controls whether a project has access to a variable set, not whether
a project owns the variable set. Ownership is specified by setting the `parent_project_id` on the
`tfe_variable_set` resource.

## Example Usage

Expand Down
60 changes: 60 additions & 0 deletions website/docs/r/variable_set.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,64 @@ resource "tfe_variable" "test-b" {
}
```

Creating a project-owned variable set that is applied to all workspaces in the project:

```hcl
resource "tfe_organization" "test" {
name = "my-org-name"
email = "[email protected]"
}
resource "tfe_project" "test" {
organization = tfe_organization.test.name
name = "projectname"
}
resource "tfe_variable_set" "test" {
name = "Project-owned Varset"
description = "Varset that is owned and managed by a project."
organization = tfe_organization.test.name
parent_project_id = tfe_project.test.id
}
resource "tfe_project_variable_set" "test" {
project_id = tfe_project.test.id
variable_set_id = tfe_variable_set.test.id
}
```

Creating a project-owned variable set that is applied to specific workspaces:

```hcl
resource "tfe_organization" "test" {
name = "my-org-name"
email = "[email protected]"
}
resource "tfe_project" "test" {
organization = tfe_organization.test.name
name = "projectname"
}
resource "tfe_workspace" "test" {
name = "my-workspace-name"
organization = tfe_organization.test.name
project_id = tfe_project.test.id
}
resource "tfe_variable_set" "test" {
name = "Project-owned Varset"
description = "Varset that is owned and managed by a project."
organization = tfe_organization.test.name
parent_project_id = tfe_project.test.id
}
resource "tfe_workspace_variable_set" "test" {
workspace_id = tfe_workspace.test.id
variable_set_id = tfe_variable_set.test.id
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -139,6 +197,8 @@ The following arguments are supported:
Must not be set if `global` is set. This argument is mutually exclusive with using the resource
[tfe_workspace_variable_set](workspace_variable_set.html) which is the preferred method of associating a workspace
with a variable set.
* `parent_project_id` - (Optional) ID of the project that should own the variable set. If set, than the value of `global` must be `false`.
To assign whether a variable set should be applied to a project, use the [`tfe_project_variable_set`](project_variable_set.html) resource.

## Attributes Reference

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/workspace_variable_set.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: |-

# tfe_workspace_variable_set

Adds and removes variable sets from a workspace
Adds and removes a workspace from a variable set's scope.

-> **Note:** `tfe_variable_set` has a deprecated argument `workspace_ids` that should not be used alongside this resource. They attempt to manage the same attachments and are mutually exclusive.

Expand Down

0 comments on commit 11b74d2

Please sign in to comment.