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