Skip to content

Commit 11b74d2

Browse files
authored
Merge pull request #1522 from hashicorp/mkam/TF-20798/project-owned-varsets
Add support for project-owned variable sets
2 parents 88c7362 + 53a3f84 commit 11b74d2

9 files changed

+250
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Unreleased
22

3+
FEATURES:
4+
* `r/tfe_variable_set`: Add `parent_project_id` attribute, by @mkam [#1522](https://github.com/hashicorp/terraform-provider-tfe/pull/1522)
5+
36
## v0.61.0
47

58
DEPRECATIONS:

internal/provider/data_source_variable_set.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ func dataSourceTFEVariableSet() *schema.Resource {
6565
Computed: true,
6666
Elem: &schema.Schema{Type: schema.TypeString},
6767
},
68+
69+
"parent_project_id": {
70+
Type: schema.TypeString,
71+
Optional: true,
72+
Computed: true,
73+
},
6874
},
6975
}
7076
}
@@ -100,6 +106,10 @@ func dataSourceTFEVariableSetRead(d *schema.ResourceData, meta interface{}) erro
100106
d.Set("global", vs.Global)
101107
d.Set("priority", vs.Priority)
102108

109+
if vs.Parent != nil && vs.Parent.Project != nil {
110+
d.Set("parent_project_id", vs.Parent.Project.ID)
111+
}
112+
103113
// Only now include vars and workspaces to cut down on request load.
104114
readOptions := tfe.VariableSetReadOptions{
105115
Include: &[]tfe.VariableSetIncludeOpt{tfe.VariableSetWorkspaces, tfe.VariableSetVars},

internal/provider/data_source_variable_set_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,31 @@ func TestAccTFEVariableSetsDataSource_full(t *testing.T) {
6767
)
6868
}
6969

70+
func TestAccTFEVariableSetsDataSource_ProjectOwned(t *testing.T) {
71+
skipUnlessBeta(t)
72+
73+
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
74+
orgName := fmt.Sprintf("org-%d", rInt)
75+
76+
resource.Test(t, resource.TestCase{
77+
PreCheck: func() { testAccPreCheck(t) },
78+
ProtoV5ProviderFactories: testAccMuxedProviders,
79+
Steps: []resource.TestStep{
80+
{
81+
Config: testAccTFEVariableSetsDataSourceConfig_ProjectOwned(rInt),
82+
Check: resource.ComposeAggregateTestCheckFunc(
83+
resource.TestCheckResourceAttrSet("data.tfe_variable_set.project_owned", "id"),
84+
resource.TestCheckResourceAttr(
85+
"data.tfe_variable_set.project_owned", "organization", orgName),
86+
resource.TestCheckResourceAttrPair(
87+
"data.tfe_variable_set.project_owned", "parent_project_id", "tfe_project.foobar", "id"),
88+
),
89+
},
90+
},
91+
},
92+
)
93+
}
94+
7095
func testAccTFEVariableSetsDataSourceConfig_basic(rInt int) string {
7196
return fmt.Sprintf(`
7297
resource "tfe_organization" "foobar" {
@@ -130,3 +155,27 @@ func testAccTFEVariableSetsDataSourceConfig_full(rInt int) string {
130155
depends_on = [tfe_variable.envfoo, tfe_project_variable_set.foobar]
131156
}`, rInt, rInt, rInt, rInt)
132157
}
158+
159+
func testAccTFEVariableSetsDataSourceConfig_ProjectOwned(rInt int) string {
160+
return fmt.Sprintf(`
161+
resource "tfe_organization" "foobar" {
162+
name = "org-%d"
163+
164+
}
165+
resource "tfe_project" "foobar" {
166+
organization = tfe_organization.foobar.id
167+
name = "project-%d"
168+
}
169+
170+
resource "tfe_variable_set" "project_owned" {
171+
name = "project_owned_variable_set_test"
172+
organization = tfe_organization.foobar.id
173+
parent_project_id = tfe_project.foobar.id
174+
}
175+
176+
data "tfe_variable_set" "project_owned" {
177+
name = tfe_variable_set.project_owned.name
178+
organization = tfe_variable_set.project_owned.organization
179+
}
180+
`, rInt, rInt)
181+
}

internal/provider/resource_tfe_variable_set.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package provider
1010

1111
import (
12+
"context"
1213
"fmt"
1314
"log"
1415
"regexp"
@@ -29,7 +30,16 @@ func resourceTFEVariableSet() *schema.Resource {
2930
StateContext: schema.ImportStatePassthroughContext,
3031
},
3132

32-
CustomizeDiff: customizeDiffIfProviderDefaultOrganizationChanged,
33+
CustomizeDiff: func(c context.Context, d *schema.ResourceDiff, meta interface{}) error {
34+
if err := customizeDiffIfProviderDefaultOrganizationChanged(c, d, meta); err != nil {
35+
return err
36+
}
37+
38+
if err := validateParentProjectID(d); err != nil {
39+
return err
40+
}
41+
return nil
42+
},
3343

3444
Schema: map[string]*schema.Schema{
3545
"name": {
@@ -68,6 +78,13 @@ func resourceTFEVariableSet() *schema.Resource {
6878
Computed: true,
6979
Elem: &schema.Schema{Type: schema.TypeString},
7080
},
81+
82+
"parent_project_id": {
83+
Type: schema.TypeString,
84+
Optional: true,
85+
Computed: true,
86+
ForceNew: true,
87+
},
7188
},
7289
}
7390
}
@@ -89,6 +106,14 @@ func resourceTFEVariableSetCreate(d *schema.ResourceData, meta interface{}) erro
89106
Priority: tfe.Bool(d.Get("priority").(bool)),
90107
}
91108

109+
if parentProject, ok := d.GetOk("parent_project_id"); ok {
110+
options.Parent = &tfe.Parent{
111+
Project: &tfe.Project{
112+
ID: parentProject.(string),
113+
},
114+
}
115+
}
116+
92117
if description, descriptionSet := d.GetOk("description"); descriptionSet {
93118
options.Description = tfe.String(description.(string))
94119
}
@@ -151,6 +176,10 @@ func resourceTFEVariableSetRead(d *schema.ResourceData, meta interface{}) error
151176
}
152177
d.Set("workspace_ids", wids)
153178

179+
if variableSet.Parent != nil && variableSet.Parent.Project != nil {
180+
d.Set("parent_project_id", variableSet.Parent.Project.ID)
181+
}
182+
154183
return nil
155184
}
156185

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

@@ -212,3 +241,19 @@ func resourceTFEVariableSetDelete(d *schema.ResourceData, meta interface{}) erro
212241
func warnWorkspaceIdsDeprecation() {
213242
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.")
214243
}
244+
245+
func validateParentProjectID(d *schema.ResourceDiff) error {
246+
_, ok := d.GetOk("parent_project_id")
247+
if !ok {
248+
return nil
249+
}
250+
251+
// If parent_project_id is set, global must be false
252+
if global, ok := d.GetOk("global"); ok {
253+
if global.(bool) {
254+
return fmt.Errorf("global must be 'false' when setting parent_project_id")
255+
}
256+
}
257+
258+
return nil
259+
}

internal/provider/resource_tfe_variable_set_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,34 @@ func TestAccTFEVariableSet_import(t *testing.T) {
144144
})
145145
}
146146

147+
func TestAccTFEVariableSet_project_owned(t *testing.T) {
148+
skipUnlessBeta(t)
149+
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
150+
151+
resource.Test(t, resource.TestCase{
152+
PreCheck: func() { testAccPreCheck(t) },
153+
Providers: testAccProviders,
154+
CheckDestroy: testAccCheckTFEVariableSetDestroy,
155+
Steps: []resource.TestStep{
156+
{
157+
Config: testACCTFEVariableSet_ProjectOwned(rInt),
158+
Check: resource.ComposeTestCheckFunc(
159+
resource.TestCheckResourceAttrPair(
160+
"tfe_variable_set.project_owned", "parent_project_id", "tfe_project.foobar", "id"),
161+
),
162+
},
163+
164+
{
165+
Config: testACCTFEVariableSet_UpdateProjectOwned(rInt),
166+
Check: resource.ComposeTestCheckFunc(
167+
resource.TestCheckResourceAttrPair(
168+
"tfe_variable_set.project_owned", "parent_project_id", "tfe_project.updated", "id"),
169+
),
170+
},
171+
},
172+
})
173+
}
174+
147175
func testAccCheckTFEVariableSetExists(
148176
n string, variableSet *tfe.VariableSet) resource.TestCheckFunc {
149177
return func(s *terraform.State) error {
@@ -325,3 +353,49 @@ func testAccTFEVariableSet_update(rInt int) string {
325353
organization = tfe_organization.foobar.id
326354
}`, rInt)
327355
}
356+
357+
func testACCTFEVariableSet_ProjectOwned(rInt int) string {
358+
return fmt.Sprintf(`
359+
resource "tfe_organization" "foobar" {
360+
name = "tst-terraform-%d"
361+
362+
}
363+
364+
resource "tfe_project" "foobar" {
365+
organization = tfe_organization.foobar.id
366+
name = "tst-terraform-%d"
367+
}
368+
369+
resource "tfe_variable_set" "project_owned" {
370+
name = "project_owned_variable_set_test"
371+
description = "a project-owned test variable set"
372+
organization = tfe_organization.foobar.id
373+
parent_project_id = tfe_project.foobar.id
374+
}`, rInt, rInt)
375+
}
376+
377+
func testACCTFEVariableSet_UpdateProjectOwned(rInt int) string {
378+
return fmt.Sprintf(`
379+
resource "tfe_organization" "foobar" {
380+
name = "tst-terraform-%d"
381+
382+
}
383+
384+
resource "tfe_project" "foobar" {
385+
organization = tfe_organization.foobar.id
386+
name = "tst-terraform-%d"
387+
}
388+
389+
resource "tfe_project" "updated" {
390+
organization = tfe_organization.foobar.id
391+
name = "updated-%d"
392+
}
393+
394+
resource "tfe_variable_set" "project_owned" {
395+
name = "project_owned_variable_set_test"
396+
description = "a project-owned test variable set"
397+
organization = tfe_organization.foobar.id
398+
global = false
399+
parent_project_id = tfe_project.updated.id
400+
}`, rInt, rInt, rInt)
401+
}

website/docs/d/variable_set.html.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ The following arguments are supported:
3838
* `workspace_ids` - IDs of the workspaces that use the variable set.
3939
* `variable_ids` - IDs of the variables attached to the variable set.
4040
* `project_ids` - IDs of the projects that use the variable set.
41+
* `parent_project_id` - ID of the project that owns the variable set.

website/docs/r/project_variable_set.html.markdown

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ description: |-
77

88
# tfe_project_variable_set
99

10-
Adds and removes variable sets from a project
10+
Adds and removes a project from a variable set's scope.
11+
12+
-> **Note:** This resource controls whether a project has access to a variable set, not whether
13+
a project owns the variable set. Ownership is specified by setting the `parent_project_id` on the
14+
`tfe_variable_set` resource.
1115

1216
## Example Usage
1317

website/docs/r/variable_set.html.markdown

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,64 @@ resource "tfe_variable" "test-b" {
126126
}
127127
```
128128

129+
Creating a project-owned variable set that is applied to all workspaces in the project:
130+
131+
```hcl
132+
resource "tfe_organization" "test" {
133+
name = "my-org-name"
134+
135+
}
136+
137+
resource "tfe_project" "test" {
138+
organization = tfe_organization.test.name
139+
name = "projectname"
140+
}
141+
142+
resource "tfe_variable_set" "test" {
143+
name = "Project-owned Varset"
144+
description = "Varset that is owned and managed by a project."
145+
organization = tfe_organization.test.name
146+
parent_project_id = tfe_project.test.id
147+
}
148+
149+
resource "tfe_project_variable_set" "test" {
150+
project_id = tfe_project.test.id
151+
variable_set_id = tfe_variable_set.test.id
152+
}
153+
```
154+
155+
Creating a project-owned variable set that is applied to specific workspaces:
156+
157+
```hcl
158+
resource "tfe_organization" "test" {
159+
name = "my-org-name"
160+
161+
}
162+
163+
resource "tfe_project" "test" {
164+
organization = tfe_organization.test.name
165+
name = "projectname"
166+
}
167+
168+
resource "tfe_workspace" "test" {
169+
name = "my-workspace-name"
170+
organization = tfe_organization.test.name
171+
project_id = tfe_project.test.id
172+
}
173+
174+
resource "tfe_variable_set" "test" {
175+
name = "Project-owned Varset"
176+
description = "Varset that is owned and managed by a project."
177+
organization = tfe_organization.test.name
178+
parent_project_id = tfe_project.test.id
179+
}
180+
181+
resource "tfe_workspace_variable_set" "test" {
182+
workspace_id = tfe_workspace.test.id
183+
variable_set_id = tfe_variable_set.test.id
184+
}
185+
```
186+
129187
## Argument Reference
130188

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

143203
## Attributes Reference
144204

website/docs/r/workspace_variable_set.html.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: |-
77

88
# tfe_workspace_variable_set
99

10-
Adds and removes variable sets from a workspace
10+
Adds and removes a workspace from a variable set's scope.
1111

1212
-> **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.
1313

0 commit comments

Comments
 (0)