diff --git a/google/resource_google_project_iam_policy.go b/google/resource_google_project_iam_policy.go index 8cfc8f4c..195c44d7 100644 --- a/google/resource_google_project_iam_policy.go +++ b/google/resource_google_project_iam_policy.go @@ -24,8 +24,7 @@ func resourceGoogleProjectIamPolicy() *schema.Resource { Schema: map[string]*schema.Schema{ "project": &schema.Schema{ Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, ForceNew: true, }, "policy_data": &schema.Schema{ @@ -33,24 +32,24 @@ func resourceGoogleProjectIamPolicy() *schema.Resource { Required: true, DiffSuppressFunc: jsonPolicyDiffSuppress, }, - "authoritative": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Deprecated: "A future version of Terraform will remove the authoritative field. To ignore changes not managed by Terraform, use google_project_iam_binding and google_project_iam_member instead. See https://www.terraform.io/docs/providers/google/r/google_project_iam.html for more information.", - }, "etag": &schema.Schema{ Type: schema.TypeString, Computed: true, }, + "authoritative": &schema.Schema{ + Removed: "The authoritative field was removed. To ignore changes not managed by Terraform, use google_project_iam_binding and google_project_iam_member instead. See https://www.terraform.io/docs/providers/google/r/google_project_iam.html for more information.", + Type: schema.TypeBool, + Optional: true, + }, "restore_policy": &schema.Schema{ - Deprecated: "This field will be removed alongside the authoritative field. To ignore changes not managed by Terraform, use google_project_iam_binding and google_project_iam_member instead. See https://www.terraform.io/docs/providers/google/r/google_project_iam.html for more information.", - Type: schema.TypeString, - Computed: true, + Removed: "This field was removed alongside the authoritative field. To ignore changes not managed by Terraform, use google_project_iam_binding and google_project_iam_member instead. See https://www.terraform.io/docs/providers/google/r/google_project_iam.html for more information.", + Type: schema.TypeString, + Computed: true, }, "disable_project": &schema.Schema{ - Deprecated: "This will be removed with the authoritative field. Use lifecycle.prevent_destroy instead.", - Type: schema.TypeBool, - Optional: true, + Removed: "This field was removed alongside the authoritative field. Use lifecycle.prevent_destroy instead.", + Type: schema.TypeBool, + Optional: true, }, }, } @@ -58,165 +57,67 @@ func resourceGoogleProjectIamPolicy() *schema.Resource { func resourceGoogleProjectIamPolicyCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - pid, err := getProject(d, config) - if err != nil { - return err - } + project := d.Get("project").(string) - mutexKey := getProjectIamPolicyMutexKey(pid) + mutexKey := getProjectIamPolicyMutexKey(project) mutexKV.Lock(mutexKey) defer mutexKV.Unlock(mutexKey) // Get the policy in the template - p, err := getResourceIamPolicy(d) + policy, err := getResourceIamPolicy(d) if err != nil { return fmt.Errorf("Could not get valid 'policy_data' from resource: %v", err) } - // An authoritative policy is applied without regard for any existing IAM - // policy. - if v, ok := d.GetOk("authoritative"); ok && v.(bool) { - log.Printf("[DEBUG] Setting authoritative IAM policy for project %q", pid) - err := setProjectIamPolicy(p, config, pid) - if err != nil { - return err - } - } else { - log.Printf("[DEBUG] Setting non-authoritative IAM policy for project %q", pid) - // This is a non-authoritative policy, meaning it should be merged with - // any existing policy - ep, err := getProjectIamPolicy(pid, config) - if err != nil { - return err - } - - // First, subtract the policy defined in the template from the - // current policy in the project, and save the result. This will - // allow us to restore the original policy at some point (which - // assumes that Terraform owns any common policy that exists in - // the template and project at create time. - rp := subtractIamPolicy(ep, p) - rps, err := json.Marshal(rp) - if err != nil { - return fmt.Errorf("Error marshaling restorable IAM policy: %v", err) - } - d.Set("restore_policy", string(rps)) - - // Merge the policies together - mb := mergeBindings(append(p.Bindings, rp.Bindings...)) - ep.Bindings = mb - if err = setProjectIamPolicy(ep, config, pid); err != nil { - return fmt.Errorf("Error applying IAM policy to project: %v", err) - } + log.Printf("[DEBUG] Setting IAM policy for project %q", project) + err = setProjectIamPolicy(policy, config, project) + if err != nil { + return err } - d.SetId(pid) + + d.SetId(project) return resourceGoogleProjectIamPolicyRead(d, meta) } func resourceGoogleProjectIamPolicyRead(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG]: Reading google_project_iam_policy") config := meta.(*Config) - pid, err := getProject(d, config) + project := d.Get("project").(string) + + policy, err := getProjectIamPolicy(project, config) if err != nil { return err } - p, err := getProjectIamPolicy(pid, config) - if err != nil { - return err - } - - var bindings []*cloudresourcemanager.Binding - if v, ok := d.GetOk("restore_policy"); ok { - var restored cloudresourcemanager.Policy - // if there's a restore policy, subtract it from the policy_data - err := json.Unmarshal([]byte(v.(string)), &restored) - if err != nil { - return fmt.Errorf("Error unmarshaling restorable IAM policy: %v", err) - } - subtracted := subtractIamPolicy(p, &restored) - bindings = subtracted.Bindings - } else { - bindings = p.Bindings - } // we only marshal the bindings, because only the bindings get set in the config - pBytes, err := json.Marshal(&cloudresourcemanager.Policy{Bindings: bindings}) + policyBytes, err := json.Marshal(&cloudresourcemanager.Policy{Bindings: policy.Bindings}) if err != nil { return fmt.Errorf("Error marshaling IAM policy: %v", err) } - log.Printf("[DEBUG]: Setting etag=%s", p.Etag) - d.Set("etag", p.Etag) - d.Set("policy_data", string(pBytes)) - d.Set("project", pid) + + d.Set("etag", policy.Etag) + d.Set("policy_data", string(policyBytes)) + d.Set("project", project) return nil } func resourceGoogleProjectIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG]: Updating google_project_iam_policy") config := meta.(*Config) - pid, err := getProject(d, config) - if err != nil { - return err - } + project := d.Get("project").(string) - mutexKey := getProjectIamPolicyMutexKey(pid) + mutexKey := getProjectIamPolicyMutexKey(project) mutexKV.Lock(mutexKey) defer mutexKV.Unlock(mutexKey) // Get the policy in the template - p, err := getResourceIamPolicy(d) + policy, err := getResourceIamPolicy(d) if err != nil { return fmt.Errorf("Could not get valid 'policy_data' from resource: %v", err) } - pBytes, _ := json.Marshal(p) - log.Printf("[DEBUG] Got policy from config: %s", string(pBytes)) - // An authoritative policy is applied without regard for any existing IAM - // policy. - if v, ok := d.GetOk("authoritative"); ok && v.(bool) { - log.Printf("[DEBUG] Updating authoritative IAM policy for project %q", pid) - err := setProjectIamPolicy(p, config, pid) - if err != nil { - return fmt.Errorf("Error setting project IAM policy: %v", err) - } - d.Set("restore_policy", "") - } else { - log.Printf("[DEBUG] Updating non-authoritative IAM policy for project %q", pid) - // Get the previous policy from state - pp, err := getPrevResourceIamPolicy(d) - if err != nil { - return fmt.Errorf("Error retrieving previous version of changed project IAM policy: %v", err) - } - ppBytes, _ := json.Marshal(pp) - log.Printf("[DEBUG] Got previous version of changed project IAM policy: %s", string(ppBytes)) - - // Get the existing IAM policy from the API - ep, err := getProjectIamPolicy(pid, config) - if err != nil { - return fmt.Errorf("Error retrieving IAM policy from project API: %v", err) - } - epBytes, _ := json.Marshal(ep) - log.Printf("[DEBUG] Got existing version of changed IAM policy from project API: %s", string(epBytes)) - - // Subtract the previous and current policies from the policy retrieved from the API - rp := subtractIamPolicy(ep, pp) - rpBytes, _ := json.Marshal(rp) - log.Printf("[DEBUG] After subtracting the previous policy from the existing policy, remaining policies: %s", string(rpBytes)) - rp = subtractIamPolicy(rp, p) - rpBytes, _ = json.Marshal(rp) - log.Printf("[DEBUG] After subtracting the remaining policies from the config policy, remaining policies: %s", string(rpBytes)) - rps, err := json.Marshal(rp) - if err != nil { - return fmt.Errorf("Error marhsaling restorable IAM policy: %v", err) - } - d.Set("restore_policy", string(rps)) - - // Merge the policies together - mb := mergeBindings(append(p.Bindings, rp.Bindings...)) - ep.Bindings = mb - if err = setProjectIamPolicy(ep, config, pid); err != nil { - return fmt.Errorf("Error applying IAM policy to project: %v", err) - } + log.Printf("[DEBUG] Updating IAM policy for project %q", project) + err = setProjectIamPolicy(policy, config, project) + if err != nil { + return fmt.Errorf("Error setting project IAM policy: %v", err) } return resourceGoogleProjectIamPolicyRead(d, meta) @@ -225,42 +126,23 @@ func resourceGoogleProjectIamPolicyUpdate(d *schema.ResourceData, meta interface func resourceGoogleProjectIamPolicyDelete(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG]: Deleting google_project_iam_policy") config := meta.(*Config) - pid, err := getProject(d, config) - if err != nil { - return err - } + project := d.Get("project").(string) - mutexKey := getProjectIamPolicyMutexKey(pid) + mutexKey := getProjectIamPolicyMutexKey(project) mutexKV.Lock(mutexKey) defer mutexKV.Unlock(mutexKey) - // Get the existing IAM policy from the API - ep, err := getProjectIamPolicy(pid, config) + // Get the existing IAM policy from the API so we can repurpose the etag and audit config + ep, err := getProjectIamPolicy(project, config) if err != nil { return fmt.Errorf("Error retrieving IAM policy from project API: %v", err) } - // Deleting an authoritative policy will leave the project with no policy, - // and unaccessible by anyone without org-level privs. For this reason, the - // "disable_project" property must be set to true, forcing the user to ack - // this outcome - if v, ok := d.GetOk("authoritative"); ok && v.(bool) { - if v, ok := d.GetOk("disable_project"); !ok || !v.(bool) { - return fmt.Errorf("You must set 'disable_project' to true before deleting an authoritative IAM policy") - } - ep.Bindings = make([]*cloudresourcemanager.Binding, 0) - } else { - // A non-authoritative policy should set the policy to the value of "restore_policy" in state - // Get the previous policy from state - rp, err := getRestoreIamPolicy(d) - if err != nil { - return fmt.Errorf("Error retrieving previous version of changed project IAM policy: %v", err) - } - ep.Bindings = rp.Bindings - } - if err = setProjectIamPolicy(ep, config, pid); err != nil { + ep.Bindings = make([]*cloudresourcemanager.Binding, 0) + if err = setProjectIamPolicy(ep, config, project); err != nil { return fmt.Errorf("Error applying IAM policy to project: %v", err) } + d.SetId("") return nil } @@ -270,24 +152,6 @@ func resourceGoogleProjectIamPolicyImport(d *schema.ResourceData, meta interface return []*schema.ResourceData{d}, nil } -// Subtract all bindings in policy b from policy a, and return the result -func subtractIamPolicy(a, b *cloudresourcemanager.Policy) *cloudresourcemanager.Policy { - am := rolesToMembersMap(a.Bindings) - - for _, b := range b.Bindings { - if _, ok := am[b.Role]; ok { - for _, m := range b.Members { - delete(am[b.Role], m) - } - if len(am[b.Role]) == 0 { - delete(am, b.Role) - } - } - } - a.Bindings = rolesToMembersBinding(am) - return a -} - func setProjectIamPolicy(policy *cloudresourcemanager.Policy, config *Config, pid string) error { // Apply the policy pbytes, _ := json.Marshal(policy) @@ -312,32 +176,6 @@ func getResourceIamPolicy(d *schema.ResourceData) (*cloudresourcemanager.Policy, return policy, nil } -// Get the previous cloudresourcemanager.Policy from a schema.ResourceData if the -// resource has changed -func getPrevResourceIamPolicy(d *schema.ResourceData) (*cloudresourcemanager.Policy, error) { - var policy *cloudresourcemanager.Policy = &cloudresourcemanager.Policy{} - if d.HasChange("policy_data") { - v, _ := d.GetChange("policy_data") - if err := json.Unmarshal([]byte(v.(string)), policy); err != nil { - return nil, fmt.Errorf("Could not unmarshal previous policy %s:\n: %v", v, err) - } - } - return policy, nil -} - -// Get the restore_policy that can be used to restore a project's IAM policy to its -// state before it was adopted into Terraform -func getRestoreIamPolicy(d *schema.ResourceData) (*cloudresourcemanager.Policy, error) { - if v, ok := d.GetOk("restore_policy"); ok { - policy := &cloudresourcemanager.Policy{} - if err := json.Unmarshal([]byte(v.(string)), policy); err != nil { - return nil, fmt.Errorf("Could not unmarshal previous policy %s:\n: %v", v, err) - } - return policy, nil - } - return nil, fmt.Errorf("Resource does not have a 'restore_policy' attribute defined.") -} - // Retrieve the existing IAM Policy for a Project func getProjectIamPolicy(project string, config *Config) (*cloudresourcemanager.Policy, error) { p, err := config.clientResourceManager.Projects.GetIamPolicy(project, @@ -349,22 +187,6 @@ func getProjectIamPolicy(project string, config *Config) (*cloudresourcemanager. return p, nil } -// Convert a map of roles->members to a list of Binding -func rolesToMembersBinding(m map[string]map[string]bool) []*cloudresourcemanager.Binding { - bindings := make([]*cloudresourcemanager.Binding, 0) - for role, members := range m { - b := cloudresourcemanager.Binding{ - Role: role, - Members: make([]string, 0), - } - for m, _ := range members { - b.Members = append(b.Members, m) - } - bindings = append(bindings, &b) - } - return bindings -} - func jsonPolicyDiffSuppress(k, old, new string, d *schema.ResourceData) bool { var oldPolicy, newPolicy cloudresourcemanager.Policy if err := json.Unmarshal([]byte(old), &oldPolicy); err != nil { diff --git a/google/resource_google_project_iam_policy_test.go b/google/resource_google_project_iam_policy_test.go index a09460ed..04cb9ee0 100644 --- a/google/resource_google_project_iam_policy_test.go +++ b/google/resource_google_project_iam_policy_test.go @@ -13,213 +13,6 @@ import ( "google.golang.org/api/cloudresourcemanager/v1" ) -func TestSubtractIamPolicy(t *testing.T) { - table := []struct { - a *cloudresourcemanager.Policy - b *cloudresourcemanager.Policy - expect cloudresourcemanager.Policy - }{ - { - a: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - }, - }, - }, - }, - b: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "3", - "4", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - }, - }, - }, - }, - expect: cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - }, - }, - }, - }, - }, - { - a: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - }, - }, - }, - }, - b: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - }, - }, - }, - }, - expect: cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{}, - }, - }, - { - a: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - "3", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - "3", - }, - }, - }, - }, - b: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "3", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - "3", - }, - }, - }, - }, - expect: cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "2", - }, - }, - }, - }, - }, - { - a: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - "3", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - "3", - }, - }, - }, - }, - b: &cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{ - { - Role: "a", - Members: []string{ - "1", - "2", - "3", - }, - }, - { - Role: "b", - Members: []string{ - "1", - "2", - "3", - }, - }, - }, - }, - expect: cloudresourcemanager.Policy{ - Bindings: []*cloudresourcemanager.Binding{}, - }, - }, - } - - for _, test := range table { - c := subtractIamPolicy(test.a, test.b) - sort.Sort(sortableBindings(c.Bindings)) - for i, _ := range c.Bindings { - sort.Strings(c.Bindings[i].Members) - } - - if !reflect.DeepEqual(derefBindings(c.Bindings), derefBindings(test.expect.Bindings)) { - t.Errorf("\ngot %+v\nexpected %+v", derefBindings(c.Bindings), derefBindings(test.expect.Bindings)) - } - } -} - // Test that an IAM policy can be applied to a project func TestAccProjectIamPolicy_basic(t *testing.T) { t.Parallel() @@ -241,52 +34,10 @@ func TestAccProjectIamPolicy_basic(t *testing.T) { // merges policies, so we validate the expected state. resource.TestStep{ Config: testAccProjectAssociatePolicyBasic(pid, pname, org), - Check: resource.ComposeTestCheckFunc( - testAccCheckGoogleProjectIamPolicyIsMerged("google_project_iam_policy.acceptance", "data.google_iam_policy.admin", pid), - ), }, resource.TestStep{ ResourceName: "google_project_iam_policy.acceptance", ImportState: true, - // Skipping the normal "ImportStateVerify" - Unfortunately, it's not - // really possible to make the imported policy match exactly, since - // the policy depends on the service account being used to create the - // project. - }, - // Finally, remove the custom IAM policy from config and apply, then - // confirm that the project is in its original state. - resource.TestStep{ - Config: testAccProject_create(pid, pname, org), - Check: resource.ComposeTestCheckFunc( - testAccProjectExistingPolicy(pid), - ), - }, - }, - }) -} - -// Test that an IAM policy can be applied to a project when no project is set in the resource -func TestAccProjectIamPolicy_defaultProject(t *testing.T) { - t.Parallel() - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - // Create a new project - resource.TestStep{ - Config: testAccProjectDefaultAssociatePolicyBasic(), - Check: resource.ComposeTestCheckFunc( - testAccProjectExistingPolicy(getTestProjectFromEnv()), - ), - }, - // Apply an IAM policy from a data source. The application - // merges policies, so we validate the expected state. - resource.TestStep{ - Config: testAccProjectDefaultAssociatePolicyBasic(), - Check: resource.ComposeTestCheckFunc( - testAccCheckGoogleProjectIamPolicyIsMerged("google_project_iam_policy.acceptance", "data.google_iam_policy.admin", getTestProjectFromEnv()), - ), }, }, }) @@ -371,165 +122,6 @@ func testAccCheckGoogleProjectIamPolicyExists(projectRes, policyRes, pid string) } } -func testAccCheckGoogleProjectIamPolicyIsMerged(projectRes, policyRes, pid string) resource.TestCheckFunc { - return func(s *terraform.State) error { - err := testAccCheckGoogleProjectIamPolicyExists(projectRes, policyRes, pid)(s) - if err != nil { - return err - } - - projectPolicy, err := getGoogleProjectIamPolicyFromState(s, projectRes, pid) - if err != nil { - return fmt.Errorf("Error retrieving IAM policy for project from state: %s", err) - } - - // Merge the project policy in Terraform state with the policy the project had before the config was applied - var expected []*cloudresourcemanager.Binding - expected = append(expected, originalPolicy.Bindings...) - expected = append(expected, projectPolicy.Bindings...) - expected = mergeBindings(expected) - - // Retrieve the actual policy from the project - c := testAccProvider.Meta().(*Config) - actual, err := getProjectIamPolicy(pid, c) - if err != nil { - return fmt.Errorf("Failed to retrieve IAM Policy for project %q: %s", pid, err) - } - // The bindings should match, indicating the policy was successfully applied and merged - if !compareBindings(actual.Bindings, expected) { - return fmt.Errorf("Actual and expected project policies do not match: actual policy is %+v, expected policy is %+v", derefBindings(actual.Bindings), derefBindings(expected)) - } - - return nil - } -} - -func TestIamRolesToMembersBinding(t *testing.T) { - table := []struct { - expect []*cloudresourcemanager.Binding - input map[string]map[string]bool - }{ - { - expect: []*cloudresourcemanager.Binding{ - { - Role: "role-1", - Members: []string{ - "member-1", - "member-2", - }, - }, - }, - input: map[string]map[string]bool{ - "role-1": map[string]bool{ - "member-1": true, - "member-2": true, - }, - }, - }, - { - expect: []*cloudresourcemanager.Binding{ - { - Role: "role-1", - Members: []string{ - "member-1", - "member-2", - }, - }, - }, - input: map[string]map[string]bool{ - "role-1": map[string]bool{ - "member-1": true, - "member-2": true, - }, - }, - }, - { - expect: []*cloudresourcemanager.Binding{ - { - Role: "role-1", - Members: []string{}, - }, - }, - input: map[string]map[string]bool{ - "role-1": map[string]bool{}, - }, - }, - } - - for _, test := range table { - got := rolesToMembersBinding(test.input) - - sort.Sort(sortableBindings(got)) - for i, _ := range got { - sort.Strings(got[i].Members) - } - - if !reflect.DeepEqual(derefBindings(got), derefBindings(test.expect)) { - t.Errorf("got %+v, expected %+v", derefBindings(got), derefBindings(test.expect)) - } - } -} -func TestIamRolesToMembersMap(t *testing.T) { - table := []struct { - input []*cloudresourcemanager.Binding - expect map[string]map[string]bool - }{ - { - input: []*cloudresourcemanager.Binding{ - { - Role: "role-1", - Members: []string{ - "member-1", - "member-2", - }, - }, - }, - expect: map[string]map[string]bool{ - "role-1": map[string]bool{ - "member-1": true, - "member-2": true, - }, - }, - }, - { - input: []*cloudresourcemanager.Binding{ - { - Role: "role-1", - Members: []string{ - "member-1", - "member-2", - "member-1", - "member-2", - }, - }, - }, - expect: map[string]map[string]bool{ - "role-1": map[string]bool{ - "member-1": true, - "member-2": true, - }, - }, - }, - { - input: []*cloudresourcemanager.Binding{ - { - Role: "role-1", - }, - }, - expect: map[string]map[string]bool{ - "role-1": map[string]bool{}, - }, - }, - } - - for _, test := range table { - got := rolesToMembersMap(test.input) - if !reflect.DeepEqual(got, test.expect) { - t.Errorf("got %+v, expected %+v", got, test.expect) - } - } -} - func TestIamMergeBindings(t *testing.T) { table := []struct { input []*cloudresourcemanager.Binding @@ -631,14 +223,12 @@ func TestIamMergeBindings(t *testing.T) { }, }, } - for _, test := range table { got := mergeBindings(test.input) sort.Sort(sortableBindings(got)) for i, _ := range got { sort.Strings(got[i].Members) } - if !reflect.DeepEqual(derefBindings(got), test.expect) { t.Errorf("\ngot %+v\nexpected %+v", derefBindings(got), test.expect) } @@ -671,29 +261,6 @@ func testAccProjectExistingPolicy(pid string) resource.TestCheckFunc { } } -func testAccProjectDefaultAssociatePolicyBasic() string { - return fmt.Sprintf(` -resource "google_project_iam_policy" "acceptance" { - policy_data = "${data.google_iam_policy.admin.policy_data}" -} -data "google_iam_policy" "admin" { - binding { - role = "roles/storage.objectViewer" - members = [ - "user:evanbrown@google.com", - ] - } - binding { - role = "roles/compute.instanceAdmin" - members = [ - "user:evanbrown@google.com", - "user:evandbrown@gmail.com", - ] - } -} -`) -} - func testAccProjectAssociatePolicyBasic(pid, name, org string) string { return fmt.Sprintf(` resource "google_project" "acceptance" { @@ -701,10 +268,12 @@ resource "google_project" "acceptance" { name = "%s" org_id = "%s" } + resource "google_project_iam_policy" "acceptance" { project = "${google_project.acceptance.id}" policy_data = "${data.google_iam_policy.admin.policy_data}" } + data "google_iam_policy" "admin" { binding { role = "roles/storage.objectViewer" @@ -723,14 +292,6 @@ data "google_iam_policy" "admin" { `, pid, name, org) } -func testAccProject_createWithoutOrg(pid, name string) string { - return fmt.Sprintf(` -resource "google_project" "acceptance" { - project_id = "%s" - name = "%s" -}`, pid, name) -} - func testAccProject_create(pid, name, org string) string { return fmt.Sprintf(` resource "google_project" "acceptance" { @@ -740,16 +301,6 @@ resource "google_project" "acceptance" { }`, pid, name, org) } -func testAccProject_createBilling(pid, name, org, billing string) string { - return fmt.Sprintf(` -resource "google_project" "acceptance" { - project_id = "%s" - name = "%s" - org_id = "%s" - billing_account = "%s" -}`, pid, name, org, billing) -} - func testAccProjectAssociatePolicyExpanded(pid, name, org string) string { return fmt.Sprintf(` resource "google_project" "acceptance" { @@ -760,8 +311,8 @@ resource "google_project" "acceptance" { resource "google_project_iam_policy" "acceptance" { project = "${google_project.acceptance.id}" policy_data = "${data.google_iam_policy.expanded.policy_data}" - authoritative = false } + data "google_iam_policy" "expanded" { binding { role = "roles/viewer" diff --git a/google/resource_google_project_test.go b/google/resource_google_project_test.go index dad6333d..833c95f1 100644 --- a/google/resource_google_project_test.go +++ b/google/resource_google_project_test.go @@ -431,6 +431,24 @@ func testAccCheckGoogleProjectHasNoLabels(r, pid string) resource.TestCheckFunc } } +func testAccProject_createWithoutOrg(pid, name string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" +}`, pid, name) +} + +func testAccProject_createBilling(pid, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +}`, pid, name, org, billing) +} + func testAccProject_labels(pid, name, org string, labels map[string]string) string { r := fmt.Sprintf(` resource "google_project" "acceptance" { diff --git a/website/docs/r/google_project_iam.html.markdown b/website/docs/r/google_project_iam.html.markdown index 0efe3389..8bbe24c8 100644 --- a/website/docs/r/google_project_iam.html.markdown +++ b/website/docs/r/google_project_iam.html.markdown @@ -21,7 +21,11 @@ Three different resources help you manage your IAM policy for a project. Each of ## google\_project\_iam\_policy ~> **Be careful!** You can accidentally lock yourself out of your project - using this resource. Proceed with caution. + using this resource. Deleting a `google_project_iam_policy` removes access + from anyone without organization-level access to the project. Proceed with caution. + It's not recommended to use `google_project_iam_policy` with your provider project + to avoid locking yourself out, and it should generally only be used with projects + fully managed by Terraform. ```hcl resource "google_project_iam_policy" "project" { @@ -86,28 +90,13 @@ The following arguments are supported: Changing this updates the policy. - Deleting this removes the policy, but leaves the original project policy - intact. If there are overlapping `binding` entries between the original - project policy and the data source policy, they will be removed. + Deleting this removes all policies from the project, locking out users without + organization-level access. -* `project` - (Optional) The project ID. If not specified, uses the - ID of the project configured with the provider. - -* `authoritative` - (DEPRECATED) (Optional, only for `google_project_iam_policy`) - A boolean value indicating if this policy - should overwrite any existing IAM policy on the project. When set to true, - **any policies not in your config file will be removed**. This can **lock - you out** of your project until an Organization Administrator grants you - access again, so please exercise caution. If this argument is `true` and you - want to delete the resource, you must set the `disable_project` argument to - `true`, acknowledging that the project will be inaccessible to anyone but the - Organization Admins, as it will no longer have an IAM policy. Rather than using - this, you should use `google_project_iam_binding` and - `google_project_iam_member`. - -* `disable_project` - (DEPRECATED) (Optional, only for `google_project_iam_policy`) - A boolean value that must be set to `true` - if you want to delete a `google_project_iam_policy` that is authoritative. +* `project` - (Optional) The project ID. If not specified for `google_project_iam_binding` +or `google_project_iam_member`, uses the ID of the project configured with the provider. +Required for `google_project_iam_policy` - you must explicitly set the project, and it +will not be inferred from the provider. ## Attributes Reference @@ -116,9 +105,6 @@ exported: * `etag` - (Computed) The etag of the project's IAM policy. -* `restore_policy` - (DEPRECATED) (Computed, only for `google_project_iam_policy`) - The IAM policy that will be restored when a - non-authoritative policy resource is deleted. ## Import