diff --git a/CHANGELOG.md b/CHANGELOG.md index 92f0c9d6c..3fc9bc864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,14 @@ IMPROVEMENTS: * **New config function: `formatlist`** - Format lists in a similar way to `format`. Useful for creating URLs from a list of IPs. [GH-1829] + * **New resource: `aws_route53_zone_association`** * provider/aws: `aws_autoscaling_group` can wait for capacity in ELB via `min_elb_capacity` [GH-1970] * provider/aws: `aws_db_instances` supports `license_model` [GH-1966] * provider/aws: `aws_elasticache_cluster` add support for Tags [GH-1965] * provider/aws: `aws_s3_bucket` exports `hosted_zone_id` and `region` [GH-1865] * provider/aws: `aws_route53_record` exports `fqdn` [GH-1847] + * provider/aws: `aws_route53_zone` can create private hosted zones [GH-1526] * provider/google: `google_compute_instance` `scratch` attribute added [GH-1920] BUG FIXES: diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 095e392dd..66446a902 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -115,6 +115,7 @@ func Provider() terraform.ResourceProvider { "aws_network_interface": resourceAwsNetworkInterface(), "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), "aws_route53_record": resourceAwsRoute53Record(), + "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), "aws_route53_zone": resourceAwsRoute53Zone(), "aws_route_table_association": resourceAwsRouteTableAssociation(), "aws_route_table": resourceAwsRouteTable(), diff --git a/builtin/providers/aws/resource_aws_route53_zone.go b/builtin/providers/aws/resource_aws_route53_zone.go index 59937fbc0..b89907371 100644 --- a/builtin/providers/aws/resource_aws_route53_zone.go +++ b/builtin/providers/aws/resource_aws_route53_zone.go @@ -28,6 +28,19 @@ func resourceAwsRoute53Zone() *schema.Resource { ForceNew: true, }, + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "vpc_region": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + "zone_id": &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -53,6 +66,16 @@ func resourceAwsRoute53ZoneCreate(d *schema.ResourceData, meta interface{}) erro HostedZoneConfig: comment, CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)), } + if v := d.Get("vpc_id"); v != "" { + req.VPC = &route53.VPC{ + VPCID: aws.String(v.(string)), + VPCRegion: aws.String(meta.(*AWSClient).region), + } + if w := d.Get("vpc_region"); w != "" { + req.VPC.VPCRegion = aws.String(w.(string)) + } + d.Set("vpc_region", req.VPC.VPCRegion) + } log.Printf("[DEBUG] Creating Route53 hosted zone: %s", *req.Name) resp, err := r53.CreateHostedZone(req) @@ -98,13 +121,33 @@ func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error return err } - ns := make([]string, len(zone.DelegationSet.NameServers)) - for i := range zone.DelegationSet.NameServers { - ns[i] = *zone.DelegationSet.NameServers[i] - } - sort.Strings(ns) - if err := d.Set("name_servers", ns); err != nil { - return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err) + if !*zone.HostedZone.Config.PrivateZone { + ns := make([]string, len(zone.DelegationSet.NameServers)) + for i := range zone.DelegationSet.NameServers { + ns[i] = *zone.DelegationSet.NameServers[i] + } + sort.Strings(ns) + if err := d.Set("name_servers", ns); err != nil { + return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err) + } + } else { + ns, err := getNameServers(d.Id(), d.Get("name").(string), r53) + if err != nil { + return err + } + if err := d.Set("name_servers", ns); err != nil { + return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err) + } + + var associatedVPC *route53.VPC + for _, vpc := range zone.VPCs { + if *vpc.VPCID == d.Get("vpc_id") { + associatedVPC = vpc + } + } + if associatedVPC == nil { + return fmt.Errorf("[DEBUG] VPC: %v is not associated with Zone: %v", d.Get("vpc_id"), d.Id()) + } } // get tags @@ -181,3 +224,23 @@ func cleanPrefix(ID, prefix string) string { } return ID } + +func getNameServers(zoneId string, zoneName string, r53 *route53.Route53) ([]string, error) { + resp, err := r53.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{ + HostedZoneID: aws.String(zoneId), + StartRecordName: aws.String(zoneName), + StartRecordType: aws.String("NS"), + }) + if err != nil { + return nil, err + } + if len(resp.ResourceRecordSets) == 0 { + return nil, nil + } + ns := make([]string, len(resp.ResourceRecordSets[0].ResourceRecords)) + for i := range resp.ResourceRecordSets[0].ResourceRecords { + ns[i] = *resp.ResourceRecordSets[0].ResourceRecords[i].Value + } + sort.Strings(ns) + return ns, nil +} diff --git a/builtin/providers/aws/resource_aws_route53_zone_association.go b/builtin/providers/aws/resource_aws_route53_zone_association.go new file mode 100644 index 000000000..d2fc2a2c4 --- /dev/null +++ b/builtin/providers/aws/resource_aws_route53_zone_association.go @@ -0,0 +1,147 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/route53" +) + +func resourceAwsRoute53ZoneAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53ZoneAssociationCreate, + Read: resourceAwsRoute53ZoneAssociationRead, + Update: resourceAwsRoute53ZoneAssociationUpdate, + Delete: resourceAwsRoute53ZoneAssociationDelete, + + Schema: map[string]*schema.Schema{ + "zone_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "vpc_region": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAwsRoute53ZoneAssociationCreate(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + + req := &route53.AssociateVPCWithHostedZoneInput{ + HostedZoneID: aws.String(d.Get("zone_id").(string)), + VPC: &route53.VPC{ + VPCID: aws.String(d.Get("vpc_id").(string)), + VPCRegion: aws.String(meta.(*AWSClient).region), + }, + Comment: aws.String("Managed by Terraform"), + } + if w := d.Get("vpc_region"); w != "" { + req.VPC.VPCRegion = aws.String(w.(string)) + } + + log.Printf("[DEBUG] Associating Route53 Private Zone %s with VPC %s with region %s", *req.HostedZoneID, *req.VPC.VPCID, *req.VPC.VPCRegion) + resp, err := r53.AssociateVPCWithHostedZone(req) + if err != nil { + return err + } + + // Store association id + d.SetId(fmt.Sprintf("%s:%s", *req.HostedZoneID, *req.VPC.VPCID)) + d.Set("vpc_region", req.VPC.VPCRegion) + + // Wait until we are done initializing + wait := resource.StateChangeConf{ + Delay: 30 * time.Second, + Pending: []string{"PENDING"}, + Target: "INSYNC", + Timeout: 10 * time.Minute, + MinTimeout: 2 * time.Second, + Refresh: func() (result interface{}, state string, err error) { + changeRequest := &route53.GetChangeInput{ + ID: aws.String(cleanChangeID(*resp.ChangeInfo.ID)), + } + return resourceAwsGoRoute53Wait(r53, changeRequest) + }, + } + _, err = wait.WaitForState() + if err != nil { + return err + } + + return resourceAwsRoute53ZoneAssociationUpdate(d, meta) +} + +func resourceAwsRoute53ZoneAssociationRead(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + zone_id, vpc_id := resourceAwsRoute53ZoneAssociationParseId(d.Id()) + zone, err := r53.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(zone_id)}) + if err != nil { + // Handle a deleted zone + if r53err, ok := err.(aws.APIError); ok && r53err.Code == "NoSuchHostedZone" { + d.SetId("") + return nil + } + return err + } + + for _, vpc := range zone.VPCs { + if vpc_id == *vpc.VPCID { + // association is there, return + return nil + } + } + + // no association found + d.SetId("") + return nil +} + +func resourceAwsRoute53ZoneAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + return resourceAwsRoute53ZoneAssociationRead(d, meta) +} + +func resourceAwsRoute53ZoneAssociationDelete(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + zone_id, vpc_id := resourceAwsRoute53ZoneAssociationParseId(d.Id()) + log.Printf("[DEBUG] Deleting Route53 Private Zone (%s) association (VPC: %s)", + zone_id, vpc_id) + + req := &route53.DisassociateVPCFromHostedZoneInput{ + HostedZoneID: aws.String(zone_id), + VPC: &route53.VPC{ + VPCID: aws.String(vpc_id), + VPCRegion: aws.String(d.Get("vpc_region").(string)), + }, + Comment: aws.String("Managed by Terraform"), + } + + _, err := r53.DisassociateVPCFromHostedZone(req) + if err != nil { + return err + } + + return nil +} + +func resourceAwsRoute53ZoneAssociationParseId(id string) (zone_id, vpc_id string) { + parts := strings.SplitN(id, ":", 2) + zone_id = parts[0] + vpc_id = parts[1] + return +} diff --git a/builtin/providers/aws/resource_aws_route53_zone_association_test.go b/builtin/providers/aws/resource_aws_route53_zone_association_test.go new file mode 100644 index 000000000..8a734463d --- /dev/null +++ b/builtin/providers/aws/resource_aws_route53_zone_association_test.go @@ -0,0 +1,218 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/route53" +) + +func TestAccRoute53ZoneAssociation_basic(t *testing.T) { + var zone route53.HostedZone + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRoute53ZoneAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ZoneAssociationExists("aws_route53_zone_association.foobar", &zone), + ), + }, + }, + }) +} + +func TestAccRoute53ZoneAssociation_region(t *testing.T) { + var zone route53.HostedZone + + // record the initialized providers so that we can use them to + // check for the instances in each region + var providers []*schema.Provider + providerFactories := map[string]terraform.ResourceProviderFactory{ + "aws": func() (terraform.ResourceProvider, error) { + p := Provider() + providers = append(providers, p.(*schema.Provider)) + return p, nil + }, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckRoute53ZoneAssociationDestroyWithProviders(&providers), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRoute53ZoneAssociationRegionConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ZoneAssociationExistsWithProviders("aws_route53_zone_association.foobar", &zone, &providers), + ), + }, + }, + }) +} + +func testAccCheckRoute53ZoneAssociationDestroy(s *terraform.State) error { + return testAccCheckRoute53ZoneAssociationDestroyWithProvider(s, testAccProvider) +} + +func testAccCheckRoute53ZoneAssociationDestroyWithProviders(providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, provider := range *providers { + if provider.Meta() == nil { + continue + } + if err := testAccCheckRoute53ZoneAssociationDestroyWithProvider(s, provider); err != nil { + return err + } + } + return nil + } +} + +func testAccCheckRoute53ZoneAssociationDestroyWithProvider(s *terraform.State, provider *schema.Provider) error { + conn := provider.Meta().(*AWSClient).r53conn + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_route53_zone_association" { + continue + } + + zone_id, vpc_id := resourceAwsRoute53ZoneAssociationParseId(rs.Primary.ID) + + resp, err := conn.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(zone_id)}) + if err != nil { + exists := false + for _, vpc := range resp.VPCs { + if vpc_id == *vpc.VPCID { + exists = true + } + } + if exists { + return fmt.Errorf("VPC: %v is still associated to HostedZone: %v", vpc_id, zone_id) + } + } + } + return nil +} + +func testAccCheckRoute53ZoneAssociationExists(n string, zone *route53.HostedZone) resource.TestCheckFunc { + return func(s *terraform.State) error { + return testAccCheckRoute53ZoneAssociationExistsWithProvider(s, n, zone, testAccProvider) + } +} + +func testAccCheckRoute53ZoneAssociationExistsWithProviders(n string, zone *route53.HostedZone, providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, provider := range *providers { + if provider.Meta() == nil { + continue + } + if err := testAccCheckRoute53ZoneAssociationExistsWithProvider(s, n, zone, provider); err != nil { + return err + } + } + return nil + } +} + +func testAccCheckRoute53ZoneAssociationExistsWithProvider(s *terraform.State, n string, zone *route53.HostedZone, provider *schema.Provider) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No zone association ID is set") + } + + zone_id, vpc_id := resourceAwsRoute53ZoneAssociationParseId(rs.Primary.ID) + + conn := provider.Meta().(*AWSClient).r53conn + resp, err := conn.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(zone_id)}) + if err != nil { + return fmt.Errorf("Hosted zone err: %v", err) + } + + exists := false + for _, vpc := range resp.VPCs { + if vpc_id == *vpc.VPCID { + exists = true + } + } + if !exists { + return fmt.Errorf("Hosted zone association not found") + } + + *zone = *resp.HostedZone + return nil +} + +const testAccRoute53ZoneAssociationConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.6.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_vpc" "bar" { + cidr_block = "10.7.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_route53_zone" "foo" { + name = "foo.com" + vpc_id = "${aws_vpc.foo.id}" +} + +resource "aws_route53_zone_association" "foobar" { + zone_id = "${aws_route53_zone.foo.id}" + vpc_id = "${aws_vpc.bar.id}" +} +` + +const testAccRoute53ZoneAssociationRegionConfig = ` +provider "aws" { + alias = "west" + region = "us-west-2" +} + +provider "aws" { + alias = "east" + region = "us-east-1" +} + +resource "aws_vpc" "foo" { + provider = "aws.west" + cidr_block = "10.6.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_vpc" "bar" { + provider = "aws.east" + cidr_block = "10.7.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_route53_zone" "foo" { + provider = "aws.west" + name = "foo.com" + vpc_id = "${aws_vpc.foo.id}" +} + +resource "aws_route53_zone_association" "foobar" { + provider = "aws.west" + zone_id = "${aws_route53_zone.foo.id}" + vpc_id = "${aws_vpc.bar.id}" + vpc_region = "us-east-1" +} +` diff --git a/builtin/providers/aws/resource_aws_route53_zone_test.go b/builtin/providers/aws/resource_aws_route53_zone_test.go index 0a32cb2cd..6009093d6 100644 --- a/builtin/providers/aws/resource_aws_route53_zone_test.go +++ b/builtin/providers/aws/resource_aws_route53_zone_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/awslabs/aws-sdk-go/aws" @@ -63,8 +64,8 @@ func TestCleanChangeID(t *testing.T) { } } -func TestAccRoute53Zone(t *testing.T) { - var zone route53.HostedZone +func TestAccRoute53Zone_basic(t *testing.T) { + var zone route53.GetHostedZoneOutput var td route53.ResourceTagSet resource.Test(t, resource.TestCase{ @@ -84,8 +85,75 @@ func TestAccRoute53Zone(t *testing.T) { }) } +func TestAccRoute53Zone_private_basic(t *testing.T) { + var zone route53.GetHostedZoneOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ZoneDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRoute53PrivateZoneConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ZoneExists("aws_route53_zone.main", &zone), + testAccCheckRoute53ZoneAssociatesWithVpc("aws_vpc.main", &zone), + ), + }, + }, + }) +} + +func TestAccRoute53Zone_private_region(t *testing.T) { + var zone route53.GetHostedZoneOutput + + // record the initialized providers so that we can use them to + // check for the instances in each region + var providers []*schema.Provider + providerFactories := map[string]terraform.ResourceProviderFactory{ + "aws": func() (terraform.ResourceProvider, error) { + p := Provider() + providers = append(providers, p.(*schema.Provider)) + return p, nil + }, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckRoute53ZoneDestroyWithProviders(&providers), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRoute53PrivateZoneRegionConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ZoneExistsWithProviders("aws_route53_zone.main", &zone, &providers), + testAccCheckRoute53ZoneAssociatesWithVpc("aws_vpc.main", &zone), + ), + }, + }, + }) +} + func testAccCheckRoute53ZoneDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).r53conn + return testAccCheckRoute53ZoneDestroyWithProvider(s, testAccProvider) +} + +func testAccCheckRoute53ZoneDestroyWithProviders(providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, provider := range *providers { + if provider.Meta() == nil { + continue + } + if err := testAccCheckRoute53ZoneDestroyWithProvider(s, provider); err != nil { + return err + } + } + return nil + } +} + +func testAccCheckRoute53ZoneDestroyWithProvider(s *terraform.State, provider *schema.Provider) error { + conn := provider.Meta().(*AWSClient).r53conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_route53_zone" { continue @@ -99,23 +167,43 @@ func testAccCheckRoute53ZoneDestroy(s *terraform.State) error { return nil } -func testAccCheckRoute53ZoneExists(n string, zone *route53.HostedZone) resource.TestCheckFunc { +func testAccCheckRoute53ZoneExists(n string, zone *route53.GetHostedZoneOutput) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } + return testAccCheckRoute53ZoneExistsWithProvider(s, n, zone, testAccProvider) + } +} - if rs.Primary.ID == "" { - return fmt.Errorf("No hosted zone ID is set") +func testAccCheckRoute53ZoneExistsWithProviders(n string, zone *route53.GetHostedZoneOutput, providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, provider := range *providers { + if provider.Meta() == nil { + continue + } + if err := testAccCheckRoute53ZoneExistsWithProvider(s, n, zone, provider); err != nil { + return err + } } + return nil + } +} - conn := testAccProvider.Meta().(*AWSClient).r53conn - resp, err := conn.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(rs.Primary.ID)}) - if err != nil { - return fmt.Errorf("Hosted zone err: %v", err) - } +func testAccCheckRoute53ZoneExistsWithProvider(s *terraform.State, n string, zone *route53.GetHostedZoneOutput, provider *schema.Provider) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No hosted zone ID is set") + } + + conn := provider.Meta().(*AWSClient).r53conn + resp, err := conn.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(rs.Primary.ID)}) + if err != nil { + return fmt.Errorf("Hosted zone err: %v", err) + } + + if !*resp.HostedZone.Config.PrivateZone { sorted_ns := make([]string, len(resp.DelegationSet.NameServers)) for i, ns := range resp.DelegationSet.NameServers { sorted_ns[i] = *ns @@ -128,17 +216,41 @@ func testAccCheckRoute53ZoneExists(n string, zone *route53.HostedZone) resource. return fmt.Errorf("Got: %v for %v, Expected: %v", dsns, attribute, ns) } } + } - *zone = *resp.HostedZone + *zone = *resp + return nil +} + +func testAccCheckRoute53ZoneAssociatesWithVpc(n string, zone *route53.GetHostedZoneOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No VPC ID is set") + } + + var associatedVPC *route53.VPC + for _, vpc := range zone.VPCs { + if *vpc.VPCID == rs.Primary.ID { + associatedVPC = vpc + } + } + if associatedVPC == nil { + return fmt.Errorf("VPC: %v is not associated to Zone: %v", n, cleanZoneID(*zone.HostedZone.ID)) + } return nil } } -func testAccLoadTagsR53(zone *route53.HostedZone, td *route53.ResourceTagSet) resource.TestCheckFunc { +func testAccLoadTagsR53(zone *route53.GetHostedZoneOutput, td *route53.ResourceTagSet) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).r53conn - zone := cleanZoneID(*zone.ID) + zone := cleanZoneID(*zone.HostedZone.ID) req := &route53.ListTagsForResourceInput{ ResourceID: aws.String(zone), ResourceType: aws.String("hostedzone"), @@ -167,3 +279,44 @@ resource "aws_route53_zone" "main" { } } ` + +const testAccRoute53PrivateZoneConfig = ` +resource "aws_vpc" "main" { + cidr_block = "172.29.0.0/24" + instance_tenancy = "default" + enable_dns_support = true + enable_dns_hostnames = true +} + +resource "aws_route53_zone" "main" { + name = "hashicorp.com" + vpc_id = "${aws_vpc.main.id}" +} +` + +const testAccRoute53PrivateZoneRegionConfig = ` +provider "aws" { + alias = "west" + region = "us-west-2" +} + +provider "aws" { + alias = "east" + region = "us-east-1" +} + +resource "aws_vpc" "main" { + provider = "aws.east" + cidr_block = "172.29.0.0/24" + instance_tenancy = "default" + enable_dns_support = true + enable_dns_hostnames = true +} + +resource "aws_route53_zone" "main" { + provider = "aws.west" + name = "hashicorp.com" + vpc_id = "${aws_vpc.main.id}" + vpc_region = "us-east-1" +} +` diff --git a/website/source/docs/providers/aws/r/route53_zone.html.markdown b/website/source/docs/providers/aws/r/route53_zone.html.markdown index 71c03a10e..7d027bd18 100644 --- a/website/source/docs/providers/aws/r/route53_zone.html.markdown +++ b/website/source/docs/providers/aws/r/route53_zone.html.markdown @@ -49,17 +49,23 @@ resource "aws_route53_record" "dev-ns" { } ``` +~> **NOTE:** The `name_servers` set is populated only for public Hosted Zones. +Private Zones will contain any empty set since AWS does not return a `DelegationSet` +for private Hosted Zones. + ## Argument Reference The following arguments are supported: * `name` - (Required) This is the name of the hosted zone. * `tags` - (Optional) A mapping of tags to assign to the zone. +* `vpc_id` - (Optional) The VPC to associate with a private hosted zone. Specifying `vpc_id` will create a private hosted zone. +* `vpc_region` - (Optional) The VPC's region. Defaults to the region of the AWS provider. ## Attributes Reference The following attributes are exported: * `zone_id` - The Hosted Zone ID. This can be referenced by zone records. -* `name_servers` - A list of name servers in a default delegation set. +* `name_servers` - A list of name servers in a default delegation set. Supported only for Public Hosted Zones. Find more about delegation sets in [AWS docs](http://docs.aws.amazon.com/Route53/latest/APIReference/actions-on-reusable-delegation-sets.html). diff --git a/website/source/docs/providers/aws/r/route53_zone_association.html.markdown b/website/source/docs/providers/aws/r/route53_zone_association.html.markdown new file mode 100644 index 000000000..49995fb63 --- /dev/null +++ b/website/source/docs/providers/aws/r/route53_zone_association.html.markdown @@ -0,0 +1,54 @@ +--- +layout: "aws" +page_title: "AWS: aws_route53_zone_association" +sidebar_current: "docs-aws-resource-route53-zone-association" +description: |- + Provides a Route53 private Hosted Zone to VPC association resource. +--- + +# aws\_route53\_zone\_association + +Provides a Route53 private Hosted Zone to VPC association resource. + +## Example Usage + +``` +resource "aws_vpc" "primary" { + cidr_block = "10.6.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_vpc" "secondary" { + cidr_block = "10.7.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_route53_zone" "example" { + name = "example.com" + vpc_id = "${aws_vpc.primary.id}" +} + +resource "aws_route53_zone_assocation" "secondary" { + zone_id = "${aws_route53_zone.example.id}" + vpc_id = "${aws_vpc.secondary.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `zone_id` - (Required) The private hosted zone to associate. +* `vpc_id` - (Required) The VPC to associate with the private hosted zone. +* `vpc_region` - (Optional) The VPC's region. Defaults to the region of the AWS provider. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The calculated unique identifier for the association. +* `zone_id` - The ID of the hosted zone for the association. +* `vpc_id` - The ID of the VPC for the association. +* `vpc_region` - The region in which the VPC identified by `vpc_id` was created. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 528946ae9..473152864 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -144,6 +144,10 @@ aws_route53_zone +