github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_iam_instance_profile.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/aws/aws-sdk-go/aws" 8 "github.com/aws/aws-sdk-go/aws/awserr" 9 "github.com/aws/aws-sdk-go/service/iam" 10 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsIamInstanceProfile() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsIamInstanceProfileCreate, 18 Read: resourceAwsIamInstanceProfileRead, 19 Update: resourceAwsIamInstanceProfileUpdate, 20 Delete: resourceAwsIamInstanceProfileDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "arn": { 27 Type: schema.TypeString, 28 Computed: true, 29 }, 30 31 "create_date": { 32 Type: schema.TypeString, 33 Computed: true, 34 }, 35 36 "unique_id": { 37 Type: schema.TypeString, 38 Computed: true, 39 }, 40 41 "name": { 42 Type: schema.TypeString, 43 Optional: true, 44 Computed: true, 45 ForceNew: true, 46 ConflictsWith: []string{"name_prefix"}, 47 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 48 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8196-L8201 49 value := v.(string) 50 if len(value) > 128 { 51 errors = append(errors, fmt.Errorf( 52 "%q cannot be longer than 128 characters", k)) 53 } 54 if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) { 55 errors = append(errors, fmt.Errorf( 56 "%q must match [\\w+=,.@-]", k)) 57 } 58 return 59 }, 60 }, 61 62 "name_prefix": { 63 Type: schema.TypeString, 64 Optional: true, 65 ForceNew: true, 66 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 67 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8196-L8201 68 value := v.(string) 69 if len(value) > 64 { 70 errors = append(errors, fmt.Errorf( 71 "%q cannot be longer than 64 characters, name is limited to 128", k)) 72 } 73 if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) { 74 errors = append(errors, fmt.Errorf( 75 "%q must match [\\w+=,.@-]", k)) 76 } 77 return 78 }, 79 }, 80 81 "path": { 82 Type: schema.TypeString, 83 Optional: true, 84 Default: "/", 85 ForceNew: true, 86 }, 87 88 "roles": { 89 Type: schema.TypeSet, 90 Optional: true, 91 Computed: true, 92 ConflictsWith: []string{"role"}, 93 Elem: &schema.Schema{Type: schema.TypeString}, 94 Set: schema.HashString, 95 Deprecated: "Use `role` instead. Only a single role can be passed to an IAM Instance Profile", 96 }, 97 98 "role": { 99 Type: schema.TypeString, 100 Optional: true, 101 Computed: true, 102 ConflictsWith: []string{"roles"}, 103 }, 104 }, 105 } 106 } 107 108 func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{}) error { 109 iamconn := meta.(*AWSClient).iamconn 110 111 var name string 112 if v, ok := d.GetOk("name"); ok { 113 name = v.(string) 114 } else if v, ok := d.GetOk("name_prefix"); ok { 115 name = resource.PrefixedUniqueId(v.(string)) 116 } else { 117 name = resource.UniqueId() 118 } 119 120 _, hasRoles := d.GetOk("roles") 121 _, hasRole := d.GetOk("role") 122 123 if hasRole == false && hasRoles == false { 124 return fmt.Errorf("Either `role` or `roles` (deprecated) must be specified when creating an IAM Instance Profile") 125 } 126 127 request := &iam.CreateInstanceProfileInput{ 128 InstanceProfileName: aws.String(name), 129 Path: aws.String(d.Get("path").(string)), 130 } 131 132 var err error 133 response, err := iamconn.CreateInstanceProfile(request) 134 if err == nil { 135 err = instanceProfileReadResult(d, response.InstanceProfile) 136 } 137 if err != nil { 138 return fmt.Errorf("Error creating IAM instance profile %s: %s", name, err) 139 } 140 141 waiterRequest := &iam.GetInstanceProfileInput{ 142 InstanceProfileName: aws.String(name), 143 } 144 // don't return until the IAM service reports that the instance profile is ready. 145 // this ensures that terraform resources which rely on the instance profile will 'see' 146 // that the instance profile exists. 147 err = iamconn.WaitUntilInstanceProfileExists(waiterRequest) 148 if err != nil { 149 return fmt.Errorf("Timed out while waiting for instance profile %s: %s", name, err) 150 } 151 152 return resourceAwsIamInstanceProfileUpdate(d, meta) 153 } 154 155 func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error { 156 request := &iam.AddRoleToInstanceProfileInput{ 157 InstanceProfileName: aws.String(profileName), 158 RoleName: aws.String(roleName), 159 } 160 161 _, err := iamconn.AddRoleToInstanceProfile(request) 162 return err 163 } 164 165 func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error { 166 request := &iam.RemoveRoleFromInstanceProfileInput{ 167 InstanceProfileName: aws.String(profileName), 168 RoleName: aws.String(roleName), 169 } 170 171 _, err := iamconn.RemoveRoleFromInstanceProfile(request) 172 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 173 return nil 174 } 175 return err 176 } 177 178 func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error { 179 oldInterface, newInterface := d.GetChange("roles") 180 oldRoles := oldInterface.(*schema.Set) 181 newRoles := newInterface.(*schema.Set) 182 183 currentRoles := schema.CopySet(oldRoles) 184 185 d.Partial(true) 186 187 for _, role := range oldRoles.Difference(newRoles).List() { 188 err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string)) 189 if err != nil { 190 return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err) 191 } 192 currentRoles.Remove(role) 193 d.Set("roles", currentRoles) 194 d.SetPartial("roles") 195 } 196 197 for _, role := range newRoles.Difference(oldRoles).List() { 198 err := instanceProfileAddRole(iamconn, d.Id(), role.(string)) 199 if err != nil { 200 return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", role, d.Id(), err) 201 } 202 currentRoles.Add(role) 203 d.Set("roles", currentRoles) 204 d.SetPartial("roles") 205 } 206 207 d.Partial(false) 208 209 return nil 210 } 211 212 func instanceProfileRemoveAllRoles(d *schema.ResourceData, iamconn *iam.IAM) error { 213 for _, role := range d.Get("roles").(*schema.Set).List() { 214 err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string)) 215 if err != nil { 216 return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err) 217 } 218 } 219 return nil 220 } 221 222 func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error { 223 iamconn := meta.(*AWSClient).iamconn 224 225 d.Partial(true) 226 227 if d.HasChange("role") { 228 oldRole, newRole := d.GetChange("role") 229 230 if oldRole.(string) != "" { 231 err := instanceProfileRemoveRole(iamconn, d.Id(), oldRole.(string)) 232 if err != nil { 233 return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", oldRole.(string), d.Id(), err) 234 } 235 } 236 237 if newRole.(string) != "" { 238 err := instanceProfileAddRole(iamconn, d.Id(), newRole.(string)) 239 if err != nil { 240 return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", newRole.(string), d.Id(), err) 241 } 242 } 243 244 d.SetPartial("role") 245 } 246 247 if d.HasChange("roles") { 248 return instanceProfileSetRoles(d, iamconn) 249 } 250 251 d.Partial(false) 252 253 return nil 254 } 255 256 func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error { 257 iamconn := meta.(*AWSClient).iamconn 258 259 request := &iam.GetInstanceProfileInput{ 260 InstanceProfileName: aws.String(d.Id()), 261 } 262 263 result, err := iamconn.GetInstanceProfile(request) 264 if err != nil { 265 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 266 d.SetId("") 267 return nil 268 } 269 return fmt.Errorf("Error reading IAM instance profile %s: %s", d.Id(), err) 270 } 271 272 return instanceProfileReadResult(d, result.InstanceProfile) 273 } 274 275 func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error { 276 iamconn := meta.(*AWSClient).iamconn 277 278 if err := instanceProfileRemoveAllRoles(d, iamconn); err != nil { 279 return err 280 } 281 282 request := &iam.DeleteInstanceProfileInput{ 283 InstanceProfileName: aws.String(d.Id()), 284 } 285 _, err := iamconn.DeleteInstanceProfile(request) 286 if err != nil { 287 return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err) 288 } 289 d.SetId("") 290 return nil 291 } 292 293 func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfile) error { 294 d.SetId(*result.InstanceProfileName) 295 if err := d.Set("name", result.InstanceProfileName); err != nil { 296 return err 297 } 298 if err := d.Set("arn", result.Arn); err != nil { 299 return err 300 } 301 if err := d.Set("path", result.Path); err != nil { 302 return err 303 } 304 d.Set("unique_id", result.InstanceProfileId) 305 306 if result.Roles != nil && len(result.Roles) > 0 { 307 d.Set("role", result.Roles[0].RoleName) //there will only be 1 role returned 308 } 309 310 roles := &schema.Set{F: schema.HashString} 311 for _, role := range result.Roles { 312 roles.Add(*role.RoleName) 313 } 314 if err := d.Set("roles", roles); err != nil { 315 return err 316 } 317 318 return nil 319 }