github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_iam_user_login_profile.go (about)

     1  package aws
     2  
     3  import (
     4  	"encoding/base64"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"math/rand"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/aws/aws-sdk-go/aws"
    13  	"github.com/aws/aws-sdk-go/aws/awserr"
    14  	"github.com/aws/aws-sdk-go/service/iam"
    15  	"github.com/hashicorp/errwrap"
    16  	"github.com/hashicorp/terraform/helper/schema"
    17  	"github.com/hashicorp/vault/helper/pgpkeys"
    18  )
    19  
    20  func resourceAwsIamUserLoginProfile() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsIamUserLoginProfileCreate,
    23  		Read:   schema.Noop,
    24  		Update: schema.Noop,
    25  		Delete: schema.RemoveFromState,
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"user": {
    29  				Type:     schema.TypeString,
    30  				Required: true,
    31  			},
    32  			"pgp_key": {
    33  				Type:     schema.TypeString,
    34  				Required: true,
    35  			},
    36  			"password_reset_required": {
    37  				Type:     schema.TypeBool,
    38  				Optional: true,
    39  				Default:  true,
    40  			},
    41  			"password_length": {
    42  				Type:         schema.TypeInt,
    43  				Optional:     true,
    44  				Default:      20,
    45  				ValidateFunc: validateAwsIamLoginProfilePasswordLength,
    46  			},
    47  
    48  			"key_fingerprint": {
    49  				Type:     schema.TypeString,
    50  				Computed: true,
    51  			},
    52  			"encrypted_password": {
    53  				Type:     schema.TypeString,
    54  				Computed: true,
    55  			},
    56  		},
    57  	}
    58  }
    59  
    60  func validateAwsIamLoginProfilePasswordLength(v interface{}, _ string) (_ []string, es []error) {
    61  	length := v.(int)
    62  	if length < 4 {
    63  		es = append(es, errors.New("minimum password_length is 4 characters"))
    64  	}
    65  	if length > 128 {
    66  		es = append(es, errors.New("maximum password_length is 128 characters"))
    67  	}
    68  	return
    69  }
    70  
    71  // generatePassword generates a random password of a given length using
    72  // characters that are likely to satisfy any possible AWS password policy
    73  // (given sufficient length).
    74  func generatePassword(length int) string {
    75  	charsets := []string{
    76  		"abcdefghijklmnopqrstuvwxyz",
    77  		"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    78  		"012346789",
    79  		"!@#$%^&*()_+-=[]{}|'",
    80  	}
    81  
    82  	// Use all character sets
    83  	random := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    84  	components := make(map[int]byte, length)
    85  	for i := 0; i < length; i++ {
    86  		charset := charsets[i%len(charsets)]
    87  		components[i] = charset[random.Intn(len(charset))]
    88  	}
    89  
    90  	// Randomise the ordering so we don't end up with a predictable
    91  	// lower case, upper case, numeric, symbol pattern
    92  	result := make([]byte, length)
    93  	i := 0
    94  	for _, b := range components {
    95  		result[i] = b
    96  		i = i + 1
    97  	}
    98  
    99  	return string(result)
   100  }
   101  
   102  func encryptPassword(password string, pgpKey string) (string, string, error) {
   103  	const keybasePrefix = "keybase:"
   104  
   105  	encryptionKey := pgpKey
   106  	if strings.HasPrefix(pgpKey, keybasePrefix) {
   107  		publicKeys, err := pgpkeys.FetchKeybasePubkeys([]string{pgpKey})
   108  		if err != nil {
   109  			return "", "", errwrap.Wrapf(
   110  				fmt.Sprintf("Error retrieving Public Key for %s: {{err}}", pgpKey), err)
   111  		}
   112  		encryptionKey = publicKeys[pgpKey]
   113  	}
   114  
   115  	fingerprints, encrypted, err := pgpkeys.EncryptShares([][]byte{[]byte(password)}, []string{encryptionKey})
   116  	if err != nil {
   117  		return "", "", errwrap.Wrapf(
   118  			fmt.Sprintf("Error encrypting password for %s: {{err}}", pgpKey), err)
   119  	}
   120  
   121  	return fingerprints[0], base64.StdEncoding.EncodeToString(encrypted[0]), nil
   122  }
   123  
   124  func resourceAwsIamUserLoginProfileCreate(d *schema.ResourceData, meta interface{}) error {
   125  	iamconn := meta.(*AWSClient).iamconn
   126  
   127  	username := d.Get("user").(string)
   128  	passwordResetRequired := d.Get("password_reset_required").(bool)
   129  	passwordLength := d.Get("password_length").(int)
   130  
   131  	_, err := iamconn.GetLoginProfile(&iam.GetLoginProfileInput{
   132  		UserName: aws.String(username),
   133  	})
   134  	if err != nil {
   135  		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() != "NoSuchEntity" {
   136  			// If there is already a login profile, bring it under management (to prevent
   137  			// resource creation diffs) - we will never modify it, but obviously cannot
   138  			// set the password.
   139  			d.SetId(username)
   140  			d.Set("key_fingerprint", "")
   141  			d.Set("encrypted_password", "")
   142  			return nil
   143  		}
   144  	}
   145  
   146  	var pgpKey string
   147  	if pgpKeyInterface, ok := d.GetOk("pgp_key"); ok {
   148  		pgpKey = pgpKeyInterface.(string)
   149  	}
   150  
   151  	initialPassword := generatePassword(passwordLength)
   152  	fingerprint, encrypted, err := encryptPassword(initialPassword, pgpKey)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	request := &iam.CreateLoginProfileInput{
   158  		UserName:              aws.String(username),
   159  		Password:              aws.String(initialPassword),
   160  		PasswordResetRequired: aws.Bool(passwordResetRequired),
   161  	}
   162  
   163  	log.Println("[DEBUG] Create IAM User Login Profile request:", request)
   164  	createResp, err := iamconn.CreateLoginProfile(request)
   165  	if err != nil {
   166  		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "EntityAlreadyExists" {
   167  			// If there is already a login profile, bring it under management (to prevent
   168  			// resource creation diffs) - we will never modify it, but obviously cannot
   169  			// set the password.
   170  			d.SetId(username)
   171  			d.Set("key_fingerprint", "")
   172  			d.Set("encrypted_password", "")
   173  			return nil
   174  		}
   175  		return errwrap.Wrapf(fmt.Sprintf("Error creating IAM User Login Profile for %q: {{err}}", username), err)
   176  	}
   177  
   178  	d.SetId(*createResp.LoginProfile.UserName)
   179  	d.Set("key_fingerprint", fingerprint)
   180  	d.Set("encrypted_password", encrypted)
   181  	return nil
   182  }