Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 7 commits into from
Dec 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions builtin/providers/aws/data_source_aws_route53_zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package aws

import (
"fmt"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have goimports run on it for correct formatting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have goimports run on it for correct formatting.

"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 + "."
}
}
133 changes: 133 additions & 0 deletions builtin/providers/aws/data_source_aws_route53_zone_test.go
Original file line number Diff line number Diff line change
@@ -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}"
}

`
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func Provider() terraform.ResourceProvider {
"aws_cloudformation_stack": dataSourceAwsCloudFormationStack(),
"aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(),
"aws_elb_service_account": dataSourceAwsElbServiceAccount(),
"aws_route53_zone": dataSourceAwsRoute53Zone(),
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
"aws_ip_ranges": dataSourceAwsIPRanges(),
"aws_prefix_list": dataSourceAwsPrefixList(),
Expand Down
59 changes: 59 additions & 0 deletions website/source/docs/providers/aws/d/hosted_zone.html.markdown
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions website/source/layouts/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
<li<%= sidebar_current("docs-aws-datasource-elb-service-account") %>>
<a href="/docs/providers/aws/d/elb_service_account.html">aws_elb_service_account</a>
</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") %>>
<a href="/docs/providers/aws/d/iam_policy_document.html">aws_iam_policy_document</a>
</li>
Expand Down