github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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",
    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 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  		_, err = iamconn.DeleteLoginProfile(&iam.DeleteLoginProfileInput{
   197  			UserName: aws.String(d.Id()),
   198  		})
   199  		if err != nil {
   200  			if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
   201  				return nil
   202  			}
   203  			return fmt.Errorf("Error deleting Account Login Profile: %s", err)
   204  		}
   205  	}
   206  
   207  	request := &iam.DeleteUserInput{
   208  		UserName: aws.String(d.Id()),
   209  	}
   210  
   211  	log.Println("[DEBUG] Delete IAM User request:", request)
   212  	if _, err := iamconn.DeleteUser(request); err != nil {
   213  		return fmt.Errorf("Error deleting IAM User %s: %s", d.Id(), err)
   214  	}
   215  	return nil
   216  }
   217  
   218  func validateAwsIamUserName(v interface{}, k string) (ws []string, errors []error) {
   219  	value := v.(string)
   220  	if !regexp.MustCompile(`^[0-9A-Za-z=,.@\-_]+$`).MatchString(value) {
   221  		errors = append(errors, fmt.Errorf(
   222  			"only alphanumeric characters, hyphens, underscores, commas, periods, @ symbols and equals signs allowed in %q: %q",
   223  			k, value))
   224  	}
   225  	return
   226  }