Consistent IAM resource imports. (#835)

Add consistency for for IAM imports.
- Adds imports for projects, folders, crypto keys, organizations, and key rings.
- Anything else with IAM can implement a simple method and begin working immediately.
- Add tests for all the IAM imports.
- Import documentation for IAM resources.
This commit is contained in:
Nathan McKinley 2017-12-11 10:24:53 -08:00 committed by GitHub
parent bf235ebc04
commit ced8cb506c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 513 additions and 12 deletions

View File

@ -34,6 +34,14 @@ type ResourceIamUpdater interface {
type newResourceIamUpdaterFunc func(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) type newResourceIamUpdaterFunc func(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error)
type iamPolicyModifyFunc func(p *cloudresourcemanager.Policy) 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 { func iamPolicyReadModifyWrite(updater ResourceIamUpdater, modify iamPolicyModifyFunc) error {
mutexKey := updater.GetMutexKey() mutexKey := updater.GetMutexKey()
mutexKV.Lock(mutexKey) mutexKV.Lock(mutexKey)

View File

@ -28,6 +28,11 @@ func NewFolderIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpd
}, nil }, nil
} }
func FolderIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("folder", d.Id())
return nil
}
func (u *FolderIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { func (u *FolderIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientResourceManagerV2Beta1.Folders.GetIamPolicy(u.folderId, p, err := u.Config.clientResourceManagerV2Beta1.Folders.GetIamPolicy(u.folderId,
&resourceManagerV2Beta1.GetIamPolicyRequest{}).Do() &resourceManagerV2Beta1.GetIamPolicyRequest{}).Do()

View File

@ -34,6 +34,11 @@ func NewKmsCryptoKeyIamUpdater(d *schema.ResourceData, config *Config) (Resource
}, nil }, nil
} }
func CryptoIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("crypto_key_id", d.Id())
return nil
}
func (u *KmsCryptoKeyIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { func (u *KmsCryptoKeyIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientKms.Projects.Locations.KeyRings.CryptoKeys.GetIamPolicy(u.resourceId).Do() p, err := u.Config.clientKms.Projects.Locations.KeyRings.CryptoKeys.GetIamPolicy(u.resourceId).Do()

View File

@ -34,6 +34,11 @@ func NewKmsKeyRingIamUpdater(d *schema.ResourceData, config *Config) (ResourceIa
}, nil }, 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) { func resourceManagerToKmsPolicy(p *cloudresourcemanager.Policy) (policy *cloudkms.Policy, err error) {
policy = &cloudkms.Policy{} policy = &cloudkms.Policy{}

View File

@ -26,6 +26,11 @@ func NewOrganizationIamUpdater(d *schema.ResourceData, config *Config) (Resource
}, nil }, nil
} }
func OrgIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("org_id", d.Id())
return nil
}
func (u *OrganizationIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { func (u *OrganizationIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientResourceManager.Organizations.GetIamPolicy("organizations/"+u.resourceId, &cloudresourcemanager.GetIamPolicyRequest{}).Do() p, err := u.Config.clientResourceManager.Organizations.GetIamPolicy("organizations/"+u.resourceId, &cloudresourcemanager.GetIamPolicyRequest{}).Do()
if err != nil { if err != nil {

View File

@ -31,6 +31,11 @@ func NewProjectIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUp
}, nil }, nil
} }
func ProjectIdParseFunc(d *schema.ResourceData, _ *Config) error {
d.Set("project", d.Id())
return nil
}
func (u *ProjectIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { func (u *ProjectIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientResourceManager.Projects.GetIamPolicy(u.resourceId, p, err := u.Config.clientResourceManager.Projects.GetIamPolicy(u.resourceId,
&cloudresourcemanager.GetIamPolicyRequest{}).Do() &cloudresourcemanager.GetIamPolicyRequest{}).Do()

View File

@ -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,
},
},
})
}

View File

@ -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,
},
},
})
}

View File

@ -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,
},
},
})
}

View File

@ -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,
},
},
})
}

View File

@ -129,32 +129,32 @@ func Provider() terraform.ResourceProvider {
"google_dns_managed_zone": resourceDnsManagedZone(), "google_dns_managed_zone": resourceDnsManagedZone(),
"google_dns_record_set": resourceDnsRecordSet(), "google_dns_record_set": resourceDnsRecordSet(),
"google_folder": resourceGoogleFolder(), "google_folder": resourceGoogleFolder(),
"google_folder_iam_policy": ResourceIamPolicy(IamFolderSchema, NewFolderIamUpdater), "google_folder_iam_policy": ResourceIamPolicyWithImport(IamFolderSchema, NewFolderIamUpdater, FolderIdParseFunc),
"google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(), "google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(),
"google_logging_billing_account_sink": resourceLoggingBillingAccountSink(), "google_logging_billing_account_sink": resourceLoggingBillingAccountSink(),
"google_logging_folder_sink": resourceLoggingFolderSink(), "google_logging_folder_sink": resourceLoggingFolderSink(),
"google_logging_project_sink": resourceLoggingProjectSink(), "google_logging_project_sink": resourceLoggingProjectSink(),
"google_kms_key_ring": resourceKmsKeyRing(), "google_kms_key_ring": resourceKmsKeyRing(),
"google_kms_key_ring_iam_binding": ResourceIamBinding(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater), "google_kms_key_ring_iam_binding": ResourceIamBindingWithImport(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater, KeyRingIdParseFunc),
"google_kms_key_ring_iam_member": ResourceIamMember(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater), "google_kms_key_ring_iam_member": ResourceIamMemberWithImport(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater, KeyRingIdParseFunc),
"google_kms_key_ring_iam_policy": ResourceIamPolicy(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater), "google_kms_key_ring_iam_policy": ResourceIamPolicyWithImport(IamKmsKeyRingSchema, NewKmsKeyRingIamUpdater, KeyRingIdParseFunc),
"google_kms_crypto_key": resourceKmsCryptoKey(), "google_kms_crypto_key": resourceKmsCryptoKey(),
"google_kms_crypto_key_iam_binding": ResourceIamBinding(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater), "google_kms_crypto_key_iam_binding": ResourceIamBindingWithImport(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater, CryptoIdParseFunc),
"google_kms_crypto_key_iam_member": ResourceIamMember(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater), "google_kms_crypto_key_iam_member": ResourceIamMemberWithImport(IamKmsCryptoKeySchema, NewKmsCryptoKeyIamUpdater, CryptoIdParseFunc),
"google_sourcerepo_repository": resourceSourceRepoRepository(), "google_sourcerepo_repository": resourceSourceRepoRepository(),
"google_spanner_instance": resourceSpannerInstance(), "google_spanner_instance": resourceSpannerInstance(),
"google_spanner_database": resourceSpannerDatabase(), "google_spanner_database": resourceSpannerDatabase(),
"google_sql_database": resourceSqlDatabase(), "google_sql_database": resourceSqlDatabase(),
"google_sql_database_instance": resourceSqlDatabaseInstance(), "google_sql_database_instance": resourceSqlDatabaseInstance(),
"google_sql_user": resourceSqlUser(), "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_custom_role": resourceGoogleOrganizationIamCustomRole(),
"google_organization_iam_member": ResourceIamMember(IamOrganizationSchema, NewOrganizationIamUpdater), "google_organization_iam_member": ResourceIamMemberWithImport(IamOrganizationSchema, NewOrganizationIamUpdater, OrgIdParseFunc),
"google_organization_policy": resourceGoogleOrganizationPolicy(), "google_organization_policy": resourceGoogleOrganizationPolicy(),
"google_project": resourceGoogleProject(), "google_project": resourceGoogleProject(),
"google_project_iam_policy": resourceGoogleProjectIamPolicy(), "google_project_iam_policy": resourceGoogleProjectIamPolicy(),
"google_project_iam_binding": ResourceIamBinding(IamProjectSchema, NewProjectIamUpdater), "google_project_iam_binding": ResourceIamBindingWithImport(IamProjectSchema, NewProjectIamUpdater, ProjectIdParseFunc),
"google_project_iam_member": ResourceIamMember(IamProjectSchema, NewProjectIamUpdater), "google_project_iam_member": ResourceIamMemberWithImport(IamProjectSchema, NewProjectIamUpdater, ProjectIdParseFunc),
"google_project_service": resourceGoogleProjectService(), "google_project_service": resourceGoogleProjectService(),
"google_project_iam_custom_role": resourceGoogleProjectIamCustomRole(), "google_project_iam_custom_role": resourceGoogleProjectIamCustomRole(),
"google_project_services": resourceGoogleProjectServices(), "google_project_services": resourceGoogleProjectServices(),

View File

@ -17,6 +17,9 @@ func resourceGoogleProjectIamPolicy() *schema.Resource {
Read: resourceGoogleProjectIamPolicyRead, Read: resourceGoogleProjectIamPolicyRead,
Update: resourceGoogleProjectIamPolicyUpdate, Update: resourceGoogleProjectIamPolicyUpdate,
Delete: resourceGoogleProjectIamPolicyDelete, Delete: resourceGoogleProjectIamPolicyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"project": &schema.Schema{ "project": &schema.Schema{

View File

@ -1,9 +1,12 @@
package google package google
import ( import (
"errors"
"fmt"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1" "google.golang.org/api/cloudresourcemanager/v1"
"log" "log"
"strings"
) )
var iamBindingSchema = map[string]*schema.Schema{ var iamBindingSchema = map[string]*schema.Schema{
@ -31,11 +34,18 @@ func ResourceIamBinding(parentSpecificSchema map[string]*schema.Schema, newUpdat
Read: resourceIamBindingRead(newUpdaterFunc), Read: resourceIamBindingRead(newUpdaterFunc),
Update: resourceIamBindingUpdate(newUpdaterFunc), Update: resourceIamBindingUpdate(newUpdaterFunc),
Delete: resourceIamBindingDelete(newUpdaterFunc), Delete: resourceIamBindingDelete(newUpdaterFunc),
Schema: mergeSchemas(iamBindingSchema, parentSpecificSchema), 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 { func resourceIamBindingCreate(newUpdaterFunc newResourceIamUpdaterFunc) schema.CreateFunc {
return func(d *schema.ResourceData, meta interface{}) error { return func(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) 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 { func resourceIamBindingUpdate(newUpdaterFunc newResourceIamUpdaterFunc) schema.UpdateFunc {
return func(d *schema.ResourceData, meta interface{}) error { return func(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)

View File

@ -1,9 +1,12 @@
package google package google
import ( import (
"errors"
"fmt"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1" "google.golang.org/api/cloudresourcemanager/v1"
"log" "log"
"strings"
) )
var IamMemberBaseSchema = map[string]*schema.Schema{ 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 { func ResourceIamMember(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc) *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceIamMemberCreate(newUpdaterFunc), 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 { func getResourceIamMember(d *schema.ResourceData) *cloudresourcemanager.Binding {
return &cloudresourcemanager.Binding{ return &cloudresourcemanager.Binding{
Members: []string{d.Get("member").(string)}, Members: []string{d.Get("member").(string)},

View File

@ -4,6 +4,7 @@ import (
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"google.golang.org/api/cloudresourcemanager/v1" "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 { func ResourceIamPolicy(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc) *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: ResourceIamPolicyCreate(newUpdaterFunc), 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 { func ResourceIamPolicyCreate(newUpdaterFunc newResourceIamUpdaterFunc) schema.CreateFunc {
return func(d *schema.ResourceData, meta interface{}) error { return func(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) config := meta.(*Config)

View File

@ -45,3 +45,10 @@ exported:
* `etag` - (Computed) The etag of the crypto key's IAM policy. * `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"
```

View File

@ -45,3 +45,11 @@ In addition to the arguments listed above, the following computed attributes are
exported: exported:
* `etag` - (Computed) The etag of the project's IAM policy. * `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"
```

View File

@ -90,3 +90,23 @@ In addition to the arguments listed above, the following computed attributes are
exported: exported:
* `etag` - (Computed) The etag of the key ring's IAM policy. * `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
```

View File

@ -46,3 +46,10 @@ exported:
* `etag` - (Computed) The etag of the organization's IAM policy. * `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"
```

View File

@ -41,3 +41,11 @@ In addition to the arguments listed above, the following computed attributes are
exported: exported:
* `etag` - (Computed) The etag of the organization's IAM policy. * `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"
```

View File

@ -54,3 +54,10 @@ exported:
* `etag` - (Computed) The etag of the project's IAM policy. * `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"
```

View File

@ -50,3 +50,11 @@ In addition to the arguments listed above, the following computed attributes are
exported: exported:
* `etag` - (Computed) The etag of the project's IAM policy. * `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"
```

View File

@ -38,7 +38,7 @@ data "google_iam_policy" "admin" {
The following arguments are supported: The following arguments are supported:
* `project` - (Required) The project ID. * `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 * `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 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 * `restore_policy` - (DEPRECATED) (Computed) The IAM policy that will be restored when a
non-authoritative policy resource is deleted. 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
```