diff --git a/.changelog/3315.txt b/.changelog/3315.txt new file mode 100644 index 0000000000..d3ff974d7c --- /dev/null +++ b/.changelog/3315.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/resource_cloudflare_zone: add support for 'vanity_name_servers' +``` \ No newline at end of file diff --git a/docs/resources/zone.md b/docs/resources/zone.md index beb84e2608..1721b53956 100644 --- a/docs/resources/zone.md +++ b/docs/resources/zone.md @@ -37,6 +37,7 @@ resource "cloudflare_zone" "example" { - `paused` (Boolean) Whether this zone is paused (traffic bypasses Cloudflare). Defaults to `false`. - `plan` (String) The name of the commercial plan to apply to the zone. Available values: `free`, `lite`, `pro`, `pro_plus`, `business`, `enterprise`, `partners_free`, `partners_pro`, `partners_business`, `partners_enterprise`. - `type` (String) A full zone implies that DNS is hosted with Cloudflare. A partial zone is typically a partner-hosted zone or a CNAME setup. Available values: `full`, `partial`, `secondary`. Defaults to `full`. +- `vanity_name_servers` (List of String) List of Vanity Nameservers (if set). ### Read-Only @@ -44,7 +45,6 @@ resource "cloudflare_zone" "example" { - `meta` (Map of Boolean) - `name_servers` (List of String) Cloudflare-assigned name servers. This is only populated for zones that use Cloudflare DNS. - `status` (String) Status of the zone. Available values: `active`, `pending`, `initializing`, `moved`, `deleted`, `deactivated`. -- `vanity_name_servers` (List of String) List of Vanity Nameservers (if set). - `verification_key` (String) Contains the TXT record value to validate domain ownership. This is only populated for zones of type `partial`. ## Import diff --git a/internal/sdkv2provider/resource_cloudflare_zone.go b/internal/sdkv2provider/resource_cloudflare_zone.go index 7d9d9e9b35..16c5364a9b 100644 --- a/internal/sdkv2provider/resource_cloudflare_zone.go +++ b/internal/sdkv2provider/resource_cloudflare_zone.go @@ -150,6 +150,15 @@ func resourceCloudflareZoneCreate(ctx context.Context, d *schema.ResourceData, m } } + vNS := []string{} + if vanityNS, ok := d.GetOk("vanity_name_servers"); ok { + vNS = expandInterfaceToStringList(vanityNS.([]interface{})) + _, err := client.ZoneSetVanityNS(ctx, zone.ID, vNS) + if err != nil { + return diag.FromErr(fmt.Errorf("error setting vanity_name_servers on zone ID %q: %w", zone.ID, err)) + } + } + return resourceCloudflareZoneRead(ctx, d, meta) } @@ -221,6 +230,15 @@ func resourceCloudflareZoneUpdate(ctx context.Context, d *schema.ResourceData, m } } + vNS := []string{} + if vanityNS, ok := d.GetOkExists("vanity_name_servers"); ok && d.HasChange("vanity_name_servers") { + vNS = expandInterfaceToStringList(vanityNS.([]interface{})) + _, err := client.ZoneSetVanityNS(ctx, zone.ID, vNS) + if err != nil { + return diag.FromErr(fmt.Errorf("error setting vanity_name_servers on zone ID %q: %w", zone.ID, err)) + } + } + // In the cases where the zone isn't completely setup yet, we need to // check the `status` field and should it be pending, use the `LegacyID` // from `zone.PlanPending` instead to account for paid plans. diff --git a/internal/sdkv2provider/resource_cloudflare_zone_test.go b/internal/sdkv2provider/resource_cloudflare_zone_test.go index 1d16174ab0..cf3964bf02 100644 --- a/internal/sdkv2provider/resource_cloudflare_zone_test.go +++ b/internal/sdkv2provider/resource_cloudflare_zone_test.go @@ -226,6 +226,31 @@ func TestAccCloudflareZone_WithEnterprisePlan(t *testing.T) { }) } +func TestAccCloudflareZone_WithEnterprisePlanVanityNameServers(t *testing.T) { + rnd := generateRandomResourceName() + name := "cloudflare_zone." + rnd + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + zoneName := os.Getenv("CLOUDFLARE_DOMAIN") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: testZoneConfigWithTypeVanityNameServersSetup(rnd, fmt.Sprintf("%s.%s", rnd, zoneName), "false", "false", "enterprise", accountID, "full"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "zone", fmt.Sprintf("%s.%s", rnd, zoneName)), + resource.TestCheckResourceAttr(name, "paused", "false"), + resource.TestCheckResourceAttr(name, "name_servers.#", "2"), + resource.TestCheckResourceAttr(name, "plan", planIDEnterprise), + resource.TestCheckResourceAttr(name, "type", "full"), + resource.TestCheckResourceAttr(name, "vanity_name_servers.#", "2"), + ), + }, + }, + }) +} + func TestAccCloudflareZone_Secondary(t *testing.T) { rnd := generateRandomResourceName() name := "cloudflare_zone." + rnd @@ -250,6 +275,31 @@ func TestAccCloudflareZone_Secondary(t *testing.T) { }) } +func TestAccCloudflareZone_SecondaryWithVanityNameServers(t *testing.T) { + rnd := generateRandomResourceName() + name := "cloudflare_zone." + rnd + accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + zoneName := os.Getenv("CLOUDFLARE_DOMAIN") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: testZoneConfigWithTypeVanityNameServersSetup(rnd, fmt.Sprintf("%s.%s", rnd, zoneName), "true", "false", "enterprise", accountID, "secondary"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "zone", fmt.Sprintf("%s.%s", rnd, zoneName)), + resource.TestCheckResourceAttr(name, "paused", "true"), + resource.TestCheckResourceAttr(name, "name_servers.#", "2"), + resource.TestCheckResourceAttr(name, "plan", planIDEnterprise), + resource.TestCheckResourceAttr(name, "type", "secondary"), + resource.TestCheckResourceAttr(name, "vanity_name_servers.#", "2"), + ), + }, + }, + }) +} + func testZoneConfig(resourceID, zoneName, paused, jumpStart, accountID string) string { return fmt.Sprintf(` resource "cloudflare_zone" "%[1]s" { @@ -303,3 +353,16 @@ func testZoneConfigWithTypeSetup(resourceID, zoneName, paused, jumpStart, plan, type = "%[7]s" }`, resourceID, zoneName, paused, jumpStart, plan, accountID, zoneType) } + +func testZoneConfigWithTypeVanityNameServersSetup(resourceID, zoneName, paused, jumpStart, plan, accountID, zoneType string) string { + return fmt.Sprintf(` + resource "cloudflare_zone" "%[1]s" { + account_id = "%[6]s" + zone = "%[2]s" + paused = %[3]s + jump_start = %[4]s + plan = "%[5]s" + type = "%[7]s" + vanity_name_servers = ["ns1.%[2]s", "ns2.%[2]s"] + }`, resourceID, zoneName, paused, jumpStart, plan, accountID, zoneType) +} diff --git a/internal/sdkv2provider/schema_cloudflare_zone.go b/internal/sdkv2provider/schema_cloudflare_zone.go index e2c5c1df94..b6d70c11da 100644 --- a/internal/sdkv2provider/schema_cloudflare_zone.go +++ b/internal/sdkv2provider/schema_cloudflare_zone.go @@ -35,6 +35,7 @@ func resourceCloudflareZoneSchema() map[string]*schema.Schema { }, "vanity_name_servers": { Type: schema.TypeList, + Optional: true, Computed: true, Elem: &schema.Schema{ Type: schema.TypeString,