Skip to content

Commit fd7b9c3

Browse files
committed
Add user_token_enabled attribute to organizations
1 parent 249013d commit fd7b9c3

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/hashicorp/go-multierror v1.1.1 // indirect
1515
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
1616
github.com/hashicorp/go-slug v0.16.7
17-
github.com/hashicorp/go-tfe v1.93.0
17+
github.com/hashicorp/go-tfe v1.93.1-0.20250924155324-62ba5e85f6fb
1818
github.com/hashicorp/go-version v1.7.0
1919
github.com/hashicorp/hcl v1.0.0
2020
github.com/hashicorp/hcl/v2 v2.24.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ github.com/hashicorp/go-slug v0.16.7 h1:sBW8y1sX+JKOZKu9a+DQZuWDVaX+U9KFnk6+VDQv
7575
github.com/hashicorp/go-slug v0.16.7/go.mod h1:X5fm++dL59cDOX8j48CqHr4KARTQau7isGh0ZVxJB5I=
7676
github.com/hashicorp/go-tfe v1.93.0 h1:hJubwn1xNCo1iBO66iQkjyC+skR61cK1AQUj4O9vvuI=
7777
github.com/hashicorp/go-tfe v1.93.0/go.mod h1:QwqgCD5seztgp76CP7F0POJPflQNSqjIvBpVohg9X50=
78+
github.com/hashicorp/go-tfe v1.93.1-0.20250923213433-bf749f02529d h1:aYUoNBYzQXEARIeOnHvS4MXvZAskNe7p0L4vWW4Jr2E=
79+
github.com/hashicorp/go-tfe v1.93.1-0.20250923213433-bf749f02529d/go.mod h1:QwqgCD5seztgp76CP7F0POJPflQNSqjIvBpVohg9X50=
80+
github.com/hashicorp/go-tfe v1.93.1-0.20250924155324-62ba5e85f6fb h1:ygCjBBxFY65m2jmo34Cta5NCFBa4HngicWcWYvZY2pk=
81+
github.com/hashicorp/go-tfe v1.93.1-0.20250924155324-62ba5e85f6fb/go.mod h1:QwqgCD5seztgp76CP7F0POJPflQNSqjIvBpVohg9X50=
7882
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
7983
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
8084
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=

internal/provider/provider_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ func getClientUsingEnv() (*tfe.Client, error) {
157157
return tfeClient, nil
158158
}
159159

160+
func getClientWithToken(token string) (*tfe.Client, error) {
161+
hostname := client.DefaultHostname
162+
if os.Getenv("TFE_HOSTNAME") != "" {
163+
hostname = os.Getenv("TFE_HOSTNAME")
164+
}
165+
166+
tfeClient, err := client.GetClient(hostname, token, defaultSSLSkipVerify)
167+
if err != nil {
168+
return nil, fmt.Errorf("Error getting client: %w", err)
169+
}
170+
return tfeClient, nil
171+
}
172+
160173
func TestProvider(t *testing.T) {
161174
if err := Provider().InternalValidate(); err != nil {
162175
t.Fatalf("err: %s", err)

internal/provider/resource_tfe_organization.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ func resourceTFEOrganization() *schema.Resource {
108108
Optional: true,
109109
Default: true,
110110
},
111+
"user_tokens_enabled": {
112+
Type: schema.TypeBool,
113+
Default: true,
114+
Optional: true,
115+
},
111116
},
112117
}
113118
}
@@ -164,6 +169,9 @@ func resourceTFEOrganizationRead(d *schema.ResourceData, meta interface{}) error
164169
d.Set("assessments_enforced", org.AssessmentsEnforced)
165170
d.Set("allow_force_delete_workspaces", org.AllowForceDeleteWorkspaces)
166171
d.Set("speculative_plan_management_enabled", org.SpeculativePlanManagementEnabled)
172+
if org.UserTokensEnabled != nil {
173+
d.Set("user_tokens_enabled", org.UserTokensEnabled)
174+
}
167175

168176
if org.DefaultProject != nil {
169177
d.Set("default_project_id", org.DefaultProject.ID)
@@ -233,6 +241,11 @@ func resourceTFEOrganizationUpdate(d *schema.ResourceData, meta interface{}) err
233241
options.SpeculativePlanManagementEnabled = tfe.Bool(speculativePlanManagementEnabled.(bool))
234242
}
235243

244+
// If user_tokens_enabled is supplied, set it using the options struct.
245+
if userTokensEnabled, ok := d.GetOkExists("user_tokens_enabled"); ok {
246+
options.UserTokensEnabled = tfe.Bool(userTokensEnabled.(bool))
247+
}
248+
236249
log.Printf("[DEBUG] Update configuration of organization: %s", d.Id())
237250
org, err := config.Client.Organizations.Update(ctx, d.Id(), options)
238251
if err != nil {

internal/provider/resource_tfe_organization_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ func TestAccTFEOrganization_full(t *testing.T) {
8585
"tfe_organization.foobar", "allow_force_delete_workspaces", "false"),
8686
resource.TestCheckResourceAttr(
8787
"tfe_organization.foobar", "speculative_plan_management_enabled", "true"),
88+
resource.TestCheckResourceAttr(
89+
"tfe_organization.foobar", "user_tokens_enabled", "true"),
8890
),
8991
},
9092
},
@@ -205,6 +207,80 @@ func TestAccTFEOrganization_update_costEstimation(t *testing.T) {
205207
})
206208
}
207209

210+
func TestAccTFEOrganization_user_tokens_enabled(t *testing.T) {
211+
// this test is a bit tricky because once user tokens are disabled, we cannot use a user token to re-enable them
212+
// through the API.
213+
// Therefore, we need to create an org, generate an owners team token for that org, and then use that token
214+
// in the go-tfe client to test the user_tokens_enabled setting.
215+
216+
org := &tfe.Organization{}
217+
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
218+
orgName := fmt.Sprintf("tst-terraform-%d", rInt)
219+
220+
resource.Test(t, resource.TestCase{
221+
PreCheck: func() { testAccPreCheck(t) },
222+
ProtoV6ProviderFactories: testAccMuxedProviders,
223+
CheckDestroy: testAccCheckTFEOrganizationDestroy,
224+
Steps: []resource.TestStep{
225+
{
226+
Config: testAccTFEOrganization_basic(rInt),
227+
Check: resource.ComposeTestCheckFunc(
228+
testAccCheckTFEOrganizationExists(
229+
"tfe_organization.foobar", org),
230+
testAccCheckTFEOrganizationAttributesBasic(org, orgName),
231+
resource.TestCheckResourceAttr(
232+
"tfe_organization.foobar", "user_tokens_enabled", "true"),
233+
),
234+
},
235+
{
236+
PreConfig: func() {
237+
// create a team token for the owners team in the org,
238+
// then set the provider token to that value
239+
ownersTeams, err := testAccConfiguredClient.Client.Teams.List(ctx, org.Name, &tfe.TeamListOptions{
240+
Names: []string{"owners"},
241+
})
242+
if err != nil {
243+
t.Fatal(err)
244+
}
245+
if len(ownersTeams.Items) != 1 {
246+
t.Fatalf("expected to find 1 owners team, found %d", len(ownersTeams.Items))
247+
}
248+
ownersTeam := ownersTeams.Items[0]
249+
250+
teamToken, err := testAccConfiguredClient.Client.TeamTokens.Create(ctx, ownersTeam.ID)
251+
if err != nil {
252+
t.Fatal(err)
253+
}
254+
255+
tfeClient, err := getClientWithToken(teamToken.ID)
256+
if err != nil {
257+
t.Fatal(err)
258+
}
259+
260+
testAccConfiguredClient = &ConfiguredClient{
261+
Client: tfeClient,
262+
Organization: org.Name,
263+
}
264+
},
265+
Config: testAccTFEOrganization_userTokensEnabled(rInt, false),
266+
Check: resource.ComposeTestCheckFunc(
267+
resource.TestCheckResourceAttr(
268+
"tfe_organization.foobar", "user_tokens_enabled", "false"),
269+
testAccCheckTFEOrganizationUserTokensEnabled(org, orgName, false),
270+
),
271+
},
272+
{
273+
Config: testAccTFEOrganization_userTokensEnabled(rInt, true),
274+
Check: resource.ComposeTestCheckFunc(
275+
resource.TestCheckResourceAttr(
276+
"tfe_organization.foobar", "user_tokens_enabled", "true"),
277+
testAccCheckTFEOrganizationUserTokensEnabled(org, orgName, true),
278+
),
279+
},
280+
},
281+
})
282+
}
283+
208284
func TestAccTFEOrganization_case(t *testing.T) {
209285
org := &tfe.Organization{}
210286
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
@@ -343,6 +419,20 @@ func testAccCheckTFEOrganizationAttributesFull(
343419
}
344420
}
345421

422+
func testAccCheckTFEOrganizationUserTokensEnabled(
423+
org *tfe.Organization, expectedOrgName string, expectedUserTokensEnabled bool) resource.TestCheckFunc {
424+
return func(s *terraform.State) error {
425+
if org.Name != expectedOrgName {
426+
return fmt.Errorf("Bad name: %s", org.Name)
427+
}
428+
429+
if org.UserTokensEnabled != nil && *org.UserTokensEnabled != expectedUserTokensEnabled {
430+
return fmt.Errorf("Bad user tokens enabled: %v", org.UserTokensEnabled)
431+
}
432+
return nil
433+
}
434+
}
435+
346436
func testAccCheckTFEOrganizationAttributesUpdated(
347437
org *tfe.Organization, expectedOrgName string, expectedCostEstimationEnabled bool) resource.TestCheckFunc {
348438
return func(s *terraform.State) error {
@@ -441,3 +531,12 @@ resource "tfe_organization" "foobar" {
441531
allow_force_delete_workspaces = %t
442532
}`, orgName, orgEmail, costEstimationEnabled, assessmentsEnforced, allowForceDeleteWorkspaces)
443533
}
534+
535+
func testAccTFEOrganization_userTokensEnabled(rInt int, userTokensEnabled bool) string {
536+
return fmt.Sprintf(`
537+
resource "tfe_organization" "foobar" {
538+
name = "tst-terraform-%d"
539+
540+
user_tokens_enabled = %t
541+
}`, rInt, userTokensEnabled)
542+
}

0 commit comments

Comments
 (0)