github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 Required: true, 91 Elem: &schema.Schema{Type: schema.TypeString}, 92 Set: schema.HashString, 93 }, 94 }, 95 } 96 } 97 98 func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{}) error { 99 iamconn := meta.(*AWSClient).iamconn 100 101 var name string 102 if v, ok := d.GetOk("name"); ok { 103 name = v.(string) 104 } else if v, ok := d.GetOk("name_prefix"); ok { 105 name = resource.PrefixedUniqueId(v.(string)) 106 } else { 107 name = resource.UniqueId() 108 } 109 110 request := &iam.CreateInstanceProfileInput{ 111 InstanceProfileName: aws.String(name), 112 Path: aws.String(d.Get("path").(string)), 113 } 114 115 var err error 116 response, err := iamconn.CreateInstanceProfile(request) 117 if err == nil { 118 err = instanceProfileReadResult(d, response.InstanceProfile) 119 } 120 if err != nil { 121 return fmt.Errorf("Error creating IAM instance profile %s: %s", name, err) 122 } 123 124 waiterRequest := &iam.GetInstanceProfileInput{ 125 InstanceProfileName: aws.String(name), 126 } 127 // don't return until the IAM service reports that the instance profile is ready. 128 // this ensures that terraform resources which rely on the instance profile will 'see' 129 // that the instance profile exists. 130 err = iamconn.WaitUntilInstanceProfileExists(waiterRequest) 131 if err != nil { 132 return fmt.Errorf("Timed out while waiting for instance profile %s: %s", name, err) 133 } 134 135 return instanceProfileSetRoles(d, iamconn) 136 } 137 138 func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error { 139 request := &iam.AddRoleToInstanceProfileInput{ 140 InstanceProfileName: aws.String(profileName), 141 RoleName: aws.String(roleName), 142 } 143 144 _, err := iamconn.AddRoleToInstanceProfile(request) 145 return err 146 } 147 148 func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error { 149 request := &iam.RemoveRoleFromInstanceProfileInput{ 150 InstanceProfileName: aws.String(profileName), 151 RoleName: aws.String(roleName), 152 } 153 154 _, err := iamconn.RemoveRoleFromInstanceProfile(request) 155 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 156 return nil 157 } 158 return err 159 } 160 161 func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error { 162 oldInterface, newInterface := d.GetChange("roles") 163 oldRoles := oldInterface.(*schema.Set) 164 newRoles := newInterface.(*schema.Set) 165 166 currentRoles := schema.CopySet(oldRoles) 167 168 d.Partial(true) 169 170 for _, role := range oldRoles.Difference(newRoles).List() { 171 err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string)) 172 if err != nil { 173 return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err) 174 } 175 currentRoles.Remove(role) 176 d.Set("roles", currentRoles) 177 d.SetPartial("roles") 178 } 179 180 for _, role := range newRoles.Difference(oldRoles).List() { 181 err := instanceProfileAddRole(iamconn, d.Id(), role.(string)) 182 if err != nil { 183 return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", role, d.Id(), err) 184 } 185 currentRoles.Add(role) 186 d.Set("roles", currentRoles) 187 d.SetPartial("roles") 188 } 189 190 d.Partial(false) 191 192 return nil 193 } 194 195 func instanceProfileRemoveAllRoles(d *schema.ResourceData, iamconn *iam.IAM) error { 196 for _, role := range d.Get("roles").(*schema.Set).List() { 197 err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string)) 198 if err != nil { 199 return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err) 200 } 201 } 202 return nil 203 } 204 205 func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error { 206 iamconn := meta.(*AWSClient).iamconn 207 208 if !d.HasChange("roles") { 209 return nil 210 } 211 212 return instanceProfileSetRoles(d, iamconn) 213 } 214 215 func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error { 216 iamconn := meta.(*AWSClient).iamconn 217 218 request := &iam.GetInstanceProfileInput{ 219 InstanceProfileName: aws.String(d.Id()), 220 } 221 222 result, err := iamconn.GetInstanceProfile(request) 223 if err != nil { 224 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 225 d.SetId("") 226 return nil 227 } 228 return fmt.Errorf("Error reading IAM instance profile %s: %s", d.Id(), err) 229 } 230 231 return instanceProfileReadResult(d, result.InstanceProfile) 232 } 233 234 func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error { 235 iamconn := meta.(*AWSClient).iamconn 236 237 if err := instanceProfileRemoveAllRoles(d, iamconn); err != nil { 238 return err 239 } 240 241 request := &iam.DeleteInstanceProfileInput{ 242 InstanceProfileName: aws.String(d.Id()), 243 } 244 _, err := iamconn.DeleteInstanceProfile(request) 245 if err != nil { 246 return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err) 247 } 248 d.SetId("") 249 return nil 250 } 251 252 func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfile) error { 253 d.SetId(*result.InstanceProfileName) 254 if err := d.Set("name", result.InstanceProfileName); err != nil { 255 return err 256 } 257 if err := d.Set("arn", result.Arn); err != nil { 258 return err 259 } 260 if err := d.Set("path", result.Path); err != nil { 261 return err 262 } 263 d.Set("unique_id", result.InstanceProfileId) 264 265 roles := &schema.Set{F: schema.HashString} 266 for _, role := range result.Roles { 267 roles.Add(*role.RoleName) 268 } 269 if err := d.Set("roles", roles); err != nil { 270 return err 271 } 272 273 return nil 274 }