Skip to content

Commit

Permalink
Merge pull request #8628 from robh007/route53-resolver-endpoint
Browse files Browse the repository at this point in the history
d/aws_route53_resolver_endpoint: Add Route53resolver endpoint datasource
  • Loading branch information
breathingdust authored Nov 5, 2020
2 parents 68158f2 + fec841c commit 4754d12
Show file tree
Hide file tree
Showing 4 changed files with 442 additions and 0 deletions.
179 changes: 179 additions & 0 deletions aws/data_source_aws_route53_resolver_endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package aws

import (
"fmt"

"github.com/aws/aws-sdk-go/service/route53resolver"

"github.com/aws/aws-sdk-go/aws"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceAwsRoute53ResolverEndpoint() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsRoute53ResolverEndpointRead,

Schema: map[string]*schema.Schema{
"filter": {
Type: schema.TypeSet,
Optional: true,
MinItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"values": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},

"direction": {
Type: schema.TypeString,
Computed: true,
},

"name": {
Type: schema.TypeString,
Computed: true,
},

"arn": {
Type: schema.TypeString,
Computed: true,
},

"resolver_endpoint_id": {
Type: schema.TypeString,
Optional: true,
},

"status": {
Type: schema.TypeString,
Computed: true,
},
"vpc_id": {
Type: schema.TypeString,
Computed: true,
},
"ip_addresses": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Set: schema.HashString,
},
},
}
}

func dataSourceAwsRoute53ResolverEndpointRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).route53resolverconn
req := &route53resolver.ListResolverEndpointsInput{}

resolvers := make([]*route53resolver.ResolverEndpoint, 0)

rID, rIDOk := d.GetOk("resolver_endpoint_id")
filters, filtersOk := d.GetOk("filter")

if filtersOk {
req.Filters = buildR53ResolverTagFilters(filters.(*schema.Set))
}

for {
resp, err := conn.ListResolverEndpoints(req)

if err != nil {
return fmt.Errorf("Error Reading Route53 Resolver Endpoints: %s", req)
}

if len(resp.ResolverEndpoints) == 0 && filtersOk {
return fmt.Errorf("Your query returned no results. Please change your search criteria and try again")
}

if len(resp.ResolverEndpoints) > 1 && !rIDOk {
return fmt.Errorf("Your query returned more than one resolver. Please change your search criteria and try again")
}

if rIDOk {
for _, r := range resp.ResolverEndpoints {
if aws.StringValue(r.Id) == rID {
resolvers = append(resolvers, r)
break
}
}
} else {
resolvers = append(resolvers, resp.ResolverEndpoints[0])
}

if len(resolvers) == 0 {
return fmt.Errorf("The ID provided could not be found")
}

resolver := resolvers[0]

d.SetId(aws.StringValue(resolver.Id))
d.Set("resolver_endpoint_id", resolver.Id)
d.Set("arn", aws.StringValue(resolver.Arn))
d.Set("status", aws.StringValue(resolver.Status))
d.Set("name", aws.StringValue(resolver.Name))
d.Set("vpc_id", aws.StringValue(resolver.HostVPCId))
d.Set("direction", aws.StringValue(resolver.Direction))

if resp.NextToken == nil {
break
}

req.NextToken = resp.NextToken
}

params := &route53resolver.ListResolverEndpointIpAddressesInput{
ResolverEndpointId: aws.String(d.Id()),
}

ipAddresses := []interface{}{}

for {
ip, err := conn.ListResolverEndpointIpAddresses(params)

if err != nil {
return fmt.Errorf("error getting Route53 Resolver endpoint (%s) IP Addresses: %s", d.Id(), err)
}

for _, vIPAddresses := range ip.IpAddresses {
ipAddresses = append(ipAddresses, aws.StringValue(vIPAddresses.Ip))
}

d.Set("ip_addresses", ipAddresses)

if ip.NextToken == nil {
break
}

params.NextToken = ip.NextToken
}

return nil
}

func buildR53ResolverTagFilters(set *schema.Set) []*route53resolver.Filter {
var filters []*route53resolver.Filter

for _, v := range set.List() {
m := v.(map[string]interface{})
var filterValues []*string
for _, e := range m["values"].([]interface{}) {
filterValues = append(filterValues, aws.String(e.(string)))
}
filters = append(filters, &route53resolver.Filter{
Name: aws.String(m["name"].(string)),
Values: filterValues,
})
}

return filters
}
215 changes: 215 additions & 0 deletions aws/data_source_aws_route53_resolver_endpoint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package aws

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceAwsRoute53ResolverEndpoint_Basic(t *testing.T) {
name := acctest.RandomWithPrefix("tf-acc-test")
rInt := acctest.RandInt()
direction := "INBOUND"
resourceName := "aws_route53_resolver_endpoint.foo"
datasourceName := "data.aws_route53_resolver_endpoint.foo"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistent,
ExpectError: regexp.MustCompile("The ID provided could not be found"),
},
{
Config: testAccDataSourceRoute53ResolverEndpointConfig_initial(rInt, direction, name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
resource.TestCheckResourceAttrPair(datasourceName, "resolver_endpoint_id", resourceName, "id"),
resource.TestCheckResourceAttr(datasourceName, "ip_addresses.#", "2"),
),
},
},
})
}

func TestAccDataSourceAwsRoute53ResolverEndpoint_Filter(t *testing.T) {
name := acctest.RandomWithPrefix("tf-acc-test")
rInt := acctest.RandInt()
direction := "OUTBOUND"
resourceName := "aws_route53_resolver_endpoint.foo"
datasourceName := "data.aws_route53_resolver_endpoint.foo"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistentFilter,
ExpectError: regexp.MustCompile("Your query returned no results. Please change your search criteria and try again"),
},
{
Config: testAccDataSourceRoute53ResolverEndpointConfig_filter(rInt, direction, name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
resource.TestCheckResourceAttrPair(datasourceName, "resolver_endpoint_id", resourceName, "id"),
resource.TestCheckResourceAttr(datasourceName, "ip_addresses.#", "2"),
),
},
},
})
}

func testAccDataSourceRoute53ResolverEndpointConfig_base(rInt int) string {
return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(`
resource "aws_vpc" "foo" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "terraform-testacc-r53-resolver-vpc-%d"
}
}
resource "aws_subnet" "sn1" {
vpc_id = aws_vpc.foo.id
cidr_block = cidrsubnet(aws_vpc.foo.cidr_block, 2, 0)
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "tf-acc-r53-resolver-sn1-%d"
}
}
resource "aws_subnet" "sn2" {
vpc_id = aws_vpc.foo.id
cidr_block = cidrsubnet(aws_vpc.foo.cidr_block, 2, 1)
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "tf-acc-r53-resolver-sn2-%d"
}
}
resource "aws_subnet" "sn3" {
vpc_id = aws_vpc.foo.id
cidr_block = cidrsubnet(aws_vpc.foo.cidr_block, 2, 2)
availability_zone = data.aws_availability_zones.available.names[2]
tags = {
Name = "tf-acc-r53-resolver-sn3-%d"
}
}
resource "aws_security_group" "sg1" {
vpc_id = aws_vpc.foo.id
name = "tf-acc-r53-resolver-sg1-%d"
tags = {
Name = "tf-acc-r53-resolver-sg1-%d"
}
}
resource "aws_security_group" "sg2" {
vpc_id = aws_vpc.foo.id
name = "tf-acc-r53-resolver-sg2-%d"
tags = {
Name = "tf-acc-r53-resolver-sg2-%d"
}
}
`, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt)
}

func testAccDataSourceRoute53ResolverEndpointConfig_initial(rInt int, direction, name string) string {
return composeConfig(testAccDataSourceRoute53ResolverEndpointConfig_base(rInt), fmt.Sprintf(`
resource "aws_route53_resolver_endpoint" "foo" {
direction = "%s"
name = "%s"
security_group_ids = [
aws_security_group.sg1.id,
aws_security_group.sg2.id,
]
ip_address {
subnet_id = aws_subnet.sn1.id
}
ip_address {
subnet_id = aws_subnet.sn2.id
ip = cidrhost(aws_subnet.sn2.cidr_block, 8)
}
tags = {
Environment = "production"
Usage = "original"
}
}
data "aws_route53_resolver_endpoint" "foo" {
resolver_endpoint_id = aws_route53_resolver_endpoint.foo.id
}
`, direction, name))
}

func testAccDataSourceRoute53ResolverEndpointConfig_filter(rInt int, direction, name string) string {
return composeConfig(testAccDataSourceRoute53ResolverEndpointConfig_base(rInt), fmt.Sprintf(`
resource "aws_route53_resolver_endpoint" "foo" {
direction = "%s"
name = "%s"
security_group_ids = [
aws_security_group.sg1.id,
aws_security_group.sg2.id,
]
ip_address {
subnet_id = aws_subnet.sn1.id
}
ip_address {
subnet_id = aws_subnet.sn2.id
ip = cidrhost(aws_subnet.sn2.cidr_block, 8)
}
tags = {
Environment = "production"
Usage = "original"
}
}
data "aws_route53_resolver_endpoint" "foo" {
filter {
name = "Name"
values = [aws_route53_resolver_endpoint.foo.name]
}
filter {
name = "SecurityGroupIds"
values = [aws_security_group.sg1.id, aws_security_group.sg2.id]
}
}
`, direction, name))
}

const testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistent = `
data "aws_route53_resolver_endpoint" "foo" {
resolver_endpoint_id = "rslvr-in-8g85830108dd4c82b"
}
`

const testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistentFilter = `
data "aws_route53_resolver_endpoint" "foo" {
filter {
name = "Name"
values = ["None-Existent-Resource"]
}
}
`
Loading

0 comments on commit 4754d12

Please sign in to comment.