From 4845f8de39fe2cf3cbe1e949c42cfc2b5a0df421 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Mon, 21 Nov 2016 00:27:44 -0800 Subject: [PATCH] provider/aws: Add aws_alb data source (#10196) * provider/aws: Add aws_alb data source This adds the aws_alb data source for getting information on an AWS Application Load Balancer. The schema is nearly the same as the resource of the same name, with most of the resource population logic de-coupled into its own function so that they can be shared between the resource and data source. * provider/aws: aws_alb data source language revisions * Multiple/zero result error slightly updated to be a bit more specific. * Fixed relic of the copy of the resource docs (resource -> data source) --- builtin/providers/aws/data_source_aws_alb.go | 127 +++++++++++++++++ .../providers/aws/data_source_aws_alb_test.go | 124 +++++++++++++++++ builtin/providers/aws/provider.go | 1 + builtin/providers/aws/resource_aws_alb.go | 131 +++++++++--------- .../docs/providers/aws/d/alb.html.markdown | 48 +++++++ website/source/layouts/aws.erb | 3 + 6 files changed, 371 insertions(+), 63 deletions(-) create mode 100644 builtin/providers/aws/data_source_aws_alb.go create mode 100644 builtin/providers/aws/data_source_aws_alb_test.go create mode 100644 website/source/docs/providers/aws/d/alb.html.markdown 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