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 }