Skip to content

Commit

Permalink
Allow tfe_organization_membership resource to be imported by org name…
Browse files Browse the repository at this point in the history
… and user email
  • Loading branch information
JarrettSpiker committed Dec 24, 2022
1 parent 317355b commit 0f3a454
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 3 deletions.
31 changes: 30 additions & 1 deletion tfe/resource_tfe_organization_membership.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package tfe

import (
"context"
"fmt"
"log"
"strings"

tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -14,7 +16,7 @@ func resourceTFEOrganizationMembership() *schema.Resource {
Read: resourceTFEOrganizationMembershipRead,
Delete: resourceTFEOrganizationMembershipDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
StateContext: resourceTFEOrganizationMembershipImporter,
},

Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -108,3 +110,30 @@ func resourceTFEOrganizationMembershipDelete(d *schema.ResourceData, meta interf

return nil
}

func resourceTFEOrganizationMembershipImporter(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
tfeClient := meta.(*tfe.Client)

// Import formats:
// - <ORGANIZATION MEMBERSHIP ID>
// - <organization name>/<user email>
s := strings.Split(d.Id(), "/")
if len(s) >= 3 {
return nil, fmt.Errorf(
"invalid organization membership input format: %s (expected <ORGANIZATION>/<USER EMAIL> or <ORGANIZATION MEMBERSHIP ID>)",
d.Id(),
)
} else if len(s) == 2 {
org := s[0]
email := s[1]
orgMembership, err := fetchOrganizationMemberByNameOrEmail(ctx, tfeClient, org, "", email)
if err != nil {
return nil, fmt.Errorf(
"error retrieving user with email %s from organization %s: %w", email, org, err)
}

d.SetId(orgMembership.ID)
}

return []*schema.ResourceData{d}, nil
}
76 changes: 75 additions & 1 deletion tfe/resource_tfe_organization_membership_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tfe
import (
"fmt"
"math/rand"
"regexp"
"testing"
"time"

Expand Down Expand Up @@ -40,7 +41,7 @@ func TestAccTFEOrganizationMembership_basic(t *testing.T) {
})
}

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

resource.Test(t, resource.TestCase{
Expand All @@ -60,6 +61,66 @@ func TestAccTFEOrganizationMembershipImport(t *testing.T) {
})
}

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

orgName := fmt.Sprintf("tst-terraform-%d", rInt)
email := "testuser@hashicorp.com"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckTFEOrganizationMembershipDestroy,
Steps: []resource.TestStep{
{
Config: testAccTFEOrganizationMembership_nameAndEmail(orgName, email),
},
{
ResourceName: "tfe_organization_membership.foobar",
ImportState: true,
ImportStateId: fmt.Sprintf("%s/%s", orgName, email),
ImportStateVerify: true,
},
},
})
}

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

orgName := fmt.Sprintf("tst-terraform-%d", rInt)
email := "testuser@hashicorp.com"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckTFEOrganizationMembershipDestroy,
Steps: []resource.TestStep{
{
Config: testAccTFEOrganizationMembership_nameAndEmail(orgName, email),
},
{
ResourceName: "tfe_organization_membership.foobar",
ImportState: true,
ImportStateId: fmt.Sprintf("%s/%s/someOtherString", orgName, email),
ExpectError: regexp.MustCompile("invalid organization membership input format"),
},
{
ResourceName: "tfe_organization_membership.foobar",
ImportState: true,
ImportStateId: fmt.Sprintf("invalid-org-%d/%s", rInt, email),
ExpectError: regexp.MustCompile(fmt.Sprintf("error retrieving user with email %s from organization invalid-org-%d", email, rInt)),
},
{
ResourceName: "tfe_organization_membership.foobar",
ImportState: true,
ImportStateId: fmt.Sprintf("%s/invalidEmail", orgName),
ExpectError: regexp.MustCompile(fmt.Sprintf("error retrieving user with email invalidEmail from organization %s", orgName)),
},
},
})
}

func testAccCheckTFEOrganizationMembershipExists(
n string, membership *tfe.OrganizationMembership) resource.TestCheckFunc {
return func(s *terraform.State) error {
Expand Down Expand Up @@ -143,3 +204,16 @@ resource "tfe_organization_membership" "foobar" {
organization = tfe_organization.foobar.id
}`, rInt)
}

func testAccTFEOrganizationMembership_nameAndEmail(orgName string, email string) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "%s"
email = "admin@company.com"
}
resource "tfe_organization_membership" "foobar" {
email = "%s"
organization = tfe_organization.foobar.id
}`, orgName, email)
}
8 changes: 7 additions & 1 deletion website/docs/r/organization_membership.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,15 @@ In addition to all arguments above, the following attributes are exported:
* `user_id` - The ID of the user associated with the organization membership.
* `username` - The username of the user associated with the organization membership.

Organization memberships can be imported; use `<ORGANIZATION MEMBERSHIP ID>` as the import ID. For
## Import

Organization memberships can be imported using `<ORGANIZATION>/<USER EMAIL>` or `<ORGANIZATION MEMBERSHIP ID>` as the import ID. For
example:

```shell
terraform import tfe_organization_membership.test my-org-name/user@example.com
```

```shell
terraform import tfe_organization_membership.test ou-wAs3zYmWAhYK7peR
```

0 comments on commit 0f3a454

Please sign in to comment.