provider/aws: data source for AWS Hosted Zone (#9766)

* provider/aws: data source for AWS Hosted Zone

* add caller_reference, resource_record_set_count fields, manage private zone and trailing dot

* fix fmt

* update documentation, use string function in hostedZoneNamewq

* add vpc_id support

* add tags support

* add documentation for hosted zone data source tags support
This commit is contained in:
Mathieu Herbert 2016-12-13 17:22:26 +01:00 committed by Paul Stack
parent b2136beff2
commit 3239138099
5 changed files with 372 additions and 0 deletions

View File

@ -0,0 +1,176 @@
package aws
import (
"fmt"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/hashicorp/terraform/helper/schema"
)
func dataSourceAwsRoute53Zone() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsRoute53ZoneRead,
Schema: map[string]*schema.Schema{
"zone_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"private_zone": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"comment": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"caller_reference": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"vpc_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"tags": tagsSchemaComputed(),
"resource_record_set_count": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
},
}
}
func dataSourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
name, nameExists := d.GetOk("name")
name = hostedZoneName(name.(string))
id, idExists := d.GetOk("zone_id")
vpcId, vpcIdExists := d.GetOk("vpc_id")
tags := tagsFromMap(d.Get("tags").(map[string]interface{}))
if nameExists && idExists {
return fmt.Errorf("zone_id and name arguments can't be used together")
} else if !nameExists && !idExists {
return fmt.Errorf("Either name or zone_id must be set")
}
var nextMarker *string
var hostedZoneFound *route53.HostedZone
// We loop through all hostedzone
for allHostedZoneListed := false; !allHostedZoneListed; {
req := &route53.ListHostedZonesInput{}
if nextMarker != nil {
req.Marker = nextMarker
}
resp, err := conn.ListHostedZones(req)
if err != nil {
return fmt.Errorf("Error finding Route 53 Hosted Zone: %v", err)
}
for _, hostedZone := range resp.HostedZones {
hostedZoneId := cleanZoneID(*hostedZone.Id)
if idExists && hostedZoneId == id.(string) {
hostedZoneFound = hostedZone
break
// we check if the name is the same as requested and if private zone field is the same as requested or if there is a vpc_id
} else if *hostedZone.Name == name && (*hostedZone.Config.PrivateZone == d.Get("private_zone").(bool) || (*hostedZone.Config.PrivateZone == true && vpcIdExists)) {
matchingVPC := false
if vpcIdExists {
reqHostedZone := &route53.GetHostedZoneInput{}
reqHostedZone.Id = aws.String(hostedZoneId)
respHostedZone, errHostedZone := conn.GetHostedZone(reqHostedZone)
if errHostedZone != nil {
return fmt.Errorf("Error finding Route 53 Hosted Zone: %v", errHostedZone)
}
// we go through all VPCs
for _, vpc := range respHostedZone.VPCs {
if *vpc.VPCId == vpcId.(string) {
matchingVPC = true
break
}
}
} else {
matchingVPC = true
}
// we check if tags match
matchingTags := true
if len(tags) > 0 {
reqListTags := &route53.ListTagsForResourceInput{}
reqListTags.ResourceId = aws.String(hostedZoneId)
reqListTags.ResourceType = aws.String("hostedzone")
respListTags, errListTags := conn.ListTagsForResource(reqListTags)
if errListTags != nil {
return fmt.Errorf("Error finding Route 53 Hosted Zone: %v", errListTags)
}
for _, tag := range tags {
found := false
for _, tagRequested := range respListTags.ResourceTagSet.Tags {
if *tag.Key == *tagRequested.Key && *tag.Value == *tagRequested.Value {
found = true
}
}
if !found {
matchingTags = false
break
}
}
}
if matchingTags && matchingVPC {
if hostedZoneFound != nil {
return fmt.Errorf("multplie Route53Zone found please use vpc_id option to filter")
} else {
hostedZoneFound = hostedZone
}
}
}
}
if *resp.IsTruncated {
nextMarker = resp.NextMarker
} else {
allHostedZoneListed = true
}
}
if hostedZoneFound == nil {
return fmt.Errorf("no matching Route53Zone found")
}
idHostedZone := cleanZoneID(*hostedZoneFound.Id)
d.SetId(idHostedZone)
d.Set("zone_id", idHostedZone)
d.Set("name", hostedZoneFound.Name)
d.Set("comment", hostedZoneFound.Config.Comment)
d.Set("private_zone", hostedZoneFound.Config.PrivateZone)
d.Set("caller_reference", hostedZoneFound.CallerReference)
d.Set("resource_record_set_count", hostedZoneFound.ResourceRecordSetCount)
return nil
}
// used to manage trailing .
func hostedZoneName(name string) string {
if strings.HasSuffix(name, ".") {
return name
} else {
return name + "."
}
}

View File

@ -0,0 +1,133 @@
package aws
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataSourceAwsRoute53ZoneConfig,
Check: resource.ComposeTestCheckFunc(
testAccDataSourceAwsRoute53ZoneCheck("data.aws_route53_zone.by_zone_id"),
testAccDataSourceAwsRoute53ZoneCheck("data.aws_route53_zone.by_name"),
testAccDataSourceAwsRoute53ZoneCheckPrivate("data.aws_route53_zone.by_vpc"),
testAccDataSourceAwsRoute53ZoneCheckPrivate("data.aws_route53_zone.by_tag"),
),
},
},
})
}
func testAccDataSourceAwsRoute53ZoneCheck(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("root module has no resource called %s", name)
}
hostedZone, ok := s.RootModule().Resources["aws_route53_zone.test"]
if !ok {
return fmt.Errorf("can't find aws_hosted_zone.test in state")
}
attr := rs.Primary.Attributes
if attr["id"] != hostedZone.Primary.Attributes["id"] {
return fmt.Errorf(
"id is %s; want %s",
attr["id"],
hostedZone.Primary.Attributes["id"],
)
}
if attr["name"] != "terraformtestacchz.com." {
return fmt.Errorf(
"Route53 Zone name is %s; want terraformtestacchz.com.",
attr["name"],
)
}
return nil
}
}
func testAccDataSourceAwsRoute53ZoneCheckPrivate(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("root module has no resource called %s", name)
}
hostedZone, ok := s.RootModule().Resources["aws_route53_zone.test_private"]
if !ok {
return fmt.Errorf("can't find aws_hosted_zone.test in state")
}
attr := rs.Primary.Attributes
if attr["id"] != hostedZone.Primary.Attributes["id"] {
return fmt.Errorf(
"id is %s; want %s",
attr["id"],
hostedZone.Primary.Attributes["id"],
)
}
if attr["name"] != "test.acc." {
return fmt.Errorf(
"Route53 Zone name is %s; want test.acc.",
attr["name"],
)
}
return nil
}
}
const testAccDataSourceAwsRoute53ZoneConfig = `
provider "aws" {
region = "us-east-2"
}
resource "aws_vpc" "test" {
cidr_block = "172.16.0.0/16"
}
resource "aws_route53_zone" "test_private" {
name = "test.acc."
vpc_id = "${aws_vpc.test.id}"
tags {
Environment = "dev"
}
}
data "aws_route53_zone" "by_vpc" {
name = "${aws_route53_zone.test_private.name}"
vpc_id = "${aws_vpc.test.id}"
}
data "aws_route53_zone" "by_tag" {
name = "${aws_route53_zone.test_private.name}"
private_zone = true
tags {
Environment = "dev"
}
}
resource "aws_route53_zone" "test" {
name = "terraformtestacchz.com."
}
data "aws_route53_zone" "by_zone_id" {
zone_id = "${aws_route53_zone.test.zone_id}"
}
data "aws_route53_zone" "by_name" {
name = "${data.aws_route53_zone.by_zone_id.name}"
}
`

View File

@ -157,6 +157,7 @@ func Provider() terraform.ResourceProvider {
"aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(), "aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(),
"aws_eip": dataSourceAwsEip(), "aws_eip": dataSourceAwsEip(),
"aws_elb_service_account": dataSourceAwsElbServiceAccount(), "aws_elb_service_account": dataSourceAwsElbServiceAccount(),
"aws_route53_zone": dataSourceAwsRoute53Zone(),
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(), "aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
"aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(), "aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(),
"aws_ip_ranges": dataSourceAwsIPRanges(), "aws_ip_ranges": dataSourceAwsIPRanges(),

View File

@ -0,0 +1,59 @@
---
layout: "aws"
page_title: "AWS: aws_hosted_zone"
sidebar_current: "docs-aws-datasource-hosted-zone"
description: |-
Provides details about a specific Hosted Zone
---
# aws\_hosted\_zone
`aws_hosted_zone` provides details about a specific Hosted Zone.
This data source allows to find a Hosted Zone ID given Hosted Zone name and certain search criteria.
## Example Usage
The following example shows how to get a Hosted Zone from it's name and from this data how to create a Record Set.
```
data "aws_route53_zone" "selected" {
name = "test.com."
private_zone = true
}
resource "aws_route53_record" "www" {
zone_id = "${data.aws_route53_zone.selected.zone_id}"
name = "www.${data.aws_route53_zone.selected.name}"
type = "A"
ttl = "300"
records = ["10.0.0.1"]
}
```
## Argument Reference
The arguments of this data source act as filters for querying the available
Hosted Zone. You have to use `zone_id` or `name`, not both of them. The given filter must match exactly one
Hosted Zone. If you use `name` field for private Hosted Zone, you need to add `private_zone` field to `true`
* `zone_id` - (Optional) The Hosted Zone id of the desired Hosted Zone.
* `name` - (Optional) The Hosted Zone name of the desired Hosted Zone.
* `private_zone` - (Optional) Used with `name` field to get a private Hosted Zone.
* `vpc_id` - (Optional) Used with `name` field to get a private Hosted Zone associated with the vpc_id (in this case, private_zone is not mandatory).
* `tags` - (Optional) Used with `name` field. A mapping of tags, each pair of which must exactly match
a pair on the desired security group.
## 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 Hosted Zone.
The following attribute is additionally exported:
* `caller_reference` - Caller Reference of the Hosted Zone.
* `comment` - The comment field of the Hosted Zone.
* `resource_record_set_count` - the number of Record Set in the Hosted Zone

View File

@ -51,6 +51,9 @@
<li<%= sidebar_current("docs-aws-datasource-elb-service-account") %>> <li<%= sidebar_current("docs-aws-datasource-elb-service-account") %>>
<a href="/docs/providers/aws/d/elb_service_account.html">aws_elb_service_account</a> <a href="/docs/providers/aws/d/elb_service_account.html">aws_elb_service_account</a>
</li> </li>
<li<%= sidebar_current("docs-aws-datasource-hosted-zone") %>>
<a href="/docs/providers/aws/d/hosted_zone.html">aws_hosted_zone</a>
</li>
<li<%= sidebar_current("docs-aws-datasource-iam-policy-document") %>> <li<%= sidebar_current("docs-aws-datasource-iam-policy-document") %>>
<a href="/docs/providers/aws/d/iam_policy_document.html">aws_iam_policy_document</a> <a href="/docs/providers/aws/d/iam_policy_document.html">aws_iam_policy_document</a>
</li> </li>