Skip to content

Commit

Permalink
Adding support for fallback domains (#1356)
Browse files Browse the repository at this point in the history
WDAPI-659 - Adding fallback domain terraform support

Co-authored-by: Kayla Handy <khandy@cloudflare.com>
Co-authored-by: Jacob Bednarz <jacob.bednarz@gmail.com>
  • Loading branch information
3 people authored Jan 13, 2022
1 parent 6abf3bf commit fc76b4d
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/1356.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
cloudflare_fallback_domain
```
1 change: 1 addition & 0 deletions cloudflare/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func Provider() *schema.Provider {
"cloudflare_custom_ssl": resourceCloudflareCustomSsl(),
"cloudflare_device_posture_rule": resourceCloudflareDevicePostureRule(),
"cloudflare_device_posture_integration": resourceCloudflareDevicePostureIntegration(),
"cloudflare_fallback_domain": resourceCloudflareFallbackDomain(),
"cloudflare_filter": resourceCloudflareFilter(),
"cloudflare_firewall_rule": resourceCloudflareFirewallRule(),
"cloudflare_healthcheck": resourceCloudflareHealthcheck(),
Expand Down
118 changes: 118 additions & 0 deletions cloudflare/resource_cloudflare_fallback_domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package cloudflare

import (
"context"
"fmt"

cloudflare "github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceCloudflareFallbackDomain() *schema.Resource {
return &schema.Resource{
Schema: resourceCloudflareFallbackDomainSchema(),
Read: resourceCloudflareFallbackDomainRead,
Create: resourceCloudflareFallbackDomainUpdate, // Intentionally identical to Update as the resource is always present
Update: resourceCloudflareFallbackDomainUpdate,
Delete: resourceCloudflareFallbackDomainDelete,
Importer: &schema.ResourceImporter{
State: resourceCloudflareFallbackDomainImport,
},
}
}

func resourceCloudflareFallbackDomainRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
accountID := d.Get("account_id").(string)

domain, err := client.ListFallbackDomains(context.Background(), accountID)
if err != nil {
return fmt.Errorf("error finding Fallback Domains: %w", err)
}

if err := d.Set("domains", flattenFallbackDomains(domain)); err != nil {
return fmt.Errorf("error setting domains attribute: %w", err)
}

return nil
}

func resourceCloudflareFallbackDomainUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
accountID := d.Get("account_id").(string)

domainList := expandFallbackDomains(d.Get("domains").([]interface{}))

newFallbackDomains, err := client.UpdateFallbackDomain(context.Background(), accountID, domainList)
if err != nil {
return fmt.Errorf("error updating Fallback Domains: %w", err)
}

if err := d.Set("domains", flattenFallbackDomains(newFallbackDomains)); err != nil {
return fmt.Errorf("error setting domain attribute: %w", err)
}

d.SetId(accountID)

return resourceCloudflareFallbackDomainRead(d, meta)
}

func resourceCloudflareFallbackDomainDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
accountID := d.Get("account_id").(string)

_, err := client.UpdateFallbackDomain(context.Background(), accountID, nil)
if err != nil {
return err
}

d.SetId("")
return nil
}

func resourceCloudflareFallbackDomainImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
accountID := d.Id()

if accountID == "" {
return nil, fmt.Errorf("must provide account ID")
}

d.Set("account_id", accountID)
d.SetId(accountID)

readErr := resourceCloudflareFallbackDomainRead(d, meta)

return []*schema.ResourceData{d}, readErr
}

// flattenFallbackDomains accepts the cloudflare.FallbackDomain struct and returns the
// schema representation for use in Terraform state.
func flattenFallbackDomains(domains []cloudflare.FallbackDomain) []interface{} {
schemaDomains := make([]interface{}, 0)

for _, d := range domains {
schemaDomains = append(schemaDomains, map[string]interface{}{
"suffix": d.Suffix,
"description": d.Description,
"dns_server": flattenStringList(d.DNSServer),
})
}

return schemaDomains
}

// expandFallbackDomains accepts the schema representation of Fallback Domains and
// returns a fully qualified struct.
func expandFallbackDomains(domains []interface{}) []cloudflare.FallbackDomain {
domainList := make([]cloudflare.FallbackDomain, 0)

for _, domain := range domains {
domainList = append(domainList, cloudflare.FallbackDomain{
Suffix: domain.(map[string]interface{})["suffix"].(string),
Description: domain.(map[string]interface{})["description"].(string),
DNSServer: expandInterfaceToStringList(domain.(map[string]interface{})["dns_server"]),
})
}

return domainList
}
64 changes: 64 additions & 0 deletions cloudflare/resource_cloudflare_fallback_domain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cloudflare

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccCloudflareFallbackDomain(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access
// service does not yet support the API tokens and it results in
// misleading state error messages.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
defer func(apiToken string) {
os.Setenv("CLOUDFLARE_API_TOKEN", apiToken)
}(os.Getenv("CLOUDFLARE_API_TOKEN"))
os.Setenv("CLOUDFLARE_API_TOKEN", "")
}

rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_fallback_domain.%s", rnd)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccessAccPreCheck(t)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCloudflareFallbackDomain(rnd, accountID, "example domain", "example.com", "2.2.2.2"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "account_id", accountID),
resource.TestCheckResourceAttr(name, "domains.0.description", "example domain"),
resource.TestCheckResourceAttr(name, "domains.0.suffix", "example.com"),
resource.TestCheckResourceAttr(name, "domains.0.dns_server.0", "2.2.2.2"),
),
},
{
Config: testAccCloudflareFallbackDomain(rnd, accountID, "second example domain", "example_two.com", "1.1.1.1"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "account_id", accountID),
resource.TestCheckResourceAttr(name, "domains.0.description", "second example domain"),
resource.TestCheckResourceAttr(name, "domains.0.suffix", "example_two.com"),
resource.TestCheckResourceAttr(name, "domains.0.dns_server.0", "1.1.1.1"),
),
},
},
})
}

func testAccCloudflareFallbackDomain(rnd, accountID string, description string, suffix string, dns_server string) string {
return fmt.Sprintf(`
resource "cloudflare_fallback_domain" "%[1]s" {
account_id = "%[2]s"
domains {
description = "%[3]s"
suffix = "%[4]s"
dns_server = ["%[5]s"]
}
}
`, rnd, accountID, description, suffix, dns_server)
}
40 changes: 40 additions & 0 deletions cloudflare/schema_cloudflare_fallback_domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cloudflare

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceCloudflareFallbackDomainSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Required: true,
},
"domains": {
Required: true,
Type: schema.TypeList,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"suffix": {
Type: schema.TypeString,
Optional: true,
Description: "The domain suffix to match when resolving locally.",
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: "A description of the fallback domain, displayed in the client UI.",
},
"dns_server": {
Type: schema.TypeList,
Optional: true,
Description: "A list of IP addresses to handle domain resolution.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
}
}
3 changes: 3 additions & 0 deletions website/cloudflare.erb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@
<li<%= sidebar_current("docs-cloudflare-resource-custom-ssl") %>>
<a href="/docs/providers/cloudflare/r/custom_ssl.html">cloudflare_custom_ssl</a>
</li>
<li<%= sidebar_current("docs-cloudflare-resource-fallback-domain") %>>
<a href="/docs/providers/cloudflare/r/fallback_domain.html">cloudflare_fallback_domain</a>
</li>
<li<%= sidebar_current("docs-cloudflare-resource-filter") %>>
<a href="/docs/providers/cloudflare/r/filter.html">cloudflare_filter</a>
</li>
Expand Down
47 changes: 47 additions & 0 deletions website/docs/r/fallback_domain.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
layout: "cloudflare"
page_title: "Cloudflare: cloudflare_fallback_domain"
sidebar_current: "docs-cloudflare-resource-fallback-domain"
description: |-
Provides a Cloudflare Fallback Domain resource.
---

# cloudflare_fallback_domain

Provides a Cloudflare Fallback Domain resource. Fallback domains are used to ignore DNS requests to a given list of domains. These DNS requests will be passed back to other DNS servers configured on existing network interfaces on the device.

## Example Usage

```hcl
# Adding example.com to the fallback domain list
resource "cloudflare_fallback_domain" "example_fallback_domain {
account_id = "1d5fdc9e88c8a8c4518b068cd94331fe"
domains {
suffix = "example.com"
description = "Example domain"
dns_server = ["1.1.1.1", "2.2.2.2"]
}
}
```

## Argument Reference

The following arguments are supported:

- `account_id` - (Required) The account to which the device posture rule should be added.
- `domains` - (Required) The value of the domain attributes (refer to the [nested schema](#nestedblock--domains)).

<a id="nestedblock--domains"></a>
**Nested schema for `domains`**

- `suffix` - (Optional) The domain to ignore DNS requests.
- `description` - (Optional) The description of the domain.
- `dns_server` - (Optional) The DNS servers to receive the redirected request.

## Import

Fallback Domains can be imported using the account identifer.

```
$ terraform import cloudflare_fallback_domain.example 1d5fdc9e88c8a8c4518b068cd94331fe
```

0 comments on commit fc76b4d

Please sign in to comment.