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