mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-28 22:16:04 +00:00
Make google_project_iam_policy authoritative. (#2315)
<!-- This change is generated by MagicModules. --> /cc @rileykarson
This commit is contained in:
parent
5a47db4d94
commit
6707290efe
@ -24,8 +24,7 @@ func resourceGoogleProjectIamPolicy() *schema.Resource {
|
|||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"project": &schema.Schema{
|
"project": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Required: true,
|
||||||
Computed: true,
|
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"policy_data": &schema.Schema{
|
"policy_data": &schema.Schema{
|
||||||
@ -33,24 +32,24 @@ func resourceGoogleProjectIamPolicy() *schema.Resource {
|
|||||||
Required: true,
|
Required: true,
|
||||||
DiffSuppressFunc: jsonPolicyDiffSuppress,
|
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{
|
"etag": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
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{
|
"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.",
|
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,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"disable_project": &schema.Schema{
|
"disable_project": &schema.Schema{
|
||||||
Deprecated: "This will be removed with the authoritative field. Use lifecycle.prevent_destroy instead.",
|
Removed: "This field was removed alongside the authoritative field. Use lifecycle.prevent_destroy instead.",
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -58,165 +57,67 @@ func resourceGoogleProjectIamPolicy() *schema.Resource {
|
|||||||
|
|
||||||
func resourceGoogleProjectIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceGoogleProjectIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
pid, err := getProject(d, config)
|
project := d.Get("project").(string)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mutexKey := getProjectIamPolicyMutexKey(pid)
|
mutexKey := getProjectIamPolicyMutexKey(project)
|
||||||
mutexKV.Lock(mutexKey)
|
mutexKV.Lock(mutexKey)
|
||||||
defer mutexKV.Unlock(mutexKey)
|
defer mutexKV.Unlock(mutexKey)
|
||||||
|
|
||||||
// Get the policy in the template
|
// Get the policy in the template
|
||||||
p, err := getResourceIamPolicy(d)
|
policy, err := getResourceIamPolicy(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not get valid 'policy_data' from resource: %v", err)
|
return fmt.Errorf("Could not get valid 'policy_data' from resource: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An authoritative policy is applied without regard for any existing IAM
|
log.Printf("[DEBUG] Setting IAM policy for project %q", project)
|
||||||
// policy.
|
err = setProjectIamPolicy(policy, config, project)
|
||||||
if v, ok := d.GetOk("authoritative"); ok && v.(bool) {
|
if err != nil {
|
||||||
log.Printf("[DEBUG] Setting authoritative IAM policy for project %q", pid)
|
return err
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
d.SetId(pid)
|
|
||||||
|
d.SetId(project)
|
||||||
return resourceGoogleProjectIamPolicyRead(d, meta)
|
return resourceGoogleProjectIamPolicyRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceGoogleProjectIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceGoogleProjectIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
log.Printf("[DEBUG]: Reading google_project_iam_policy")
|
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
pid, err := getProject(d, config)
|
project := d.Get("project").(string)
|
||||||
|
|
||||||
|
policy, err := getProjectIamPolicy(project, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("Error marshaling IAM policy: %v", err)
|
return fmt.Errorf("Error marshaling IAM policy: %v", err)
|
||||||
}
|
}
|
||||||
log.Printf("[DEBUG]: Setting etag=%s", p.Etag)
|
|
||||||
d.Set("etag", p.Etag)
|
d.Set("etag", policy.Etag)
|
||||||
d.Set("policy_data", string(pBytes))
|
d.Set("policy_data", string(policyBytes))
|
||||||
d.Set("project", pid)
|
d.Set("project", project)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceGoogleProjectIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
|
func resourceGoogleProjectIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
log.Printf("[DEBUG]: Updating google_project_iam_policy")
|
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
pid, err := getProject(d, config)
|
project := d.Get("project").(string)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mutexKey := getProjectIamPolicyMutexKey(pid)
|
mutexKey := getProjectIamPolicyMutexKey(project)
|
||||||
mutexKV.Lock(mutexKey)
|
mutexKV.Lock(mutexKey)
|
||||||
defer mutexKV.Unlock(mutexKey)
|
defer mutexKV.Unlock(mutexKey)
|
||||||
|
|
||||||
// Get the policy in the template
|
// Get the policy in the template
|
||||||
p, err := getResourceIamPolicy(d)
|
policy, err := getResourceIamPolicy(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not get valid 'policy_data' from resource: %v", err)
|
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
|
log.Printf("[DEBUG] Updating IAM policy for project %q", project)
|
||||||
// policy.
|
err = setProjectIamPolicy(policy, config, project)
|
||||||
if v, ok := d.GetOk("authoritative"); ok && v.(bool) {
|
if err != nil {
|
||||||
log.Printf("[DEBUG] Updating authoritative IAM policy for project %q", pid)
|
return fmt.Errorf("Error setting project IAM policy: %v", err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceGoogleProjectIamPolicyRead(d, meta)
|
return resourceGoogleProjectIamPolicyRead(d, meta)
|
||||||
@ -225,42 +126,23 @@ func resourceGoogleProjectIamPolicyUpdate(d *schema.ResourceData, meta interface
|
|||||||
func resourceGoogleProjectIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceGoogleProjectIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
log.Printf("[DEBUG]: Deleting google_project_iam_policy")
|
log.Printf("[DEBUG]: Deleting google_project_iam_policy")
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
pid, err := getProject(d, config)
|
project := d.Get("project").(string)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mutexKey := getProjectIamPolicyMutexKey(pid)
|
mutexKey := getProjectIamPolicyMutexKey(project)
|
||||||
mutexKV.Lock(mutexKey)
|
mutexKV.Lock(mutexKey)
|
||||||
defer mutexKV.Unlock(mutexKey)
|
defer mutexKV.Unlock(mutexKey)
|
||||||
|
|
||||||
// Get the existing IAM policy from the API
|
// Get the existing IAM policy from the API so we can repurpose the etag and audit config
|
||||||
ep, err := getProjectIamPolicy(pid, config)
|
ep, err := getProjectIamPolicy(project, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error retrieving IAM policy from project API: %v", err)
|
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 {
|
ep.Bindings = make([]*cloudresourcemanager.Binding, 0)
|
||||||
// A non-authoritative policy should set the policy to the value of "restore_policy" in state
|
if err = setProjectIamPolicy(ep, config, project); err != nil {
|
||||||
// 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 {
|
|
||||||
return fmt.Errorf("Error applying IAM policy to project: %v", err)
|
return fmt.Errorf("Error applying IAM policy to project: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -270,24 +152,6 @@ func resourceGoogleProjectIamPolicyImport(d *schema.ResourceData, meta interface
|
|||||||
return []*schema.ResourceData{d}, nil
|
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 {
|
func setProjectIamPolicy(policy *cloudresourcemanager.Policy, config *Config, pid string) error {
|
||||||
// Apply the policy
|
// Apply the policy
|
||||||
pbytes, _ := json.Marshal(policy)
|
pbytes, _ := json.Marshal(policy)
|
||||||
@ -312,32 +176,6 @@ func getResourceIamPolicy(d *schema.ResourceData) (*cloudresourcemanager.Policy,
|
|||||||
return policy, nil
|
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
|
// Retrieve the existing IAM Policy for a Project
|
||||||
func getProjectIamPolicy(project string, config *Config) (*cloudresourcemanager.Policy, error) {
|
func getProjectIamPolicy(project string, config *Config) (*cloudresourcemanager.Policy, error) {
|
||||||
p, err := config.clientResourceManager.Projects.GetIamPolicy(project,
|
p, err := config.clientResourceManager.Projects.GetIamPolicy(project,
|
||||||
@ -349,22 +187,6 @@ func getProjectIamPolicy(project string, config *Config) (*cloudresourcemanager.
|
|||||||
return p, nil
|
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 {
|
func jsonPolicyDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
|
||||||
var oldPolicy, newPolicy cloudresourcemanager.Policy
|
var oldPolicy, newPolicy cloudresourcemanager.Policy
|
||||||
if err := json.Unmarshal([]byte(old), &oldPolicy); err != nil {
|
if err := json.Unmarshal([]byte(old), &oldPolicy); err != nil {
|
||||||
|
@ -13,213 +13,6 @@ import (
|
|||||||
"google.golang.org/api/cloudresourcemanager/v1"
|
"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
|
// Test that an IAM policy can be applied to a project
|
||||||
func TestAccProjectIamPolicy_basic(t *testing.T) {
|
func TestAccProjectIamPolicy_basic(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
@ -241,52 +34,10 @@ func TestAccProjectIamPolicy_basic(t *testing.T) {
|
|||||||
// merges policies, so we validate the expected state.
|
// merges policies, so we validate the expected state.
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccProjectAssociatePolicyBasic(pid, pname, org),
|
Config: testAccProjectAssociatePolicyBasic(pid, pname, org),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
|
||||||
testAccCheckGoogleProjectIamPolicyIsMerged("google_project_iam_policy.acceptance", "data.google_iam_policy.admin", pid),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
ResourceName: "google_project_iam_policy.acceptance",
|
ResourceName: "google_project_iam_policy.acceptance",
|
||||||
ImportState: true,
|
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) {
|
func TestIamMergeBindings(t *testing.T) {
|
||||||
table := []struct {
|
table := []struct {
|
||||||
input []*cloudresourcemanager.Binding
|
input []*cloudresourcemanager.Binding
|
||||||
@ -631,14 +223,12 @@ func TestIamMergeBindings(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
got := mergeBindings(test.input)
|
got := mergeBindings(test.input)
|
||||||
sort.Sort(sortableBindings(got))
|
sort.Sort(sortableBindings(got))
|
||||||
for i, _ := range got {
|
for i, _ := range got {
|
||||||
sort.Strings(got[i].Members)
|
sort.Strings(got[i].Members)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(derefBindings(got), test.expect) {
|
if !reflect.DeepEqual(derefBindings(got), test.expect) {
|
||||||
t.Errorf("\ngot %+v\nexpected %+v", 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 {
|
func testAccProjectAssociatePolicyBasic(pid, name, org string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_project" "acceptance" {
|
resource "google_project" "acceptance" {
|
||||||
@ -701,10 +268,12 @@ resource "google_project" "acceptance" {
|
|||||||
name = "%s"
|
name = "%s"
|
||||||
org_id = "%s"
|
org_id = "%s"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_project_iam_policy" "acceptance" {
|
resource "google_project_iam_policy" "acceptance" {
|
||||||
project = "${google_project.acceptance.id}"
|
project = "${google_project.acceptance.id}"
|
||||||
policy_data = "${data.google_iam_policy.admin.policy_data}"
|
policy_data = "${data.google_iam_policy.admin.policy_data}"
|
||||||
}
|
}
|
||||||
|
|
||||||
data "google_iam_policy" "admin" {
|
data "google_iam_policy" "admin" {
|
||||||
binding {
|
binding {
|
||||||
role = "roles/storage.objectViewer"
|
role = "roles/storage.objectViewer"
|
||||||
@ -723,14 +292,6 @@ data "google_iam_policy" "admin" {
|
|||||||
`, pid, name, org)
|
`, 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 {
|
func testAccProject_create(pid, name, org string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_project" "acceptance" {
|
resource "google_project" "acceptance" {
|
||||||
@ -740,16 +301,6 @@ resource "google_project" "acceptance" {
|
|||||||
}`, pid, name, org)
|
}`, 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 {
|
func testAccProjectAssociatePolicyExpanded(pid, name, org string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "google_project" "acceptance" {
|
resource "google_project" "acceptance" {
|
||||||
@ -760,8 +311,8 @@ resource "google_project" "acceptance" {
|
|||||||
resource "google_project_iam_policy" "acceptance" {
|
resource "google_project_iam_policy" "acceptance" {
|
||||||
project = "${google_project.acceptance.id}"
|
project = "${google_project.acceptance.id}"
|
||||||
policy_data = "${data.google_iam_policy.expanded.policy_data}"
|
policy_data = "${data.google_iam_policy.expanded.policy_data}"
|
||||||
authoritative = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data "google_iam_policy" "expanded" {
|
data "google_iam_policy" "expanded" {
|
||||||
binding {
|
binding {
|
||||||
role = "roles/viewer"
|
role = "roles/viewer"
|
||||||
|
@ -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 {
|
func testAccProject_labels(pid, name, org string, labels map[string]string) string {
|
||||||
r := fmt.Sprintf(`
|
r := fmt.Sprintf(`
|
||||||
resource "google_project" "acceptance" {
|
resource "google_project" "acceptance" {
|
||||||
|
@ -21,7 +21,11 @@ Three different resources help you manage your IAM policy for a project. Each of
|
|||||||
## google\_project\_iam\_policy
|
## google\_project\_iam\_policy
|
||||||
|
|
||||||
~> **Be careful!** You can accidentally lock yourself out of your project
|
~> **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
|
```hcl
|
||||||
resource "google_project_iam_policy" "project" {
|
resource "google_project_iam_policy" "project" {
|
||||||
@ -86,28 +90,13 @@ The following arguments are supported:
|
|||||||
|
|
||||||
Changing this updates the policy.
|
Changing this updates the policy.
|
||||||
|
|
||||||
Deleting this removes the policy, but leaves the original project policy
|
Deleting this removes all policies from the project, locking out users without
|
||||||
intact. If there are overlapping `binding` entries between the original
|
organization-level access.
|
||||||
project policy and the data source policy, they will be removed.
|
|
||||||
|
|
||||||
* `project` - (Optional) The project ID. If not specified, uses the
|
* `project` - (Optional) The project ID. If not specified for `google_project_iam_binding`
|
||||||
ID of the project configured with the provider.
|
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
|
||||||
* `authoritative` - (DEPRECATED) (Optional, only for `google_project_iam_policy`)
|
will not be inferred from the provider.
|
||||||
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.
|
|
||||||
|
|
||||||
## Attributes Reference
|
## Attributes Reference
|
||||||
|
|
||||||
@ -116,9 +105,6 @@ exported:
|
|||||||
|
|
||||||
* `etag` - (Computed) The etag of the project's IAM policy.
|
* `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
|
## Import
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user