package aws import ( "errors" "fmt" "testing" "time" "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/vault/helper/pgpkeys" ) func TestAccAWSUserLoginProfile_basic(t *testing.T) { var conf iam.GetLoginProfileOutput username := fmt.Sprintf("test-user-%d", acctest.RandInt()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSUserLoginProfileDestroy, Steps: []resource.TestStep{ { Config: testAccAWSUserLoginProfileConfig(username, "/", testPubKey1), Check: resource.ComposeTestCheckFunc( testAccCheckAWSUserLoginProfileExists("aws_iam_user_login_profile.user", &conf), testDecryptPasswordAndTest("aws_iam_user_login_profile.user", "aws_iam_access_key.user", testPrivKey1), ), }, }, }) } func TestAccAWSUserLoginProfile_keybase(t *testing.T) { var conf iam.GetLoginProfileOutput username := fmt.Sprintf("test-user-%d", acctest.RandInt()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSUserLoginProfileDestroy, Steps: []resource.TestStep{ { Config: testAccAWSUserLoginProfileConfig(username, "/", "keybase:terraformacctest"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSUserLoginProfileExists("aws_iam_user_login_profile.user", &conf), resource.TestCheckResourceAttrSet("aws_iam_user_login_profile.user", "encrypted_password"), resource.TestCheckResourceAttrSet("aws_iam_user_login_profile.user", "key_fingerprint"), ), }, }, }) } func TestAccAWSUserLoginProfile_keybaseDoesntExist(t *testing.T) { username := fmt.Sprintf("test-user-%d", acctest.RandInt()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSUserLoginProfileDestroy, Steps: []resource.TestStep{ { // We own this account but it doesn't have any key associated with it Config: testAccAWSUserLoginProfileConfig(username, "/", "keybase:terraform_nope"), ExpectError: regexp.MustCompile(`Error retrieving Public Key`), }, }, }) } func TestAccAWSUserLoginProfile_notAKey(t *testing.T) { username := fmt.Sprintf("test-user-%d", acctest.RandInt()) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSUserLoginProfileDestroy, Steps: []resource.TestStep{ { // We own this account but it doesn't have any key associated with it Config: testAccAWSUserLoginProfileConfig(username, "/", "lolimnotakey"), ExpectError: regexp.MustCompile(`Error encrypting Password`), }, }, }) } func testAccCheckAWSUserLoginProfileDestroy(s *terraform.State) error { iamconn := testAccProvider.Meta().(*AWSClient).iamconn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_iam_user_login_profile" { continue } // Try to get user _, err := iamconn.GetLoginProfile(&iam.GetLoginProfileInput{ UserName: aws.String(rs.Primary.ID), }) if err == nil { return fmt.Errorf("still exists.") } // Verify the error is what we want ec2err, ok := err.(awserr.Error) if !ok { return err } if ec2err.Code() != "NoSuchEntity" { return err } } return nil } func testDecryptPasswordAndTest(nProfile, nAccessKey, key string) resource.TestCheckFunc { return func(s *terraform.State) error { profileResource, ok := s.RootModule().Resources[nProfile] if !ok { return fmt.Errorf("Not found: %s", nProfile) } password, ok := profileResource.Primary.Attributes["encrypted_password"] if !ok { return errors.New("No password in state") } accessKeyResource, ok := s.RootModule().Resources[nAccessKey] if !ok { return fmt.Errorf("Not found: %s", nAccessKey) } accessKeyId := accessKeyResource.Primary.ID secretAccessKey, ok := accessKeyResource.Primary.Attributes["secret"] if !ok { return errors.New("No secret access key in state") } decryptedPassword, err := pgpkeys.DecryptBytes(password, key) if err != nil { return fmt.Errorf("Error decrypting password: %s", err) } iamAsCreatedUserSession := session.New(&aws.Config{ Region: aws.String("us-west-2"), Credentials: credentials.NewStaticCredentials(accessKeyId, secretAccessKey, ""), }) _, err = iamAsCreatedUserSession.Config.Credentials.Get() if err != nil { return fmt.Errorf("Error getting session credentials: %s", err) } return resource.Retry(2*time.Minute, func() *resource.RetryError { iamAsCreatedUser := iam.New(iamAsCreatedUserSession) _, err = iamAsCreatedUser.ChangePassword(&iam.ChangePasswordInput{ OldPassword: aws.String(decryptedPassword.String()), NewPassword: aws.String(generatePassword(20)), }) if err != nil { if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "InvalidClientTokenId" { return resource.RetryableError(err) } return resource.NonRetryableError(fmt.Errorf("Error changing decrypted password: %s", err)) } return nil }) } } func testAccCheckAWSUserLoginProfileExists(n string, res *iam.GetLoginProfileOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { return errors.New("No UserName is set") } iamconn := testAccProvider.Meta().(*AWSClient).iamconn resp, err := iamconn.GetLoginProfile(&iam.GetLoginProfileInput{ UserName: aws.String(rs.Primary.ID), }) if err != nil { return err } *res = *resp return nil } } func testAccAWSUserLoginProfileConfig(r, p, key string) string { return fmt.Sprintf(` resource "aws_iam_user" "user" { name = "%s" path = "%s" force_destroy = true } data "aws_caller_identity" "current" {} data "aws_iam_policy_document" "user" { statement { effect = "Allow" actions = ["iam:GetAccountPasswordPolicy"] resources = ["*"] } statement { effect = "Allow" actions = ["iam:ChangePassword"] resources = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/&{aws:username}"] } } resource "aws_iam_user_policy" "user" { name = "AllowChangeOwnPassword" user = "${aws_iam_user.user.name}" policy = "${data.aws_iam_policy_document.user.json}" } resource "aws_iam_access_key" "user" { user = "${aws_iam_user.user.name}" } resource "aws_iam_user_login_profile" "user" { user = "${aws_iam_user.user.name}" pgp_key = <