diff --git a/builtin/providers/aws/resource_aws_alb.go b/builtin/providers/aws/resource_aws_alb.go index 1a577e4b0..ed756e939 100644 --- a/builtin/providers/aws/resource_aws_alb.go +++ b/builtin/providers/aws/resource_aws_alb.go @@ -108,6 +108,12 @@ func resourceAwsAlb() *schema.Resource { Default: 60, }, + "ip_address_type": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "vpc_id": { Type: schema.TypeString, Computed: true, @@ -158,6 +164,10 @@ func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error { elbOpts.Subnets = expandStringList(v.(*schema.Set).List()) } + if v, ok := d.GetOk("ip_address_type"); ok { + elbOpts.IpAddressType = aws.String(v.(string)) + } + log.Printf("[DEBUG] ALB create configuration: %#v", elbOpts) resp, err := elbconn.CreateLoadBalancer(elbOpts) @@ -174,7 +184,7 @@ func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] ALB ID: %s", d.Id()) stateConf := &resource.StateChangeConf{ - Pending: []string{"active", "provisioning", "failed"}, + Pending: []string{"provisioning", "failed"}, Target: []string{"active"}, Refresh: func() (interface{}, string, error) { describeResp, err := elbconn.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{ @@ -193,7 +203,7 @@ func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error { return describeResp, *dLb.State.Code, nil }, - Timeout: 5 * time.Minute, + Timeout: 10 * time.Minute, MinTimeout: 3 * time.Second, } _, err = stateConf.WaitForState() @@ -325,6 +335,48 @@ func resourceAwsAlbUpdate(d *schema.ResourceData, meta interface{}) error { } } + if d.HasChange("ip_address_type") { + + params := &elbv2.SetIpAddressTypeInput{ + LoadBalancerArn: aws.String(d.Id()), + IpAddressType: aws.String(d.Get("ip_address_type").(string)), + } + + _, err := elbconn.SetIpAddressType(params) + if err != nil { + return fmt.Errorf("Failure Setting ALB IP Address Type: %s", err) + } + + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"active", "provisioning", "failed"}, + Target: []string{"active"}, + Refresh: func() (interface{}, string, error) { + describeResp, err := elbconn.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{ + LoadBalancerArns: []*string{aws.String(d.Id())}, + }) + if err != nil { + return nil, "", err + } + + if len(describeResp.LoadBalancers) != 1 { + return nil, "", fmt.Errorf("No load balancers returned for %s", d.Id()) + } + dLb := describeResp.LoadBalancers[0] + + log.Printf("[INFO] ALB state: %s", *dLb.State.Code) + + return describeResp, *dLb.State.Code, nil + }, + Timeout: 10 * time.Minute, + MinTimeout: 3 * time.Second, + } + _, err := stateConf.WaitForState() + if err != nil { + return err + } + return resourceAwsAlbRead(d, meta) } @@ -381,6 +433,7 @@ func flattenAwsAlbResource(d *schema.ResourceData, meta interface{}, alb *elbv2. d.Set("vpc_id", alb.VpcId) d.Set("zone_id", alb.CanonicalHostedZoneId) d.Set("dns_name", alb.DNSName) + d.Set("ip_address_type", alb.IpAddressType) respTags, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{ ResourceArns: []*string{alb.LoadBalancerArn}, diff --git a/builtin/providers/aws/resource_aws_alb_test.go b/builtin/providers/aws/resource_aws_alb_test.go index de127dfc4..fbebb390d 100644 --- a/builtin/providers/aws/resource_aws_alb_test.go +++ b/builtin/providers/aws/resource_aws_alb_test.go @@ -67,6 +67,7 @@ func TestAccAWSALB_basic(t *testing.T) { resource.TestCheckResourceAttr("aws_alb.alb_test", "tags.TestName", "TestAccAWSALB_basic"), resource.TestCheckResourceAttr("aws_alb.alb_test", "enable_deletion_protection", "false"), resource.TestCheckResourceAttr("aws_alb.alb_test", "idle_timeout", "30"), + resource.TestCheckResourceAttr("aws_alb.alb_test", "ip_address_type", "ipv4"), resource.TestCheckResourceAttrSet("aws_alb.alb_test", "vpc_id"), resource.TestCheckResourceAttrSet("aws_alb.alb_test", "zone_id"), resource.TestCheckResourceAttrSet("aws_alb.alb_test", "dns_name"), @@ -208,6 +209,34 @@ func TestAccAWSALB_updatedSubnets(t *testing.T) { }) } +func TestAccAWSALB_updatedIpAddressType(t *testing.T) { + var pre, post elbv2.LoadBalancer + albName := fmt.Sprintf("testaccawsalb-basic-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_alb.alb_test", + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSALBDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSALBConfigWithIpAddressType(albName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSALBExists("aws_alb.alb_test", &pre), + resource.TestCheckResourceAttr("aws_alb.alb_test", "ip_address_type", "ipv4"), + ), + }, + { + Config: testAccAWSALBConfigWithIpAddressTypeUpdated(albName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSALBExists("aws_alb.alb_test", &post), + resource.TestCheckResourceAttr("aws_alb.alb_test", "ip_address_type", "dualstack"), + ), + }, + }, + }) +} + // TestAccAWSALB_noSecurityGroup regression tests the issue in #8264, // where if an ALB is created without a security group, a default one // is assigned. @@ -388,6 +417,228 @@ func testAccCheckAWSALBDestroy(s *terraform.State) error { return nil } +func testAccAWSALBConfigWithIpAddressTypeUpdated(albName string) string { + return fmt.Sprintf(`resource "aws_alb" "alb_test" { + name = "%s" + security_groups = ["${aws_security_group.alb_test.id}"] + subnets = ["${aws_subnet.alb_test_1.id}", "${aws_subnet.alb_test_2.id}"] + + ip_address_type = "dualstack" + + idle_timeout = 30 + enable_deletion_protection = false + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_alb_listener" "test" { + load_balancer_arn = "${aws_alb.alb_test.id}" + protocol = "HTTP" + port = "80" + + default_action { + target_group_arn = "${aws_alb_target_group.test.id}" + type = "forward" + } +} + +resource "aws_alb_target_group" "test" { + name = "%s" + port = 80 + protocol = "HTTP" + vpc_id = "${aws_vpc.alb_test.id}" + + deregistration_delay = 200 + + stickiness { + type = "lb_cookie" + cookie_duration = 10000 + } + + health_check { + path = "/health2" + interval = 30 + port = 8082 + protocol = "HTTPS" + timeout = 4 + healthy_threshold = 4 + unhealthy_threshold = 4 + matcher = "200" + } +} + +resource "aws_egress_only_internet_gateway" "igw" { + vpc_id = "${aws_vpc.alb_test.id}" +} + +resource "aws_vpc" "alb_test" { + cidr_block = "10.0.0.0/16" + assign_generated_ipv6_cidr_block = true + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_internet_gateway" "foo" { + vpc_id = "${aws_vpc.alb_test.id}" +} + +resource "aws_subnet" "alb_test_1" { + vpc_id = "${aws_vpc.alb_test.id}" + cidr_block = "10.0.1.0/24" + map_public_ip_on_launch = true + availability_zone = "us-west-2a" + ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 1)}" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_subnet" "alb_test_2" { + vpc_id = "${aws_vpc.alb_test.id}" + cidr_block = "10.0.2.0/24" + map_public_ip_on_launch = true + availability_zone = "us-west-2b" + ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 2)}" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_security_group" "alb_test" { + name = "allow_all_alb_test" + description = "Used for ALB Testing" + vpc_id = "${aws_vpc.alb_test.id}" + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags { + TestName = "TestAccAWSALB_basic" + } +}`, albName, albName) +} + +func testAccAWSALBConfigWithIpAddressType(albName string) string { + return fmt.Sprintf(`resource "aws_alb" "alb_test" { + name = "%s" + security_groups = ["${aws_security_group.alb_test.id}"] + subnets = ["${aws_subnet.alb_test_1.id}", "${aws_subnet.alb_test_2.id}"] + + ip_address_type = "ipv4" + + idle_timeout = 30 + enable_deletion_protection = false + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_alb_listener" "test" { + load_balancer_arn = "${aws_alb.alb_test.id}" + protocol = "HTTP" + port = "80" + + default_action { + target_group_arn = "${aws_alb_target_group.test.id}" + type = "forward" + } +} + +resource "aws_alb_target_group" "test" { + name = "%s" + port = 80 + protocol = "HTTP" + vpc_id = "${aws_vpc.alb_test.id}" + + deregistration_delay = 200 + + stickiness { + type = "lb_cookie" + cookie_duration = 10000 + } + + health_check { + path = "/health2" + interval = 30 + port = 8082 + protocol = "HTTPS" + timeout = 4 + healthy_threshold = 4 + unhealthy_threshold = 4 + matcher = "200" + } +} + +resource "aws_egress_only_internet_gateway" "igw" { + vpc_id = "${aws_vpc.alb_test.id}" +} + +resource "aws_vpc" "alb_test" { + cidr_block = "10.0.0.0/16" + assign_generated_ipv6_cidr_block = true + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_internet_gateway" "foo" { + vpc_id = "${aws_vpc.alb_test.id}" +} + +resource "aws_subnet" "alb_test_1" { + vpc_id = "${aws_vpc.alb_test.id}" + cidr_block = "10.0.1.0/24" + map_public_ip_on_launch = true + availability_zone = "us-west-2a" + ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 1)}" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_subnet" "alb_test_2" { + vpc_id = "${aws_vpc.alb_test.id}" + cidr_block = "10.0.2.0/24" + map_public_ip_on_launch = true + availability_zone = "us-west-2b" + ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 2)}" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_security_group" "alb_test" { + name = "allow_all_alb_test" + description = "Used for ALB Testing" + vpc_id = "${aws_vpc.alb_test.id}" + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags { + TestName = "TestAccAWSALB_basic" + } +}`, albName, albName) +} + func testAccAWSALBConfig_basic(albName string) string { return fmt.Sprintf(`resource "aws_alb" "alb_test" { name = "%s" diff --git a/website/source/docs/providers/aws/r/alb.html.markdown b/website/source/docs/providers/aws/r/alb.html.markdown index d91706eca..62a446c8f 100644 --- a/website/source/docs/providers/aws/r/alb.html.markdown +++ b/website/source/docs/providers/aws/r/alb.html.markdown @@ -52,8 +52,11 @@ Terraform will autogenerate a name beginning with `tf-lb`. * `idle_timeout` - (Optional) The time in seconds that the connection is allowed to be idle. Default: 60. * `enable_deletion_protection` - (Optional) If true, deletion of the load balancer will be disabled via the AWS API. This will prevent Terraform from deleting the load balancer. Defaults to `false`. +* `ip_address_type` - (Optional) The type of IP addresses used by the subnets for your load balancer. The possible values are `ipv4` and `dualstack` * `tags` - (Optional) A mapping of tags to assign to the resource. +~> **NOTE::** Please note that internal ALBs can only use `ipv4` as the ip_address_type. You can only change to `dualstack` ip_address_type if the selected subnets are IPv6 enabled. + Access Logs (`access_logs`) support the following: * `bucket` - (Required) The S3 bucket name to store the logs in.