From b6871921a1bd67e0ea879ad8c6de25f34ea4888d Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Thu, 9 Apr 2015 09:38:16 -0400 Subject: [PATCH 01/17] Implement a hash function for string sets Sets of strings are pretty common. Let's not duplicate the function necessary to create a set of strings in so many places. --- .../aws/resource_aws_autoscaling_group.go | 17 ++++------------- .../providers/aws/resource_aws_db_instance.go | 17 ++++------------- .../aws/resource_aws_db_subnet_group.go | 5 +---- builtin/providers/aws/resource_aws_elb.go | 16 ++++------------ builtin/providers/aws/resource_aws_instance.go | 4 +--- .../aws/resource_aws_launch_configuration.go | 4 +--- .../aws/resource_aws_network_interface.go | 8 ++------ .../aws/resource_aws_route53_record.go | 5 +---- builtin/providers/aws/structure_test.go | 9 ++------- helper/schema/set.go | 8 ++++++++ 10 files changed, 28 insertions(+), 65 deletions(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index da8548e3fba9..99ddc5c2b74f 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" @@ -82,9 +81,7 @@ func resourceAwsAutoscalingGroup() *schema.Resource { Required: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "load_balancers": &schema.Schema{ @@ -92,9 +89,7 @@ func resourceAwsAutoscalingGroup() *schema.Resource { Optional: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "vpc_zone_identifier": &schema.Schema{ @@ -103,9 +98,7 @@ func resourceAwsAutoscalingGroup() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "termination_policies": &schema.Schema{ @@ -114,9 +107,7 @@ func resourceAwsAutoscalingGroup() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "tag": autoscalingTagsSchema(), diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index ed2da52ac6af..d4f37052baca 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -9,7 +9,6 @@ import ( "github.com/awslabs/aws-sdk-go/service/iam" "github.com/awslabs/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" ) @@ -132,18 +131,14 @@ func resourceAwsDbInstance() *schema.Resource { Optional: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "security_group_names": &schema.Schema{ Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "final_snapshot_identifier": &schema.Schema{ @@ -372,9 +367,7 @@ func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { // Create an empty schema.Set to hold all vpc security group ids ids := &schema.Set{ - F: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + F: schema.HashString, } for _, v := range v.VPCSecurityGroups { ids.Add(*v.VPCSecurityGroupID) @@ -383,9 +376,7 @@ func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { // Create an empty schema.Set to hold all security group names sgn := &schema.Set{ - F: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + F: schema.HashString, } for _, v := range v.DBSecurityGroups { sgn.Add(*v.DBSecurityGroupName) diff --git a/builtin/providers/aws/resource_aws_db_subnet_group.go b/builtin/providers/aws/resource_aws_db_subnet_group.go index e92350208415..0e82667549f5 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group.go @@ -8,7 +8,6 @@ import ( "github.com/awslabs/aws-sdk-go/aws" "github.com/awslabs/aws-sdk-go/service/rds" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" ) @@ -37,9 +36,7 @@ func resourceAwsDbSubnetGroup() *schema.Resource { Required: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, }, } diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index b36e752e9358..7f2d77f825d0 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -43,9 +43,7 @@ func resourceAwsElb() *schema.Resource { Optional: true, ForceNew: true, Computed: true, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "instances": &schema.Schema{ @@ -53,9 +51,7 @@ func resourceAwsElb() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Computed: true, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, // TODO: could be not ForceNew @@ -65,9 +61,7 @@ func resourceAwsElb() *schema.Resource { Optional: true, ForceNew: true, Computed: true, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "subnets": &schema.Schema{ @@ -76,9 +70,7 @@ func resourceAwsElb() *schema.Resource { Optional: true, ForceNew: true, Computed: true, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "listener": &schema.Schema{ diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index 3874af81a2a1..c523e8630d5a 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -100,9 +100,7 @@ func resourceAwsInstance() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "vpc_security_group_ids": &schema.Schema{ diff --git a/builtin/providers/aws/resource_aws_launch_configuration.go b/builtin/providers/aws/resource_aws_launch_configuration.go index 3ed551c86798..1537d1f0d1c2 100644 --- a/builtin/providers/aws/resource_aws_launch_configuration.go +++ b/builtin/providers/aws/resource_aws_launch_configuration.go @@ -76,9 +76,7 @@ func resourceAwsLaunchConfiguration() *schema.Resource { Optional: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "associate_public_ip_address": &schema.Schema{ diff --git a/builtin/providers/aws/resource_aws_network_interface.go b/builtin/providers/aws/resource_aws_network_interface.go index 00c913b119d0..095bfb955b2b 100644 --- a/builtin/providers/aws/resource_aws_network_interface.go +++ b/builtin/providers/aws/resource_aws_network_interface.go @@ -34,9 +34,7 @@ func resourceAwsNetworkInterface() *schema.Resource { Optional: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "security_groups": &schema.Schema{ @@ -44,9 +42,7 @@ func resourceAwsNetworkInterface() *schema.Resource { Optional: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, "attachment": &schema.Schema{ diff --git a/builtin/providers/aws/resource_aws_route53_record.go b/builtin/providers/aws/resource_aws_route53_record.go index a5b47d0b17ca..d3b030ec4b80 100644 --- a/builtin/providers/aws/resource_aws_route53_record.go +++ b/builtin/providers/aws/resource_aws_route53_record.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" @@ -49,9 +48,7 @@ func resourceAwsRoute53Record() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Required: true, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, + Set: schema.HashString, }, }, } diff --git a/builtin/providers/aws/structure_test.go b/builtin/providers/aws/structure_test.go index 9c7eaf88d411..6ec80274cedc 100644 --- a/builtin/providers/aws/structure_test.go +++ b/builtin/providers/aws/structure_test.go @@ -10,7 +10,6 @@ import ( "github.com/awslabs/aws-sdk-go/service/rds" "github.com/awslabs/aws-sdk-go/service/route53" "github.com/hashicorp/terraform/flatmap" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" ) @@ -38,9 +37,7 @@ func testConf() map[string]string { } func TestexpandIPPerms(t *testing.T) { - hash := func(v interface{}) int { - return hashcode.String(v.(string)) - } + hash := schema.HashString expanded := []interface{}{ map[string]interface{}{ @@ -121,9 +118,7 @@ func TestexpandIPPerms(t *testing.T) { } func TestExpandIPPerms_nonVPC(t *testing.T) { - hash := func(v interface{}) int { - return hashcode.String(v.(string)) - } + hash := schema.HashString expanded := []interface{}{ map[string]interface{}{ diff --git a/helper/schema/set.go b/helper/schema/set.go index 965ad6f8b8a5..44ff3837eace 100644 --- a/helper/schema/set.go +++ b/helper/schema/set.go @@ -5,8 +5,16 @@ import ( "reflect" "sort" "sync" + + "github.com/hashicorp/terraform/helper/hashcode" ) +// HashString hashes strings. If you want a Set of strings, this is the +// SchemaSetFunc you want. +func HashString(v interface{}) int { + return hashcode.String(v.(string)) +} + // Set is a set data structure that is returned for elements of type // TypeSet. type Set struct { From cf5939f78006f995ca19c624435e3e9033b714a0 Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Fri, 6 Feb 2015 10:34:24 -0500 Subject: [PATCH 02/17] Implement a subset of IAM resources Implements support for IAM users, user policies, and access keys. This is only a subset of what IAM can do (notably missing: roles and instance profiles and associated policies), but it's a start. Makes a dent in #28. --- builtin/providers/aws/config.go | 1 - builtin/providers/aws/provider.go | 5 +- .../aws/resource_aws_iam_access_key.go | 116 ++++++++++++++++++ .../providers/aws/resource_aws_iam_user.go | 114 +++++++++++++++++ .../aws/resource_aws_iam_user_policy.go | 110 +++++++++++++++++ .../aws/r/iam_access_key.html.markdown | 59 +++++++++ .../providers/aws/r/iam_user.html.markdown | 60 +++++++++ .../aws/r/iam_user_policy.html.markdown | 57 +++++++++ website/source/layouts/aws.erb | 12 ++ 9 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_iam_access_key.go create mode 100644 builtin/providers/aws/resource_aws_iam_user.go create mode 100644 builtin/providers/aws/resource_aws_iam_user_policy.go create mode 100644 website/source/docs/providers/aws/r/iam_access_key.html.markdown create mode 100644 website/source/docs/providers/aws/r/iam_user.html.markdown create mode 100644 website/source/docs/providers/aws/r/iam_user_policy.html.markdown diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index d9e0768411cd..aa4fcf2c17eb 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -85,7 +85,6 @@ func (c *Config) Client() (interface{}, error) { Credentials: creds, Region: "us-east-1", }) - } if len(errs) > 0 { diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 50596512e364..3ce7a1354ddd 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -52,6 +52,9 @@ func Provider() terraform.ResourceProvider { "aws_db_subnet_group": resourceAwsDbSubnetGroup(), "aws_eip": resourceAwsEip(), "aws_elb": resourceAwsElb(), + "aws_iam_access_key": resourceAwsIamAccessKey(), + "aws_iam_user_policy": resourceAwsIamUserPolicy(), + "aws_iam_user": resourceAwsIamUser(), "aws_instance": resourceAwsInstance(), "aws_internet_gateway": resourceAwsInternetGateway(), "aws_key_pair": resourceAwsKeyPair(), @@ -61,8 +64,8 @@ func Provider() terraform.ResourceProvider { "aws_network_interface": resourceAwsNetworkInterface(), "aws_route53_record": resourceAwsRoute53Record(), "aws_route53_zone": resourceAwsRoute53Zone(), - "aws_route_table": resourceAwsRouteTable(), "aws_route_table_association": resourceAwsRouteTableAssociation(), + "aws_route_table": resourceAwsRouteTable(), "aws_s3_bucket": resourceAwsS3Bucket(), "aws_security_group": resourceAwsSecurityGroup(), "aws_subnet": resourceAwsSubnet(), diff --git a/builtin/providers/aws/resource_aws_iam_access_key.go b/builtin/providers/aws/resource_aws_iam_access_key.go new file mode 100644 index 000000000000..4a7bd0d00560 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_access_key.go @@ -0,0 +1,116 @@ +package aws + +import ( + "fmt" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamAccessKey() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsIamAccessKeyCreate, + Read: resourceAwsIamAccessKeyRead, + Delete: resourceAwsIamAccessKeyDelete, + + Schema: map[string]*schema.Schema{ + "user": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "status": &schema.Schema{ + Type: schema.TypeString, + // this could be settable, but goamz does not support the + // UpdateAccessKey API yet. + Computed: true, + }, + "secret": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsIamAccessKeyCreate(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.CreateAccessKeyInput{ + UserName: aws.String(d.Get("user").(string)), + } + + createResp, err := iamconn.CreateAccessKey(request) + if err != nil { + return fmt.Errorf( + "Error creating access key for user %s: %s", + request.UserName, + err, + ) + } + + if err := d.Set("secret", createResp.AccessKey.SecretAccessKey); err != nil { + return err + } + return resourceAwsIamAccessKeyReadResult(d, &iam.AccessKeyMetadata{ + AccessKeyID: createResp.AccessKey.AccessKeyID, + CreateDate: createResp.AccessKey.CreateDate, + Status: createResp.AccessKey.Status, + UserName: createResp.AccessKey.UserName, + }) +} + +func resourceAwsIamAccessKeyRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.ListAccessKeysInput{ + UserName: aws.String(d.Get("user").(string)), + } + + getResp, err := iamconn.ListAccessKeys(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX TEST ME + // the user does not exist, so the key can't exist. + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM acces key: %s", err) + } + + for _, key := range getResp.AccessKeyMetadata { + if key.AccessKeyID != nil && *key.AccessKeyID == d.Id() { + return resourceAwsIamAccessKeyReadResult(d, key) + } + } + + // Guess the key isn't around anymore. + d.SetId("") + return nil +} + +func resourceAwsIamAccessKeyReadResult(d *schema.ResourceData, key *iam.AccessKeyMetadata) error { + d.SetId(*key.AccessKeyID) + if err := d.Set("user", key.UserName); err != nil { + return err + } + if err := d.Set("status", key.Status); err != nil { + return err + } + return nil +} + +func resourceAwsIamAccessKeyDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.DeleteAccessKeyInput{ + AccessKeyID: aws.String(d.Id()), + UserName: aws.String(d.Get("user").(string)), + } + + if _, err := iamconn.DeleteAccessKey(request); err != nil { + return fmt.Errorf("Error deleting access key %s: %s", d.Id(), err) + } + return nil +} diff --git a/builtin/providers/aws/resource_aws_iam_user.go b/builtin/providers/aws/resource_aws_iam_user.go new file mode 100644 index 000000000000..99c3f785e136 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_user.go @@ -0,0 +1,114 @@ +package aws + +import ( + "fmt" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamUser() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsIamUserCreate, + Read: resourceAwsIamUserRead, + // There is an UpdateUser API call, but goamz doesn't support it yet. + // XXX but we aren't using goamz anymore. + //Update: resourceAwsIamUserUpdate, + Delete: resourceAwsIamUserDelete, + + Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + /* + The UniqueID could be used as the Id(), but none of the API + calls allow specifying a user by the UniqueID: they require the + name. The only way to locate a user by UniqueID is to list them + all and that would make this provider unnecessarilly complex + and inefficient. Still, there are other reasons one might want + the UniqueID, so we can make it availible. + */ + "unique_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "/", + ForceNew: true, + }, + }, + } +} + +func resourceAwsIamUserCreate(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.CreateUserInput{ + Path: aws.String(d.Get("path").(string)), + UserName: aws.String(d.Get("name").(string)), + } + + createResp, err := iamconn.CreateUser(request) + if err != nil { + return fmt.Errorf("Error creating IAM User %s: %s", request.UserName, err) + } + return resourceAwsIamUserReadResult(d, createResp.User) +} + +func resourceAwsIamUserRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.GetUserInput{ + UserName: aws.String(d.Id()), + } + + getResp, err := iamconn.GetUser(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM User %s: %s", d.Id(), err) + } + return resourceAwsIamUserReadResult(d, getResp.User) +} + +func resourceAwsIamUserReadResult(d *schema.ResourceData, user *iam.User) error { + d.SetId(*user.UserName) + if err := d.Set("name", user.UserName); err != nil { + return err + } + if err := d.Set("arn", user.ARN); err != nil { + return err + } + if err := d.Set("path", user.Path); err != nil { + return err + } + if err := d.Set("unique_id", user.UserID); err != nil { + return err + } + return nil +} + +func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.DeleteUserInput{ + UserName: aws.String(d.Id()), + } + + if _, err := iamconn.DeleteUser(request); err != nil { + return fmt.Errorf("Error deleting IAM User %s: %s", d.Id(), err) + } + return nil +} diff --git a/builtin/providers/aws/resource_aws_iam_user_policy.go b/builtin/providers/aws/resource_aws_iam_user_policy.go new file mode 100644 index 000000000000..5d84659b44d7 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_user_policy.go @@ -0,0 +1,110 @@ +package aws + +import ( + "fmt" + "net/url" + "strings" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamUserPolicy() *schema.Resource { + return &schema.Resource{ + // PutUserPolicy API is idempotent, so these can be the same. + Create: resourceAwsIamUserPolicyPut, + Update: resourceAwsIamUserPolicyPut, + + Read: resourceAwsIamUserPolicyRead, + Delete: resourceAwsIamUserPolicyDelete, + + Schema: map[string]*schema.Schema{ + "policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "user": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsIamUserPolicyPut(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.PutUserPolicyInput{ + UserName: aws.String(d.Get("user").(string)), + PolicyName: aws.String(d.Get("name").(string)), + PolicyDocument: aws.String(d.Get("policy").(string)), + } + + if _, err := iamconn.PutUserPolicy(request); err != nil { + return fmt.Errorf("Error putting IAM user policy %s: %s", request.PolicyName, err) + } + + d.SetId(fmt.Sprintf("%s:%s", *request.UserName, *request.PolicyName)) + return nil +} + +func resourceAwsIamUserPolicyRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + user, name := resourceAwsIamUserPolicyParseId(d) + + request := &iam.GetUserPolicyInput{ + PolicyName: aws.String(name), + UserName: aws.String(user), + } + + getResp, err := iamconn.GetUserPolicy(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM policy %s from user %s: %s", name, user, err) + } + + if getResp.PolicyDocument == nil { + return fmt.Errorf("GetUserPolicy returned a nil policy document") + } + + policy, err := url.QueryUnescape(*getResp.PolicyDocument) + if err != nil { + return err + } + return d.Set("policy", policy) +} + +func resourceAwsIamUserPolicyDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + user, name := resourceAwsIamUserPolicyParseId(d) + + request := &iam.DeleteUserPolicyInput{ + PolicyName: aws.String(name), + UserName: aws.String(user), + } + + if _, err := iamconn.DeleteUserPolicy(request); err != nil { + return fmt.Errorf("Error deleting IAM user policy %s: %s", d.Id(), err) + } + return nil +} + +func resourceAwsIamUserPolicyParseId(d *schema.ResourceData) (userName, policyName string) { + parts := strings.SplitN(d.Id(), ":", 2) + userName = parts[0] + policyName = parts[1] + return +} diff --git a/website/source/docs/providers/aws/r/iam_access_key.html.markdown b/website/source/docs/providers/aws/r/iam_access_key.html.markdown new file mode 100644 index 000000000000..042372c3cb94 --- /dev/null +++ b/website/source/docs/providers/aws/r/iam_access_key.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "aws" +page_title: "AWS: aws_iam_access_key" +sidebar_current: "docs-aws-resource-iam-access-key" +description: |- + Provides an IAM access key. This is a set of credentials that allow API requests to be made as an IAM user. +--- + +# aws\_iam\_access\_key + +Provides an IAM access key. This is a set of credentials that allow API requests to be made as an IAM user. + +## Example Usage + +``` +resource "aws_iam_user" "lb" { + name = "loadbalancer" + path = "/system/" +} + +resource "aws_iam_access_key" "lb" { + user = "${aws_iam_user.lb.name}" + status = "Active" +} + +resource "aws_iam_user_policy" "lb_ro" { + name = "test" + user = "${aws_iam_user.lb.name}" + policy = <aws_elb + > + aws_iam_access_key + + + > + aws_iam_user + + + > + aws_iam_user_policy + + > aws_instance From d213355bbe231d9abc9e2815207910c264caf788 Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Tue, 7 Apr 2015 13:21:22 -0400 Subject: [PATCH 03/17] Implement group and group policy resources --- builtin/providers/aws/provider.go | 2 + .../providers/aws/resource_aws_iam_group.go | 106 +++++++++++++++++ .../aws/resource_aws_iam_group_policy.go | 110 ++++++++++++++++++ .../providers/aws/resource_aws_iam_user.go | 5 +- 4 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_iam_group.go create mode 100644 builtin/providers/aws/resource_aws_iam_group_policy.go diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 3ce7a1354ddd..56338f570e0b 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -53,6 +53,8 @@ func Provider() terraform.ResourceProvider { "aws_eip": resourceAwsEip(), "aws_elb": resourceAwsElb(), "aws_iam_access_key": resourceAwsIamAccessKey(), + "aws_iam_group_policy": resourceAwsIamGroupPolicy(), + "aws_iam_group": resourceAwsIamGroup(), "aws_iam_user_policy": resourceAwsIamUserPolicy(), "aws_iam_user": resourceAwsIamUser(), "aws_instance": resourceAwsInstance(), diff --git a/builtin/providers/aws/resource_aws_iam_group.go b/builtin/providers/aws/resource_aws_iam_group.go new file mode 100644 index 000000000000..2e89d95bc027 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_group.go @@ -0,0 +1,106 @@ +package aws + +import ( + "fmt" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsIamGroupCreate, + Read: resourceAwsIamGroupRead, + // TODO + //Update: resourceAwsIamGroupUpdate, + Delete: resourceAwsIamGroupDelete, + + Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "unique_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "/", + ForceNew: true, + }, + }, + } +} + +func resourceAwsIamGroupCreate(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + name := d.Get("name").(string) + + request := &iam.CreateGroupRequest{ + Path: aws.String(d.Get("path").(string)), + GroupName: aws.String(name), + } + + createResp, err := iamconn.CreateGroup(request) + if err != nil { + return fmt.Errorf("Error creating IAM Group %s: %s", name, err) + } + return resourceAwsIamGroupReadResult(d, createResp.Group) +} + +func resourceAwsIamGroupRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.GetGroupRequest{ + GroupName: aws.String(d.Id()), + } + + getResp, err := iamconn.GetGroup(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM Group %s: %s", d.Id(), err) + } + return resourceAwsIamGroupReadResult(d, getResp.Group) +} + +func resourceAwsIamGroupReadResult(d *schema.ResourceData, group *iam.Group) error { + d.SetId(*group.GroupName) + if err := d.Set("name", group.GroupName); err != nil { + return err + } + if err := d.Set("arn", group.ARN); err != nil { + return err + } + if err := d.Set("path", group.Path); err != nil { + return err + } + if err := d.Set("unique_id", group.GroupID); err != nil { + return err + } + return nil +} + +func resourceAwsIamGroupDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.DeleteGroupRequest{ + GroupName: aws.String(d.Id()), + } + + if err := iamconn.DeleteGroup(request); err != nil { + return fmt.Errorf("Error deleting IAM Group %s: %s", d.Id(), err) + } + return nil +} diff --git a/builtin/providers/aws/resource_aws_iam_group_policy.go b/builtin/providers/aws/resource_aws_iam_group_policy.go new file mode 100644 index 000000000000..d5f6a753ca19 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_group_policy.go @@ -0,0 +1,110 @@ +package aws + +import ( + "fmt" + "net/url" + "strings" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamGroupPolicy() *schema.Resource { + return &schema.Resource{ + // PutGroupPolicy API is idempotent, so these can be the same. + Create: resourceAwsIamGroupPolicyPut, + Update: resourceAwsIamGroupPolicyPut, + + Read: resourceAwsIamGroupPolicyRead, + Delete: resourceAwsIamGroupPolicyDelete, + + Schema: map[string]*schema.Schema{ + "policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "group": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsIamGroupPolicyPut(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.PutGroupPolicyRequest{ + GroupName: aws.String(d.Get("group").(string)), + PolicyName: aws.String(d.Get("name").(string)), + PolicyDocument: aws.String(d.Get("policy").(string)), + } + + if err := iamconn.PutGroupPolicy(request); err != nil { + return fmt.Errorf("Error putting IAM group policy %s: %s", request.PolicyName, err) + } + + d.SetId(fmt.Sprintf("%s:%s", *request.GroupName, *request.PolicyName)) + return nil +} + +func resourceAwsIamGroupPolicyRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + group, name := resourceAwsIamGroupPolicyParseId(d) + + request := &iam.GetGroupPolicyRequest{ + PolicyName: aws.String(name), + GroupName: aws.String(group), + } + + getResp, err := iamconn.GetGroupPolicy(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM policy %s from group %s: %s", name, group, err) + } + + if getResp.PolicyDocument == nil { + return fmt.Errorf("GetGroupPolicy returned a nil policy document") + } + + policy, err := url.QueryUnescape(*getResp.PolicyDocument) + if err != nil { + return err + } + return d.Set("policy", policy) +} + +func resourceAwsIamGroupPolicyDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + group, name := resourceAwsIamGroupPolicyParseId(d) + + request := &iam.DeleteGroupPolicyRequest{ + PolicyName: aws.String(name), + GroupName: aws.String(group), + } + + if err := iamconn.DeleteGroupPolicy(request); err != nil { + return fmt.Errorf("Error deleting IAM group policy %s: %s", d.Id(), err) + } + return nil +} + +func resourceAwsIamGroupPolicyParseId(d *schema.ResourceData) (groupName, policyName string) { + parts := strings.SplitN(d.Id(), ":", 2) + groupName = parts[0] + policyName = parts[1] + return +} diff --git a/builtin/providers/aws/resource_aws_iam_user.go b/builtin/providers/aws/resource_aws_iam_user.go index 99c3f785e136..73c5ac8f0505 100644 --- a/builtin/providers/aws/resource_aws_iam_user.go +++ b/builtin/providers/aws/resource_aws_iam_user.go @@ -52,15 +52,16 @@ func resourceAwsIamUser() *schema.Resource { func resourceAwsIamUserCreate(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn + name := d.Get("name").(string) request := &iam.CreateUserInput{ Path: aws.String(d.Get("path").(string)), - UserName: aws.String(d.Get("name").(string)), + UserName: aws.String(name), } createResp, err := iamconn.CreateUser(request) if err != nil { - return fmt.Errorf("Error creating IAM User %s: %s", request.UserName, err) + return fmt.Errorf("Error creating IAM User %s: %s", name, err) } return resourceAwsIamUserReadResult(d, createResp.User) } From c134ce84e219b4489aff6311237d8c4a9e76e893 Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Tue, 7 Apr 2015 16:59:10 -0400 Subject: [PATCH 04/17] Implement IAM roles --- builtin/providers/aws/provider.go | 2 + .../providers/aws/resource_aws_iam_role.go | 112 ++++++++++++++++++ .../aws/resource_aws_iam_role_policy.go | 110 +++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_role.go create mode 100644 builtin/providers/aws/resource_aws_iam_role_policy.go diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 56338f570e0b..235fb2f9c0cb 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -55,6 +55,8 @@ func Provider() terraform.ResourceProvider { "aws_iam_access_key": resourceAwsIamAccessKey(), "aws_iam_group_policy": resourceAwsIamGroupPolicy(), "aws_iam_group": resourceAwsIamGroup(), + "aws_iam_role_policy": resourceAwsIamRolePolicy(), + "aws_iam_role": resourceAwsIamRole(), "aws_iam_user_policy": resourceAwsIamUserPolicy(), "aws_iam_user": resourceAwsIamUser(), "aws_instance": resourceAwsInstance(), diff --git a/builtin/providers/aws/resource_aws_iam_role.go b/builtin/providers/aws/resource_aws_iam_role.go new file mode 100644 index 000000000000..d92bf465599b --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_role.go @@ -0,0 +1,112 @@ +package aws + +import ( + "fmt" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamRole() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsIamRoleCreate, + Read: resourceAwsIamRoleRead, + // TODO + //Update: resourceAwsIamRoleUpdate, + Delete: resourceAwsIamRoleDelete, + + Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "unique_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "/", + ForceNew: true, + }, + "assume_role_policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + name := d.Get("name").(string) + + request := &iam.CreateRoleRequest{ + Path: aws.String(d.Get("path").(string)), + RoleName: aws.String(name), + AssumeRolePolicyDocument: aws.String(d.Get("assume_role_policy").(string)), + } + + createResp, err := iamconn.CreateRole(request) + if err != nil { + return fmt.Errorf("Error creating IAM Role %s: %s", name, err) + } + return resourceAwsIamRoleReadResult(d, createResp.Role) +} + +func resourceAwsIamRoleRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.GetRoleRequest{ + RoleName: aws.String(d.Id()), + } + + getResp, err := iamconn.GetRole(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM Role %s: %s", d.Id(), err) + } + return resourceAwsIamRoleReadResult(d, getResp.Role) +} + +func resourceAwsIamRoleReadResult(d *schema.ResourceData, role *iam.Role) error { + d.SetId(*role.RoleName) + if err := d.Set("name", role.RoleName); err != nil { + return err + } + if err := d.Set("arn", role.ARN); err != nil { + return err + } + if err := d.Set("path", role.Path); err != nil { + return err + } + if err := d.Set("unique_id", role.RoleID); err != nil { + return err + } + return nil +} + +func resourceAwsIamRoleDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.DeleteRoleRequest{ + RoleName: aws.String(d.Id()), + } + + if err := iamconn.DeleteRole(request); err != nil { + return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err) + } + return nil +} diff --git a/builtin/providers/aws/resource_aws_iam_role_policy.go b/builtin/providers/aws/resource_aws_iam_role_policy.go new file mode 100644 index 000000000000..e02c3a381f35 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_role_policy.go @@ -0,0 +1,110 @@ +package aws + +import ( + "fmt" + "net/url" + "strings" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamRolePolicy() *schema.Resource { + return &schema.Resource{ + // PutRolePolicy API is idempotent, so these can be the same. + Create: resourceAwsIamRolePolicyPut, + Update: resourceAwsIamRolePolicyPut, + + Read: resourceAwsIamRolePolicyRead, + Delete: resourceAwsIamRolePolicyDelete, + + Schema: map[string]*schema.Schema{ + "policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsIamRolePolicyPut(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.PutRolePolicyRequest{ + RoleName: aws.String(d.Get("role").(string)), + PolicyName: aws.String(d.Get("name").(string)), + PolicyDocument: aws.String(d.Get("policy").(string)), + } + + if err := iamconn.PutRolePolicy(request); err != nil { + return fmt.Errorf("Error putting IAM role policy %s: %s", request.PolicyName, err) + } + + d.SetId(fmt.Sprintf("%s:%s", *request.RoleName, *request.PolicyName)) + return nil +} + +func resourceAwsIamRolePolicyRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + role, name := resourceAwsIamRolePolicyParseId(d) + + request := &iam.GetRolePolicyRequest{ + PolicyName: aws.String(name), + RoleName: aws.String(role), + } + + getResp, err := iamconn.GetRolePolicy(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { // XXX test me + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM policy %s from role %s: %s", name, role, err) + } + + if getResp.PolicyDocument == nil { + return fmt.Errorf("GetRolePolicy returned a nil policy document") + } + + policy, err := url.QueryUnescape(*getResp.PolicyDocument) + if err != nil { + return err + } + return d.Set("policy", policy) +} + +func resourceAwsIamRolePolicyDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + role, name := resourceAwsIamRolePolicyParseId(d) + + request := &iam.DeleteRolePolicyRequest{ + PolicyName: aws.String(name), + RoleName: aws.String(role), + } + + if err := iamconn.DeleteRolePolicy(request); err != nil { + return fmt.Errorf("Error deleting IAM role policy %s: %s", d.Id(), err) + } + return nil +} + +func resourceAwsIamRolePolicyParseId(d *schema.ResourceData) (userName, policyName string) { + parts := strings.SplitN(d.Id(), ":", 2) + userName = parts[0] + policyName = parts[1] + return +} From 251adeb664ff894843da1729fa684052a52a1658 Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Wed, 8 Apr 2015 17:04:33 -0400 Subject: [PATCH 05/17] Implement IAM instance profiles --- builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_iam_instance_profile.go | 186 ++++++++++++++++++ helper/schema/set.go | 19 ++ 3 files changed, 206 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_instance_profile.go diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 235fb2f9c0cb..373f6819fa0a 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -55,6 +55,7 @@ func Provider() terraform.ResourceProvider { "aws_iam_access_key": resourceAwsIamAccessKey(), "aws_iam_group_policy": resourceAwsIamGroupPolicy(), "aws_iam_group": resourceAwsIamGroup(), + "aws_iam_instance_profile": resourceAwsIamInstanceProfile(), "aws_iam_role_policy": resourceAwsIamRolePolicy(), "aws_iam_role": resourceAwsIamRole(), "aws_iam_user_policy": resourceAwsIamUserPolicy(), diff --git a/builtin/providers/aws/resource_aws_iam_instance_profile.go b/builtin/providers/aws/resource_aws_iam_instance_profile.go new file mode 100644 index 000000000000..519bf3f3feeb --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_instance_profile.go @@ -0,0 +1,186 @@ +package aws + +import ( + "fmt" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/iam" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsIamInstanceProfile() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsIamInstanceProfileCreate, + Read: resourceAwsIamInstanceProfileRead, + Update: resourceAwsIamInstanceProfileUpdate, + Delete: resourceAwsIamInstanceProfileDelete, + + Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "create_date": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "unique_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "/", + ForceNew: true, + }, + "roles": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + } +} + +func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + name := d.Get("name").(string) + + request := &iam.CreateInstanceProfileRequest{ + InstanceProfileName: aws.String(name), + Path: aws.String(d.Get("path").(string)), + } + + response, err := iamconn.CreateInstanceProfile(request) + if err == nil { + err = instanceProfileReadResult(d, response.InstanceProfile) + } + if err != nil { + return fmt.Errorf("Error creating IAM instance profile %s: %s", name, err) + } + + return instanceProfileSetRoles(d, iamconn) +} + +func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error { + request := &iam.AddRoleToInstanceProfileRequest{ + InstanceProfileName: aws.String(profileName), + RoleName: aws.String(roleName), + } + + return iamconn.AddRoleToInstanceProfile(request) +} + +func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error { + request := &iam.RemoveRoleFromInstanceProfileRequest{ + InstanceProfileName: aws.String(profileName), + RoleName: aws.String(roleName), + } + + return iamconn.RemoveRoleFromInstanceProfile(request) +} + +func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error { + oldInterface, newInterface := d.GetChange("roles") + oldRoles := oldInterface.(*schema.Set) + newRoles := newInterface.(*schema.Set) + + currentRoles := schema.CopySet(oldRoles) + + d.Partial(true) + + for _, role := range oldRoles.Difference(newRoles).List() { + err := instanceProfileRemoveRole(iamconn, d.Id(), role.(string)) + if err != nil { + return fmt.Errorf("Error removing role %s from IAM instance profile %s: %s", role, d.Id(), err) + } + currentRoles.Remove(role) + d.Set("roles", currentRoles) + d.SetPartial("roles") + } + + for _, role := range newRoles.Difference(oldRoles).List() { + err := instanceProfileAddRole(iamconn, d.Id(), role.(string)) + if err != nil { + return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", role, d.Id(), err) + } + currentRoles.Add(role) + d.Set("roles", currentRoles) + d.SetPartial("roles") + } + + d.Partial(false) + + return nil +} + +func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + if !d.HasChange("roles") { + return nil + } + + return instanceProfileSetRoles(d, iamconn) +} + +func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.GetInstanceProfileRequest{ + InstanceProfileName: aws.String(d.Id()), + } + + result, err := iamconn.GetInstanceProfile(request) + if err != nil { + if iamerr, ok := err.(aws.APIError); ok && iamerr.Code == "NoSuchEntity" { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading IAM instance profile %s: %s", d.Id(), err) + } + + return instanceProfileReadResult(d, result.InstanceProfile) +} + +func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + request := &iam.DeleteInstanceProfileRequest{ + InstanceProfileName: aws.String(d.Id()), + } + err := iamconn.DeleteInstanceProfile(request) + if err != nil { + return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err) + } + d.SetId("") + return nil +} + +func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfile) error { + d.SetId(*result.InstanceProfileName) + if err := d.Set("name", result.InstanceProfileName); err != nil { + return err + } + if err := d.Set("path", result.Path); err != nil { + return err + } + + roles := &schema.Set{F: schema.HashString} + for _, role := range result.Roles { + roles.Add(*role.RoleName) + } + if err := d.Set("roles", roles); err != nil { + return err + } + + return nil +} diff --git a/helper/schema/set.go b/helper/schema/set.go index 44ff3837eace..6496a5d769c6 100644 --- a/helper/schema/set.go +++ b/helper/schema/set.go @@ -35,11 +35,21 @@ func NewSet(f SchemaSetFunc, items []interface{}) *Set { return s } +// CopySet returns a copy of another set. +func CopySet(otherSet *Set) *Set { + return NewSet(otherSet.F, otherSet.List()) +} + // Add adds an item to the set if it isn't already in the set. func (s *Set) Add(item interface{}) { s.add(item) } +// Remove removes an item if it's already in the set. Idempotent. +func (s *Set) Remove(item interface{}) { + s.remove(item) +} + // Contains checks if the set has the given item. func (s *Set) Contains(item interface{}) bool { _, ok := s.m[s.F(item)] @@ -138,6 +148,15 @@ func (s *Set) add(item interface{}) int { return code } +func (s *Set) remove(item interface{}) int { + s.once.Do(s.init) + + code := s.F(item) + delete(s.m, code) + + return code +} + func (s *Set) index(item interface{}) int { return sort.SearchInts(s.listCode(), s.F(item)) } From 48a273e06a035379d8fd2469b9f112cee8366e6d Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Fri, 17 Apr 2015 12:52:28 -0400 Subject: [PATCH 06/17] Convert to awslabs SDK --- .../providers/aws/resource_aws_iam_group.go | 12 +++++----- .../aws/resource_aws_iam_group_policy.go | 14 ++++++------ .../aws/resource_aws_iam_instance_profile.go | 22 ++++++++++--------- .../providers/aws/resource_aws_iam_role.go | 12 +++++----- .../aws/resource_aws_iam_role_policy.go | 14 ++++++------ 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/builtin/providers/aws/resource_aws_iam_group.go b/builtin/providers/aws/resource_aws_iam_group.go index 2e89d95bc027..1d0bc6cf4bc6 100644 --- a/builtin/providers/aws/resource_aws_iam_group.go +++ b/builtin/providers/aws/resource_aws_iam_group.go @@ -3,8 +3,8 @@ package aws import ( "fmt" - "github.com/hashicorp/aws-sdk-go/aws" - "github.com/hashicorp/aws-sdk-go/gen/iam" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" "github.com/hashicorp/terraform/helper/schema" ) @@ -45,7 +45,7 @@ func resourceAwsIamGroupCreate(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn name := d.Get("name").(string) - request := &iam.CreateGroupRequest{ + request := &iam.CreateGroupInput{ Path: aws.String(d.Get("path").(string)), GroupName: aws.String(name), } @@ -60,7 +60,7 @@ func resourceAwsIamGroupCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsIamGroupRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.GetGroupRequest{ + request := &iam.GetGroupInput{ GroupName: aws.String(d.Id()), } @@ -95,11 +95,11 @@ func resourceAwsIamGroupReadResult(d *schema.ResourceData, group *iam.Group) err func resourceAwsIamGroupDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.DeleteGroupRequest{ + request := &iam.DeleteGroupInput{ GroupName: aws.String(d.Id()), } - if err := iamconn.DeleteGroup(request); err != nil { + if _, err := iamconn.DeleteGroup(request); err != nil { return fmt.Errorf("Error deleting IAM Group %s: %s", d.Id(), err) } return nil diff --git a/builtin/providers/aws/resource_aws_iam_group_policy.go b/builtin/providers/aws/resource_aws_iam_group_policy.go index d5f6a753ca19..9fa474acc0f2 100644 --- a/builtin/providers/aws/resource_aws_iam_group_policy.go +++ b/builtin/providers/aws/resource_aws_iam_group_policy.go @@ -5,8 +5,8 @@ import ( "net/url" "strings" - "github.com/hashicorp/aws-sdk-go/aws" - "github.com/hashicorp/aws-sdk-go/gen/iam" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" "github.com/hashicorp/terraform/helper/schema" ) @@ -42,13 +42,13 @@ func resourceAwsIamGroupPolicy() *schema.Resource { func resourceAwsIamGroupPolicyPut(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.PutGroupPolicyRequest{ + request := &iam.PutGroupPolicyInput{ GroupName: aws.String(d.Get("group").(string)), PolicyName: aws.String(d.Get("name").(string)), PolicyDocument: aws.String(d.Get("policy").(string)), } - if err := iamconn.PutGroupPolicy(request); err != nil { + if _, err := iamconn.PutGroupPolicy(request); err != nil { return fmt.Errorf("Error putting IAM group policy %s: %s", request.PolicyName, err) } @@ -61,7 +61,7 @@ func resourceAwsIamGroupPolicyRead(d *schema.ResourceData, meta interface{}) err group, name := resourceAwsIamGroupPolicyParseId(d) - request := &iam.GetGroupPolicyRequest{ + request := &iam.GetGroupPolicyInput{ PolicyName: aws.String(name), GroupName: aws.String(group), } @@ -91,12 +91,12 @@ func resourceAwsIamGroupPolicyDelete(d *schema.ResourceData, meta interface{}) e group, name := resourceAwsIamGroupPolicyParseId(d) - request := &iam.DeleteGroupPolicyRequest{ + request := &iam.DeleteGroupPolicyInput{ PolicyName: aws.String(name), GroupName: aws.String(group), } - if err := iamconn.DeleteGroupPolicy(request); err != nil { + if _, err := iamconn.DeleteGroupPolicy(request); err != nil { return fmt.Errorf("Error deleting IAM group policy %s: %s", d.Id(), err) } return nil diff --git a/builtin/providers/aws/resource_aws_iam_instance_profile.go b/builtin/providers/aws/resource_aws_iam_instance_profile.go index 519bf3f3feeb..237d4348af39 100644 --- a/builtin/providers/aws/resource_aws_iam_instance_profile.go +++ b/builtin/providers/aws/resource_aws_iam_instance_profile.go @@ -3,8 +3,8 @@ package aws import ( "fmt" - "github.com/hashicorp/aws-sdk-go/aws" - "github.com/hashicorp/aws-sdk-go/gen/iam" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" "github.com/hashicorp/terraform/helper/schema" ) @@ -54,7 +54,7 @@ func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{ iamconn := meta.(*AWSClient).iamconn name := d.Get("name").(string) - request := &iam.CreateInstanceProfileRequest{ + request := &iam.CreateInstanceProfileInput{ InstanceProfileName: aws.String(name), Path: aws.String(d.Get("path").(string)), } @@ -71,21 +71,23 @@ func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{ } func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error { - request := &iam.AddRoleToInstanceProfileRequest{ + request := &iam.AddRoleToInstanceProfileInput{ InstanceProfileName: aws.String(profileName), RoleName: aws.String(roleName), } - return iamconn.AddRoleToInstanceProfile(request) + _, err := iamconn.AddRoleToInstanceProfile(request) + return err } func instanceProfileRemoveRole(iamconn *iam.IAM, profileName, roleName string) error { - request := &iam.RemoveRoleFromInstanceProfileRequest{ + request := &iam.RemoveRoleFromInstanceProfileInput{ InstanceProfileName: aws.String(profileName), RoleName: aws.String(roleName), } - return iamconn.RemoveRoleFromInstanceProfile(request) + _, err := iamconn.RemoveRoleFromInstanceProfile(request) + return err } func instanceProfileSetRoles(d *schema.ResourceData, iamconn *iam.IAM) error { @@ -135,7 +137,7 @@ func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{ func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.GetInstanceProfileRequest{ + request := &iam.GetInstanceProfileInput{ InstanceProfileName: aws.String(d.Id()), } @@ -154,10 +156,10 @@ func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) func resourceAwsIamInstanceProfileDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.DeleteInstanceProfileRequest{ + request := &iam.DeleteInstanceProfileInput{ InstanceProfileName: aws.String(d.Id()), } - err := iamconn.DeleteInstanceProfile(request) + _, err := iamconn.DeleteInstanceProfile(request) if err != nil { return fmt.Errorf("Error deleting IAM instance profile %s: %s", d.Id(), err) } diff --git a/builtin/providers/aws/resource_aws_iam_role.go b/builtin/providers/aws/resource_aws_iam_role.go index d92bf465599b..6a9bdc00ad47 100644 --- a/builtin/providers/aws/resource_aws_iam_role.go +++ b/builtin/providers/aws/resource_aws_iam_role.go @@ -3,8 +3,8 @@ package aws import ( "fmt" - "github.com/hashicorp/aws-sdk-go/aws" - "github.com/hashicorp/aws-sdk-go/gen/iam" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" "github.com/hashicorp/terraform/helper/schema" ) @@ -50,7 +50,7 @@ func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn name := d.Get("name").(string) - request := &iam.CreateRoleRequest{ + request := &iam.CreateRoleInput{ Path: aws.String(d.Get("path").(string)), RoleName: aws.String(name), AssumeRolePolicyDocument: aws.String(d.Get("assume_role_policy").(string)), @@ -66,7 +66,7 @@ func resourceAwsIamRoleCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsIamRoleRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.GetRoleRequest{ + request := &iam.GetRoleInput{ RoleName: aws.String(d.Id()), } @@ -101,11 +101,11 @@ func resourceAwsIamRoleReadResult(d *schema.ResourceData, role *iam.Role) error func resourceAwsIamRoleDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.DeleteRoleRequest{ + request := &iam.DeleteRoleInput{ RoleName: aws.String(d.Id()), } - if err := iamconn.DeleteRole(request); err != nil { + if _, err := iamconn.DeleteRole(request); err != nil { return fmt.Errorf("Error deleting IAM Role %s: %s", d.Id(), err) } return nil diff --git a/builtin/providers/aws/resource_aws_iam_role_policy.go b/builtin/providers/aws/resource_aws_iam_role_policy.go index e02c3a381f35..923931a909ef 100644 --- a/builtin/providers/aws/resource_aws_iam_role_policy.go +++ b/builtin/providers/aws/resource_aws_iam_role_policy.go @@ -5,8 +5,8 @@ import ( "net/url" "strings" - "github.com/hashicorp/aws-sdk-go/aws" - "github.com/hashicorp/aws-sdk-go/gen/iam" + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" "github.com/hashicorp/terraform/helper/schema" ) @@ -42,13 +42,13 @@ func resourceAwsIamRolePolicy() *schema.Resource { func resourceAwsIamRolePolicyPut(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - request := &iam.PutRolePolicyRequest{ + request := &iam.PutRolePolicyInput{ RoleName: aws.String(d.Get("role").(string)), PolicyName: aws.String(d.Get("name").(string)), PolicyDocument: aws.String(d.Get("policy").(string)), } - if err := iamconn.PutRolePolicy(request); err != nil { + if _, err := iamconn.PutRolePolicy(request); err != nil { return fmt.Errorf("Error putting IAM role policy %s: %s", request.PolicyName, err) } @@ -61,7 +61,7 @@ func resourceAwsIamRolePolicyRead(d *schema.ResourceData, meta interface{}) erro role, name := resourceAwsIamRolePolicyParseId(d) - request := &iam.GetRolePolicyRequest{ + request := &iam.GetRolePolicyInput{ PolicyName: aws.String(name), RoleName: aws.String(role), } @@ -91,12 +91,12 @@ func resourceAwsIamRolePolicyDelete(d *schema.ResourceData, meta interface{}) er role, name := resourceAwsIamRolePolicyParseId(d) - request := &iam.DeleteRolePolicyRequest{ + request := &iam.DeleteRolePolicyInput{ PolicyName: aws.String(name), RoleName: aws.String(role), } - if err := iamconn.DeleteRolePolicy(request); err != nil { + if _, err := iamconn.DeleteRolePolicy(request); err != nil { return fmt.Errorf("Error deleting IAM role policy %s: %s", d.Id(), err) } return nil From 73affc0e7b3d0569f3a9ab35236629669a3d041f Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 15:56:54 -0500 Subject: [PATCH 07/17] Fix 'go vet' findings. --- builtin/providers/aws/resource_aws_iam_access_key.go | 2 +- builtin/providers/aws/resource_aws_iam_group_policy.go | 2 +- builtin/providers/aws/resource_aws_iam_role_policy.go | 2 +- builtin/providers/aws/resource_aws_iam_user_policy.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_iam_access_key.go b/builtin/providers/aws/resource_aws_iam_access_key.go index 4a7bd0d00560..efeec62f723b 100644 --- a/builtin/providers/aws/resource_aws_iam_access_key.go +++ b/builtin/providers/aws/resource_aws_iam_access_key.go @@ -46,7 +46,7 @@ func resourceAwsIamAccessKeyCreate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf( "Error creating access key for user %s: %s", - request.UserName, + *request.UserName, err, ) } diff --git a/builtin/providers/aws/resource_aws_iam_group_policy.go b/builtin/providers/aws/resource_aws_iam_group_policy.go index 9fa474acc0f2..1e6c244a5d26 100644 --- a/builtin/providers/aws/resource_aws_iam_group_policy.go +++ b/builtin/providers/aws/resource_aws_iam_group_policy.go @@ -49,7 +49,7 @@ func resourceAwsIamGroupPolicyPut(d *schema.ResourceData, meta interface{}) erro } if _, err := iamconn.PutGroupPolicy(request); err != nil { - return fmt.Errorf("Error putting IAM group policy %s: %s", request.PolicyName, err) + return fmt.Errorf("Error putting IAM group policy %s: %s", *request.PolicyName, err) } d.SetId(fmt.Sprintf("%s:%s", *request.GroupName, *request.PolicyName)) diff --git a/builtin/providers/aws/resource_aws_iam_role_policy.go b/builtin/providers/aws/resource_aws_iam_role_policy.go index 923931a909ef..854803f24f3e 100644 --- a/builtin/providers/aws/resource_aws_iam_role_policy.go +++ b/builtin/providers/aws/resource_aws_iam_role_policy.go @@ -49,7 +49,7 @@ func resourceAwsIamRolePolicyPut(d *schema.ResourceData, meta interface{}) error } if _, err := iamconn.PutRolePolicy(request); err != nil { - return fmt.Errorf("Error putting IAM role policy %s: %s", request.PolicyName, err) + return fmt.Errorf("Error putting IAM role policy %s: %s", *request.PolicyName, err) } d.SetId(fmt.Sprintf("%s:%s", *request.RoleName, *request.PolicyName)) diff --git a/builtin/providers/aws/resource_aws_iam_user_policy.go b/builtin/providers/aws/resource_aws_iam_user_policy.go index 5d84659b44d7..3fb97b29dc6f 100644 --- a/builtin/providers/aws/resource_aws_iam_user_policy.go +++ b/builtin/providers/aws/resource_aws_iam_user_policy.go @@ -49,7 +49,7 @@ func resourceAwsIamUserPolicyPut(d *schema.ResourceData, meta interface{}) error } if _, err := iamconn.PutUserPolicy(request); err != nil { - return fmt.Errorf("Error putting IAM user policy %s: %s", request.PolicyName, err) + return fmt.Errorf("Error putting IAM user policy %s: %s", *request.PolicyName, err) } d.SetId(fmt.Sprintf("%s:%s", *request.UserName, *request.PolicyName)) From 2c1cc6b83073265d930857f1a99b40e706693b23 Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 15:58:08 -0500 Subject: [PATCH 08/17] Add aws_iam_role acc test. --- .../aws/resource_aws_iam_role_test.go | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_role_test.go diff --git a/builtin/providers/aws/resource_aws_iam_role_test.go b/builtin/providers/aws/resource_aws_iam_role_test.go new file mode 100644 index 000000000000..9698a60bf601 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_role_test.go @@ -0,0 +1,110 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSRole_normal(t *testing.T) { + var conf iam.GetRoleOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSRoleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSRoleConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSRoleExists("aws_iam_role.role", &conf), + testAccCheckAWSRoleAttributes(&conf), + ), + }, + }, + }) +} + +func testAccCheckAWSRoleDestroy(s *terraform.State) error { + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_iam_role" { + continue + } + + // Try to get role + _, err := iamconn.GetRole(&iam.GetRoleInput{ + RoleName: aws.String(rs.Primary.ID), + }) + if err == nil { + return fmt.Errorf("still exist.") + } + + // Verify the error is what we want + ec2err, ok := err.(aws.APIError) + if !ok { + return err + } + if ec2err.Code != "NoSuchEntity" { + return err + } + } + + return nil +} + +func testAccCheckAWSRoleExists(n string, res *iam.GetRoleOutput) 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 fmt.Errorf("No Role name is set") + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + resp, err := iamconn.GetRole(&iam.GetRoleInput{ + RoleName: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *res = *resp + + return nil + } +} + +func testAccCheckAWSRoleAttributes(role *iam.GetRoleOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if *role.Role.RoleName != "test-role" { + return fmt.Errorf("Bad name: %s", *role.Role.RoleName) + } + + if *role.Role.Path != "/" { + return fmt.Errorf("Bad path: %s", *role.Role.Path) + } + + if *role.Role.AssumeRolePolicyDocument != "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" { + return fmt.Errorf("Bad policy: %s", *role.Role.AssumeRolePolicyDocument) + } + return nil + } +} + +const testAccAWSRoleConfig = ` +resource "aws_iam_role" "role" { + name = "test-role" + path = "/" + assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" +} +` From 5512496e26afcadd1aa2910eb80cf1df569328ab Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 19:29:55 -0500 Subject: [PATCH 09/17] Add IAM access key acc test. --- .../aws/resource_aws_iam_access_key_test.go | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_access_key_test.go diff --git a/builtin/providers/aws/resource_aws_iam_access_key_test.go b/builtin/providers/aws/resource_aws_iam_access_key_test.go new file mode 100644 index 000000000000..c780e1b7e508 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_access_key_test.go @@ -0,0 +1,113 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSAccessKey_normal(t *testing.T) { + var conf iam.AccessKeyMetadata + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAccessKeyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSAccessKeyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAccessKeyExists("aws_iam_access_key.a_key", &conf), + testAccCheckAWSAccessKeyAttributes(&conf), + ), + }, + }, + }) +} + +func testAccCheckAWSAccessKeyDestroy(s *terraform.State) error { + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_access_key" { + continue + } + + // Try to get access key + resp, err := iamconn.ListAccessKeys(&iam.ListAccessKeysInput{ + UserName: aws.String(rs.Primary.ID), + }) + if err == nil { + if len(resp.AccessKeyMetadata) > 0 { + return fmt.Errorf("still exist.") + } + return nil + } + + // Verify the error is what we want + ec2err, ok := err.(aws.APIError) + if !ok { + return err + } + if ec2err.Code != "NoSuchEntity" { + return err + } + } + + return nil +} + +func testAccCheckAWSAccessKeyExists(n string, res *iam.AccessKeyMetadata) 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 fmt.Errorf("No Role name is set") + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + resp, err := iamconn.ListAccessKeys(&iam.ListAccessKeysInput{ + UserName: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + if len(resp.AccessKeyMetadata) != 1 || + *resp.AccessKeyMetadata[0].UserName != rs.Primary.ID { + return fmt.Errorf("User not found not found") + } + + *res = *resp.AccessKeyMetadata[0] + + return nil + } +} + +func testAccCheckAWSAccessKeyAttributes(accessKeyMetadata *iam.AccessKeyMetadata) resource.TestCheckFunc { + return func(s *terraform.State) error { + if *accessKeyMetadata.UserName != "some_usr" { + return fmt.Errorf("Bad username: %s", *accessKeyMetadata.UserName) + } + + if *accessKeyMetadata.Status != "Active" { + return fmt.Errorf("Bad status: %s", *accessKeyMetadata.Status) + } + + return nil + } +} + +const testAccAWSAccessKeyConfig = ` +resource "aws_iam_access_key" "a_key" { + user = "some_user" +} +` From 77eb81e9ed5ffdb48553e8767dba747c7d318cc4 Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 19:35:14 -0500 Subject: [PATCH 10/17] add IAM group acc test. --- .../aws/resource_aws_iam_group_test.go | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_group_test.go diff --git a/builtin/providers/aws/resource_aws_iam_group_test.go b/builtin/providers/aws/resource_aws_iam_group_test.go new file mode 100644 index 000000000000..4b9486019344 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_group_test.go @@ -0,0 +1,106 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSGroup_normal(t *testing.T) { + var conf iam.GetGroupOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGroupExists("aws_iam_group.group", &conf), + testAccCheckAWSGroupAttributes(&conf), + ), + }, + }, + }) +} + +func testAccCheckAWSGroupDestroy(s *terraform.State) error { + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_iam_group" { + continue + } + + // Try to get role + _, err := iamconn.GetGroup(&iam.GetGroupInput{ + GroupName: aws.String(rs.Primary.ID), + }) + if err == nil { + return fmt.Errorf("still exist.") + } + + // Verify the error is what we want + ec2err, ok := err.(aws.APIError) + if !ok { + return err + } + if ec2err.Code != "NoSuchEntity" { + return err + } + } + + return nil +} + +func testAccCheckAWSGroupExists(n string, res *iam.GetGroupOutput) 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 fmt.Errorf("No Group name is set") + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + resp, err := iamconn.GetGroup(&iam.GetGroupInput{ + GroupName: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *res = *resp + + return nil + } +} + +func testAccCheckAWSGroupAttributes(group *iam.GetGroupOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if *group.Group.GroupName != "test-group" { + return fmt.Errorf("Bad name: %s", *group.Group.GroupName) + } + + if *group.Group.Path != "/" { + return fmt.Errorf("Bad path: %s", *group.Group.Path) + } + + return nil + } +} + +const testAccAWSGroupConfig = ` +resource "aws_iam_group" "group" { + name = "test-group" + path = "/" +} +` From eed6c7e736e229076c594c5c580479d3072d433c Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 19:38:11 -0500 Subject: [PATCH 11/17] fix some tabs --- builtin/providers/aws/resource_aws_iam_group_test.go | 4 ++-- builtin/providers/aws/resource_aws_iam_role_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_iam_group_test.go b/builtin/providers/aws/resource_aws_iam_group_test.go index 4b9486019344..a475f3d00fab 100644 --- a/builtin/providers/aws/resource_aws_iam_group_test.go +++ b/builtin/providers/aws/resource_aws_iam_group_test.go @@ -100,7 +100,7 @@ func testAccCheckAWSGroupAttributes(group *iam.GetGroupOutput) resource.TestChec const testAccAWSGroupConfig = ` resource "aws_iam_group" "group" { - name = "test-group" - path = "/" + name = "test-group" + path = "/" } ` diff --git a/builtin/providers/aws/resource_aws_iam_role_test.go b/builtin/providers/aws/resource_aws_iam_role_test.go index 9698a60bf601..0a42a3c3229a 100644 --- a/builtin/providers/aws/resource_aws_iam_role_test.go +++ b/builtin/providers/aws/resource_aws_iam_role_test.go @@ -104,7 +104,7 @@ func testAccCheckAWSRoleAttributes(role *iam.GetRoleOutput) resource.TestCheckFu const testAccAWSRoleConfig = ` resource "aws_iam_role" "role" { name = "test-role" - path = "/" + path = "/" assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" } ` From 02ffdf352b326b9857b6dd31addc4e46deade954 Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 19:42:26 -0500 Subject: [PATCH 12/17] fix comment --- builtin/providers/aws/resource_aws_iam_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_iam_group_test.go b/builtin/providers/aws/resource_aws_iam_group_test.go index a475f3d00fab..b28f152fde98 100644 --- a/builtin/providers/aws/resource_aws_iam_group_test.go +++ b/builtin/providers/aws/resource_aws_iam_group_test.go @@ -37,7 +37,7 @@ func testAccCheckAWSGroupDestroy(s *terraform.State) error { continue } - // Try to get role + // Try to get group _, err := iamconn.GetGroup(&iam.GetGroupInput{ GroupName: aws.String(rs.Primary.ID), }) From 32146ee3b343d5d0db3765cce684428a2721812a Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 19:42:34 -0500 Subject: [PATCH 13/17] Add IAM user acc test. --- .../aws/resource_aws_iam_user_test.go | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_user_test.go diff --git a/builtin/providers/aws/resource_aws_iam_user_test.go b/builtin/providers/aws/resource_aws_iam_user_test.go new file mode 100644 index 000000000000..513636f98613 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_user_test.go @@ -0,0 +1,106 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSUser_normal(t *testing.T) { + var conf iam.GetUserOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSUserDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSUserConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSUserExists("aws_iam_user.user", &conf), + testAccCheckAWSUserAttributes(&conf), + ), + }, + }, + }) +} + +func testAccCheckAWSUserDestroy(s *terraform.State) error { + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_iam_user" { + continue + } + + // Try to get user + _, err := iamconn.GetUser(&iam.GetUserInput{ + UserName: aws.String(rs.Primary.ID), + }) + if err == nil { + return fmt.Errorf("still exist.") + } + + // Verify the error is what we want + ec2err, ok := err.(aws.APIError) + if !ok { + return err + } + if ec2err.Code != "NoSuchEntity" { + return err + } + } + + return nil +} + +func testAccCheckAWSUserExists(n string, res *iam.GetUserOutput) 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 fmt.Errorf("No User name is set") + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + + resp, err := iamconn.GetUser(&iam.GetUserInput{ + UserName: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + *res = *resp + + return nil + } +} + +func testAccCheckAWSUserAttributes(user *iam.GetUserOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if *user.User.UserName != "test-user" { + return fmt.Errorf("Bad name: %s", *user.User.UserName) + } + + if *user.User.Path != "/" { + return fmt.Errorf("Bad path: %s", *user.User.Path) + } + + return nil + } +} + +const testAccAWSUserConfig = ` +resource "aws_iam_user" "user" { + name = "test-user" + path = "/" +} +` From b74265b46779af47cf427053e451f0c90f07ee93 Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 21:16:06 -0500 Subject: [PATCH 14/17] Update grouppolicy func so that it can be used in test. --- builtin/providers/aws/resource_aws_iam_group_policy.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_iam_group_policy.go b/builtin/providers/aws/resource_aws_iam_group_policy.go index 1e6c244a5d26..51190aa5fc32 100644 --- a/builtin/providers/aws/resource_aws_iam_group_policy.go +++ b/builtin/providers/aws/resource_aws_iam_group_policy.go @@ -59,7 +59,7 @@ func resourceAwsIamGroupPolicyPut(d *schema.ResourceData, meta interface{}) erro func resourceAwsIamGroupPolicyRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - group, name := resourceAwsIamGroupPolicyParseId(d) + group, name := resourceAwsIamGroupPolicyParseId(d.Id()) request := &iam.GetGroupPolicyInput{ PolicyName: aws.String(name), @@ -89,7 +89,7 @@ func resourceAwsIamGroupPolicyRead(d *schema.ResourceData, meta interface{}) err func resourceAwsIamGroupPolicyDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - group, name := resourceAwsIamGroupPolicyParseId(d) + group, name := resourceAwsIamGroupPolicyParseId(d.Id()) request := &iam.DeleteGroupPolicyInput{ PolicyName: aws.String(name), @@ -102,8 +102,8 @@ func resourceAwsIamGroupPolicyDelete(d *schema.ResourceData, meta interface{}) e return nil } -func resourceAwsIamGroupPolicyParseId(d *schema.ResourceData) (groupName, policyName string) { - parts := strings.SplitN(d.Id(), ":", 2) +func resourceAwsIamGroupPolicyParseId(id string) (groupName, policyName string) { + parts := strings.SplitN(id, ":", 2) groupName = parts[0] policyName = parts[1] return From 08f6ab30651a5b07034d6ad6692d84a10b335eb4 Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 21:16:25 -0500 Subject: [PATCH 15/17] Add IAM GroupPolicy acc test. --- .../aws/resource_aws_iam_group_policy_test.go | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_iam_group_policy_test.go diff --git a/builtin/providers/aws/resource_aws_iam_group_policy_test.go b/builtin/providers/aws/resource_aws_iam_group_policy_test.go new file mode 100644 index 000000000000..1778d52165a7 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_group_policy_test.go @@ -0,0 +1,112 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSIAMGroupPolicy(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMGroupPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccIAMGroupPolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMGroupPolicy( + "aws_iam_group.group", + "aws_iam_group_policy.foo", + ), + ), + }, + resource.TestStep{ + Config: testAccIAMGroupPolicyConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMGroupPolicy( + "aws_iam_group.group", + "aws_iam_group_policy.bar", + ), + ), + }, + }, + }) +} + +func testAccCheckIAMGroupPolicyDestroy(s *terraform.State) error { + if len(s.RootModule().Resources) > 0 { + return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources) + } + + return nil +} + +func testAccCheckIAMGroupPolicy( + iamGroupResource string, + iamGroupPolicyResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[iamGroupResource] + if !ok { + return fmt.Errorf("Not Found: %s", iamGroupResource) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + policy, ok := s.RootModule().Resources[iamGroupPolicyResource] + if !ok { + return fmt.Errorf("Not Found: %s", iamGroupPolicyResource) + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + group, name := resourceAwsIamGroupPolicyParseId(policy.Primary.ID) + _, err := iamconn.GetGroupPolicy(&iam.GetGroupPolicyInput{ + GroupName: aws.String(group), + PolicyName: aws.String(name), + }) + + if err != nil { + return err + } + + return nil + } +} + +const testAccIAMGroupPolicyConfig = ` +resource "aws_iam_group" "group" { + name = "test_group" + path = "/" +} + +resource "aws_iam_group_policy" "foo" { + name = "foo_policy" + group = "${aws_iam_group.group.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} +` + +const testAccIAMGroupPolicyConfigUpdate = ` +resource "aws_iam_group" "group" { + name = "test_group" + path = "/" +} + +resource "aws_iam_group_policy" "foo" { + name = "foo_policy" + group = "${aws_iam_group.group.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} + +resource "aws_iam_group_policy" "bar" { + name = "bar_policy" + group = "${aws_iam_group.group.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} +` From d0ba6926e24635c30e99c10389e19fee5a4cfe9d Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 21:25:04 -0500 Subject: [PATCH 16/17] Add IAM Role Policy acc test. --- .../aws/resource_aws_iam_role_policy.go | 10 +- .../aws/resource_aws_iam_role_policy_test.go | 112 ++++++++++++++++++ 2 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_iam_role_policy_test.go diff --git a/builtin/providers/aws/resource_aws_iam_role_policy.go b/builtin/providers/aws/resource_aws_iam_role_policy.go index 854803f24f3e..0eb073a9d7d4 100644 --- a/builtin/providers/aws/resource_aws_iam_role_policy.go +++ b/builtin/providers/aws/resource_aws_iam_role_policy.go @@ -59,7 +59,7 @@ func resourceAwsIamRolePolicyPut(d *schema.ResourceData, meta interface{}) error func resourceAwsIamRolePolicyRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - role, name := resourceAwsIamRolePolicyParseId(d) + role, name := resourceAwsIamRolePolicyParseId(d.Id()) request := &iam.GetRolePolicyInput{ PolicyName: aws.String(name), @@ -89,7 +89,7 @@ func resourceAwsIamRolePolicyRead(d *schema.ResourceData, meta interface{}) erro func resourceAwsIamRolePolicyDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - role, name := resourceAwsIamRolePolicyParseId(d) + role, name := resourceAwsIamRolePolicyParseId(d.Id()) request := &iam.DeleteRolePolicyInput{ PolicyName: aws.String(name), @@ -102,9 +102,9 @@ func resourceAwsIamRolePolicyDelete(d *schema.ResourceData, meta interface{}) er return nil } -func resourceAwsIamRolePolicyParseId(d *schema.ResourceData) (userName, policyName string) { - parts := strings.SplitN(d.Id(), ":", 2) - userName = parts[0] +func resourceAwsIamRolePolicyParseId(id string) (roleName, policyName string) { + parts := strings.SplitN(id, ":", 2) + roleName = parts[0] policyName = parts[1] return } diff --git a/builtin/providers/aws/resource_aws_iam_role_policy_test.go b/builtin/providers/aws/resource_aws_iam_role_policy_test.go new file mode 100644 index 000000000000..38f76dbfd9e3 --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_role_policy_test.go @@ -0,0 +1,112 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSIAMRolePolicy(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMRolePolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccIAMRolePolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMRolePolicy( + "aws_iam_role.role", + "aws_iam_role_policy.foo", + ), + ), + }, + resource.TestStep{ + Config: testAccIAMRolePolicyConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMRolePolicy( + "aws_iam_role.role", + "aws_iam_role_policy.bar", + ), + ), + }, + }, + }) +} + +func testAccCheckIAMRolePolicyDestroy(s *terraform.State) error { + if len(s.RootModule().Resources) > 0 { + return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources) + } + + return nil +} + +func testAccCheckIAMRolePolicy( + iamRoleResource string, + iamRolePolicyResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[iamRoleResource] + if !ok { + return fmt.Errorf("Not Found: %s", iamRoleResource) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + policy, ok := s.RootModule().Resources[iamRolePolicyResource] + if !ok { + return fmt.Errorf("Not Found: %s", iamRolePolicyResource) + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + role, name := resourceAwsIamRolePolicyParseId(policy.Primary.ID) + _, err := iamconn.GetRolePolicy(&iam.GetRolePolicyInput{ + RoleName: aws.String(role), + PolicyName: aws.String(name), + }) + + if err != nil { + return err + } + + return nil + } +} + +const testAccIAMRolePolicyConfig = ` +resource "aws_iam_role" "role" { + name = "test_role" + path = "/" +} + +resource "aws_iam_role_policy" "foo" { + name = "foo_policy" + role = "${aws_iam_role.role.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} +` + +const testAccIAMRolePolicyConfigUpdate = ` +resource "aws_iam_role" "role" { + name = "test_role" + path = "/" +} + +resource "aws_iam_role_policy" "foo" { + name = "foo_policy" + role = "${aws_iam_role.role.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} + +resource "aws_iam_role_policy" "bar" { + name = "bar_policy" + role = "${aws_iam_role.role.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} +` From f2b34c8decbda241646464488de63c9c4a6794b9 Mon Sep 17 00:00:00 2001 From: John Engelman Date: Sat, 18 Apr 2015 21:31:21 -0500 Subject: [PATCH 17/17] Add IAM UserPolicy acc test. --- .../aws/resource_aws_iam_user_policy.go | 8 +- .../aws/resource_aws_iam_user_policy_test.go | 112 ++++++++++++++++++ 2 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_iam_user_policy_test.go diff --git a/builtin/providers/aws/resource_aws_iam_user_policy.go b/builtin/providers/aws/resource_aws_iam_user_policy.go index 3fb97b29dc6f..96ea6afc0e88 100644 --- a/builtin/providers/aws/resource_aws_iam_user_policy.go +++ b/builtin/providers/aws/resource_aws_iam_user_policy.go @@ -59,7 +59,7 @@ func resourceAwsIamUserPolicyPut(d *schema.ResourceData, meta interface{}) error func resourceAwsIamUserPolicyRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - user, name := resourceAwsIamUserPolicyParseId(d) + user, name := resourceAwsIamUserPolicyParseId(d.Id()) request := &iam.GetUserPolicyInput{ PolicyName: aws.String(name), @@ -89,7 +89,7 @@ func resourceAwsIamUserPolicyRead(d *schema.ResourceData, meta interface{}) erro func resourceAwsIamUserPolicyDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - user, name := resourceAwsIamUserPolicyParseId(d) + user, name := resourceAwsIamUserPolicyParseId(d.Id()) request := &iam.DeleteUserPolicyInput{ PolicyName: aws.String(name), @@ -102,8 +102,8 @@ func resourceAwsIamUserPolicyDelete(d *schema.ResourceData, meta interface{}) er return nil } -func resourceAwsIamUserPolicyParseId(d *schema.ResourceData) (userName, policyName string) { - parts := strings.SplitN(d.Id(), ":", 2) +func resourceAwsIamUserPolicyParseId(id string) (userName, policyName string) { + parts := strings.SplitN(id, ":", 2) userName = parts[0] policyName = parts[1] return diff --git a/builtin/providers/aws/resource_aws_iam_user_policy_test.go b/builtin/providers/aws/resource_aws_iam_user_policy_test.go new file mode 100644 index 000000000000..2a2147e567de --- /dev/null +++ b/builtin/providers/aws/resource_aws_iam_user_policy_test.go @@ -0,0 +1,112 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSIAMUserPolicy(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMUserPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccIAMUserPolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMUserPolicy( + "aws_iam_user.user", + "aws_iam_user_policy.foo", + ), + ), + }, + resource.TestStep{ + Config: testAccIAMUserPolicyConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckIAMUserPolicy( + "aws_iam_user.user", + "aws_iam_user_policy.bar", + ), + ), + }, + }, + }) +} + +func testAccCheckIAMUserPolicyDestroy(s *terraform.State) error { + if len(s.RootModule().Resources) > 0 { + return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources) + } + + return nil +} + +func testAccCheckIAMUserPolicy( + iamUserResource string, + iamUserPolicyResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[iamUserResource] + if !ok { + return fmt.Errorf("Not Found: %s", iamUserResource) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + policy, ok := s.RootModule().Resources[iamUserPolicyResource] + if !ok { + return fmt.Errorf("Not Found: %s", iamUserPolicyResource) + } + + iamconn := testAccProvider.Meta().(*AWSClient).iamconn + username, name := resourceAwsIamUserPolicyParseId(policy.Primary.ID) + _, err := iamconn.GetUserPolicy(&iam.GetUserPolicyInput{ + UserName: aws.String(username), + PolicyName: aws.String(name), + }) + + if err != nil { + return err + } + + return nil + } +} + +const testAccIAMUserPolicyConfig = ` +resource "aws_iam_user" "user" { + name = "test_user" + path = "/" +} + +resource "aws_iam_user_policy" "foo" { + name = "foo_policy" + user = "${aws_iam_user.user.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} +` + +const testAccIAMUserPolicyConfigUpdate = ` +resource "aws_iam_user" "user" { + name = "test_user" + path = "/" +} + +resource "aws_iam_user_policy" "foo" { + name = "foo_policy" + user = "${aws_iam_user.user.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} + +resource "aws_iam_user_policy" "bar" { + name = "bar_policy" + user = "${aws_iam_user.user.name}" + policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}" +} +`