github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_iam_policy_attachment.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/iam"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsIamPolicyAttachment() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAwsIamPolicyAttachmentCreate,
    19  		Read:   resourceAwsIamPolicyAttachmentRead,
    20  		Update: resourceAwsIamPolicyAttachmentUpdate,
    21  		Delete: resourceAwsIamPolicyAttachmentDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"name": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    29  					if v.(string) == "" {
    30  						errors = append(errors, fmt.Errorf(
    31  							"%q cannot be an empty string", k))
    32  					}
    33  					return
    34  				},
    35  			},
    36  			"users": &schema.Schema{
    37  				Type:     schema.TypeSet,
    38  				Optional: true,
    39  				Elem:     &schema.Schema{Type: schema.TypeString},
    40  				Set:      schema.HashString,
    41  			},
    42  			"roles": &schema.Schema{
    43  				Type:     schema.TypeSet,
    44  				Optional: true,
    45  				Elem:     &schema.Schema{Type: schema.TypeString},
    46  				Set:      schema.HashString,
    47  			},
    48  			"groups": &schema.Schema{
    49  				Type:     schema.TypeSet,
    50  				Optional: true,
    51  				Elem:     &schema.Schema{Type: schema.TypeString},
    52  				Set:      schema.HashString,
    53  			},
    54  			"policy_arn": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Required: true,
    57  				ForceNew: true,
    58  			},
    59  		},
    60  	}
    61  }
    62  
    63  func resourceAwsIamPolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
    64  	conn := meta.(*AWSClient).iamconn
    65  
    66  	name := d.Get("name").(string)
    67  	arn := d.Get("policy_arn").(string)
    68  	users := expandStringList(d.Get("users").(*schema.Set).List())
    69  	roles := expandStringList(d.Get("roles").(*schema.Set).List())
    70  	groups := expandStringList(d.Get("groups").(*schema.Set).List())
    71  
    72  	if len(users) == 0 && len(roles) == 0 && len(groups) == 0 {
    73  		return fmt.Errorf("[WARN] No Users, Roles, or Groups specified for IAM Policy Attachment %s", name)
    74  	} else {
    75  		var userErr, roleErr, groupErr error
    76  		if users != nil {
    77  			userErr = attachPolicyToUsers(conn, users, arn)
    78  		}
    79  		if roles != nil {
    80  			roleErr = attachPolicyToRoles(conn, roles, arn)
    81  		}
    82  		if groups != nil {
    83  			groupErr = attachPolicyToGroups(conn, groups, arn)
    84  		}
    85  		if userErr != nil || roleErr != nil || groupErr != nil {
    86  			return composeErrors(fmt.Sprint("[WARN] Error attaching policy with IAM Policy Attachment ", name, ":"), userErr, roleErr, groupErr)
    87  		}
    88  	}
    89  	d.SetId(d.Get("name").(string))
    90  	return resourceAwsIamPolicyAttachmentRead(d, meta)
    91  }
    92  
    93  func resourceAwsIamPolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error {
    94  	conn := meta.(*AWSClient).iamconn
    95  	arn := d.Get("policy_arn").(string)
    96  	name := d.Get("name").(string)
    97  
    98  	_, err := conn.GetPolicy(&iam.GetPolicyInput{
    99  		PolicyArn: aws.String(arn),
   100  	})
   101  
   102  	if err != nil {
   103  		if awsErr, ok := err.(awserr.Error); ok {
   104  			if awsErr.Code() == "NoSuchEntity" {
   105  				log.Printf("[WARN] No such entity found for Policy Attachment (%s)", d.Id())
   106  				d.SetId("")
   107  				return nil
   108  			}
   109  		}
   110  		return err
   111  	}
   112  
   113  	ul := make([]string, 0)
   114  	rl := make([]string, 0)
   115  	gl := make([]string, 0)
   116  
   117  	args := iam.ListEntitiesForPolicyInput{
   118  		PolicyArn: aws.String(arn),
   119  	}
   120  	err = conn.ListEntitiesForPolicyPages(&args, func(page *iam.ListEntitiesForPolicyOutput, lastPage bool) bool {
   121  		for _, u := range page.PolicyUsers {
   122  			ul = append(ul, *u.UserName)
   123  		}
   124  
   125  		for _, r := range page.PolicyRoles {
   126  			rl = append(rl, *r.RoleName)
   127  		}
   128  
   129  		for _, g := range page.PolicyGroups {
   130  			gl = append(gl, *g.GroupName)
   131  		}
   132  		return true
   133  	})
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	userErr := d.Set("users", ul)
   139  	roleErr := d.Set("roles", rl)
   140  	groupErr := d.Set("groups", gl)
   141  
   142  	if userErr != nil || roleErr != nil || groupErr != nil {
   143  		return composeErrors(fmt.Sprint("[WARN} Error setting user, role, or group list from IAM Policy Attachment ", name, ":"), userErr, roleErr, groupErr)
   144  	}
   145  
   146  	return nil
   147  }
   148  func resourceAwsIamPolicyAttachmentUpdate(d *schema.ResourceData, meta interface{}) error {
   149  	conn := meta.(*AWSClient).iamconn
   150  	name := d.Get("name").(string)
   151  	var userErr, roleErr, groupErr error
   152  
   153  	if d.HasChange("users") {
   154  		userErr = updateUsers(conn, d, meta)
   155  	}
   156  	if d.HasChange("roles") {
   157  		roleErr = updateRoles(conn, d, meta)
   158  	}
   159  	if d.HasChange("groups") {
   160  		groupErr = updateGroups(conn, d, meta)
   161  	}
   162  	if userErr != nil || roleErr != nil || groupErr != nil {
   163  		return composeErrors(fmt.Sprint("[WARN] Error updating user, role, or group list from IAM Policy Attachment ", name, ":"), userErr, roleErr, groupErr)
   164  	}
   165  	return resourceAwsIamPolicyAttachmentRead(d, meta)
   166  }
   167  
   168  func resourceAwsIamPolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
   169  	conn := meta.(*AWSClient).iamconn
   170  	name := d.Get("name").(string)
   171  	arn := d.Get("policy_arn").(string)
   172  	users := expandStringList(d.Get("users").(*schema.Set).List())
   173  	roles := expandStringList(d.Get("roles").(*schema.Set).List())
   174  	groups := expandStringList(d.Get("groups").(*schema.Set).List())
   175  
   176  	var userErr, roleErr, groupErr error
   177  	if len(users) != 0 {
   178  		userErr = detachPolicyFromUsers(conn, users, arn)
   179  	}
   180  	if len(roles) != 0 {
   181  		roleErr = detachPolicyFromRoles(conn, roles, arn)
   182  	}
   183  	if len(groups) != 0 {
   184  		groupErr = detachPolicyFromGroups(conn, groups, arn)
   185  	}
   186  	if userErr != nil || roleErr != nil || groupErr != nil {
   187  		return composeErrors(fmt.Sprint("[WARN] Error removing user, role, or group list from IAM Policy Detach ", name, ":"), userErr, roleErr, groupErr)
   188  	}
   189  	return nil
   190  }
   191  
   192  func composeErrors(desc string, uErr error, rErr error, gErr error) error {
   193  	errMsg := fmt.Sprintf(desc)
   194  	errs := []error{uErr, rErr, gErr}
   195  	for _, e := range errs {
   196  		if e != nil {
   197  			errMsg = errMsg + "\n– " + e.Error()
   198  		}
   199  	}
   200  	return fmt.Errorf(errMsg)
   201  }
   202  
   203  func attachPolicyToUsers(conn *iam.IAM, users []*string, arn string) error {
   204  	for _, u := range users {
   205  		_, err := conn.AttachUserPolicy(&iam.AttachUserPolicyInput{
   206  			UserName:  u,
   207  			PolicyArn: aws.String(arn),
   208  		})
   209  		if err != nil {
   210  			return err
   211  		}
   212  	}
   213  	return nil
   214  }
   215  func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
   216  	for _, r := range roles {
   217  		_, err := conn.AttachRolePolicy(&iam.AttachRolePolicyInput{
   218  			RoleName:  r,
   219  			PolicyArn: aws.String(arn),
   220  		})
   221  		if err != nil {
   222  			return err
   223  		}
   224  
   225  		var attachmentErr error
   226  		attachmentErr = resource.Retry(2*time.Minute, func() *resource.RetryError {
   227  
   228  			input := iam.ListRolePoliciesInput{
   229  				RoleName: r,
   230  			}
   231  
   232  			attachedPolicies, err := conn.ListRolePolicies(&input)
   233  			if err != nil {
   234  				return resource.NonRetryableError(err)
   235  			}
   236  
   237  			if len(attachedPolicies.PolicyNames) > 0 {
   238  				var foundPolicy bool
   239  				for _, policyName := range attachedPolicies.PolicyNames {
   240  					if strings.HasSuffix(arn, *policyName) {
   241  						foundPolicy = true
   242  						break
   243  					}
   244  				}
   245  
   246  				if !foundPolicy {
   247  					return resource.NonRetryableError(err)
   248  				}
   249  			}
   250  
   251  			return nil
   252  		})
   253  
   254  		if attachmentErr != nil {
   255  			return attachmentErr
   256  		}
   257  	}
   258  	return nil
   259  }
   260  func attachPolicyToGroups(conn *iam.IAM, groups []*string, arn string) error {
   261  	for _, g := range groups {
   262  		_, err := conn.AttachGroupPolicy(&iam.AttachGroupPolicyInput{
   263  			GroupName: g,
   264  			PolicyArn: aws.String(arn),
   265  		})
   266  		if err != nil {
   267  			return err
   268  		}
   269  	}
   270  	return nil
   271  }
   272  func updateUsers(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
   273  	arn := d.Get("policy_arn").(string)
   274  	o, n := d.GetChange("users")
   275  	if o == nil {
   276  		o = new(schema.Set)
   277  	}
   278  	if n == nil {
   279  		n = new(schema.Set)
   280  	}
   281  	os := o.(*schema.Set)
   282  	ns := n.(*schema.Set)
   283  	remove := expandStringList(os.Difference(ns).List())
   284  	add := expandStringList(ns.Difference(os).List())
   285  
   286  	if rErr := detachPolicyFromUsers(conn, remove, arn); rErr != nil {
   287  		return rErr
   288  	}
   289  	if aErr := attachPolicyToUsers(conn, add, arn); aErr != nil {
   290  		return aErr
   291  	}
   292  	return nil
   293  }
   294  func updateRoles(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
   295  	arn := d.Get("policy_arn").(string)
   296  	o, n := d.GetChange("roles")
   297  	if o == nil {
   298  		o = new(schema.Set)
   299  	}
   300  	if n == nil {
   301  		n = new(schema.Set)
   302  	}
   303  	os := o.(*schema.Set)
   304  	ns := n.(*schema.Set)
   305  	remove := expandStringList(os.Difference(ns).List())
   306  	add := expandStringList(ns.Difference(os).List())
   307  
   308  	if rErr := detachPolicyFromRoles(conn, remove, arn); rErr != nil {
   309  		return rErr
   310  	}
   311  	if aErr := attachPolicyToRoles(conn, add, arn); aErr != nil {
   312  		return aErr
   313  	}
   314  	return nil
   315  }
   316  func updateGroups(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
   317  	arn := d.Get("policy_arn").(string)
   318  	o, n := d.GetChange("groups")
   319  	if o == nil {
   320  		o = new(schema.Set)
   321  	}
   322  	if n == nil {
   323  		n = new(schema.Set)
   324  	}
   325  	os := o.(*schema.Set)
   326  	ns := n.(*schema.Set)
   327  	remove := expandStringList(os.Difference(ns).List())
   328  	add := expandStringList(ns.Difference(os).List())
   329  
   330  	if rErr := detachPolicyFromGroups(conn, remove, arn); rErr != nil {
   331  		return rErr
   332  	}
   333  	if aErr := attachPolicyToGroups(conn, add, arn); aErr != nil {
   334  		return aErr
   335  	}
   336  	return nil
   337  
   338  }
   339  func detachPolicyFromUsers(conn *iam.IAM, users []*string, arn string) error {
   340  	for _, u := range users {
   341  		_, err := conn.DetachUserPolicy(&iam.DetachUserPolicyInput{
   342  			UserName:  u,
   343  			PolicyArn: aws.String(arn),
   344  		})
   345  		if err != nil {
   346  			return err
   347  		}
   348  	}
   349  	return nil
   350  }
   351  func detachPolicyFromRoles(conn *iam.IAM, roles []*string, arn string) error {
   352  	for _, r := range roles {
   353  		_, err := conn.DetachRolePolicy(&iam.DetachRolePolicyInput{
   354  			RoleName:  r,
   355  			PolicyArn: aws.String(arn),
   356  		})
   357  		if err != nil {
   358  			return err
   359  		}
   360  	}
   361  	return nil
   362  }
   363  func detachPolicyFromGroups(conn *iam.IAM, groups []*string, arn string) error {
   364  	for _, g := range groups {
   365  		_, err := conn.DetachGroupPolicy(&iam.DetachGroupPolicyInput{
   366  			GroupName: g,
   367  			PolicyArn: aws.String(arn),
   368  		})
   369  		if err != nil {
   370  			return err
   371  		}
   372  	}
   373  	return nil
   374  }