diff --git a/google/resource_dns_record_set.go b/google/resource_dns_record_set.go index 0f322bd8..743aed57 100644 --- a/google/resource_dns_record_set.go +++ b/google/resource_dns_record_set.go @@ -77,6 +77,33 @@ func resourceDnsRecordSetCreate(d *schema.ResourceData, meta interface{}) error }, } + // we need to replace NS record sets in the same call. That means + // we need to list all the current NS record sets attached to the + // zone and add them to the change as deletions. We can't just add + // new NS record sets, or we'll get an error about the NS record set + // already existing; see terraform-providers/terraform-provider-google#95. + // We also can't just remove the NS recordsets on creation, as at + // least one is required. So the solution is to "update in place" by + // putting the addition and the removal in the same API call. + if d.Get("type").(string) == "NS" { + log.Printf("[DEBUG] DNS record list request for %q", zone) + res, err := config.clientDns.ResourceRecordSets.List(project, zone).Do() + if err != nil { + return fmt.Errorf("Error retrieving record sets for %q: %s", zone, err) + } + var deletions []*dns.ResourceRecordSet + + for _, record := range res.Rrsets { + if record.Type != "NS" { + continue + } + deletions = append(deletions, record) + } + if len(deletions) > 0 { + chg.Deletions = deletions + } + } + log.Printf("[DEBUG] DNS Record create request: %#v", chg) chg, err = config.clientDns.Changes.Create(project, zone, chg).Do() if err != nil { @@ -135,6 +162,14 @@ func resourceDnsRecordSetRead(d *schema.ResourceData, meta interface{}) error { } func resourceDnsRecordSetDelete(d *schema.ResourceData, meta interface{}) error { + + // NS records must always have a value, so we short-circuit delete + // this allows terraform delete to work, but may have unexpected + // side-effects when deleting just that record set. + if d.Get("type").(string) == "NS" { + log.Println("[DEBUG] NS records can't be deleted due to API restrictions, so they're being left in place. See https://www.terraform.io/docs/providers/google/r/dns_record_set.html for more information.") + return nil + } config := meta.(*Config) project, err := getProject(d, config) diff --git a/google/resource_dns_record_set_test.go b/google/resource_dns_record_set_test.go index 7787186a..94aac3ef 100644 --- a/google/resource_dns_record_set_test.go +++ b/google/resource_dns_record_set_test.go @@ -90,6 +90,24 @@ func TestAccDnsRecordSet_changeType(t *testing.T) { }) } +func TestAccDnsRecordSet_ns(t *testing.T) { + zoneName := fmt.Sprintf("dnszone-test-ns-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDnsRecordSetDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDnsRecordSet_ns(zoneName, 300), + Check: resource.ComposeTestCheckFunc( + testAccCheckDnsRecordSetExists( + "google_dns_record_set.foobar", zoneName), + ), + }, + }, + }) +} + func testAccCheckDnsRecordSetDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -157,6 +175,23 @@ func testAccDnsRecordSet_basic(zoneName string, addr2 string, ttl int) string { `, zoneName, addr2, ttl) } +func testAccDnsRecordSet_ns(name string, ttl int) string { + return fmt.Sprintf(` + resource "google_dns_managed_zone" "parent-zone" { + name = "%s" + dns_name = "hashicorptest.com." + description = "Test Description" + } + resource "google_dns_record_set" "foobar" { + managed_zone = "${google_dns_managed_zone.parent-zone.name}" + name = "hashicorptest.com." + type = "NS" + rrdatas = ["ns.hashicorp.services.", "ns2.hashicorp.services."] + ttl = %d + } + `, name, ttl) +} + func testAccDnsRecordSet_bigChange(zoneName string, ttl int) string { return fmt.Sprintf(` resource "google_dns_managed_zone" "parent-zone" { diff --git a/website/docs/r/dns_record_set.markdown b/website/docs/r/dns_record_set.markdown index f0b721b9..5e18c048 100644 --- a/website/docs/r/dns_record_set.markdown +++ b/website/docs/r/dns_record_set.markdown @@ -10,6 +10,12 @@ description: |- Manages a set of DNS records within Google Cloud DNS. +~> **Note:** The Google Cloud DNS API requires NS records be present at all +times. To accommodate this, when creating NS records, the default records +Google automatically creates will be silently overwritten. Also, when +destroying NS records, Terraform will not actually remove NS records, but will +report that it did. + ## Example Usage ### Binding a DNS name to the ephemeral IP of a new instance: