From 3aa9feaa3c52e409f80a1bcf182252ff8dc01170 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Tue, 21 Jul 2020 08:57:18 +1000 Subject: [PATCH 01/10] add schema scaffold for custom_hostname --- .../resource_cloudflare_custom_hostname.go | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 cloudflare/resource_cloudflare_custom_hostname.go diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go new file mode 100644 index 0000000000..c484a5b265 --- /dev/null +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -0,0 +1,168 @@ +package cloudflare + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceCloudflareCustomHostname() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudflareCustomHostnameCreate, + Read: resourceCloudflareCustomHostnameRead, + Update: resourceCloudflareCustomHostnameUpdate, + Delete: resourceCloudflareCustomHostnameDelete, + Importer: &schema.ResourceImporter{ + State: resourceCloudflareCustomHostnameImport, + }, + + SchemaVersion: 0, + Schema: map[string]*schema.Schema{ + "hostname": { + Type: schema.TypeString, + Required: true, + }, + "custom_origin_servver": { + Type: schema.TypeString, + Required: true, + }, + "ssl": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "status": { + Type: schema.TypeString, + Optional: true, + }, + "method": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + }, + "cname_target": { + Type: schema.TypeString, + Optional: true, + }, + "cname_name": { + Type: schema.TypeString, + Optional: true, + }, + "wildcard": { + Type: schema.TypeBool, + Optional: true, + }, + "custom_certificate": { + Type: schema.TypeString, + Optional: true, + }, + "custom_key": { + Type: schema.TypeString, + Optional: true, + }, + "settings": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "http2": { + Type: schema.TypeString, + Optional: true, + }, + "tls13": { + Type: schema.TypeString, + Optional: true, + }, + "min_tls_version": { + Type: schema.TypeString, + Optional: true, + }, + "ciphers": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "ownership_verification": { + Type: schema.TypeList, + Computed: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "ownership_verification_http": { + Type: schema.TypeList, + Computed: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "http_url": { + Type: schema.TypeString, + Computed: true, + }, + "http_body": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceCloudflareCustomHostnameRead(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceCloudflareCustomHostnameDelete(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceCloudflareCustomHostnameCreate(d *schema.ResourceData, meta interface{}) error { + return resourceCloudflareCustomHostnameRead(d, meta) +} + +func resourceCloudflareCustomHostnameUpdate(d *schema.ResourceData, meta interface{}) error { + return resourceCloudflareCustomHostnameRead(d, meta) +} + +func resourceCloudflareCustomHostnameImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + return []*schema.ResourceData{d}, nil +} From 4834f2343b59d91bdc1ef2eab1ebe4c99f7e42ef Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Tue, 21 Jul 2020 09:04:33 +1000 Subject: [PATCH 02/10] add basic schema validation --- .../resource_cloudflare_custom_hostname.go | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go index c484a5b265..caa016f32a 100644 --- a/cloudflare/resource_cloudflare_custom_hostname.go +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -2,6 +2,7 @@ package cloudflare import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) func resourceCloudflareCustomHostname() *schema.Resource { @@ -17,12 +18,13 @@ func resourceCloudflareCustomHostname() *schema.Resource { SchemaVersion: 0, Schema: map[string]*schema.Schema{ "hostname": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 255), }, "custom_origin_servver": { Type: schema.TypeString, - Required: true, + Optional: true, }, "ssl": { Type: schema.TypeList, @@ -37,12 +39,14 @@ func resourceCloudflareCustomHostname() *schema.Resource { Optional: true, }, "method": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"http", "txt", "email"}, false), }, "type": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"dv"}, false), }, "cname_target": { Type: schema.TypeString, @@ -73,16 +77,19 @@ func resourceCloudflareCustomHostname() *schema.Resource { SchemaVersion: 1, Schema: map[string]*schema.Schema{ "http2": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), }, "tls13": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false), }, "min_tls_version": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"1.0", "1.1", "1.2", "1.3"}, false), }, "ciphers": { Type: schema.TypeList, From a92e09bd104dcf581b77b9683f7425f02398e9cc Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Tue, 21 Jul 2020 09:14:03 +1000 Subject: [PATCH 03/10] add certificate_authority to ssl settings schema --- cloudflare/resource_cloudflare_custom_hostname.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go index caa016f32a..3ef04d914c 100644 --- a/cloudflare/resource_cloudflare_custom_hostname.go +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -48,6 +48,11 @@ func resourceCloudflareCustomHostname() *schema.Resource { Optional: true, ValidateFunc: validation.StringInSlice([]string{"dv"}, false), }, + "certificate_authority": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"lets_encrypt", "digicert"}, false), + }, "cname_target": { Type: schema.TypeString, Optional: true, From 6588f77de20636f9b01b075e1a799c981d4eef2b Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 24 Jul 2020 08:00:30 +1000 Subject: [PATCH 04/10] Add provider setup for new resource --- cloudflare/provider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudflare/provider.go b/cloudflare/provider.go index b1f6351de3..4b8fd6f23d 100644 --- a/cloudflare/provider.go +++ b/cloudflare/provider.go @@ -112,6 +112,7 @@ func Provider() terraform.ResourceProvider { "cloudflare_argo": resourceCloudflareArgo(), "cloudflare_byo_ip_prefix": resourceCloudflareBYOIPPrefix(), "cloudflare_custom_pages": resourceCloudflareCustomPages(), + "cloudflare_custom_hostname": resourceCloudflareCustomHostname(), "cloudflare_custom_ssl": resourceCloudflareCustomSsl(), "cloudflare_filter": resourceCloudflareFilter(), "cloudflare_firewall_rule": resourceCloudflareFirewallRule(), From 88c2bf8f8634e679547b8fc83f4d538f72f75a01 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 24 Jul 2020 08:00:49 +1000 Subject: [PATCH 05/10] Add tests for custom hostname resource --- ...esource_cloudflare_custom_hostname_test.go | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 cloudflare/resource_cloudflare_custom_hostname_test.go diff --git a/cloudflare/resource_cloudflare_custom_hostname_test.go b/cloudflare/resource_cloudflare_custom_hostname_test.go new file mode 100644 index 0000000000..900a37bf28 --- /dev/null +++ b/cloudflare/resource_cloudflare_custom_hostname_test.go @@ -0,0 +1,244 @@ +package cloudflare + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccCloudflareCustomHostnameBasic(t *testing.T) { + t.Parallel() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + domain := os.Getenv("CLOUDFLARE_DOMAIN") + rnd := generateRandomResourceName() + resourceName := "cloudflare_custom_hostname." + rnd + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareCustomHostnameBasic(zoneID, rnd, domain), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID), + resource.TestCheckResourceAttr(resourceName, "hostname", fmt.Sprintf("%s.%s", rnd, domain)), + resource.TestCheckResourceAttr(resourceName, "ssl.0.method", "txt"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.value"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.type"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.name"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_url"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_body"), + ), + }, + }, + }) +} + +func testAccCheckCloudflareCustomHostnameBasic(zoneID, rnd, domain string) string { + return fmt.Sprintf(` +resource "cloudflare_custom_hostname" "%[2]s" { + zone_id = "%[1]s" + hostname = "%[2]s.%[3]s" + ssl { + method = "txt" + } +} +`, zoneID, rnd, domain) +} + +func TestAccCloudflareCustomHostnameWithCustomOriginServer(t *testing.T) { + t.Parallel() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + domain := os.Getenv("CLOUDFLARE_DOMAIN") + rnd := generateRandomResourceName() + resourceName := "cloudflare_custom_hostname." + rnd + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareCustomHostnameWithCustomOriginServer(zoneID, rnd, domain), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID), + resource.TestCheckResourceAttr(resourceName, "hostname", fmt.Sprintf("%s.%s", rnd, domain)), + resource.TestCheckResourceAttr(resourceName, "custom_origin_server", fmt.Sprintf("origin.%s.terraform.cfapi.net", rnd)), + resource.TestCheckResourceAttr(resourceName, "ssl.0.method", "txt"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.value"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.type"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.name"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_url"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_body"), + ), + }, + }, + }) +} + +func testAccCheckCloudflareCustomHostnameWithCustomOriginServer(zoneID, rnd, domain string) string { + return fmt.Sprintf(` +resource "cloudflare_custom_hostname" "%[2]s" { + zone_id = "%[1]s" + hostname = "%[2]s.%[3]s" + custom_origin_server = "origin.%[2]s.terraform.cfapi.net" + ssl { + method = "txt" + } +} + +resource "cloudflare_record" "%[2]s" { + zone_id = "%[1]s" + name = "origin.%[2]s.terraform.cfapi.net" + value = "example.com" + type = "CNAME" + ttl = 3600 +}`, zoneID, rnd, domain) +} + +func TestAccCloudflareCustomHostnameWithHTTPValidation(t *testing.T) { + t.Parallel() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + domain := os.Getenv("CLOUDFLARE_DOMAIN") + rnd := generateRandomResourceName() + resourceName := "cloudflare_custom_hostname." + rnd + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareCustomHostnameWithHTTPValidation(zoneID, rnd, domain), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID), + resource.TestCheckResourceAttr(resourceName, "hostname", fmt.Sprintf("%s.%s", rnd, domain)), + resource.TestCheckResourceAttr(resourceName, "ssl.0.method", "http"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.value"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.type"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.name"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_url"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_body"), + ), + }, + }, + }) +} + +func testAccCheckCloudflareCustomHostnameWithHTTPValidation(zoneID, rnd, domain string) string { + return fmt.Sprintf(` +resource "cloudflare_custom_hostname" "%[2]s" { + zone_id = "%[1]s" + hostname = "%[2]s.%[3]s" + ssl { + method = "http" + } +} +`, zoneID, rnd, domain) +} + +func TestAccCloudflareCustomHostnameWithCustomSSLSettings(t *testing.T) { + t.Parallel() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + domain := os.Getenv("CLOUDFLARE_DOMAIN") + rnd := generateRandomResourceName() + resourceName := "cloudflare_custom_hostname." + rnd + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareCustomHostnameWithCustomSSLSettings(zoneID, rnd, domain), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID), + resource.TestCheckResourceAttr(resourceName, "hostname", fmt.Sprintf("%s.%s", rnd, domain)), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.http2", "off"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.min_tls_version", "1.2"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.0", "ECDHE-RSA-AES128-GCM-SHA256"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.1", "AES128-SHA"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.value"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.type"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification.name"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_url"), + resource.TestCheckResourceAttrSet(resourceName, "ownership_verification_http.http_body"), + ), + }, + }, + }) +} + +func testAccCheckCloudflareCustomHostnameWithCustomSSLSettings(zoneID, rnd, domain string) string { + return fmt.Sprintf(` +resource "cloudflare_custom_hostname" "%[2]s" { + zone_id = "%[1]s" + hostname = "%[2]s.%[3]s" + ssl { + method = "http" + settings { + http2 = "off" + min_tls_version = "1.2" + ciphers = [ + "ECDHE-RSA-AES128-GCM-SHA256", + "AES128-SHA" + ] + } + } +} +`, zoneID, rnd, domain) +} + +func TestAccCloudflareCustomHostnameUpdate(t *testing.T) { + t.Parallel() + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + domain := os.Getenv("CLOUDFLARE_DOMAIN") + rnd := generateRandomResourceName() + resourceName := "cloudflare_custom_hostname." + rnd + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareCustomHostnameWithCustomSSLSettings(zoneID, rnd, domain), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID), + resource.TestCheckResourceAttr(resourceName, "hostname", fmt.Sprintf("%s.%s", rnd, domain)), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.http2", "off"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.min_tls_version", "1.2"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.0", "ECDHE-RSA-AES128-GCM-SHA256"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.1", "AES128-SHA"), + ), + }, + { + Config: testAccCheckCloudflareCustomHostnameWithCustomSSLSettingsUpdated(zoneID, rnd, domain), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID), + resource.TestCheckResourceAttr(resourceName, "hostname", fmt.Sprintf("%s.%s", rnd, domain)), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.http2", "off"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.min_tls_version", "1.1"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.0", "ECDHE-RSA-AES128-GCM-SHA256"), + resource.TestCheckResourceAttr(resourceName, "ssl.0.settings.0.ciphers.1", "AES128-SHA"), + ), + }, + }, + }) +} + +func testAccCheckCloudflareCustomHostnameWithCustomSSLSettingsUpdated(zoneID, rnd, domain string) string { + return fmt.Sprintf(` +resource "cloudflare_custom_hostname" "%[2]s" { + zone_id = "%[1]s" + hostname = "%[2]s.%[3]s" + ssl { + method = "http" + settings { + http2 = "off" + min_tls_version = "1.1" + ciphers = [ + "ECDHE-RSA-AES128-GCM-SHA256", + "AES128-SHA" + ] + } + } +} +`, zoneID, rnd, domain) +} From 443dfb149037e6352562bc912af47539e36b3d8e Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 24 Jul 2020 08:01:16 +1000 Subject: [PATCH 06/10] Tweak the schema to support attributes --- .../resource_cloudflare_custom_hostname.go | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go index 3ef04d914c..e50348f643 100644 --- a/cloudflare/resource_cloudflare_custom_hostname.go +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -17,20 +17,23 @@ func resourceCloudflareCustomHostname() *schema.Resource { SchemaVersion: 0, Schema: map[string]*schema.Schema{ + "zone_id": { + Type: schema.TypeString, + Required: true, + }, "hostname": { Type: schema.TypeString, Required: true, + ForceNew: true, ValidateFunc: validation.StringLenBetween(0, 255), }, - "custom_origin_servver": { + "custom_origin_server": { Type: schema.TypeString, Optional: true, }, "ssl": { Type: schema.TypeList, Required: true, - MinItems: 1, - MaxItems: 1, Elem: &schema.Resource{ SchemaVersion: 1, Schema: map[string]*schema.Schema{ @@ -46,6 +49,7 @@ func resourceCloudflareCustomHostname() *schema.Resource { "type": { Type: schema.TypeString, Optional: true, + Default: "dv", ValidateFunc: validation.StringInSlice([]string{"dv"}, false), }, "certificate_authority": { @@ -75,9 +79,7 @@ func resourceCloudflareCustomHostname() *schema.Resource { }, "settings": { Type: schema.TypeList, - Required: true, - MinItems: 1, - MaxItems: 1, + Optional: true, Elem: &schema.Resource{ SchemaVersion: 1, Schema: map[string]*schema.Schema{ @@ -114,10 +116,8 @@ func resourceCloudflareCustomHostname() *schema.Resource { Computed: true, }, "ownership_verification": { - Type: schema.TypeList, + Type: schema.TypeMap, Computed: true, - MinItems: 1, - MaxItems: 1, Elem: &schema.Resource{ SchemaVersion: 1, Schema: map[string]*schema.Schema{ @@ -137,10 +137,8 @@ func resourceCloudflareCustomHostname() *schema.Resource { }, }, "ownership_verification_http": { - Type: schema.TypeList, + Type: schema.TypeMap, Computed: true, - MinItems: 1, - MaxItems: 1, Elem: &schema.Resource{ SchemaVersion: 1, Schema: map[string]*schema.Schema{ From cec8cb889c1eea73fab97ac3997774f4f147b7f4 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 24 Jul 2020 08:01:57 +1000 Subject: [PATCH 07/10] Add `Read` method for syncing state --- .../resource_cloudflare_custom_hostname.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go index e50348f643..d476fcde2a 100644 --- a/cloudflare/resource_cloudflare_custom_hostname.go +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -158,6 +158,42 @@ func resourceCloudflareCustomHostname() *schema.Resource { } func resourceCloudflareCustomHostnameRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + zoneID := d.Get("zone_id").(string) + hostnameID := d.Id() + + customHostname, err := client.CustomHostname(zoneID, hostnameID) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("error reading custom hostname %q", hostnameID)) + } + + d.Set("ssl.custom_origin_server", customHostname.CustomOriginServer) + + d.Set("ssl.0.type", customHostname.SSL.Type) + d.Set("ssl.0.method", customHostname.SSL.Method) + d.Set("ssl.0.wildcard", customHostname.SSL.Wildcard) + d.Set("ssl.0.status", customHostname.SSL.Status) + d.Set("ssl.0.cname_target", customHostname.SSL.CnameTarget) + d.Set("ssl.0.cname_name", customHostname.SSL.CnameName) + d.Set("ssl.0.custom_certificate", customHostname.SSL.CustomCertificate) + d.Set("ssl.0.custom_key", customHostname.SSL.CustomKey) + + d.Set("ssl.0.settings.0.http2", customHostname.SSL.Settings.HTTP2) + d.Set("ssl.0.settings.0.tls13", customHostname.SSL.Settings.TLS13) + d.Set("ssl.0.settings.0.min_tls_version", customHostname.SSL.Settings.MinTLSVersion) + d.Set("ssl.0.settings.0.ciphers", flattenStringList(customHostname.SSL.Settings.Ciphers)) + + ownershipVerificationCfg := map[string]interface{}{} + ownershipVerificationCfg["type"] = customHostname.OwnershipVerification.Type + ownershipVerificationCfg["value"] = customHostname.OwnershipVerification.Value + ownershipVerificationCfg["name"] = customHostname.OwnershipVerification.Name + d.Set("ownership_verification", ownershipVerificationCfg) + + ownershipVerificationHTTPCfg := map[string]interface{}{} + ownershipVerificationHTTPCfg["http_body"] = customHostname.OwnershipVerificationHTTP.HTTPBody + ownershipVerificationHTTPCfg["http_url"] = customHostname.OwnershipVerificationHTTP.HTTPUrl + d.Set("ownership_verification_http", ownershipVerificationHTTPCfg) + return nil } From e053dea0c939b67c62ed871ada162099e5f3235c Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 24 Jul 2020 08:02:13 +1000 Subject: [PATCH 08/10] Add remainder of CRUD operations --- .../resource_cloudflare_custom_hostname.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/cloudflare/resource_cloudflare_custom_hostname.go b/cloudflare/resource_cloudflare_custom_hostname.go index d476fcde2a..ce1a884b1f 100644 --- a/cloudflare/resource_cloudflare_custom_hostname.go +++ b/cloudflare/resource_cloudflare_custom_hostname.go @@ -1,8 +1,14 @@ package cloudflare import ( + "fmt" + "log" + "strings" + + "github.com/cloudflare/cloudflare-go" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/pkg/errors" ) func resourceCloudflareCustomHostname() *schema.Resource { @@ -198,17 +204,85 @@ func resourceCloudflareCustomHostnameRead(d *schema.ResourceData, meta interface } func resourceCloudflareCustomHostnameDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + zoneID := d.Get("zone_id").(string) + hostnameID := d.Id() + + err := client.DeleteCustomHostname(zoneID, hostnameID) + if err != nil { + return errors.Wrap(err, "failed to delete custom hostname certificate") + } + return nil } func resourceCloudflareCustomHostnameCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + zoneID := d.Get("zone_id").(string) + + certificate := buildCustomHostname(d) + + newCertificate, err := client.CreateCustomHostname(zoneID, certificate) + if err != nil { + return errors.Wrap(err, "failed to create custom hostname certificate") + } + + d.SetId(newCertificate.Result.ID) + return resourceCloudflareCustomHostnameRead(d, meta) } func resourceCloudflareCustomHostnameUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + zoneID := d.Get("zone_id").(string) + hostnameID := d.Id() + certificate := buildCustomHostname(d) + + _, err := client.UpdateCustomHostname(zoneID, hostnameID, certificate) + if err != nil { + return errors.Wrap(err, "failed to update custom hostname certificate") + } + return resourceCloudflareCustomHostnameRead(d, meta) } func resourceCloudflareCustomHostnameImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idAttr := strings.SplitN(d.Id(), "/", 2) + + if len(idAttr) != 2 { + return nil, fmt.Errorf("invalid id (\"%s\") specified, should be in format \"zoneID/customHostnameID\"", d.Id()) + } + + zoneID, hostnameID := idAttr[0], idAttr[1] + + log.Printf("[DEBUG] Importing Cloudflare Custom Hostname: id %s for zone %s", hostnameID, zoneID) + + d.Set("zone_id", zoneID) + d.SetId(hostnameID) + return []*schema.ResourceData{d}, nil } + +// buildCustomHostname takes the existing schema and returns a +// `cloudflare.CustomHostname`. +func buildCustomHostname(d *schema.ResourceData) cloudflare.CustomHostname { + return cloudflare.CustomHostname{ + Hostname: d.Get("hostname").(string), + CustomOriginServer: d.Get("custom_origin_server").(string), + SSL: cloudflare.CustomHostnameSSL{ + Method: d.Get("ssl.0.method").(string), + Type: d.Get("ssl.0.type").(string), + Wildcard: d.Get("ssl.0.wildcard").(bool), + CnameTarget: d.Get("ssl.0.cname_target").(string), + CnameName: d.Get("ssl.0.cname_name").(string), + CustomCertificate: d.Get("ssl.0.custom_certificate").(string), + CustomKey: d.Get("ssl.0.custom_key").(string), + Settings: cloudflare.CustomHostnameSSLSettings{ + HTTP2: d.Get("ssl.0.settings.0.http2").(string), + TLS13: d.Get("ssl.0.settings.0.tls13").(string), + MinTLSVersion: d.Get("ssl.0.settings.0.min_tls_version").(string), + Ciphers: expandInterfaceToStringList(d.Get("ssl.0.settings.0.ciphers").([]interface{})), + }, + }, + } +} From 27a1777a9449bb4b73ca2ca938ab1d7992dd0746 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Fri, 24 Jul 2020 08:36:40 +1000 Subject: [PATCH 09/10] add website documentation --- website/cloudflare.erb | 3 + website/docs/r/custom_hostname.html.markdown | 74 ++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 website/docs/r/custom_hostname.html.markdown diff --git a/website/cloudflare.erb b/website/cloudflare.erb index 54cf272146..9c9ed271bd 100644 --- a/website/cloudflare.erb +++ b/website/cloudflare.erb @@ -73,6 +73,9 @@ > cloudflare_custom_pages + > + cloudflare_custom_hostname + > cloudflare_custom_ssl diff --git a/website/docs/r/custom_hostname.html.markdown b/website/docs/r/custom_hostname.html.markdown new file mode 100644 index 0000000000..0e5da6507b --- /dev/null +++ b/website/docs/r/custom_hostname.html.markdown @@ -0,0 +1,74 @@ +--- +layout: "cloudflare" +page_title: "Cloudflare: cloudflare_custom_hostname" +sidebar_current: "docs-cloudflare-resource-custom-hostname" +description: !- + Provides a Cloudflare custom hostname resource. +--- + +# cloudflare_custom_hostname + +Provides a Cloudflare custom hostname (also known as SSL for SaaS) resource. + +## Example Usage + +```hcl +resource "cloudflare_custom_hostname" "example_hostname" { + zone_id = "d41d8cd98f00b204e9800998ecf8427e" + hostname = "hostname.example.com" + ssl { + method = "txt" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `zone_id` - (Required) The DNS zone ID where the custom hostname should be assigned. +* `hostname` - (Required) Hostname you intend to request a certificate for. +* `custom_origin_server` - (Optional) The custom origin server used for certificates. +* `ssl` - (Required) SSL configuration of the certificate. See further notes below. + +**ssl** block supports: + +* `method` - (Required) Domain control validation (DCV) method used for this + hostname. Valid values are `"txt"`, `"http"` and `"email"`. +* `type` - (Required) Level of validation to be used for this hostname. Domain validation ("dv") must be used. +* `wildcard` - (Required) Indicates whether the certificate covers a wildcard. +* `custom_certificate` - (Optional) If a custom uploaded certificate is used. +* `custom_key` - (Optional) The key for a custom uploaded certificate. +* `settings` - (Required) SSL/TLS settings for the certificate. See further notes below. + +**settings** block supports: + +* `http2` - (Optional) Whether or not HTTP2 should be supported. Valid values are `"on"` or `"off"`. +* `tls12` - (Optional) Whether or not TLSv1.3 should be supported. Valid values are `"on"` or `"off"`. +* `min_tls_version` - (Optional) Lowest version of TLS this certificate should + support. Valid values are `"1.0"`, `"1.1"`, `"1.2"` and `"1.3"`. +* `ciphers` - (Optional) List of SSL/TLS ciphers to associate with this certificate. + +## Attributes Reference + +The following attributes are exported: + +* `ownership_verification.type` - Domain control validation (DCV) method used + for the hostname. +* `ownership_verification.value` - Domain control validation (DCV) value for + confirming ownership. Example, "_cf-custom-hostname.example.com` +* `ownership_verification.name` - Domain control validation (DCV) name + confirming ownership. Example, "03f28e11-fa64-4966-bb1e-dd2423e16f36"` +* `ownership_verification_http.http_url` - Domain control validation (DCV) URL for + confirming ownership. Example, `http://hostname.example.com/.well-known/cf-custom-hostname-challenge/643395f9-de80-42f5-a2a0-e03ff60cf2a7` +* `ownership_verification_http.http_body` - Domain control validation (DCV) body for + confirming ownership. Example, `03f28e11-fa64-4966-bb1e-dd2423e16f36` + +## Import + +Custom hostname certificates can be imported using a composite ID formed of the zone ID and [hostname ID](https://api.cloudflare.com/#custom-hostname-for-a-zone-properties), +separated by a "/" e.g. + +``` +$ terraform import cloudflare_custom_hostname.example d41d8cd98f00b204e9800998ecf8427e/0d89c70d-ad9f-4843-b99f-6cc0252067e9 +``` From eadab77d346d8da39df6b1e782043cba7ad444d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Szczyg=C5=82owski?= Date: Fri, 24 Jul 2020 11:54:26 +0100 Subject: [PATCH 10/10] typo --- website/docs/r/custom_hostname.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/custom_hostname.html.markdown b/website/docs/r/custom_hostname.html.markdown index 0e5da6507b..0f6b5b0086 100644 --- a/website/docs/r/custom_hostname.html.markdown +++ b/website/docs/r/custom_hostname.html.markdown @@ -44,7 +44,7 @@ The following arguments are supported: **settings** block supports: * `http2` - (Optional) Whether or not HTTP2 should be supported. Valid values are `"on"` or `"off"`. -* `tls12` - (Optional) Whether or not TLSv1.3 should be supported. Valid values are `"on"` or `"off"`. +* `tls13` - (Optional) Whether or not TLSv1.3 should be supported. Valid values are `"on"` or `"off"`. * `min_tls_version` - (Optional) Lowest version of TLS this certificate should support. Valid values are `"1.0"`, `"1.1"`, `"1.2"` and `"1.3"`. * `ciphers` - (Optional) List of SSL/TLS ciphers to associate with this certificate.