From 0e99fac806a6cf61113a43956116d2b4838ef4e8 Mon Sep 17 00:00:00 2001 From: Erran Carey Date: Wed, 10 Jan 2018 17:37:15 +0000 Subject: [PATCH 1/4] test/aws_iam_user: Create failing test for iam user import with policy --- aws/import_aws_iam_user_test.go | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/aws/import_aws_iam_user_test.go b/aws/import_aws_iam_user_test.go index abfd1f89e99..d1b6aff623e 100644 --- a/aws/import_aws_iam_user_test.go +++ b/aws/import_aws_iam_user_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" ) func TestAccAWSUser_importBasic(t *testing.T) { @@ -32,3 +33,54 @@ func TestAccAWSUser_importBasic(t *testing.T) { }, }) } + +func TestAccAWSUser_importWithPolicy(t *testing.T) { + resourceName := "aws_iam_user.user" + + rInt := acctest.RandInt() + + checkFn := func(s []*terraform.InstanceState) error { + // Expect 2: user + policy + if len(s) != 2 { + return fmt.Errorf("expected 2 states: %#v", s) + } + + // TODO: Is this order guaranteed? + policyState, userState := s[0], s[1] + + expectedUserId := fmt.Sprintf("test_user_%d", rInt) + expectedPolicyId := fmt.Sprintf("test_user_%d:foo_policy_%d", rInt, rInt) + + if userState.ID != expectedUserId { + return fmt.Errorf("expected user of ID %s, %s received", + expectedUserId, userState.ID) + } + + if policyState.ID != expectedPolicyId { + return fmt.Errorf("expected policy of ID %s, %s received", + expectedPolicyId, policyState.ID) + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSUserDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccIAMUserPolicyConfig(rInt), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateCheck: checkFn, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "force_destroy"}, + }, + }, + }) +} From 3ee9a15b7217f80e71726a0d1155493068b889ef Mon Sep 17 00:00:00 2001 From: Erran Carey Date: Wed, 10 Jan 2018 19:33:08 +0000 Subject: [PATCH 2/4] r/aws_iam_user: Import inline policies with iam user When performing a `terraform import` of a `aws_iam_user` resource now we import state for `aws_iam_user_policy` resources based on the inline policies. This format can be used to import additional aws_iam_user related resources such as managed policy attachments, login profiles, and group associations. --- aws/import_aws_iam_user.go | 65 ++++++++++++++++++++++++++++++++++++ aws/resource_aws_iam_user.go | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 aws/import_aws_iam_user.go diff --git a/aws/import_aws_iam_user.go b/aws/import_aws_iam_user.go new file mode 100644 index 00000000000..304ff457710 --- /dev/null +++ b/aws/import_aws_iam_user.go @@ -0,0 +1,65 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +func awsIamUserInlinePolicies(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + results := make([]*schema.ResourceData, 0) + conn := meta.(*AWSClient).iamconn + policyNames := make([]*string, 0) + err := conn.ListUserPoliciesPages(&iam.ListUserPoliciesInput{ + UserName: aws.String(d.Id()), + }, func(page *iam.ListUserPoliciesOutput, lastPage bool) bool { + for _, policyName := range page.PolicyNames { + policyNames = append(policyNames, policyName) + } + + return lastPage + }) + if err != nil { + return nil, err + } + + for _, policyName := range policyNames { + policy, err := conn.GetUserPolicy(&iam.GetUserPolicyInput{ + PolicyName: policyName, + UserName: aws.String(d.Id()), + }) + if err != nil { + return nil, errwrap.Wrapf("Error importing AWS IAM User Policy: {{err}}", err) + } + + policyResource := resourceAwsIamUserPolicy() + pData := policyResource.Data(nil) + pData.SetId(fmt.Sprintf("%s:%s", *policy.UserName, *policy.PolicyName)) + pData.SetType("aws_iam_user_policy") + pData.Set("name", policy.PolicyName) + pData.Set("policy", policy.PolicyDocument) + pData.Set("user", policy.UserName) + results = append(results, pData) + } + + return results, nil +} + +func resourceAwsIamUserImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + results := make([]*schema.ResourceData, 1) + results[0] = d + + policyData, err := awsIamUserInlinePolicies(d, meta) + if err != nil { + return nil, err + } + + for _, data := range policyData { + results = append(results, data) + } + + return results, nil +} diff --git a/aws/resource_aws_iam_user.go b/aws/resource_aws_iam_user.go index 082e9d4dd9d..5f1a8452cc8 100644 --- a/aws/resource_aws_iam_user.go +++ b/aws/resource_aws_iam_user.go @@ -19,7 +19,7 @@ func resourceAwsIamUser() *schema.Resource { Update: resourceAwsIamUserUpdate, Delete: resourceAwsIamUserDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceAwsIamUserImportState, }, Schema: map[string]*schema.Schema{ From 0e311a5aa296b1e4f6c0a8e11a3cb19554455187 Mon Sep 17 00:00:00 2001 From: Erran Carey Date: Tue, 16 Jan 2018 10:44:27 +0000 Subject: [PATCH 3/4] r/aws_iam_user_policy: Add support to import state NOTE: I also fixed the broken testAccCheckIAMUserPolicyDestroy function which was previously checking for a role policy instead of a user policy (which as far as I know would always come back as non-existent). --- aws/import_aws_iam_user_policy_test.go | 54 ++++++++++++++++++++++++ aws/resource_aws_iam_user_policy.go | 30 ++++++++++--- aws/resource_aws_iam_user_policy_test.go | 24 +++++++---- 3 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 aws/import_aws_iam_user_policy_test.go diff --git a/aws/import_aws_iam_user_policy_test.go b/aws/import_aws_iam_user_policy_test.go new file mode 100644 index 00000000000..64d325ef870 --- /dev/null +++ b/aws/import_aws_iam_user_policy_test.go @@ -0,0 +1,54 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func testAccAwsIamUserPolicyConfig(suffix string) string { + return fmt.Sprintf(` +resource "aws_iam_user" "user_%[1]s" { + name = "tf_test_user_test_%[1]s" + path = "/" +} + +resource "aws_iam_user_policy" "foo_%[1]s" { + name = "tf_test_policy_test_%[1]s" + user = "${aws_iam_user.user_%[1]s.name}" + policy = <:") + return + } + userName = parts[0] policyName = parts[1] return diff --git a/aws/resource_aws_iam_user_policy_test.go b/aws/resource_aws_iam_user_policy_test.go index 99836b230f6..622969279a7 100644 --- a/aws/resource_aws_iam_user_policy_test.go +++ b/aws/resource_aws_iam_user_policy_test.go @@ -94,25 +94,27 @@ func testAccCheckIAMUserPolicyDestroy(s *terraform.State) error { continue } - role, name := resourceAwsIamUserPolicyParseId(rs.Primary.ID) + user, name, err := resourceAwsIamUserPolicyParseId(rs.Primary.ID) + if err != nil { + return err + } - request := &iam.GetRolePolicyInput{ + request := &iam.GetUserPolicyInput{ PolicyName: aws.String(name), - RoleName: aws.String(role), + UserName: aws.String(user), } - var err error - getResp, err := iamconn.GetRolePolicy(request) + getResp, err := iamconn.GetUserPolicy(request) if err != nil { if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { // none found, that's good return nil } - return fmt.Errorf("Error reading IAM policy %s from role %s: %s", name, role, err) + return fmt.Errorf("Error reading IAM policy %s from user %s: %s", name, user, err) } if getResp != nil { - return fmt.Errorf("Found IAM Role, expected none: %s", getResp) + return fmt.Errorf("Found IAM User, expected none: %s", getResp) } } @@ -138,8 +140,12 @@ func testAccCheckIAMUserPolicy( } iamconn := testAccProvider.Meta().(*AWSClient).iamconn - username, name := resourceAwsIamUserPolicyParseId(policy.Primary.ID) - _, err := iamconn.GetUserPolicy(&iam.GetUserPolicyInput{ + username, name, err := resourceAwsIamUserPolicyParseId(policy.Primary.ID) + if err != nil { + return err + } + + _, err = iamconn.GetUserPolicy(&iam.GetUserPolicyInput{ UserName: aws.String(username), PolicyName: aws.String(name), }) From a954d9733ec2d0da8429c8299b23796714db24f7 Mon Sep 17 00:00:00 2001 From: Erran Carey Date: Tue, 30 Jan 2018 03:19:59 +0000 Subject: [PATCH 4/4] test/aws_iam_user_policy: Validate ParseId put policy --- aws/resource_aws_iam_user_policy.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_iam_user_policy.go b/aws/resource_aws_iam_user_policy.go index b1579b8764b..e73f07f0953 100644 --- a/aws/resource_aws_iam_user_policy.go +++ b/aws/resource_aws_iam_user_policy.go @@ -64,7 +64,10 @@ func resourceAwsIamUserPolicyPut(d *schema.ResourceData, meta interface{}) error var policyName string if !d.IsNewResource() { - _, policyName = resourceAwsIamUserPolicyParseId(d.Id()) + _, policyName, err := resourceAwsIamUserPolicyParseId(d.Id()) + if err != nil { + return err + } } else if v, ok := d.GetOk("name"); ok { policyName = v.(string) } else if v, ok := d.GetOk("name_prefix"); ok {