diff --git a/google/iam.go b/google/iam.go index b373faf27ff..844687e3759 100644 --- a/google/iam.go +++ b/google/iam.go @@ -34,6 +34,14 @@ 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 iamPolicyReadModifyWrite(updater ResourceIamUpdater, modify iamPolicyModifyFunc) error { mutexKey := updater.GetMutexKey() mutexKV.Lock(mutexKey) diff --git a/google/iam_folder.go b/google/iam_folder.go index db69dad2a2d..3af044afc8c 100644 --- a/google/iam_folder.go +++ b/google/iam_folder.go @@ -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() diff --git a/google/iam_kms_crypto_key.go b/google/iam_kms_crypto_key.go index 5beff11cedc..5df532e47df 100644 --- a/google/iam_kms_crypto_key.go +++ b/google/iam_kms_crypto_key.go @@ -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() diff --git a/google/iam_kms_key_ring.go b/google/iam_kms_key_ring.go index b0c41aa0747..93a174942f6 100644 --- a/google/iam_kms_key_ring.go +++ b/google/iam_kms_key_ring.go @@ -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{} diff --git a/google/iam_organization.go b/google/iam_organization.go index d16c1df58d9..d5f845e5be8 100644 --- a/google/iam_organization.go +++ b/google/iam_organization.go @@ -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 { diff --git a/google/iam_project.go b/google/iam_project.go index 8f765ac2eac..6f564950feb 100644 --- a/google/iam_project.go +++ b/google/iam_project.go @@ -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() diff --git a/google/import_google_kms_crypto_key_iam_test.go b/google/import_google_kms_crypto_key_iam_test.go new file mode 100644 index 00000000000..81f97f1a577 --- /dev/null +++ b/google/import_google_kms_crypto_key_iam_test.go @@ -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, + }, + }, + }) +} diff --git a/google/import_google_kms_key_ring_iam_test.go b/google/import_google_kms_key_ring_iam_test.go new file mode 100644 index 00000000000..27493bc6401 --- /dev/null +++ b/google/import_google_kms_key_ring_iam_test.go @@ -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, + }, + }, + }) +} diff --git a/google/import_google_organization_iam_test.go b/google/import_google_organization_iam_test.go new file mode 100644 index 00000000000..2f34006c322 --- /dev/null +++ b/google/import_google_organization_iam_test.go @@ -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, + }, + }, + }) +} diff --git a/google/import_google_project_iam_test.go b/google/import_google_project_iam_test.go new file mode 100644 index 00000000000..c228b07c312 --- /dev/null +++ b/google/import_google_project_iam_test.go @@ -0,0 +1,57 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccGoogleProjectIamMember_importBasic(t *testing.T) { + t.Parallel() + + resourceName := "google_project_iam_member.acceptance" + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccGoogleProjectAssociateMemberBasic(pid, "Acceptance", org), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportStateId: fmt.Sprintf("%s %s %s", pid, "roles/compute.instanceAdmin", "user:admin@hashicorptest.com"), + ImportState: true, + }, + }, + }) +} + +func TestAccGoogleProjectIamBinding_importBasic(t *testing.T) { + t.Parallel() + + resourceName := "google_project_iam_binding.acceptance" + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccGoogleProjectAssociateBindingBasic(pid, "Acceptance", org), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportStateId: fmt.Sprintf("%s %s", pid, "roles/compute.instanceAdmin"), + ImportState: true, + }, + }, + }) +} diff --git a/google/provider.go b/google/provider.go index 42889b2f5a8..f32f5700c26 100644 --- a/google/provider.go +++ b/google/provider.go @@ -117,32 +117,32 @@ func Provider() terraform.ResourceProvider { "google_dns_managed_zone": resourceDnsManagedZone(), "google_dns_record_set": resourceDnsRecordSet(), "google_folder": resourceGoogleFolder(), - "google_folder_iam_policy": ResourceIamPolicy(IamFolderSchema, NewFolderIamUpdater), + "google_folder_iam_policy": ResourceIamPolicyWithImport(IamFolderSchema, NewFolderIamUpdater, FolderIdParseFunc), "google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(), "google_logging_billing_account_sink": resourceLoggingBillingAccountSink(), "google_logging_folder_sink": resourceLoggingFolderSink(), "google_logging_project_sink": resourceLoggingProjectSink(), "google_kms_key_ring": resourceKmsKeyRing(), - "google_kms_key_ring_iam_binding": ResourceIamBinding(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater), - "google_kms_key_ring_iam_member": ResourceIamMember(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater), - "google_kms_key_ring_iam_policy": ResourceIamPolicy(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater), + "google_kms_key_ring_iam_binding": ResourceIamBindingWithImport(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater, KeyRingIdParseFunc), + "google_kms_key_ring_iam_member": ResourceIamMemberWithImport(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater, KeyRingIdParseFunc), + "google_kms_key_ring_iam_policy": ResourceIamPolicyWithImport(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater, KeyRingIdParseFunc), "google_kms_crypto_key": resourceKmsCryptoKey(), - "google_kms_crypto_key_iam_binding": ResourceIamBinding(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater), - "google_kms_crypto_key_iam_member": ResourceIamMember(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater), + "google_kms_crypto_key_iam_binding": ResourceIamBindingWithImport(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater, CryptoIdParseFunc), + "google_kms_crypto_key_iam_member": ResourceIamMemberWithImport(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater, CryptoIdParseFunc), "google_sourcerepo_repository": resourceSourceRepoRepository(), "google_spanner_instance": resourceSpannerInstance(), "google_spanner_database": resourceSpannerDatabase(), "google_sql_database": resourceSqlDatabase(), "google_sql_database_instance": resourceSqlDatabaseInstance(), "google_sql_user": resourceSqlUser(), - "google_organization_iam_binding": ResourceIamBinding(IamOrganizationSchema, NewOrganizationIamUpdater), + "google_organization_iam_binding": ResourceIamBindingWithImport(IamOrganizationSchema, NewOrganizationIamUpdater, OrgIdParseFunc), "google_organization_iam_custom_role": resourceGoogleOrganizationIamCustomRole(), - "google_organization_iam_member": ResourceIamMember(IamOrganizationSchema, NewOrganizationIamUpdater), + "google_organization_iam_member": ResourceIamMemberWithImport(IamOrganizationSchema, NewOrganizationIamUpdater, OrgIdParseFunc), "google_organization_policy": resourceGoogleOrganizationPolicy(), "google_project": resourceGoogleProject(), "google_project_iam_policy": resourceGoogleProjectIamPolicy(), - "google_project_iam_binding": ResourceIamBinding(IamProjectSchema, NewProjectIamUpdater), - "google_project_iam_member": ResourceIamMember(IamProjectSchema, NewProjectIamUpdater), + "google_project_iam_binding": ResourceIamBindingWithImport(IamProjectSchema, NewProjectIamUpdater, ProjectIdParseFunc), + "google_project_iam_member": ResourceIamMemberWithImport(IamProjectSchema, NewProjectIamUpdater, ProjectIdParseFunc), "google_project_service": resourceGoogleProjectService(), "google_project_iam_custom_role": resourceGoogleProjectIamCustomRole(), "google_project_services": resourceGoogleProjectServices(), diff --git a/google/resource_google_project_iam_policy.go b/google/resource_google_project_iam_policy.go index 90e011b59ea..a4dd0ed7592 100644 --- a/google/resource_google_project_iam_policy.go +++ b/google/resource_google_project_iam_policy.go @@ -17,6 +17,9 @@ func resourceGoogleProjectIamPolicy() *schema.Resource { Read: resourceGoogleProjectIamPolicyRead, Update: resourceGoogleProjectIamPolicyUpdate, Delete: resourceGoogleProjectIamPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, Schema: map[string]*schema.Schema{ "project": &schema.Schema{ diff --git a/google/resource_iam_binding.go b/google/resource_iam_binding.go index c887425880b..35069d08d01 100644 --- a/google/resource_iam_binding.go +++ b/google/resource_iam_binding.go @@ -1,9 +1,12 @@ package google import ( + "errors" + "fmt" "github.com/hashicorp/terraform/helper/schema" "google.golang.org/api/cloudresourcemanager/v1" "log" + "strings" ) var iamBindingSchema = map[string]*schema.Schema{ @@ -31,11 +34,18 @@ func ResourceIamBinding(parentSpecificSchema map[string]*schema.Schema, newUpdat Read: resourceIamBindingRead(newUpdaterFunc), Update: resourceIamBindingUpdate(newUpdaterFunc), Delete: resourceIamBindingDelete(newUpdaterFunc), - Schema: mergeSchemas(iamBindingSchema, parentSpecificSchema), } } +func ResourceIamBindingWithImport(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc, resourceIdParser resourceIdParserFunc) *schema.Resource { + r := ResourceIamBinding(parentSpecificSchema, newUpdaterFunc) + r.Importer = &schema.ResourceImporter{ + State: iamBindingImport(resourceIdParser), + } + return r +} + func resourceIamBindingCreate(newUpdaterFunc newResourceIamUpdaterFunc) schema.CreateFunc { return func(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -96,6 +106,38 @@ func resourceIamBindingRead(newUpdaterFunc newResourceIamUpdaterFunc) schema.Rea } } +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 resourceIamBindingUpdate(newUpdaterFunc newResourceIamUpdaterFunc) schema.UpdateFunc { return func(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) diff --git a/google/resource_iam_member.go b/google/resource_iam_member.go index 3fe927a08b8..22302e32921 100644 --- a/google/resource_iam_member.go +++ b/google/resource_iam_member.go @@ -1,9 +1,12 @@ package google import ( + "errors" + "fmt" "github.com/hashicorp/terraform/helper/schema" "google.golang.org/api/cloudresourcemanager/v1" "log" + "strings" ) var IamMemberBaseSchema = map[string]*schema.Schema{ @@ -23,6 +26,29 @@ var IamMemberBaseSchema = map[string]*schema.Schema{ }, } +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 ResourceIamMember(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc) *schema.Resource { return &schema.Resource{ Create: resourceIamMemberCreate(newUpdaterFunc), @@ -33,6 +59,14 @@ func ResourceIamMember(parentSpecificSchema map[string]*schema.Schema, newUpdate } } +func ResourceIamMemberWithImport(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc, resourceIdParser resourceIdParserFunc) *schema.Resource { + r := ResourceIamMember(parentSpecificSchema, newUpdaterFunc) + r.Importer = &schema.ResourceImporter{ + State: iamMemberImport(resourceIdParser), + } + return r +} + func getResourceIamMember(d *schema.ResourceData) *cloudresourcemanager.Binding { return &cloudresourcemanager.Binding{ Members: []string{d.Get("member").(string)}, diff --git a/google/resource_iam_policy.go b/google/resource_iam_policy.go index b5add4ed835..54bafdc5a7f 100644 --- a/google/resource_iam_policy.go +++ b/google/resource_iam_policy.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "encoding/json" + "errors" "fmt" "google.golang.org/api/cloudresourcemanager/v1" ) @@ -21,6 +22,20 @@ var IamPolicyBaseSchema = map[string]*schema.Schema{ }, } +func iamPolicyImport(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) + err := resourceIdParser(d, config) + if err != nil { + return nil, err + } + return []*schema.ResourceData{d}, nil + } +} + func ResourceIamPolicy(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc) *schema.Resource { return &schema.Resource{ Create: ResourceIamPolicyCreate(newUpdaterFunc), @@ -32,6 +47,14 @@ func ResourceIamPolicy(parentSpecificSchema map[string]*schema.Schema, newUpdate } } +func ResourceIamPolicyWithImport(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc, resourceIdParser resourceIdParserFunc) *schema.Resource { + r := ResourceIamPolicy(parentSpecificSchema, newUpdaterFunc) + r.Importer = &schema.ResourceImporter{ + State: iamPolicyImport(resourceIdParser), + } + return r +} + func ResourceIamPolicyCreate(newUpdaterFunc newResourceIamUpdaterFunc) schema.CreateFunc { return func(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) diff --git a/website/docs/r/google_kms_crypto_key_iam_binding.html.markdown b/website/docs/r/google_kms_crypto_key_iam_binding.html.markdown index 2a8f25b3a4d..d95457b75df 100644 --- a/website/docs/r/google_kms_crypto_key_iam_binding.html.markdown +++ b/website/docs/r/google_kms_crypto_key_iam_binding.html.markdown @@ -45,3 +45,10 @@ exported: * `etag` - (Computed) The etag of the crypto key's IAM policy. +## Import + +IAM binding imports use space-delimited identifiers; first the resource in question and then the role. These bindings can be imported using the `crypto_key_id` and role, e.g. + +``` +$ terraform import google_kms_crypto_key_binding.my_binding "your-project-id/location-name/key-name roles/viewer" +``` diff --git a/website/docs/r/google_kms_crypto_key_iam_member.html.markdown b/website/docs/r/google_kms_crypto_key_iam_member.html.markdown index 8297adb7dad..5d0e3a081da 100644 --- a/website/docs/r/google_kms_crypto_key_iam_member.html.markdown +++ b/website/docs/r/google_kms_crypto_key_iam_member.html.markdown @@ -45,3 +45,11 @@ In addition to the arguments listed above, the following computed attributes are exported: * `etag` - (Computed) The etag of the project's IAM policy. + +## Import + +IAM member imports use space-delimited identifiers; the resource in question, the role, and the account. This member resource can be imported using the `crypto_key_id`, role, and account e.g. + +``` +$ terraform import google_kms_crypto_key_iam_member.member "your-project-id/location-name/key-name roles/viewer foo@example.com" +``` diff --git a/website/docs/r/google_kms_key_ring_iam.html.markdown b/website/docs/r/google_kms_key_ring_iam.html.markdown index aab2b01adb8..a70d4c26345 100644 --- a/website/docs/r/google_kms_key_ring_iam.html.markdown +++ b/website/docs/r/google_kms_key_ring_iam.html.markdown @@ -90,3 +90,23 @@ In addition to the arguments listed above, the following computed attributes are exported: * `etag` - (Computed) The etag of the key ring's IAM policy. + +## Import + +IAM member imports use space-delimited identifiers; the resource in question, the role, and the account. This member resource can be imported using the `key_ring_id`, role, and account e.g. + +``` +$ terraform import google_kms_key_ring_iam_member.key_ring_iam "your-project-id/location-name/key-ring-name roles/viewer foo@example.com" +``` + +IAM binging imports use space-delimited identifiers; the resource in question and the role. This binding resource can be imported using the `key_ring_id`, role, and account e.g. + +``` +$ terraform import google_kms_key_ring_iam_binding.key_ring_iam "your-project-id/location-name/key-ring-name roles/viewer" +``` + +IAM policy imports use the identifier of the resource in question. This policy resource can be imported using the `key_ring_id`, role, and account e.g. + +``` +$ terraform import google_kms_key_ring_iam_policy.key_ring_iam your-project-id/location-name/key-ring-name +``` diff --git a/website/docs/r/google_organization_iam_binding.md b/website/docs/r/google_organization_iam_binding.md index 348754f73f0..a268a4cb374 100644 --- a/website/docs/r/google_organization_iam_binding.md +++ b/website/docs/r/google_organization_iam_binding.md @@ -46,3 +46,10 @@ exported: * `etag` - (Computed) The etag of the organization's IAM policy. +## Import + +IAM binding imports use space-delimited identifiers; first the resource in question and then the role. These bindings can be imported using the `org_id` and role, e.g. + +``` +$ terraform import google_organization_iam_binding.my_org "your-org-id roles/viewer" +``` diff --git a/website/docs/r/google_organization_iam_member.md b/website/docs/r/google_organization_iam_member.md index 55f8cb31886..8d259c6058c 100644 --- a/website/docs/r/google_organization_iam_member.md +++ b/website/docs/r/google_organization_iam_member.md @@ -41,3 +41,11 @@ In addition to the arguments listed above, the following computed attributes are exported: * `etag` - (Computed) The etag of the organization's IAM policy. + +## Import + +IAM member imports use space-delimited identifiers; the resource in question, the role, and the account. This member resource can be imported using the `org_id`, role, and account e.g. + +``` +$ terraform import google_organization_iam_member.my_org "your-org-id roles/viewer foo@example.com" +``` diff --git a/website/docs/r/google_project_iam_binding.html.markdown b/website/docs/r/google_project_iam_binding.html.markdown index 59a9c90eaf9..b0c9f11c5e8 100644 --- a/website/docs/r/google_project_iam_binding.html.markdown +++ b/website/docs/r/google_project_iam_binding.html.markdown @@ -54,3 +54,10 @@ exported: * `etag` - (Computed) The etag of the project's IAM policy. +## Import + +IAM binding imports use space-delimited identifiers; first the resource in question and then the role. These bindings can be imported using the `project_id` and role, e.g. + +``` +$ terraform import google_project_iam_binding.my_project "your-project-id roles/viewer" +``` diff --git a/website/docs/r/google_project_iam_member.html.markdown b/website/docs/r/google_project_iam_member.html.markdown index 82b7b328175..268b2b40c94 100644 --- a/website/docs/r/google_project_iam_member.html.markdown +++ b/website/docs/r/google_project_iam_member.html.markdown @@ -50,3 +50,11 @@ In addition to the arguments listed above, the following computed attributes are exported: * `etag` - (Computed) The etag of the project's IAM policy. + +## Import + +IAM member imports use space-delimited identifiers; the resource in question, the role, and the account. This member resource can be imported using the `project_id`, role, and account e.g. + +``` +$ terraform import google_project_iam_member.my_project "your-project-id roles/viewer foo@example.com" +``` diff --git a/website/docs/r/google_project_iam_policy.html.markdown b/website/docs/r/google_project_iam_policy.html.markdown index 247721154b2..01da5ff9e95 100644 --- a/website/docs/r/google_project_iam_policy.html.markdown +++ b/website/docs/r/google_project_iam_policy.html.markdown @@ -38,7 +38,7 @@ data "google_iam_policy" "admin" { The following arguments are supported: * `project` - (Required) The project ID. - Changing this forces a new project to be created. + Changing this forces a new resource to be created. * `policy_data` - (Required) The `google_iam_policy` data source that represents the IAM policy that will be applied to the project. The policy will be @@ -73,3 +73,11 @@ exported: * `restore_policy` - (DEPRECATED) (Computed) The IAM policy that will be restored when a non-authoritative policy resource is deleted. + +## Import + +IAM policy imports use the identifier of the resource in question. This policy resource can be imported using the `project_id` e.g. + +``` +$ terraform import google_project_iam_policy.my_project your-project-id +```