diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 0ab1a5889b32..2573b8edb04a 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -153,6 +153,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "aws_alb": resourceAwsAlb(), + "aws_alb_target_group": resourceAwsAlbTargetGroup(), "aws_ami": resourceAwsAmi(), "aws_ami_copy": resourceAwsAmiCopy(), "aws_ami_from_instance": resourceAwsAmiFromInstance(), diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go new file mode 100644 index 000000000000..adb2a9f4f17d --- /dev/null +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -0,0 +1,454 @@ +package aws + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsAlbTargetGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAlbTargetGroupCreate, + Read: resourceAwsAlbTargetGroupRead, + Update: resourceAwsAlbTargetGroupUpdate, + Delete: resourceAwsAlbTargetGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validateAwsAlbTargetGroupPort, + }, + + "protocol": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsAlbTargetGroupProtocol, + }, + + "vpc_id": { + Type: schema.TypeString, + Required: true, + }, + + "deregistration_delay": { + Type: schema.TypeInt, + Optional: true, + Default: 300, + ValidateFunc: validateAwsAlbTargetGroupDeregistrationDelay, + }, + + "stickiness": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsAlbTargetGroupStickinessType, + }, + "cookie_duration": { + Type: schema.TypeInt, + Optional: true, + Default: 86400, + ValidateFunc: validateAwsAlbTargetGroupStickinessCookieDuration, + }, + }, + }, + }, + + "health_check": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interval": { + Type: schema.TypeInt, + Optional: true, + Default: 30, + }, + + "path": { + Type: schema.TypeString, + Optional: true, + Default: "/", + ValidateFunc: validateAwsAlbTargetGroupHealthCheckPath, + }, + + "port": { + Type: schema.TypeString, + Optional: true, + Default: "traffic-port", + ValidateFunc: validateAwsAlbTargetGroupHealthCheckPort, + }, + + "protocol": { + Type: schema.TypeString, + Optional: true, + Default: "HTTP", + StateFunc: func(v interface{}) string { + return strings.ToUpper(v.(string)) + }, + ValidateFunc: validateAwsAlbTargetGroupHealthCheckProtocol, + }, + + "timeout": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + ValidateFunc: validateAwsAlbTargetGroupHealthCheckTimeout, + }, + + "healthy_threshold": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold, + }, + + "matcher": { + Type: schema.TypeString, + Optional: true, + Default: "200", + ValidateFunc: validateAwsAlbTargetGroupHealthCheckMatcher, + }, + + "unhealthy_threshold": { + Type: schema.TypeInt, + Optional: true, + Default: 2, + ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold, + }, + }, + }, + }, + }, + } +} + +func resourceAwsAlbTargetGroupCreate(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbv2conn + + params := &elbv2.CreateTargetGroupInput{ + Name: aws.String(d.Get("name").(string)), + Port: aws.Int64(int64(d.Get("port").(int))), + Protocol: aws.String(d.Get("protocol").(string)), + VpcId: aws.String(d.Get("vpc_id").(string)), + } + + if healthChecks := d.Get("health_check").([]interface{}); len(healthChecks) == 1 { + healthCheck := healthChecks[0].(map[string]interface{}) + + params.HealthCheckIntervalSeconds = aws.Int64(int64(healthCheck["interval"].(int))) + params.HealthCheckPath = aws.String(healthCheck["path"].(string)) + params.HealthCheckPort = aws.String(healthCheck["port"].(string)) + params.HealthCheckProtocol = aws.String(healthCheck["protocol"].(string)) + params.HealthCheckTimeoutSeconds = aws.Int64(int64(healthCheck["timeout"].(int))) + params.HealthyThresholdCount = aws.Int64(int64(healthCheck["healthy_threshold"].(int))) + params.UnhealthyThresholdCount = aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))) + params.Matcher = &elbv2.Matcher{ + HttpCode: aws.String(healthCheck["matcher"].(string)), + } + } + + resp, err := elbconn.CreateTargetGroup(params) + if err != nil { + return errwrap.Wrapf("Error creating ALB Target Group: {{err}}", err) + } + + if len(resp.TargetGroups) == 0 { + return errors.New("Error creating ALB Target Group: no groups returned in response") + } + + targetGroupArn := resp.TargetGroups[0].TargetGroupArn + d.SetId(*targetGroupArn) + + return resourceAwsAlbTargetGroupUpdate(d, meta) +} + +func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbv2conn + + resp, err := elbconn.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{ + TargetGroupArns: []*string{aws.String(d.Id())}, + }) + if err != nil { + if isTargetGroupNotFound(err) { + fmt.Sprintf("[DEBUG] DescribeTargetGroups - removing %s from state", d.Id()) + d.SetId("") + return nil + } + return errwrap.Wrapf("Error retrieving Target Group: {{err}}", err) + } + + if len(resp.TargetGroups) != 1 { + return fmt.Errorf("Error retrieving Target Group %q", d.Id()) + } + + targetGroup := resp.TargetGroups[0] + + d.Set("name", targetGroup.TargetGroupName) + d.Set("port", targetGroup.Port) + d.Set("protocol", targetGroup.Protocol) + d.Set("vpc_id", targetGroup.VpcId) + + healthCheck := make(map[string]interface{}) + healthCheck["interval"] = *targetGroup.HealthCheckIntervalSeconds + healthCheck["path"] = *targetGroup.HealthCheckPath + healthCheck["port"] = *targetGroup.HealthCheckPort + healthCheck["protocol"] = *targetGroup.HealthCheckProtocol + healthCheck["timeout"] = *targetGroup.HealthCheckTimeoutSeconds + healthCheck["healthy_threshold"] = *targetGroup.HealthyThresholdCount + healthCheck["unhealthy_threshold"] = *targetGroup.UnhealthyThresholdCount + healthCheck["matcher"] = *targetGroup.Matcher.HttpCode + d.Set("health_check", []interface{}{healthCheck}) + + attrResp, err := elbconn.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ + TargetGroupArn: aws.String(d.Id()), + }) + if err != nil { + return errwrap.Wrapf("Error retrieving Target Group Attributes: {{err}}", err) + } + + stickinessMap := map[string]interface{}{} + for _, attr := range attrResp.Attributes { + switch *attr.Key { + case "stickiness.type": + stickinessMap["type"] = *attr.Value + case "stickiness.lb_cookie.duration_seconds": + stickinessMap["cookie_duration"] = *attr.Value + case "deregistration_delay.timeout_seconds": + timeout, err := strconv.Atoi(*attr.Value) + if err != nil { + return fmt.Errorf("Error converting deregistration_delay.timeout_seconds to int: %s", *attr.Value) + } + d.Set("deregistration_delay", timeout) + } + } + d.Set("stickiness", []interface{}{stickinessMap}) + + return nil +} + +func resourceAwsAlbTargetGroupUpdate(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbv2conn + + if d.HasChange("health_check") { + healthChecks := d.Get("health_check").([]interface{}) + + var params *elbv2.ModifyTargetGroupInput + if len(healthChecks) == 1 { + healthCheck := healthChecks[0].(map[string]interface{}) + + params = &elbv2.ModifyTargetGroupInput{ + TargetGroupArn: aws.String(d.Id()), + HealthCheckIntervalSeconds: aws.Int64(int64(healthCheck["interval"].(int))), + HealthCheckPath: aws.String(healthCheck["path"].(string)), + HealthCheckPort: aws.String(healthCheck["port"].(string)), + HealthCheckProtocol: aws.String(healthCheck["protocol"].(string)), + HealthCheckTimeoutSeconds: aws.Int64(int64(healthCheck["timeout"].(int))), + HealthyThresholdCount: aws.Int64(int64(healthCheck["healthy_threshold"].(int))), + UnhealthyThresholdCount: aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))), + Matcher: &elbv2.Matcher{ + HttpCode: aws.String(healthCheck["matcher"].(string)), + }, + } + } else { + params = &elbv2.ModifyTargetGroupInput{ + TargetGroupArn: aws.String(d.Id()), + } + } + + _, err := elbconn.ModifyTargetGroup(params) + if err != nil { + return errwrap.Wrapf("Error modifying Target Group: {{err}}", err) + } + } + + var attrs []*elbv2.TargetGroupAttribute + + if d.HasChange("deregistration_delay") { + attrs = append(attrs, &elbv2.TargetGroupAttribute{ + Key: aws.String("deregistration_delay.timeout_seconds"), + Value: aws.String(fmt.Sprintf("%d", d.Get("deregistration_delay").(int))), + }) + } + + if d.HasChange("stickiness") { + stickinessBlocks := d.Get("stickiness").([]interface{}) + if len(stickinessBlocks) == 1 { + stickiness := stickinessBlocks[0].(map[string]interface{}) + + attrs = append(attrs, + &elbv2.TargetGroupAttribute{ + Key: aws.String("stickiness.enabled"), + Value: aws.String("true"), + }, + &elbv2.TargetGroupAttribute{ + Key: aws.String("stickiness.type"), + Value: aws.String(stickiness["type"].(string)), + }, + &elbv2.TargetGroupAttribute{ + Key: aws.String("stickiness.lb_cookie.duration_seconds"), + Value: aws.String(fmt.Sprintf("%d", stickiness["cookie_duration"].(int))), + }) + } else if len(stickinessBlocks) == 0 { + attrs = append(attrs, &elbv2.TargetGroupAttribute{ + Key: aws.String("stickiness.enabled"), + Value: aws.String("false"), + }) + } + } + + if len(attrs) > 0 { + params := &elbv2.ModifyTargetGroupAttributesInput{ + TargetGroupArn: aws.String(d.Id()), + Attributes: attrs, + } + + _, err := elbconn.ModifyTargetGroupAttributes(params) + if err != nil { + return errwrap.Wrapf("Error modifying Target Group Attributes: {{err}}", err) + } + } + + return resourceAwsAlbTargetGroupRead(d, meta) +} + +func resourceAwsAlbTargetGroupDelete(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbv2conn + + _, err := elbconn.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ + TargetGroupArn: aws.String(d.Id()), + }) + if err != nil { + return errwrap.Wrapf("Error deleting Target Group: {{err}}", err) + } + + return nil +} + +func isTargetGroupNotFound(err error) bool { + elberr, ok := err.(awserr.Error) + return ok && elberr.Code() == "TargetGroupNotFound" +} + +func validateAwsAlbTargetGroupHealthCheckPath(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 1024 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 1024 characters: %q", k, value)) + } + return +} + +func validateAwsAlbTargetGroupHealthCheckPort(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value == "traffic-port" { + return + } + + port, err := strconv.Atoi(value) + if err != nil { + errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port")) + } + + if port < 0 || port > 65536 { + errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port")) + } + + return +} + +func validateAwsAlbTargetGroupHealthCheckHealthyThreshold(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 2 || value > 10 { + errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 10", k)) + } + return +} + +func validateAwsAlbTargetGroupHealthCheckTimeout(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 2 || value > 60 { + errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 60", k)) + } + return +} + +func validateAwsAlbTargetGroupHealthCheckProtocol(v interface{}, k string) (ws []string, errors []error) { + value := strings.ToLower(v.(string)) + if value == "http" || value == "https" { + return + } + + errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS")) + return +} + +func validateAwsAlbTargetGroupHealthCheckMatcher(v interface{}, k string) (ws []string, errors []error) { + //TODO(jen20) implement this + return +} + +func validateAwsAlbTargetGroupPort(v interface{}, k string) (ws []string, errors []error) { + port := v.(int) + if port < 0 || port > 65536 { + errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536)", k)) + } + return +} + +func validateAwsAlbTargetGroupProtocol(v interface{}, k string) (ws []string, errors []error) { + protocol := strings.ToLower(v.(string)) + if protocol == "http" || protocol == "https" { + return + } + + errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS")) + return +} + +func validateAwsAlbTargetGroupDeregistrationDelay(v interface{}, k string) (ws []string, errors []error) { + delay := v.(int) + if delay < 0 || delay > 3600 { + errors = append(errors, fmt.Errorf("%q must be in the range 0-3600 seconds", k)) + } + return +} + +func validateAwsAlbTargetGroupStickinessType(v interface{}, k string) (ws []string, errors []error) { + stickinessType := v.(string) + if stickinessType != "lb_cookie" { + errors = append(errors, fmt.Errorf("%q must have the value %q", k, "lb_cookie")) + } + return +} + +func validateAwsAlbTargetGroupStickinessCookieDuration(v interface{}, k string) (ws []string, errors []error) { + duration := v.(int) + if duration < 1 || duration > 604800 { + errors = append(errors, fmt.Errorf("%q must be a between 1 second and 1 week (1-604800 seconds))", k)) + } + return +} diff --git a/builtin/providers/aws/resource_aws_alb_target_group_test.go b/builtin/providers/aws/resource_aws_alb_target_group_test.go new file mode 100644 index 000000000000..5ab86dd38f73 --- /dev/null +++ b/builtin/providers/aws/resource_aws_alb_target_group_test.go @@ -0,0 +1,242 @@ +package aws + +import ( + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSALBTargetGroup_basic(t *testing.T) { + var conf elbv2.TargetGroup + targetGroupName := fmt.Sprintf("test-target-group-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_alb_target_group.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSALBTargetGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSALBTargetGroupConfig_basic(targetGroupName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSALBTargetGroupExists("aws_alb_target_group.test", &conf), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "name", targetGroupName), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "port", "443"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "protocol", "HTTPS"), + resource.TestCheckResourceAttrSet("aws_alb_target_group.test", "vpc_id"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "deregistration_delay", "200"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.#", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.0.type", "lb_cookie"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.0.cookie_duration", "10000"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.#", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.path", "/health"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.interval", "60"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.port", "8081"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.protocol", "HTTP"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.timeout", "3"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.healthy_threshold", "3"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.unhealthy_threshold", "3"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.matcher", "200-299"), + ), + }, + }, + }) +} + +func TestAccAWSALBTargetGroup_updateHealthCheck(t *testing.T) { + var conf elbv2.TargetGroup + targetGroupName := fmt.Sprintf("test-target-group-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_alb_target_group.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSALBTargetGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSALBTargetGroupConfig_basic(targetGroupName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSALBTargetGroupExists("aws_alb_target_group.test", &conf), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "name", targetGroupName), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "port", "443"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "protocol", "HTTPS"), + resource.TestCheckResourceAttrSet("aws_alb_target_group.test", "vpc_id"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "deregistration_delay", "200"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.#", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.0.type", "lb_cookie"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.0.cookie_duration", "10000"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.#", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.path", "/health"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.interval", "60"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.port", "8081"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.protocol", "HTTP"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.timeout", "3"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.healthy_threshold", "3"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.unhealthy_threshold", "3"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.matcher", "200-299"), + ), + }, + { + Config: testAccAWSALBTargetGroupConfig_updateHealthCheck(targetGroupName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSALBTargetGroupExists("aws_alb_target_group.test", &conf), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "name", targetGroupName), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "port", "443"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "protocol", "HTTPS"), + resource.TestCheckResourceAttrSet("aws_alb_target_group.test", "vpc_id"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "deregistration_delay", "200"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.#", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.0.type", "lb_cookie"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "stickiness.0.cookie_duration", "10000"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.#", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.path", "/health2"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.interval", "30"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.port", "8082"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.protocol", "HTTPS"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.timeout", "4"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.healthy_threshold", "4"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.unhealthy_threshold", "4"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.matcher", "200"), + ), + }, + }, + }) +} + +func testAccCheckAWSALBTargetGroupExists(n string, res *elbv2.TargetGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("No Target Group ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).elbv2conn + + describe, err := conn.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{ + TargetGroupArns: []*string{aws.String(rs.Primary.ID)}, + }) + + if err != nil { + return err + } + + if len(describe.TargetGroups) != 1 || + *describe.TargetGroups[0].TargetGroupArn != rs.Primary.ID { + return errors.New("Target Group not found") + } + + *res = *describe.TargetGroups[0] + return nil + } +} + +func testAccCheckAWSALBTargetGroupDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).elbv2conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_alb_target_group" { + continue + } + + describe, err := conn.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{ + TargetGroupArns: []*string{aws.String(rs.Primary.ID)}, + }) + + if err == nil { + if len(describe.TargetGroups) != 0 && + *describe.TargetGroups[0].TargetGroupArn == rs.Primary.ID { + return fmt.Errorf("Target Group %q still exists", rs.Primary.ID) + } + } + + // Verify the error + if isTargetGroupNotFound(err) { + return nil + } else { + return errwrap.Wrapf("Unexpected error checking ALB destroyed: {{err}}", err) + } + } + + return nil +} + +func testAccAWSALBTargetGroupConfig_basic(targetGroupName string) string { + return fmt.Sprintf(`resource "aws_alb_target_group" "test" { + name = "%s" + port = 443 + protocol = "HTTPS" + vpc_id = "${aws_vpc.test.id}" + + deregistration_delay = 200 + + stickiness { + type = "lb_cookie" + cookie_duration = 10000 + } + + health_check { + path = "/health" + interval = 60 + port = 8081 + protocol = "HTTP" + timeout = 3 + healthy_threshold = 3 + unhealthy_threshold = 3 + matcher = "200-299" + } +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags { + TestName = "TestAccAWSALBTargetGroup_basic" + } +}`, targetGroupName) +} + +func testAccAWSALBTargetGroupConfig_updateHealthCheck(targetGroupName string) string { + return fmt.Sprintf(`resource "aws_alb_target_group" "test" { + name = "%s" + port = 443 + protocol = "HTTPS" + vpc_id = "${aws_vpc.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_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags { + TestName = "TestAccAWSALBTargetGroup_basic" + } +}`, targetGroupName) +} diff --git a/website/source/docs/providers/aws/r/alb_target_group.html.markdown b/website/source/docs/providers/aws/r/alb_target_group.html.markdown new file mode 100644 index 000000000000..e13a033b54be --- /dev/null +++ b/website/source/docs/providers/aws/r/alb_target_group.html.markdown @@ -0,0 +1,60 @@ +--- +layout: "aws" +page_title: "AWS: aws_alb_target_group" +sidebar_current: "docs-aws-resource-alb-target-group" +description: |- + Provides a Target Group resource for use with Application Load + Balancers. +--- + +# aws\_alb\_target\_group + +Provides a Target Group resource for use with Application Load Balancer +resources. + +## Example Usage + +``` +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the target group. +* `port` - (Required) The port on which targets receive traffic, unless overriden when registering a specific target. +* `protocol` - (Required) The protocol to use for routing traffic to the targets. +* `vpc_id` - (Required) The identifier of the VPC in which to create the target group. +* `deregistration_delay` - (Optional) The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused. The range is 0-3600 seconds. The default value is 300 seconds. +* `stickiness` - (Optional) A Stickiness block. Stickiness blocks are documented below. +* `health_check` - (Optional) A Health Check block. Health Check blocks are documented below. + +Stickiness Blocks (`stickiness`) support the following: + +* `type` - (Required) The type of sticky sessions. The only current possible value is `lb_cookie`. +* `cookie_duration` - (Optional) The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the load balancer-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds). The default value is 1 day (86400 seconds). + +Health Check Blocks (`health_check`) support the following: + +* `interval` - (Optional) The approximate amount of time, in seconds, between health checks of an individual target. Minimum value 5 seconds, Maximum value 300 seconds. Default 30 seconds. +* `path` - (Optional) The destination for the health check request. Default `/`. +* `port` - (Optional) The port to use to connect with the target. Valid values are either ports 1-65536, or `traffic-port`. Defaults to `traffic-port`. +* `protocol` - (Optional) The protocol to use to connect with the target. Defaults to `HTTP`. +* `timeout` - (Optional) The amount of time, in seconds, during which no response means a failed health check. Defaults to 5 seconds. +* `healthy_threshold` - (Optional) The number of consecutive health checks successes required before considering an unhealthy target healthy. Defaults to 5. +* `unhealthy_threshold` - (Optional) The number of consecutive health check failures required before considering the target unhealthy. Defaults to 2. +* `matcher` (Optional) The HTTP codes to use when checking for a successful response from a target. Defaults to `200`. You can specify multiple values (for example, "200,202") or a range of values (for example, "200-299"). + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `id` - The ARN of the target group. + +## Import + +Target Groups can be imported using their ARN, e.g. + +``` +$ terraform import aws_alb_target_group.app_front_end arn:aws:elasticloadbalancing:us-west-2:187416307283:targetgroup/app-front-end/20cfe21448b66314 +``` diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 4cef8e3cb3f7..507ac556eb5d 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -214,6 +214,10 @@ aws_alb +