diff --git a/builtin/providers/aws/resource_aws_redshift_cluster.go b/builtin/providers/aws/resource_aws_redshift_cluster.go index 30ebf31595ff..1ab69d596410 100644 --- a/builtin/providers/aws/resource_aws_redshift_cluster.go +++ b/builtin/providers/aws/resource_aws_redshift_cluster.go @@ -195,6 +195,14 @@ func resourceAwsRedshiftCluster() *schema.Resource { Optional: true, Computed: true, }, + + "iam_roles": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, }, } } @@ -259,6 +267,10 @@ func resourceAwsRedshiftClusterCreate(d *schema.ResourceData, meta interface{}) createOpts.ElasticIp = aws.String(v.(string)) } + if v, ok := d.GetOk("iam_roles"); ok { + createOpts.IamRoles = expandStringList(v.(*schema.Set).List()) + } + log.Printf("[DEBUG] Redshift Cluster create options: %s", createOpts) resp, err := conn.CreateCluster(createOpts) if err != nil { @@ -355,6 +367,14 @@ func resourceAwsRedshiftClusterRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("[DEBUG] Error saving Cluster Security Group Names to state for Redshift Cluster (%s): %s", d.Id(), err) } + var iamRoles []string + for _, i := range rsc.IamRoles { + iamRoles = append(iamRoles, *i.IamRoleArn) + } + if err := d.Set("iam_roles", iamRoles); err != nil { + return fmt.Errorf("[DEBUG] Error saving IAM Roles to state for Redshift Cluster (%s): %s", d.Id(), err) + } + d.Set("cluster_public_key", rsc.ClusterPublicKey) d.Set("cluster_revision_number", rsc.ClusterRevisionNumber) @@ -444,6 +464,38 @@ func resourceAwsRedshiftClusterUpdate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("[WARN] Error Modifying Redshift Cluster (%s): %s", d.Id(), err) } + if d.HasChange("iam_roles") { + o, n := d.GetChange("iam_roles") + if o == nil { + o = new(schema.Set) + } + if n == nil { + n = new(schema.Set) + } + + os := o.(*schema.Set) + ns := n.(*schema.Set) + + removeIams := os.Difference(ns).List() + addIams := ns.Difference(os).List() + + log.Printf("[INFO] Building Redshift Modify Cluster IAM Role Options") + req := &redshift.ModifyClusterIamRolesInput{ + ClusterIdentifier: aws.String(d.Id()), + AddIamRoles: expandStringList(addIams), + RemoveIamRoles: expandStringList(removeIams), + } + + log.Printf("[INFO] Modifying Redshift Cluster IAM Roles: %s", d.Id()) + log.Printf("[DEBUG] Redshift Cluster Modify IAM Role options: %s", req) + _, err := conn.ModifyClusterIamRoles(req) + if err != nil { + return fmt.Errorf("[WARN] Error modifying Redshift Cluster IAM Roles (%s): %s", d.Id(), err) + } + + d.SetPartial("iam_roles") + } + return resourceAwsRedshiftClusterRead(d, meta) } diff --git a/builtin/providers/aws/resource_aws_redshift_cluster_test.go b/builtin/providers/aws/resource_aws_redshift_cluster_test.go index 1c3c1ef8547a..82500174a9ad 100644 --- a/builtin/providers/aws/resource_aws_redshift_cluster_test.go +++ b/builtin/providers/aws/resource_aws_redshift_cluster_test.go @@ -38,6 +38,39 @@ func TestAccAWSRedshiftCluster_basic(t *testing.T) { }) } +func TestAccAWSRedshiftCluster_iamRoles(t *testing.T) { + var v redshift.Cluster + + ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + preConfig := fmt.Sprintf(testAccAWSRedshiftClusterConfig_iamRoles, ri, ri, ri) + postConfig := fmt.Sprintf(testAccAWSRedshiftClusterConfig_updateIamRoles, ri, ri, ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSRedshiftClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSRedshiftClusterExists("aws_redshift_cluster.default", &v), + resource.TestCheckResourceAttr( + "aws_redshift_cluster.default", "iam_roles.#", "2"), + ), + }, + + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSRedshiftClusterExists("aws_redshift_cluster.default", &v), + resource.TestCheckResourceAttr( + "aws_redshift_cluster.default", "iam_roles.#", "1"), + ), + }, + }, + }) +} + func TestAccAWSRedshiftCluster_publiclyAccessible(t *testing.T) { var v redshift.Cluster @@ -339,6 +372,64 @@ resource "aws_redshift_cluster" "default" { allow_version_upgrade = false }` +var testAccAWSRedshiftClusterConfig_iamRoles = ` +provider "aws" { + region = "us-west-2" +} + +resource "aws_iam_role" "ec2-role" { + name = "test-role-ec2-%d" + path = "/" + assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" +} + +resource "aws_iam_role" "lambda-role" { + name = "test-role-lambda-%d" + path = "/" + assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"lambda.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" +} + +resource "aws_redshift_cluster" "default" { + cluster_identifier = "tf-redshift-cluster-%d" + availability_zone = "us-west-2a" + database_name = "mydb" + master_username = "foo_test" + master_password = "Mustbe8characters" + node_type = "dc1.large" + automated_snapshot_retention_period = 0 + allow_version_upgrade = false + iam_roles = ["${aws_iam_role.ec2-role.arn}", "${aws_iam_role.lambda-role.arn}"] +}` + +var testAccAWSRedshiftClusterConfig_updateIamRoles = ` +provider "aws" { + region = "us-west-2" +} + +resource "aws_iam_role" "ec2-role" { + name = "test-role-ec2-%d" + path = "/" + assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" +} + +resource "aws_iam_role" "lambda-role" { + name = "test-role-lambda-%d" + path = "/" + assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"lambda.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}" +} + +resource "aws_redshift_cluster" "default" { + cluster_identifier = "tf-redshift-cluster-%d" + availability_zone = "us-west-2a" + database_name = "mydb" + master_username = "foo_test" + master_password = "Mustbe8characters" + node_type = "dc1.large" + automated_snapshot_retention_period = 0 + allow_version_upgrade = false + iam_roles = ["${aws_iam_role.ec2-role.arn}"] +}` + var testAccAWSRedshiftClusterConfig_notPubliclyAccessible = ` provider "aws" { region = "us-west-2" diff --git a/website/source/docs/providers/aws/r/redshift_cluster.html.markdown b/website/source/docs/providers/aws/r/redshift_cluster.html.markdown index e99fc3513e06..9bb382da2a89 100644 --- a/website/source/docs/providers/aws/r/redshift_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/redshift_cluster.html.markdown @@ -55,7 +55,7 @@ string. * `kms_key_id` - (Optional) The KMS key ID for the cluster. * `elastic_ip` - (Optional) The Elastic IP (EIP) address for the cluster. * `skip_final_snapshot` - (Optional) Determines whether a final snapshot of the cluster is created before Amazon Redshift deletes the cluster. If true , a final cluster snapshot is not created. If false , a final cluster snapshot is created before the cluster is deleted. Default is true. -* `final_snapshot_identifier` - (Optional) The identifier of the final snapshot that is to be created immediately before deleting the cluster. If this parameter is provided, `skip_final_snapshot` must be false. +* `final_snapshot_identifier` - (Optional) The identifier of the final snapshot that is to be created immediately before deleting the cluster. If this parameter is provided, `skip_final_snapshot` must be false. * `iam_roles` - (Optional) A list of IAM Role ARNs to associate with the cluster. A Maximum of 10 can be associated to the cluster at any time. ## Attributes Reference @@ -79,4 +79,3 @@ The following attributes are exported: * `cluster_subnet_group_name` - The name of a cluster subnet group to be associated with this cluster * `cluster_public_key` - The public key for the cluster * `cluster_revision_number` - The specific revision number of the database in the cluster -