github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_iam_user.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 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/schema" 13 ) 14 15 func resourceAwsIamUser() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsIamUserCreate, 18 Read: resourceAwsIamUserRead, 19 Update: resourceAwsIamUserUpdate, 20 Delete: resourceAwsIamUserDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "arn": &schema.Schema{ 27 Type: schema.TypeString, 28 Computed: true, 29 }, 30 /* 31 The UniqueID could be used as the Id(), but none of the API 32 calls allow specifying a user by the UniqueID: they require the 33 name. The only way to locate a user by UniqueID is to list them 34 all and that would make this provider unnecessarily complex 35 and inefficient. Still, there are other reasons one might want 36 the UniqueID, so we can make it available. 37 */ 38 "unique_id": &schema.Schema{ 39 Type: schema.TypeString, 40 Computed: true, 41 }, 42 "name": &schema.Schema{ 43 Type: schema.TypeString, 44 Required: true, 45 ValidateFunc: validateAwsIamUserName, 46 }, 47 "path": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 Default: "/", 51 ForceNew: true, 52 }, 53 "force_destroy": &schema.Schema{ 54 Type: schema.TypeBool, 55 Optional: true, 56 Default: false, 57 Description: "Delete user even if it has non-Terraform-managed IAM access keys, login profile or MFA devices", 58 }, 59 }, 60 } 61 } 62 63 func resourceAwsIamUserCreate(d *schema.ResourceData, meta interface{}) error { 64 iamconn := meta.(*AWSClient).iamconn 65 name := d.Get("name").(string) 66 path := d.Get("path").(string) 67 68 request := &iam.CreateUserInput{ 69 Path: aws.String(path), 70 UserName: aws.String(name), 71 } 72 73 log.Println("[DEBUG] Create IAM User request:", request) 74 createResp, err := iamconn.CreateUser(request) 75 if err != nil { 76 return fmt.Errorf("Error creating IAM User %s: %s", name, err) 77 } 78 d.SetId(*createResp.User.UserName) 79 return resourceAwsIamUserReadResult(d, createResp.User) 80 } 81 82 func resourceAwsIamUserRead(d *schema.ResourceData, meta interface{}) error { 83 iamconn := meta.(*AWSClient).iamconn 84 85 request := &iam.GetUserInput{ 86 UserName: aws.String(d.Id()), 87 } 88 89 getResp, err := iamconn.GetUser(request) 90 if err != nil { 91 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { // XXX test me 92 log.Printf("[WARN] No IAM user by name (%s) found", d.Id()) 93 d.SetId("") 94 return nil 95 } 96 return fmt.Errorf("Error reading IAM User %s: %s", d.Id(), err) 97 } 98 return resourceAwsIamUserReadResult(d, getResp.User) 99 } 100 101 func resourceAwsIamUserReadResult(d *schema.ResourceData, user *iam.User) error { 102 if err := d.Set("name", user.UserName); err != nil { 103 return err 104 } 105 if err := d.Set("arn", user.Arn); err != nil { 106 return err 107 } 108 if err := d.Set("path", user.Path); err != nil { 109 return err 110 } 111 if err := d.Set("unique_id", user.UserId); err != nil { 112 return err 113 } 114 return nil 115 } 116 117 func resourceAwsIamUserUpdate(d *schema.ResourceData, meta interface{}) error { 118 if d.HasChange("name") || d.HasChange("path") { 119 iamconn := meta.(*AWSClient).iamconn 120 on, nn := d.GetChange("name") 121 _, np := d.GetChange("path") 122 123 request := &iam.UpdateUserInput{ 124 UserName: aws.String(on.(string)), 125 NewUserName: aws.String(nn.(string)), 126 NewPath: aws.String(np.(string)), 127 } 128 129 log.Println("[DEBUG] Update IAM User request:", request) 130 _, err := iamconn.UpdateUser(request) 131 if err != nil { 132 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 133 log.Printf("[WARN] No IAM user by name (%s) found", d.Id()) 134 d.SetId("") 135 return nil 136 } 137 return fmt.Errorf("Error updating IAM User %s: %s", d.Id(), err) 138 } 139 return resourceAwsIamUserRead(d, meta) 140 } 141 return nil 142 } 143 144 func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error { 145 iamconn := meta.(*AWSClient).iamconn 146 147 // IAM Users must be removed from all groups before they can be deleted 148 var groups []string 149 listGroups := &iam.ListGroupsForUserInput{ 150 UserName: aws.String(d.Id()), 151 } 152 pageOfGroups := func(page *iam.ListGroupsForUserOutput, lastPage bool) (shouldContinue bool) { 153 for _, g := range page.Groups { 154 groups = append(groups, *g.GroupName) 155 } 156 return !lastPage 157 } 158 err := iamconn.ListGroupsForUserPages(listGroups, pageOfGroups) 159 if err != nil { 160 return fmt.Errorf("Error removing user %q from all groups: %s", d.Id(), err) 161 } 162 for _, g := range groups { 163 // use iam group membership func to remove user from all groups 164 log.Printf("[DEBUG] Removing IAM User %s from IAM Group %s", d.Id(), g) 165 if err := removeUsersFromGroup(iamconn, []*string{aws.String(d.Id())}, g); err != nil { 166 return err 167 } 168 } 169 170 // All access keys, MFA devices and login profile for the user must be removed 171 if d.Get("force_destroy").(bool) { 172 var accessKeys []string 173 listAccessKeys := &iam.ListAccessKeysInput{ 174 UserName: aws.String(d.Id()), 175 } 176 pageOfAccessKeys := func(page *iam.ListAccessKeysOutput, lastPage bool) (shouldContinue bool) { 177 for _, k := range page.AccessKeyMetadata { 178 accessKeys = append(accessKeys, *k.AccessKeyId) 179 } 180 return !lastPage 181 } 182 err = iamconn.ListAccessKeysPages(listAccessKeys, pageOfAccessKeys) 183 if err != nil { 184 return fmt.Errorf("Error removing access keys of user %s: %s", d.Id(), err) 185 } 186 for _, k := range accessKeys { 187 _, err := iamconn.DeleteAccessKey(&iam.DeleteAccessKeyInput{ 188 UserName: aws.String(d.Id()), 189 AccessKeyId: aws.String(k), 190 }) 191 if err != nil { 192 return fmt.Errorf("Error deleting access key %s: %s", k, err) 193 } 194 } 195 196 var MFADevices []string 197 listMFADevices := &iam.ListMFADevicesInput{ 198 UserName: aws.String(d.Id()), 199 } 200 pageOfMFADevices := func(page *iam.ListMFADevicesOutput, lastPage bool) (shouldContinue bool) { 201 for _, m := range page.MFADevices { 202 MFADevices = append(MFADevices, *m.SerialNumber) 203 } 204 return !lastPage 205 } 206 err = iamconn.ListMFADevicesPages(listMFADevices, pageOfMFADevices) 207 if err != nil { 208 return fmt.Errorf("Error removing MFA devices of user %s: %s", d.Id(), err) 209 } 210 for _, m := range MFADevices { 211 _, err := iamconn.DeactivateMFADevice(&iam.DeactivateMFADeviceInput{ 212 UserName: aws.String(d.Id()), 213 SerialNumber: aws.String(m), 214 }) 215 if err != nil { 216 return fmt.Errorf("Error deactivating MFA device %s: %s", m, err) 217 } 218 } 219 220 _, err = iamconn.DeleteLoginProfile(&iam.DeleteLoginProfileInput{ 221 UserName: aws.String(d.Id()), 222 }) 223 if err != nil { 224 if iamerr, ok := err.(awserr.Error); !ok || iamerr.Code() != "NoSuchEntity" { 225 return fmt.Errorf("Error deleting Account Login Profile: %s", err) 226 } 227 } 228 } 229 230 request := &iam.DeleteUserInput{ 231 UserName: aws.String(d.Id()), 232 } 233 234 log.Println("[DEBUG] Delete IAM User request:", request) 235 if _, err := iamconn.DeleteUser(request); err != nil { 236 return fmt.Errorf("Error deleting IAM User %s: %s", d.Id(), err) 237 } 238 return nil 239 } 240 241 func validateAwsIamUserName(v interface{}, k string) (ws []string, errors []error) { 242 value := v.(string) 243 if !regexp.MustCompile(`^[0-9A-Za-z=,.@\-_+]+$`).MatchString(value) { 244 errors = append(errors, fmt.Errorf( 245 "only alphanumeric characters, hyphens, underscores, commas, periods, @ symbols, plus and equals signs allowed in %q: %q", 246 k, value)) 247 } 248 return 249 }