terraform/builtin/providers/aws/resource_aws_iam_policy_att...

375 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package aws
import (
"fmt"
"log"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsIamPolicyAttachment() *schema.Resource {
return &schema.Resource{
Create: resourceAwsIamPolicyAttachmentCreate,
Read: resourceAwsIamPolicyAttachmentRead,
Update: resourceAwsIamPolicyAttachmentUpdate,
Delete: resourceAwsIamPolicyAttachmentDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
if v.(string) == "" {
errors = append(errors, fmt.Errorf(
"%q cannot be an empty string", k))
}
return
},
},
"users": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"roles": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"groups": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"policy_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}
func resourceAwsIamPolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iamconn
name := d.Get("name").(string)
arn := d.Get("policy_arn").(string)
users := expandStringList(d.Get("users").(*schema.Set).List())
roles := expandStringList(d.Get("roles").(*schema.Set).List())
groups := expandStringList(d.Get("groups").(*schema.Set).List())
if len(users) == 0 && len(roles) == 0 && len(groups) == 0 {
return fmt.Errorf("[WARN] No Users, Roles, or Groups specified for IAM Policy Attachment %s", name)
} else {
var userErr, roleErr, groupErr error
if users != nil {
userErr = attachPolicyToUsers(conn, users, arn)
}
if roles != nil {
roleErr = attachPolicyToRoles(conn, roles, arn)
}
if groups != nil {
groupErr = attachPolicyToGroups(conn, groups, arn)
}
if userErr != nil || roleErr != nil || groupErr != nil {
return composeErrors(fmt.Sprint("[WARN] Error attaching policy with IAM Policy Attachment ", name, ":"), userErr, roleErr, groupErr)
}
}
d.SetId(d.Get("name").(string))
return resourceAwsIamPolicyAttachmentRead(d, meta)
}
func resourceAwsIamPolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iamconn
arn := d.Get("policy_arn").(string)
name := d.Get("name").(string)
_, err := conn.GetPolicy(&iam.GetPolicyInput{
PolicyArn: aws.String(arn),
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NoSuchEntity" {
log.Printf("[WARN] No such entity found for Policy Attachment (%s)", d.Id())
d.SetId("")
return nil
}
}
return err
}
ul := make([]string, 0)
rl := make([]string, 0)
gl := make([]string, 0)
args := iam.ListEntitiesForPolicyInput{
PolicyArn: aws.String(arn),
}
err = conn.ListEntitiesForPolicyPages(&args, func(page *iam.ListEntitiesForPolicyOutput, lastPage bool) bool {
for _, u := range page.PolicyUsers {
ul = append(ul, *u.UserName)
}
for _, r := range page.PolicyRoles {
rl = append(rl, *r.RoleName)
}
for _, g := range page.PolicyGroups {
gl = append(gl, *g.GroupName)
}
return true
})
if err != nil {
return err
}
userErr := d.Set("users", ul)
roleErr := d.Set("roles", rl)
groupErr := d.Set("groups", gl)
if userErr != nil || roleErr != nil || groupErr != nil {
return composeErrors(fmt.Sprint("[WARN} Error setting user, role, or group list from IAM Policy Attachment ", name, ":"), userErr, roleErr, groupErr)
}
return nil
}
func resourceAwsIamPolicyAttachmentUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iamconn
name := d.Get("name").(string)
var userErr, roleErr, groupErr error
if d.HasChange("users") {
userErr = updateUsers(conn, d, meta)
}
if d.HasChange("roles") {
roleErr = updateRoles(conn, d, meta)
}
if d.HasChange("groups") {
groupErr = updateGroups(conn, d, meta)
}
if userErr != nil || roleErr != nil || groupErr != nil {
return composeErrors(fmt.Sprint("[WARN] Error updating user, role, or group list from IAM Policy Attachment ", name, ":"), userErr, roleErr, groupErr)
}
return resourceAwsIamPolicyAttachmentRead(d, meta)
}
func resourceAwsIamPolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iamconn
name := d.Get("name").(string)
arn := d.Get("policy_arn").(string)
users := expandStringList(d.Get("users").(*schema.Set).List())
roles := expandStringList(d.Get("roles").(*schema.Set).List())
groups := expandStringList(d.Get("groups").(*schema.Set).List())
var userErr, roleErr, groupErr error
if len(users) != 0 {
userErr = detachPolicyFromUsers(conn, users, arn)
}
if len(roles) != 0 {
roleErr = detachPolicyFromRoles(conn, roles, arn)
}
if len(groups) != 0 {
groupErr = detachPolicyFromGroups(conn, groups, arn)
}
if userErr != nil || roleErr != nil || groupErr != nil {
return composeErrors(fmt.Sprint("[WARN] Error removing user, role, or group list from IAM Policy Detach ", name, ":"), userErr, roleErr, groupErr)
}
return nil
}
func composeErrors(desc string, uErr error, rErr error, gErr error) error {
errMsg := fmt.Sprintf(desc)
errs := []error{uErr, rErr, gErr}
for _, e := range errs {
if e != nil {
errMsg = errMsg + "\n " + e.Error()
}
}
return fmt.Errorf(errMsg)
}
func attachPolicyToUsers(conn *iam.IAM, users []*string, arn string) error {
for _, u := range users {
_, err := conn.AttachUserPolicy(&iam.AttachUserPolicyInput{
UserName: u,
PolicyArn: aws.String(arn),
})
if err != nil {
return err
}
}
return nil
}
func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
for _, r := range roles {
_, err := conn.AttachRolePolicy(&iam.AttachRolePolicyInput{
RoleName: r,
PolicyArn: aws.String(arn),
})
if err != nil {
return err
}
var attachmentErr error
attachmentErr = resource.Retry(2*time.Minute, func() *resource.RetryError {
input := iam.ListRolePoliciesInput{
RoleName: r,
}
attachedPolicies, err := conn.ListRolePolicies(&input)
if err != nil {
return resource.NonRetryableError(err)
}
if len(attachedPolicies.PolicyNames) > 0 {
var foundPolicy bool
for _, policyName := range attachedPolicies.PolicyNames {
if strings.HasSuffix(arn, *policyName) {
foundPolicy = true
break
}
}
if !foundPolicy {
return resource.NonRetryableError(err)
}
}
return nil
})
if attachmentErr != nil {
return attachmentErr
}
}
return nil
}
func attachPolicyToGroups(conn *iam.IAM, groups []*string, arn string) error {
for _, g := range groups {
_, err := conn.AttachGroupPolicy(&iam.AttachGroupPolicyInput{
GroupName: g,
PolicyArn: aws.String(arn),
})
if err != nil {
return err
}
}
return nil
}
func updateUsers(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
arn := d.Get("policy_arn").(string)
o, n := d.GetChange("users")
if o == nil {
o = new(schema.Set)
}
if n == nil {
n = new(schema.Set)
}
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := expandStringList(os.Difference(ns).List())
add := expandStringList(ns.Difference(os).List())
if rErr := detachPolicyFromUsers(conn, remove, arn); rErr != nil {
return rErr
}
if aErr := attachPolicyToUsers(conn, add, arn); aErr != nil {
return aErr
}
return nil
}
func updateRoles(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
arn := d.Get("policy_arn").(string)
o, n := d.GetChange("roles")
if o == nil {
o = new(schema.Set)
}
if n == nil {
n = new(schema.Set)
}
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := expandStringList(os.Difference(ns).List())
add := expandStringList(ns.Difference(os).List())
if rErr := detachPolicyFromRoles(conn, remove, arn); rErr != nil {
return rErr
}
if aErr := attachPolicyToRoles(conn, add, arn); aErr != nil {
return aErr
}
return nil
}
func updateGroups(conn *iam.IAM, d *schema.ResourceData, meta interface{}) error {
arn := d.Get("policy_arn").(string)
o, n := d.GetChange("groups")
if o == nil {
o = new(schema.Set)
}
if n == nil {
n = new(schema.Set)
}
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := expandStringList(os.Difference(ns).List())
add := expandStringList(ns.Difference(os).List())
if rErr := detachPolicyFromGroups(conn, remove, arn); rErr != nil {
return rErr
}
if aErr := attachPolicyToGroups(conn, add, arn); aErr != nil {
return aErr
}
return nil
}
func detachPolicyFromUsers(conn *iam.IAM, users []*string, arn string) error {
for _, u := range users {
_, err := conn.DetachUserPolicy(&iam.DetachUserPolicyInput{
UserName: u,
PolicyArn: aws.String(arn),
})
if err != nil {
return err
}
}
return nil
}
func detachPolicyFromRoles(conn *iam.IAM, roles []*string, arn string) error {
for _, r := range roles {
_, err := conn.DetachRolePolicy(&iam.DetachRolePolicyInput{
RoleName: r,
PolicyArn: aws.String(arn),
})
if err != nil {
return err
}
}
return nil
}
func detachPolicyFromGroups(conn *iam.IAM, groups []*string, arn string) error {
for _, g := range groups {
_, err := conn.DetachGroupPolicy(&iam.DetachGroupPolicyInput{
GroupName: g,
PolicyArn: aws.String(arn),
})
if err != nil {
return err
}
}
return nil
}