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  }