diff --git a/builtin/providers/aws/data_source_aws_alb.go b/builtin/providers/aws/data_source_aws_alb.go new file mode 100644 index 000000000..d314e0ed7 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_alb.go @@ -0,0 +1,127 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsAlb() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsAlbRead, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "arn_suffix": { + Type: schema.TypeString, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "internal": { + Type: schema.TypeBool, + Computed: true, + }, + + "security_groups": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "subnets": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Set: schema.HashString, + }, + + "access_logs": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Computed: true, + }, + "prefix": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + + "enable_deletion_protection": { + Type: schema.TypeBool, + Computed: true, + }, + + "idle_timeout": { + Type: schema.TypeInt, + Computed: true, + }, + + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, + + "zone_id": { + Type: schema.TypeString, + Computed: true, + }, + + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchemaComputed(), + }, + } +} + +func dataSourceAwsAlbRead(d *schema.ResourceData, meta interface{}) error { + elbconn := meta.(*AWSClient).elbv2conn + albArn := d.Get("arn").(string) + albName := d.Get("name").(string) + + describeAlbOpts := &elbv2.DescribeLoadBalancersInput{} + switch { + case albArn != "": + describeAlbOpts.LoadBalancerArns = []*string{aws.String(albArn)} + case albName != "": + describeAlbOpts.Names = []*string{aws.String(albName)} + } + + describeResp, err := elbconn.DescribeLoadBalancers(describeAlbOpts) + if err != nil { + return errwrap.Wrapf("Error retrieving ALB: {{err}}", err) + } + if len(describeResp.LoadBalancers) != 1 { + return fmt.Errorf("Search returned %d results, please revise so only one is returned", len(describeResp.LoadBalancers)) + } + d.SetId(*describeResp.LoadBalancers[0].LoadBalancerArn) + + return flattenAwsAlbResource(d, meta, describeResp.LoadBalancers[0]) +} diff --git a/builtin/providers/aws/data_source_aws_alb_test.go b/builtin/providers/aws/data_source_aws_alb_test.go new file mode 100644 index 000000000..2382b6ed2 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_alb_test.go @@ -0,0 +1,124 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceAWSALB_basic(t *testing.T) { + albName := fmt.Sprintf("testaccawsalb-basic-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAWSALBConfigBasic(albName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "name", albName), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "internal", "false"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "subnets.#", "2"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "security_groups.#", "1"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "tags.%", "1"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "tags.TestName", "TestAccAWSALB_basic"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "enable_deletion_protection", "false"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_arn", "idle_timeout", "30"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_arn", "vpc_id"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_arn", "zone_id"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_arn", "dns_name"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_arn", "arn"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "name", albName), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "internal", "false"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "subnets.#", "2"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "security_groups.#", "1"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "tags.%", "1"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "tags.TestName", "TestAccAWSALB_basic"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "enable_deletion_protection", "false"), + resource.TestCheckResourceAttr("data.aws_alb.alb_test_with_name", "idle_timeout", "30"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_name", "vpc_id"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_name", "zone_id"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_name", "dns_name"), + resource.TestCheckResourceAttrSet("data.aws_alb.alb_test_with_name", "arn"), + ), + }, + }, + }) +} + +func testAccDataSourceAWSALBConfigBasic(albName string) string { + return fmt.Sprintf(`resource "aws_alb" "alb_test" { + name = "%s" + internal = false + security_groups = ["${aws_security_group.alb_test.id}"] + subnets = ["${aws_subnet.alb_test.*.id}"] + + idle_timeout = 30 + enable_deletion_protection = false + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +variable "subnets" { + default = ["10.0.1.0/24", "10.0.2.0/24"] + type = "list" +} + +data "aws_availability_zones" "available" {} + +resource "aws_vpc" "alb_test" { + cidr_block = "10.0.0.0/16" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_subnet" "alb_test" { + count = 2 + vpc_id = "${aws_vpc.alb_test.id}" + cidr_block = "${element(var.subnets, count.index)}" + map_public_ip_on_launch = true + availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}" + + 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"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +data "aws_alb" "alb_test_with_arn" { + arn = "${aws_alb.alb_test.arn}" +} + +data "aws_alb" "alb_test_with_name" { + name = "${aws_alb.alb_test.name}" +}`, albName) +} diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 5d69cfdb4..468260d2d 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -144,6 +144,7 @@ func Provider() terraform.ResourceProvider { DataSourcesMap: map[string]*schema.Resource{ "aws_acm_certificate": dataSourceAwsAcmCertificate(), + "aws_alb": dataSourceAwsAlb(), "aws_alb_listener": dataSourceAwsAlbListener(), "aws_ami": dataSourceAwsAmi(), "aws_availability_zone": dataSourceAwsAvailabilityZone(), diff --git a/builtin/providers/aws/resource_aws_alb.go b/builtin/providers/aws/resource_aws_alb.go index 7faf119cb..d8f8fe98b 100644 --- a/builtin/providers/aws/resource_aws_alb.go +++ b/builtin/providers/aws/resource_aws_alb.go @@ -198,69 +198,7 @@ func resourceAwsAlbRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Unable to find ALB: %#v", describeResp.LoadBalancers) } - alb := describeResp.LoadBalancers[0] - - d.Set("arn", alb.LoadBalancerArn) - d.Set("arn_suffix", albSuffixFromARN(alb.LoadBalancerArn)) - d.Set("name", alb.LoadBalancerName) - d.Set("internal", (alb.Scheme != nil && *alb.Scheme == "internal")) - d.Set("security_groups", flattenStringList(alb.SecurityGroups)) - d.Set("subnets", flattenSubnetsFromAvailabilityZones(alb.AvailabilityZones)) - d.Set("vpc_id", alb.VpcId) - d.Set("zone_id", alb.CanonicalHostedZoneId) - d.Set("dns_name", alb.DNSName) - - respTags, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{ - ResourceArns: []*string{alb.LoadBalancerArn}, - }) - if err != nil { - return errwrap.Wrapf("Error retrieving ALB Tags: {{err}}", err) - } - - var et []*elbv2.Tag - if len(respTags.TagDescriptions) > 0 { - et = respTags.TagDescriptions[0].Tags - } - d.Set("tags", tagsToMapELBv2(et)) - - attributesResp, err := elbconn.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: aws.String(d.Id()), - }) - if err != nil { - return errwrap.Wrapf("Error retrieving ALB Attributes: {{err}}", err) - } - - accessLogMap := map[string]interface{}{} - for _, attr := range attributesResp.Attributes { - switch *attr.Key { - case "access_logs.s3.enabled": - accessLogMap["enabled"] = *attr.Value - case "access_logs.s3.bucket": - accessLogMap["bucket"] = *attr.Value - case "access_logs.s3.prefix": - accessLogMap["prefix"] = *attr.Value - case "idle_timeout.timeout_seconds": - timeout, err := strconv.Atoi(*attr.Value) - if err != nil { - return errwrap.Wrapf("Error parsing ALB timeout: {{err}}", err) - } - log.Printf("[DEBUG] Setting ALB Timeout Seconds: %d", timeout) - d.Set("idle_timeout", timeout) - case "deletion_protection.enabled": - protectionEnabled := (*attr.Value) == "true" - log.Printf("[DEBUG] Setting ALB Deletion Protection Enabled: %t", protectionEnabled) - d.Set("enable_deletion_protection", protectionEnabled) - } - } - - log.Printf("[DEBUG] Setting ALB Access Logs: %#v", accessLogMap) - if accessLogMap["bucket"] != "" || accessLogMap["prefix"] != "" { - d.Set("access_logs", []interface{}{accessLogMap}) - } else { - d.Set("access_logs", []interface{}{}) - } - - return nil + return flattenAwsAlbResource(d, meta, describeResp.LoadBalancers[0]) } func resourceAwsAlbUpdate(d *schema.ResourceData, meta interface{}) error { @@ -386,3 +324,70 @@ func albSuffixFromARN(arn *string) string { return "" } + +// flattenAwsAlbResource takes a *elbv2.LoadBalancer and populates all respective resource fields. +func flattenAwsAlbResource(d *schema.ResourceData, meta interface{}, alb *elbv2.LoadBalancer) error { + elbconn := meta.(*AWSClient).elbv2conn + + d.Set("arn", alb.LoadBalancerArn) + d.Set("arn_suffix", albSuffixFromARN(alb.LoadBalancerArn)) + d.Set("name", alb.LoadBalancerName) + d.Set("internal", (alb.Scheme != nil && *alb.Scheme == "internal")) + d.Set("security_groups", flattenStringList(alb.SecurityGroups)) + d.Set("subnets", flattenSubnetsFromAvailabilityZones(alb.AvailabilityZones)) + d.Set("vpc_id", alb.VpcId) + d.Set("zone_id", alb.CanonicalHostedZoneId) + d.Set("dns_name", alb.DNSName) + + respTags, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{ + ResourceArns: []*string{alb.LoadBalancerArn}, + }) + if err != nil { + return errwrap.Wrapf("Error retrieving ALB Tags: {{err}}", err) + } + + var et []*elbv2.Tag + if len(respTags.TagDescriptions) > 0 { + et = respTags.TagDescriptions[0].Tags + } + d.Set("tags", tagsToMapELBv2(et)) + + attributesResp, err := elbconn.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ + LoadBalancerArn: aws.String(d.Id()), + }) + if err != nil { + return errwrap.Wrapf("Error retrieving ALB Attributes: {{err}}", err) + } + + accessLogMap := map[string]interface{}{} + for _, attr := range attributesResp.Attributes { + switch *attr.Key { + case "access_logs.s3.enabled": + accessLogMap["enabled"] = *attr.Value + case "access_logs.s3.bucket": + accessLogMap["bucket"] = *attr.Value + case "access_logs.s3.prefix": + accessLogMap["prefix"] = *attr.Value + case "idle_timeout.timeout_seconds": + timeout, err := strconv.Atoi(*attr.Value) + if err != nil { + return errwrap.Wrapf("Error parsing ALB timeout: {{err}}", err) + } + log.Printf("[DEBUG] Setting ALB Timeout Seconds: %d", timeout) + d.Set("idle_timeout", timeout) + case "deletion_protection.enabled": + protectionEnabled := (*attr.Value) == "true" + log.Printf("[DEBUG] Setting ALB Deletion Protection Enabled: %t", protectionEnabled) + d.Set("enable_deletion_protection", protectionEnabled) + } + } + + log.Printf("[DEBUG] Setting ALB Access Logs: %#v", accessLogMap) + if accessLogMap["bucket"] != "" || accessLogMap["prefix"] != "" { + d.Set("access_logs", []interface{}{accessLogMap}) + } else { + d.Set("access_logs", []interface{}{}) + } + + return nil +} diff --git a/website/source/docs/providers/aws/d/alb.html.markdown b/website/source/docs/providers/aws/d/alb.html.markdown new file mode 100644 index 000000000..2e5c086ef --- /dev/null +++ b/website/source/docs/providers/aws/d/alb.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "aws" +page_title: "AWS: aws_alb" +sidebar_current: "docs-aws-datasource-alb" +description: |- + Provides an Application Load Balancer data source. +--- + +# aws\_alb + +Provides information about an Application Load Balancer. + +This data source can prove useful when a module accepts an ALB as an input +variable and needs to, for example, determine the security groups associated +with it, etc. + +## Example Usage + +``` +variable "alb_arn" { + type = "string" + default = "" +} + +variable "alb_name" { + type = "string" + default = "" +} + +data "aws_alb" "test" { + arn = "${var.alb_arn}" + name = "${var.alb_arn}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `arn` - (Optional) The full ARN of the load balancer. +* `name` - (Optional) The unique name of the load balancer. + +~> **NOTE**: When both `arn` and `name` are specified, `arn` takes precedence. + +## Attributes Reference + +See the [ALB Resource](/docs/providers/aws/r/alb.html) for details on the +returned attributes - they are identical. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index b29c946ad..a7b3c5641 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -17,6 +17,9 @@ > aws_acm_certificate + > + aws_alb + > aws_ami