github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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 "description": { 86 Type: schema.TypeString, 87 Optional: true, 88 ValidateFunc: validateIamRoleDescription, 89 }, 90 91 "assume_role_policy": { 92 Type: schema.TypeString, 93 Required: true, 94 DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs, 95 ValidateFunc: validateJsonString, 96 }, 97 98 "create_date": { 99 Type: schema.TypeString, 100 Computed: true, 101 }, 102 }, 103 } 104 } 105 106 func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error { 107 iamconn := meta.(*AWSClient).iamconn 108 109 var name string 110 if v, ok := d.GetOk("name"); ok { 111 name = v.(string) 112 } else if v, ok := d.GetOk("name_prefix"); ok { 113 name = resource.PrefixedUniqueId(v.(string)) 114 } else { 115 name = resource.UniqueId() 116 } 117 118 request := &iam.CreateRoleInput{ 119 Path: aws.String(d.Get("path").(string)), 120 RoleName: aws.String(name), 121 AssumeRolePolicyDocument: aws.String(d.Get("assume_role_policy").(string)), 122 } 123 124 if v, ok := d.GetOk("description"); ok { 125 request.Description = aws.String(v.(string)) 126 } 127 128 var createResp *iam.CreateRoleOutput 129 err := resource.Retry(30*time.Second, func() *resource.RetryError { 130 var err error 131 createResp, err = iamconn.CreateRole(request) 132 // IAM users (referenced in Principal field of assume policy) 133 // can take ~30 seconds to propagate in AWS 134 if isAWSErr(err, "MalformedPolicyDocument", "Invalid principal in policy") { 135 return resource.RetryableError(err) 136 } 137 return resource.NonRetryableError(err) 138 }) 139 if err != nil { 140 return fmt.Errorf("Error creating IAM Role %s: %s", name, err) 141 } 142 d.SetId(*createResp.Role.RoleName) 143 return resourceAwsIamRoleRead(d, meta) 144 } 145 146 func resourceAwsIamRoleRead(d *schema.ResourceData, meta interface{}) error { 147 iamconn := meta.(*AWSClient).iamconn 148 149 request := &iam.GetRoleInput{ 150 RoleName: aws.String(d.Id()), 151 } 152 153 getResp, err := iamconn.GetRole(request) 154 if err != nil { 155 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { // XXX test me 156 d.SetId("") 157 return nil 158 } 159 return fmt.Errorf("Error reading IAM Role %s: %s", d.Id(), err) 160 } 161 162 role := getResp.Role 163 164 if err := d.Set("name", role.RoleName); err != nil { 165 return err 166 } 167 if err := d.Set("arn", role.Arn); err != nil { 168 return err 169 } 170 if err := d.Set("path", role.Path); err != nil { 171 return err 172 } 173 if err := d.Set("unique_id", role.RoleId); err != nil { 174 return err 175 } 176 if err := d.Set("create_date", role.CreateDate.Format(time.RFC3339)); err != nil { 177 return err 178 } 179 180 if role.Description != nil { 181 // the description isn't present in the response to CreateRole. 182 if err := d.Set("description", role.Description); err != nil { 183 return err 184 } 185 } 186 187 assumRolePolicy, err := url.QueryUnescape(*role.AssumeRolePolicyDocument) 188 if err != nil { 189 return err 190 } 191 if err := d.Set("assume_role_policy", assumRolePolicy); err != nil { 192 return err 193 } 194 return nil 195 } 196 197 func resourceAwsIamRoleUpdate(d *schema.ResourceData, meta interface{}) error { 198 iamconn := meta.(*AWSClient).iamconn 199 200 if d.HasChange("assume_role_policy") { 201 assumeRolePolicyInput := &iam.UpdateAssumeRolePolicyInput{ 202 RoleName: aws.String(d.Id()), 203 PolicyDocument: aws.String(d.Get("assume_role_policy").(string)), 204 } 205 _, err := iamconn.UpdateAssumeRolePolicy(assumeRolePolicyInput) 206 if err != nil { 207 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 208 d.SetId("") 209 return nil 210 } 211 return fmt.Errorf("Error Updating IAM Role (%s) Assume Role Policy: %s", d.Id(), err) 212 } 213 } 214 215 if d.HasChange("description") { 216 roleDescriptionInput := &iam.UpdateRoleDescriptionInput{ 217 RoleName: aws.String(d.Id()), 218 Description: aws.String(d.Get("description").(string)), 219 } 220 _, err := iamconn.UpdateRoleDescription(roleDescriptionInput) 221 if err != nil { 222 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 223 d.SetId("") 224 return nil 225 } 226 return fmt.Errorf("Error Updating IAM Role (%s) Assume Role Policy: %s", d.Id(), err) 227 } 228 } 229 230 return nil 231 } 232 233 func resourceAwsIamRoleDelete(d *schema.ResourceData, meta interface{}) error { 234 iamconn := meta.(*AWSClient).iamconn 235 236 // Roles cannot be destroyed when attached to an existing Instance Profile 237 resp, err := iamconn.ListInstanceProfilesForRole(&iam.ListInstanceProfilesForRoleInput{ 238 RoleName: aws.String(d.Id()), 239 }) 240 if err != nil { 241 return fmt.Errorf("Error listing Profiles for IAM Role (%s) when trying to delete: %s", d.Id(), err) 242 } 243 244 // Loop and remove this Role from any Profiles 245 if len(resp.InstanceProfiles) > 0 { 246 for _, i := range resp.InstanceProfiles { 247 _, err := iamconn.RemoveRoleFromInstanceProfile(&iam.RemoveRoleFromInstanceProfileInput{ 248 InstanceProfileName: i.InstanceProfileName, 249 RoleName: aws.String(d.Id()), 250 }) 251 if err != nil { 252 return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err) 253 } 254 } 255 } 256 257 request := &iam.DeleteRoleInput{ 258 RoleName: aws.String(d.Id()), 259 } 260 261 if _, err := iamconn.DeleteRole(request); err != nil { 262 return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err) 263 } 264 return nil 265 }