github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_iam_role.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "net/url" 6 "regexp" 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 13 "github.com/hashicorp/terraform/helper/resource" 14 "github.com/hashicorp/terraform/helper/schema" 15 ) 16 17 func resourceAwsIamRole() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsIamRoleCreate, 20 Read: resourceAwsIamRoleRead, 21 Update: resourceAwsIamRoleUpdate, 22 Delete: resourceAwsIamRoleDelete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "arn": { 29 Type: schema.TypeString, 30 Computed: true, 31 }, 32 33 "unique_id": { 34 Type: schema.TypeString, 35 Computed: true, 36 }, 37 38 "name": { 39 Type: schema.TypeString, 40 Optional: true, 41 Computed: true, 42 ForceNew: true, 43 ConflictsWith: []string{"name_prefix"}, 44 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 45 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334 46 value := v.(string) 47 if len(value) > 64 { 48 errors = append(errors, fmt.Errorf( 49 "%q cannot be longer than 64 characters", k)) 50 } 51 if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) { 52 errors = append(errors, fmt.Errorf( 53 "%q must match [\\w+=,.@-]", k)) 54 } 55 return 56 }, 57 }, 58 59 "name_prefix": { 60 Type: schema.TypeString, 61 Optional: true, 62 ForceNew: true, 63 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 64 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334 65 value := v.(string) 66 if len(value) > 32 { 67 errors = append(errors, fmt.Errorf( 68 "%q cannot be longer than 32 characters, name is limited to 64", k)) 69 } 70 if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) { 71 errors = append(errors, fmt.Errorf( 72 "%q must match [\\w+=,.@-]", k)) 73 } 74 return 75 }, 76 }, 77 78 "path": { 79 Type: schema.TypeString, 80 Optional: true, 81 Default: "/", 82 ForceNew: true, 83 }, 84 85 "assume_role_policy": { 86 Type: schema.TypeString, 87 Required: true, 88 DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs, 89 ValidateFunc: validateJsonString, 90 }, 91 92 "create_date": { 93 Type: schema.TypeString, 94 Computed: true, 95 }, 96 }, 97 } 98 } 99 100 func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error { 101 iamconn := meta.(*AWSClient).iamconn 102 103 var name string 104 if v, ok := d.GetOk("name"); ok { 105 name = v.(string) 106 } else if v, ok := d.GetOk("name_prefix"); ok { 107 name = resource.PrefixedUniqueId(v.(string)) 108 } else { 109 name = resource.UniqueId() 110 } 111 112 request := &iam.CreateRoleInput{ 113 Path: aws.String(d.Get("path").(string)), 114 RoleName: aws.String(name), 115 AssumeRolePolicyDocument: aws.String(d.Get("assume_role_policy").(string)), 116 } 117 118 var createResp *iam.CreateRoleOutput 119 err := resource.Retry(30*time.Second, func() *resource.RetryError { 120 var err error 121 createResp, err = iamconn.CreateRole(request) 122 // IAM users (referenced in Principal field of assume policy) 123 // can take ~30 seconds to propagate in AWS 124 if isAWSErr(err, "MalformedPolicyDocument", "Invalid principal in policy") { 125 return resource.RetryableError(err) 126 } 127 return resource.NonRetryableError(err) 128 }) 129 if err != nil { 130 return fmt.Errorf("Error creating IAM Role %s: %s", name, err) 131 } 132 return resourceAwsIamRoleReadResult(d, createResp.Role) 133 } 134 135 func resourceAwsIamRoleRead(d *schema.ResourceData, meta interface{}) error { 136 iamconn := meta.(*AWSClient).iamconn 137 138 request := &iam.GetRoleInput{ 139 RoleName: aws.String(d.Id()), 140 } 141 142 getResp, err := iamconn.GetRole(request) 143 if err != nil { 144 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { // XXX test me 145 d.SetId("") 146 return nil 147 } 148 return fmt.Errorf("Error reading IAM Role %s: %s", d.Id(), err) 149 } 150 return resourceAwsIamRoleReadResult(d, getResp.Role) 151 } 152 153 func resourceAwsIamRoleUpdate(d *schema.ResourceData, meta interface{}) error { 154 iamconn := meta.(*AWSClient).iamconn 155 156 if d.HasChange("assume_role_policy") { 157 assumeRolePolicyInput := &iam.UpdateAssumeRolePolicyInput{ 158 RoleName: aws.String(d.Id()), 159 PolicyDocument: aws.String(d.Get("assume_role_policy").(string)), 160 } 161 _, err := iamconn.UpdateAssumeRolePolicy(assumeRolePolicyInput) 162 if err != nil { 163 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 164 d.SetId("") 165 return nil 166 } 167 return fmt.Errorf("Error Updating IAM Role (%s) Assume Role Policy: %s", d.Id(), err) 168 } 169 } 170 171 return nil 172 } 173 174 func resourceAwsIamRoleReadResult(d *schema.ResourceData, role *iam.Role) error { 175 d.SetId(*role.RoleName) 176 if err := d.Set("name", role.RoleName); err != nil { 177 return err 178 } 179 if err := d.Set("arn", role.Arn); err != nil { 180 return err 181 } 182 if err := d.Set("path", role.Path); err != nil { 183 return err 184 } 185 if err := d.Set("unique_id", role.RoleId); err != nil { 186 return err 187 } 188 if err := d.Set("create_date", role.CreateDate.Format(time.RFC3339)); err != nil { 189 return err 190 } 191 192 assumRolePolicy, err := url.QueryUnescape(*role.AssumeRolePolicyDocument) 193 if err != nil { 194 return err 195 } 196 if err := d.Set("assume_role_policy", assumRolePolicy); err != nil { 197 return err 198 } 199 return nil 200 } 201 202 func resourceAwsIamRoleDelete(d *schema.ResourceData, meta interface{}) error { 203 iamconn := meta.(*AWSClient).iamconn 204 205 // Roles cannot be destroyed when attached to an existing Instance Profile 206 resp, err := iamconn.ListInstanceProfilesForRole(&iam.ListInstanceProfilesForRoleInput{ 207 RoleName: aws.String(d.Id()), 208 }) 209 if err != nil { 210 return fmt.Errorf("Error listing Profiles for IAM Role (%s) when trying to delete: %s", d.Id(), err) 211 } 212 213 // Loop and remove this Role from any Profiles 214 if len(resp.InstanceProfiles) > 0 { 215 for _, i := range resp.InstanceProfiles { 216 _, err := iamconn.RemoveRoleFromInstanceProfile(&iam.RemoveRoleFromInstanceProfileInput{ 217 InstanceProfileName: i.InstanceProfileName, 218 RoleName: aws.String(d.Id()), 219 }) 220 if err != nil { 221 return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err) 222 } 223 } 224 } 225 226 request := &iam.DeleteRoleInput{ 227 RoleName: aws.String(d.Id()), 228 } 229 230 if _, err := iamconn.DeleteRole(request); err != nil { 231 return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err) 232 } 233 return nil 234 }