diff --git a/aws/data_source_aws_eip.go b/aws/data_source_aws_eip.go index 23c6ea4cf55c..9b25d16701f5 100644 --- a/aws/data_source_aws_eip.go +++ b/aws/data_source_aws_eip.go @@ -14,7 +14,7 @@ func dataSourceAwsEip() *schema.Resource { Read: dataSourceAwsEipRead, Schema: map[string]*schema.Schema{ - "filter": dataSourceFiltersSchema(), + "filter": ec2CustomFiltersSchema(), "id": { Type: schema.TypeString, Optional: true, @@ -33,39 +33,35 @@ func dataSourceAwsEip() *schema.Resource { func dataSourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - filters, filtersOk := d.GetOk("filter") - id, idOk := d.GetOk("id") - publicIP, publicIPOk := d.GetOk("public_ip") - - if (idOk || publicIPOk) && filtersOk { - return fmt.Errorf("filter cannot be used when id or public_ip is set") - } - req := &ec2.DescribeAddressesInput{} - req.Filters = []*ec2.Filter{} - if idOk { - req.AllocationIds = []*string{aws.String(id.(string))} + if v, ok := d.GetOk("id"); ok { + req.AllocationIds = []*string{aws.String(v.(string))} } - if publicIPOk { - req.PublicIps = []*string{aws.String(publicIP.(string))} + if v, ok := d.GetOk("public_ip"); ok { + req.PublicIps = []*string{aws.String(v.(string))} } - if filtersOk { - req.Filters = buildAwsDataSourceFilters(filters.(*schema.Set)) - } + req.Filters = []*ec2.Filter{} + + req.Filters = append(req.Filters, buildEC2CustomFilterList( + d.Get("filter").(*schema.Set), + )...) + + req.Filters = append(req.Filters, buildEC2TagFilterList( + tagsFromMap(d.Get("tags").(map[string]interface{})), + )...) - if tags, ok := d.GetOk("tags"); ok { - req.Filters = append(req.Filters, buildEC2TagFilterList( - tagsFromMap(tags.(map[string]interface{})), - )...) + if len(req.Filters) == 0 { + // Don't send an empty filters list; the EC2 API won't accept it. + req.Filters = nil } log.Printf("[DEBUG] Reading EIP: %s", req) resp, err := conn.DescribeAddresses(req) if err != nil { - return err + return fmt.Errorf("error describing EC2 Address: %s", err) } if resp == nil || len(resp.Addresses) == 0 { return fmt.Errorf("no matching Elastic IP found") @@ -76,11 +72,11 @@ func dataSourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { eip := resp.Addresses[0] - if *eip.Domain == "vpc" { - d.SetId(*eip.AllocationId) + if aws.StringValue(eip.Domain) == ec2.DomainTypeVpc { + d.SetId(aws.StringValue(eip.AllocationId)) } else { log.Printf("[DEBUG] Reading EIP, has no AllocationId, this means we have a Classic EIP, the id will also be the public ip : %s", req) - d.SetId(*eip.PublicIp) + d.SetId(aws.StringValue(eip.PublicIp)) } d.Set("public_ip", eip.PublicIp) diff --git a/aws/data_source_aws_eip_test.go b/aws/data_source_aws_eip_test.go index a7537b6580e3..b6703ac5ad78 100644 --- a/aws/data_source_aws_eip_test.go +++ b/aws/data_source_aws_eip_test.go @@ -6,177 +6,172 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" ) -func TestAccDataSourceAwsEip_classic(t *testing.T) { - resource.Test(t, resource.TestCase{ +func TestAccDataSourceAwsEip_Filter(t *testing.T) { + dataSourceName := "data.aws_eip.test" + resourceName := "aws_eip.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccDataSourceAwsEipClassicConfig, + { + Config: testAccDataSourceAwsEipConfigFilter(rName), Check: resource.ComposeTestCheckFunc( - testAccDataSourceAwsEipCheck("data.aws_eip.test_classic", "aws_eip.test_classic"), + resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(dataSourceName, "public_ip", resourceName, "public_ip"), ), }, }, }) } -func TestAccDataSourceAwsEip_vpc(t *testing.T) { - resource.Test(t, resource.TestCase{ +func TestAccDataSourceAwsEip_Id(t *testing.T) { + dataSourceName := "data.aws_eip.test" + resourceName := "aws_eip.test" + + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccDataSourceAwsEipVPCConfig, + { + Config: testAccDataSourceAwsEipConfigId, Check: resource.ComposeTestCheckFunc( - testAccDataSourceAwsEipCheck("data.aws_eip.test_vpc_by_id", "aws_eip.test_vpc"), - testAccDataSourceAwsEipCheck("data.aws_eip.test_vpc_by_public_ip", "aws_eip.test_vpc"), + resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(dataSourceName, "public_ip", resourceName, "public_ip"), ), }, }, }) } -func TestAccDataSourceAwsEip_filter(t *testing.T) { +func TestAccDataSourceAwsEip_PublicIP_EC2Classic(t *testing.T) { + dataSourceName := "data.aws_eip.test" + resourceName := "aws_eip.test" + + // Do not parallelize this test until the provider testing framework + // has a stable us-east-1 alias resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccDataSourceAwsEipFilterConfig, + { + Config: testAccDataSourceAwsEipConfigPublicIpEc2Classic, Check: resource.ComposeTestCheckFunc( - testAccDataSourceAwsEipCheck("data.aws_eip.by_filter", "aws_eip.test"), + resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(dataSourceName, "public_ip", resourceName, "public_ip"), ), }, }, }) } -func testAccDataSourceAwsEipCheck(data_path string, resource_path string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[data_path] - if !ok { - return fmt.Errorf("root module has no resource called %s", data_path) - } - - eipRs, ok := s.RootModule().Resources[resource_path] - if !ok { - return fmt.Errorf("can't find %s in state", resource_path) - } +func TestAccDataSourceAwsEip_PublicIP_VPC(t *testing.T) { + dataSourceName := "data.aws_eip.test" + resourceName := "aws_eip.test" - attr := rs.Primary.Attributes - - if attr["id"] != eipRs.Primary.Attributes["id"] { - return fmt.Errorf( - "id is %s; want %s", - attr["id"], - eipRs.Primary.Attributes["id"], - ) - } - - if attr["public_ip"] != eipRs.Primary.Attributes["public_ip"] { - return fmt.Errorf( - "public_ip is %s; want %s", - attr["public_ip"], - eipRs.Primary.Attributes["public_ip"], - ) - } - - return nil - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsEipConfigPublicIpVpc, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(dataSourceName, "public_ip", resourceName, "public_ip"), + ), + }, + }, + }) } -func TestAccDataSourceAwsEip_tags(t *testing.T) { - rInt := acctest.RandInt() - resource.Test(t, resource.TestCase{ +func TestAccDataSourceAwsEip_Tags(t *testing.T) { + dataSourceName := "data.aws_eip.test" + resourceName := "aws_eip.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccDataSourceAwsEip_tags_config(rInt), + { + Config: testAccDataSourceAwsEipConfigTags(rName), Check: resource.ComposeTestCheckFunc( - testAccDataSourceAwsEipCheck("data.aws_eip.by_tag", "aws_eip.test"), + resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"), + resource.TestCheckResourceAttrPair(dataSourceName, "public_ip", resourceName, "public_ip"), ), }, }, }) } -const testAccDataSourceAwsEipClassicConfig = ` -provider "aws" { - region = "us-west-2" +func testAccDataSourceAwsEipConfigFilter(rName string) string { + return fmt.Sprintf(` +resource "aws_eip" "test" { + vpc = true + + tags { + Name = %q + } } -resource "aws_eip" "test_classic" {} +data "aws_eip" "test" { + filter { + name = "tag:Name" + values = ["${aws_eip.test.tags.Name}"] + } +} +`, rName) +} -data "aws_eip" "test_classic" { - public_ip = "${aws_eip.test_classic.public_ip}" +const testAccDataSourceAwsEipConfigId = ` +resource "aws_eip" "test" { + vpc = true } +data "aws_eip" "test" { + id = "${aws_eip.test.id}" +} ` -const testAccDataSourceAwsEipVPCConfig = ` +const testAccDataSourceAwsEipConfigPublicIpEc2Classic = ` provider "aws" { - region = "us-west-2" + region = "us-east-1" } -resource "aws_eip" "test_vpc" { - vpc = true -} +resource "aws_eip" "test" {} -data "aws_eip" "test_vpc_by_id" { - id = "${aws_eip.test_vpc.id}" +data "aws_eip" "test" { + public_ip = "${aws_eip.test.public_ip}" } +` -data "aws_eip" "test_vpc_by_public_ip" { - public_ip = "${aws_eip.test_vpc.public_ip}" +const testAccDataSourceAwsEipConfigPublicIpVpc = ` +resource "aws_eip" "test" { + vpc = true } -` -const testAccDataSourceAwsEipFilterConfig = ` -provider "aws" { - region = "us-west-2" +data "aws_eip" "test" { + public_ip = "${aws_eip.test.public_ip}" } +` +func testAccDataSourceAwsEipConfigTags(rName string) string { + return fmt.Sprintf(` resource "aws_eip" "test" { - vpc = true - - tags { - Name = "testeip" - } -} + vpc = true -data "aws_eip" "by_filter" { - filter { - name = "tag:Name" - values = ["${aws_eip.test.tags.Name}"] + tags { + Name = %q } } -` -func testAccDataSourceAwsEip_tags_config(rInt int) string { - return fmt.Sprintf( - ` -provider "aws" { - region = "us-west-2" +data "aws_eip" "test" { + tags { + Name = "${aws_eip.test.tags["Name"]}" + } } - -resource "aws_eip" "test" { - tags { - test_tag = "hello tag" - random_tag = "%d" - } -} - -data "aws_eip" "by_tag" { - tags { - test_tag = "${aws_eip.test.tags["test_tag"]}" - random_tag = "%d" - } - public_ip = "${aws_eip.test.public_ip}" -} -`, rInt, rInt) +`, rName) } diff --git a/website/docs/d/eip.html.markdown b/website/docs/d/eip.html.markdown index dd857d7970b4..26efdf434466 100644 --- a/website/docs/d/eip.html.markdown +++ b/website/docs/d/eip.html.markdown @@ -10,59 +10,44 @@ description: |- `aws_eip` provides details about a specific Elastic IP. -This resource can prove useful when a module accepts an allocation ID or -public IP as an input variable and needs to determine the other. - ## Example Usage -The following example shows how one might accept a public IP as a variable -and use this data source to obtain the allocation ID when using an VPC EIP. +### Search By Allocation ID (VPC only) -### ip or id ```hcl -variable "instance_id" {} -variable "public_ip" {} - -data "aws_eip" "proxy_ip" { - public_ip = "${var.public_ip}" -} - -resource "aws_eip_association" "proxy_eip" { - instance_id = "${var.instance_id}" - allocation_id = "${data.aws_eip.proxy_ip.id}" +data "aws_eip" "by_allocation_id" { + id = "eipalloc-12345678" } ``` -### filter +### Search By Filters (EC2-Classic or VPC) ```hcl -variable "instance_id" {} - -resource "aws_eip" "proxy_ip" { - vpc = true - - tags { - Name = "proxy" - } -} - data "aws_eip" "by_filter" { filter { name = "tag:Name" - values = ["${aws_eip.test.tags.Name}"] + values = ["exampleNameTagValue"] } } +``` -resource "aws_eip_association" "proxy_eip" { - instance_id = "${var.instance_id}" - allocation_id = "${data.aws_eip.proxy_ip.id}" -} +### Search By Public IP (EC2-Classic or VPC) +```hcl +data "aws_eip" "by_public_ip" { + public_ip = "1.2.3.4" +} ``` -~> **NOTE:** if using `data "aws_eip"` on a none pre exsiting EIP, ensure you reference the tag of the EIP by interpolation as shown in the example. +### Search By Tags (EC2-Classic or VPC) -Classic EIP's do not have an allocation_id, only use `public_ip` in the `data "aws_eip"` block. +```hcl +data "aws_eip" "by_tags" { + tags { + Name = "exampleNameTagValue" + } +} +``` ## Argument Reference @@ -70,20 +55,15 @@ The arguments of this data source act as filters for querying the available Elastic IPs in the current region. The given filters must match exactly one Elastic IP whose data will be exported as attributes. +* `filter` - (Optional) One or more name/value pairs to use as filters. There are several valid keys, for a full reference, check out the [EC2 API Reference](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAddresses.html). * `id` - (Optional) The allocation id of the specific VPC EIP to retrieve. If a classic EIP is required, do NOT set `id`, only set `public_ip` - * `public_ip` - (Optional) The public IP of the specific EIP to retrieve. - * `tags` - (Optional) A mapping of tags, each pair of which must exactly match a pair on the desired Elastic IP -* `filter` - (Optional) One or more name/value pairs to use as filters. There are -several valid keys, for a full reference, check out -[describe-addresses in the AWS CLI reference][1]. - -~> **NOTE:** `filter` cannot be used when `id` or `public_ip` is being used. ## Attributes Reference -All of the argument attributes are also exported as result attributes. This -data source will complete the data by populating any fields that are not -included in the configuration with the data for the selected Elastic IP. +In addition to all arguments above, the following attributes are exported: +* `id` - If VPC Elastic IP, the allocation identifier. If EC2-Classic Elastic IP, the public IP address. +* `public_ip` - Public IP address of Elastic IP. +* `tags` - Key-value map of tags associated with Elastic IP.