Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistent IAM resource imports. #835

Merged
merged 6 commits into from
Dec 11, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions google/iam.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package google

import (
"errors"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1"
"log"
"strings"
"time"
)

Expand Down Expand Up @@ -34,6 +36,83 @@ type ResourceIamUpdater interface {
type newResourceIamUpdaterFunc func(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error)
type iamPolicyModifyFunc func(p *cloudresourcemanager.Policy) error

// This method parses identifiers specific to the resource (d.GetId()) into the ResourceData
// object, so that it can be given to the resource's Read method. Externally, this is wrapped
// into schema.StateFunc functions - one each for a _member, a _binding, and a _policy. Any
// GCP resource supporting IAM policy might support one, two, or all of these. Any GCP resource
// for which an implementation of this interface exists could support any of the three.

type resourceIdParserFunc func(d *schema.ResourceData, config *Config) error

func iamPolicyImport(resourceIdParser resourceIdParserFunc) schema.StateFunc {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider moving the iamPolicyImport, iamPolicyBinding and iamPolicyMember functions into their respective resource_iam_* file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call! Done.

return func(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
if resourceIdParser == nil {
return nil, errors.New("Import not supported for this IAM resource.")
}
config := m.(*Config)
err := resourceIdParser(d, config)
if err != nil {
return nil, err
}
return []*schema.ResourceData{d}, nil
}
}

func iamBindingImport(resourceIdParser resourceIdParserFunc) schema.StateFunc {
return func(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
if resourceIdParser == nil {
return nil, errors.New("Import not supported for this IAM resource.")
}
config := m.(*Config)
s := strings.Split(d.Id(), " ")
if len(s) != 2 {
d.SetId("")
return nil, fmt.Errorf("Wrong number of parts to Binding id %s; expected 'resource_name role'.", s)
}
id, role := s[0], s[1]
d.SetId(id)
d.Set("role", role)
err := resourceIdParser(d, config)
if err != nil {
return nil, err
}
// It is possible to return multiple bindings, since we can learn about all the bindings
// for this resource here. Unfortunately, `terraform import` has some messy behavior here -
// there's no way to know at this point which resource is being imported, so it's not possible
// to order this list in a useful way. In the event of a complex set of bindings, the user
// will have a terribly confusing set of imported resources and no way to know what matches
// up to what. And since the only users who will do a terraform import on their IAM bindings
// are users who aren't too familiar with Google Cloud IAM (because a "create" for bindings or
// members is idempotent), it's reasonable to expect that the user will be very alarmed by the
// plan that terraform will output which mentions destroying a dozen-plus IAM bindings. With
// that in mind, we return only the binding that matters.
return []*schema.ResourceData{d}, nil
}
}

func iamMemberImport(resourceIdParser resourceIdParserFunc) schema.StateFunc {
return func(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
if resourceIdParser == nil {
return nil, errors.New("Import not supported for this IAM resource.")
}
config := m.(*Config)
s := strings.Split(d.Id(), " ")
if len(s) != 3 {
d.SetId("")
return nil, fmt.Errorf("Wrong number of parts to Member id %s; expected 'resource_name role username'.", s)
}
id, role, member := s[0], s[1], s[2]
d.SetId(id)
d.Set("role", role)
d.Set("member", member)
err := resourceIdParser(d, config)
if err != nil {
return nil, err
}
return []*schema.ResourceData{d}, nil
}
}

func iamPolicyReadModifyWrite(updater ResourceIamUpdater, modify iamPolicyModifyFunc) error {
mutexKey := updater.GetMutexKey()
mutexKV.Lock(mutexKey)
Expand Down
5 changes: 5 additions & 0 deletions google/iam_folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ func NewFolderIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpd
}, nil
}

func FolderIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("folder", d.Id())
return nil
}

func (u *FolderIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientResourceManagerV2Beta1.Folders.GetIamPolicy(u.folderId,
&resourceManagerV2Beta1.GetIamPolicyRequest{}).Do()
Expand Down
5 changes: 5 additions & 0 deletions google/iam_kms_crypto_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func NewKmsCryptoKeyIamUpdater(d *schema.ResourceData, config *Config) (Resource
}, nil
}

func CryptoIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("crypto_key_id", d.Id())
return nil
}

func (u *KmsCryptoKeyIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientKms.Projects.Locations.KeyRings.CryptoKeys.GetIamPolicy(u.resourceId).Do()

Expand Down
5 changes: 5 additions & 0 deletions google/iam_kms_key_ring.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func NewKmsKeyRingIamUpdater(d *schema.ResourceData, config *Config) (ResourceIa
}, nil
}

func KeyRingIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("key_ring_id", d.Id())
return nil
}

func resourceManagerToKmsPolicy(p *cloudresourcemanager.Policy) (policy *cloudkms.Policy, err error) {
policy = &cloudkms.Policy{}

Expand Down
5 changes: 5 additions & 0 deletions google/iam_organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func NewOrganizationIamUpdater(d *schema.ResourceData, config *Config) (Resource
}, nil
}

func OrgIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("org_id", d.Id())
return nil
}

func (u *OrganizationIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientResourceManager.Organizations.GetIamPolicy("organizations/"+u.resourceId, &cloudresourcemanager.GetIamPolicyRequest{}).Do()
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions google/iam_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ func NewProjectIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUp
}, nil
}

func ProjectIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("project", d.Id())
return nil
}

func (u *ProjectIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientResourceManager.Projects.GetIamPolicy(u.resourceId,
&cloudresourcemanager.GetIamPolicyRequest{}).Do()
Expand Down
75 changes: 75 additions & 0 deletions google/import_google_kms_crypto_key_iam_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccKmsCryptoKeyIamMember_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
projectId := acctest.RandomWithPrefix("tf-test")
billingAccount := getTestBillingAccountFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
roleId := "roles/cloudkms.cryptoKeyEncrypter"
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
keyRingId := &kmsKeyRingId{
Project: projectId,
Location: DEFAULT_KMS_TEST_LOCATION,
Name: keyRingName,
}
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleKmsCryptoKeyIamMember_basic(projectId, orgId, billingAccount, account, keyRingName, cryptoKeyName, roleId),
},

resource.TestStep{
ResourceName: "google_kms_crypto_key_iam_member.foo",
ImportStateId: fmt.Sprintf("%s/%s %s serviceAccount:%s@%s.iam.gserviceaccount.com", keyRingId.terraformId(), cryptoKeyName, roleId, account, projectId),
ImportState: true,
},
},
})
}

func TestAccKmsCryptoKeyIamBinding_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
projectId := acctest.RandomWithPrefix("tf-test")
billingAccount := getTestBillingAccountFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
roleId := "roles/cloudkms.cryptoKeyEncrypter"
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
keyRingId := &kmsKeyRingId{
Project: projectId,
Location: DEFAULT_KMS_TEST_LOCATION,
Name: keyRingName,
}
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleKmsCryptoKeyIamBinding_basic(projectId, orgId, billingAccount, account, keyRingName, cryptoKeyName, roleId),
},

resource.TestStep{
ResourceName: "google_kms_crypto_key_iam_binding.foo",
ImportStateId: fmt.Sprintf("%s/%s %s", keyRingId.terraformId(), cryptoKeyName, roleId),
ImportState: true,
},
},
})
}
104 changes: 104 additions & 0 deletions google/import_google_kms_key_ring_iam_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccKmsKeyRingIamMember_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
projectId := acctest.RandomWithPrefix("tf-test")
billingAccount := getTestBillingAccountFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
roleId := "roles/cloudkms.cryptoKeyEncrypter"
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
keyRingId := &kmsKeyRingId{
Project: projectId,
Location: DEFAULT_KMS_TEST_LOCATION,
Name: keyRingName,
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleKmsKeyRingIamMember_basic(projectId, orgId, billingAccount, account, keyRingName, roleId),
},

resource.TestStep{
ResourceName: "google_kms_key_ring_iam_member.foo",
ImportStateId: fmt.Sprintf("%s %s serviceAccount:%s@%s.iam.gserviceaccount.com", keyRingId.terraformId(), roleId, account, projectId),
ImportState: true,
},
},
})
}

func TestAccKmsKeyRingIamPolicy_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
projectId := acctest.RandomWithPrefix("tf-test")
billingAccount := getTestBillingAccountFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
roleId := "roles/cloudkms.cryptoKeyEncrypter"
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
keyRingId := &kmsKeyRingId{
Project: projectId,
Location: DEFAULT_KMS_TEST_LOCATION,
Name: keyRingName,
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleKmsKeyRingIamPolicy_basic(projectId, orgId, billingAccount, account, keyRingName, roleId),
},

resource.TestStep{
ResourceName: "google_kms_key_ring_iam_policy.foo",
ImportStateId: keyRingId.terraformId(),
ImportState: true,
},
},
})
}

func TestAccKmsKeyRingIamBinding_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
projectId := acctest.RandomWithPrefix("tf-test")
billingAccount := getTestBillingAccountFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
roleId := "roles/cloudkms.cryptoKeyEncrypter"
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
keyRingId := &kmsKeyRingId{
Project: projectId,
Location: DEFAULT_KMS_TEST_LOCATION,
Name: keyRingName,
}
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleKmsKeyRingIamBinding_basic(projectId, orgId, billingAccount, account, keyRingName, roleId),
},

resource.TestStep{
ResourceName: "google_kms_key_ring_iam_binding.foo",
ImportStateId: fmt.Sprintf("%s %s", keyRingId.terraformId(), roleId),
ImportState: true,
},
},
})
}
57 changes: 57 additions & 0 deletions google/import_google_organization_iam_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccGoogleOrganizationIamMember_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
projectId := getTestProjectFromEnv()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleOrganizationIamMember_basic(account, orgId),
},

resource.TestStep{
ResourceName: "google_organization_iam_member.foo",
ImportStateId: fmt.Sprintf("%s roles/browser serviceAccount:%s@%s.iam.gserviceaccount.com", orgId, account, projectId),
ImportState: true,
},
},
})
}

func TestAccGoogleOrganizationIamBinding_importBasic(t *testing.T) {
t.Parallel()

orgId := getTestOrgFromEnv(t)
account := acctest.RandomWithPrefix("tf-test")
roleId := "tfIamTest" + acctest.RandString(10)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGoogleOrganizationIamBinding_basic(account, roleId, orgId),
},

resource.TestStep{
ResourceName: "google_organization_iam_binding.foo",
ImportStateId: fmt.Sprintf("%s organizations/%s/roles/%s", orgId, orgId, roleId),
ImportState: true,
},
},
})
}
Loading