375 lines
9.7 KiB
Go
375 lines
9.7 KiB
Go
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
|
||
}
|