Skip to content

Commit

Permalink
Merge pull request #1906 from Cyb3r-Jak3/record-data-source
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobbednarz committed Sep 21, 2022
2 parents 2bf2499 + 9a13178 commit 16ebd1c
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/1906.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-data-source
cloudflare_record
```
43 changes: 43 additions & 0 deletions docs/data-sources/record.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
page_title: "cloudflare_record Data Source - Cloudflare"
subcategory: ""
description: |-
Use this data source to lookup a single DNS Record https://api.cloudflare.com/#dns-records-for-a-zone-properties.
---

# cloudflare_record (Data Source)

Use this data source to lookup a single [DNS Record](https://api.cloudflare.com/#dns-records-for-a-zone-properties).

## Example Usage

```terraform
data "cloudflare_record" "example" {
zone_id = var.zone_id
hostname = "example.com"
}
```
<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `hostname` (String) Hostname to filter DNS record results on.
- `zone_id` (String) The zone identifier to target for the resource.

### Optional

- `priority` (Number) DNS priority to filter record results on.
- `type` (String) DNS record type to filter record results on. Defaults to `A`.

### Read-Only

- `id` (String) The ID of this resource.
- `locked` (Boolean) Locked status of the found DNS record.
- `proxiable` (Boolean) Proxiable status of the found DNS record.
- `proxied` (Boolean) Proxied status of the found DNS record.
- `ttl` (Number) TTL of the found DNS record.
- `value` (String) Value of the found DNS record.
- `zone_name` (String) Zone name of the found DNS record.


4 changes: 4 additions & 0 deletions examples/data-sources/cloudflare_record/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
data "cloudflare_record" "example" {
zone_id = var.zone_id
hostname = "example.com"
}
129 changes: 129 additions & 0 deletions internal/provider/data_source_record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package provider

import (
"context"
"fmt"

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

func dataSourceCloudflareRecord() *schema.Resource {
return &schema.Resource{
Description: heredoc.Doc(`
Use this data source to lookup a single [DNS Record](https://api.cloudflare.com/#dns-records-for-a-zone-properties).
`),
ReadContext: dataSourceCloudflareRecordRead,
Schema: map[string]*schema.Schema{
"zone_id": {
Description: "The zone identifier to target for the resource.",
Type: schema.TypeString,
Required: true,
},
"hostname": {
Type: schema.TypeString,
Required: true,
Description: "Hostname to filter DNS record results on.",
},
"type": {
Type: schema.TypeString,
Optional: true,
Default: "A",
ValidateFunc: validation.StringInSlice([]string{"A", "AAAA", "CAA", "CNAME", "TXT", "SRV", "LOC", "MX", "NS", "SPF", "CERT", "DNSKEY", "DS", "NAPTR", "SMIMEA", "SSHFP", "TLSA", "URI", "PTR", "HTTPS"}, false),
Description: "DNS record type to filter record results on.",
},
"priority": {
Type: schema.TypeInt,
Optional: true,
DiffSuppressFunc: suppressPriority,
Description: "DNS priority to filter record results on.",
},
"value": {
Type: schema.TypeString,
Computed: true,
Description: "Value of the found DNS record.",
},
"proxied": {
Type: schema.TypeBool,
Computed: true,
Description: "Proxied status of the found DNS record.",
},
"ttl": {
Type: schema.TypeInt,
Computed: true,
Description: "TTL of the found DNS record.",
},
"proxiable": {
Type: schema.TypeBool,
Computed: true,
Description: "Proxiable status of the found DNS record.",
},
"locked": {
Type: schema.TypeBool,
Computed: true,
Description: "Locked status of the found DNS record.",
},
"zone_name": {
Type: schema.TypeString,
Computed: true,
Description: "Zone name of the found DNS record.",
},
},
}
}

func dataSourceCloudflareRecordRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)

searchRecord := cloudflare.DNSRecord{
Name: d.Get("hostname").(string),
Type: d.Get("type").(string),
}
if priority, ok := d.GetOkExists("priority"); ok {
p := uint16(priority.(int))
searchRecord.Priority = cloudflare.Uint16Ptr(p)
}

records, err := client.DNSRecords(ctx, zoneID, searchRecord)
if err != nil {
return diag.FromErr(fmt.Errorf("error listing DNS records: %w", err))
}

if len(records) == 0 {
return diag.Errorf("didn't get any DNS records for hostname: %s", searchRecord.Name)
}

if len(records) != 1 && !contains([]string{"MX", "URI"}, searchRecord.Type) {
return diag.Errorf("only wanted 1 DNS record. Got %d records", len(records))
} else {
for _, record := range records {
if cloudflare.Uint16(record.Priority) == cloudflare.Uint16(searchRecord.Priority) {
records = []cloudflare.DNSRecord{record}
break
}
}
if len(records) != 1 {
return diag.Errorf("unable to find single record for %s type %s", searchRecord.Name, searchRecord.Type)
}
}

record := records[0]
d.SetId(record.ID)
d.Set("type", record.Type)
d.Set("value", record.Content)
d.Set("proxied", record.Proxied)
d.Set("ttl", record.TTL)
d.Set("proxiable", record.Proxiable)
d.Set("locked", record.Locked)
d.Set("zone_name", record.ZoneName)

if record.Priority != nil {
d.Set("priority", int(cloudflare.Uint16(record.Priority)))
}

return nil
}
145 changes: 145 additions & 0 deletions internal/provider/data_source_record_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package provider

import (
"fmt"
"os"
"testing"

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

func TestAccCloudflareRecordDataSource(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("data.cloudflare_record.%s", rnd)
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
domain := os.Getenv("CLOUDFLARE_DOMAIN")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareRecordDataSourceConfig(rnd, zoneID, domain),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "hostname", rnd+"."+domain),
resource.TestCheckResourceAttr(name, "type", "A"),
resource.TestCheckResourceAttr(name, "value", "192.0.2.0"),
resource.TestCheckResourceAttr(name, "proxied", "false"),
resource.TestCheckResourceAttr(name, "ttl", "1"),
resource.TestCheckResourceAttr(name, "proxiable", "true"),
resource.TestCheckResourceAttr(name, "locked", "false"),
resource.TestCheckResourceAttr(name, "zone_name", domain),
),
},
},
})
}

func TestAccCloudflareRecordDataSourceTXT(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("data.cloudflare_record.%s", rnd)
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
domain := os.Getenv("CLOUDFLARE_DOMAIN")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareRecordDataSourceConfigTXT(rnd, zoneID, domain),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "hostname", rnd+"."+domain),
resource.TestCheckResourceAttr(name, "type", "TXT"),
resource.TestCheckResourceAttr(name, "value", "i am a text record"),
resource.TestCheckResourceAttr(name, "proxied", "false"),
resource.TestCheckResourceAttr(name, "ttl", "1"),
resource.TestCheckResourceAttr(name, "proxiable", "false"),
resource.TestCheckResourceAttr(name, "locked", "false"),
resource.TestCheckResourceAttr(name, "zone_name", domain),
),
},
},
})
}

func TestAccCloudflareRecordDataSourceMX(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("data.cloudflare_record.%s", rnd)
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
domain := os.Getenv("CLOUDFLARE_DOMAIN")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareRecordDataSourceConfigMX(rnd, zoneID, domain),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "hostname", rnd+"."+domain),
resource.TestCheckResourceAttr(name, "type", "MX"),
resource.TestCheckResourceAttr(name, "value", "mx1.example.com"),
resource.TestCheckResourceAttr(name, "proxied", "false"),
resource.TestCheckResourceAttr(name, "ttl", "1"),
resource.TestCheckResourceAttr(name, "proxiable", "false"),
resource.TestCheckResourceAttr(name, "locked", "false"),
resource.TestCheckResourceAttr(name, "zone_name", domain),
resource.TestCheckResourceAttr(name, "priority", "10"),
),
},
},
})
}

func testAccCloudflareRecordDataSourceConfig(rnd, zoneID, domain string) string {
return fmt.Sprintf(`
data "cloudflare_record" "%[1]s" {
zone_id = "%[2]s"
hostname = cloudflare_record.%[1]s.hostname
}
resource "cloudflare_record" "%[1]s" {
zone_id = "%[2]s"
type = "A"
name = "%[1]s.%[3]s"
value = "192.0.2.0"
}`, rnd, zoneID, domain)
}

func testAccCloudflareRecordDataSourceConfigTXT(rnd, zoneID, domain string) string {
return fmt.Sprintf(`
data "cloudflare_record" "%[1]s" {
zone_id = "%[2]s"
type = "TXT"
hostname = cloudflare_record.%[1]s.hostname
}
resource "cloudflare_record" "%[1]s" {
zone_id = "%[2]s"
type = "TXT"
name = "%[1]s.%[3]s"
value = "i am a text record"
}`, rnd, zoneID, domain)
}

func testAccCloudflareRecordDataSourceConfigMX(rnd, zoneID, domain string) string {
return fmt.Sprintf(`
data "cloudflare_record" "%[1]s" {
zone_id = "%[2]s"
type = "MX"
priority = 10
hostname = cloudflare_record.%[1]s.hostname
}
resource "cloudflare_record" "%[1]s" {
zone_id = "%[2]s"
type = "MX"
name = "%[1]s.%[3]s"
value = "mx1.example.com"
priority = 10
}
resource "cloudflare_record" "%[1]s_2" {
zone_id = "%[2]s"
type = "MX"
name = "%[1]s.%[3]s"
value = "mx1.example.com"
priority = 20
}
`, rnd, zoneID, domain)
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_devices": dataSourceCloudflareDevices(),
"cloudflare_ip_ranges": dataSourceCloudflareIPRanges(),
"cloudflare_origin_ca_root_certificate": dataSourceCloudflareOriginCARootCertificate(),
"cloudflare_record": dataSourceCloudflareRecord(),
"cloudflare_waf_groups": dataSourceCloudflareWAFGroups(),
"cloudflare_waf_packages": dataSourceCloudflareWAFPackages(),
"cloudflare_waf_rules": dataSourceCloudflareWAFRules(),
Expand Down

0 comments on commit 16ebd1c

Please sign in to comment.