provider/dns: DNS dynamic updates (RFC 2136)
This commit is contained in:
parent
04b698f409
commit
e3934c23c8
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/builtin/providers/dns"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Serve(&plugin.ServeOpts{
|
||||
ProviderFunc: dns.Provider,
|
||||
})
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package main
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
set -x
|
||||
|
||||
# Test domains
|
||||
export DNS_DOMAIN_FORWARD="example.com."
|
||||
export DNS_DOMAIN_REVERSE="1.168.192.in-addr.arpa."
|
||||
|
||||
# Run with no authentication
|
||||
|
||||
export DNS_UPDATE_SERVER=127.0.0.1
|
||||
docker run -d -p 53:53/udp \
|
||||
-e BIND_DOMAIN_FORWARD=${DNS_DOMAIN_FORWARD} \
|
||||
-e BIND_DOMAIN_REVERSE=${DNS_DOMAIN_REVERSE} \
|
||||
-e BIND_INSECURE=true \
|
||||
--name bind_insecure drebes/bind
|
||||
make testacc TEST=./builtin/providers/dns
|
||||
docker stop bind_insecure
|
||||
docker rm bind_insecure
|
||||
|
||||
# Run with authentication
|
||||
|
||||
export DNS_UPDATE_KEYNAME=${DNS_DOMAIN_FORWARD}
|
||||
export DNS_UPDATE_KEYALGORITHM="hmac-md5"
|
||||
export DNS_UPDATE_KEYSECRET="c3VwZXJzZWNyZXQ="
|
||||
docker run -d -p 53:53/udp \
|
||||
-e BIND_DOMAIN_FORWARD=${DNS_DOMAIN_FORWARD} \
|
||||
-e BIND_DOMAIN_REVERSE=${DNS_DOMAIN_REVERSE} \
|
||||
-e BIND_KEY_NAME=${DNS_UPDATE_KEYNAME} \
|
||||
-e BIND_KEY_ALGORITHM=${DNS_UPDATE_KEYALGORITHM} \
|
||||
-e BIND_KEY_SECRET=${DNS_UPDATE_KEYSECRET} \
|
||||
--name bind_secure drebes/bind
|
||||
make testacc TEST=./builtin/providers/dns
|
||||
docker stop bind_secure
|
||||
docker rm bind_secure
|
|
@ -0,0 +1,67 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
server string
|
||||
port int
|
||||
keyname string
|
||||
keyalgo string
|
||||
keysecret string
|
||||
}
|
||||
|
||||
type DNSClient struct {
|
||||
c *dns.Client
|
||||
srv_addr string
|
||||
keyname string
|
||||
keysecret string
|
||||
keyalgo string
|
||||
}
|
||||
|
||||
// Configures and returns a fully initialized DNSClient
|
||||
func (c *Config) Client() (interface{}, error) {
|
||||
log.Println("[INFO] Building DNSClient config structure")
|
||||
|
||||
var client DNSClient
|
||||
client.srv_addr = fmt.Sprintf("%s:%d", c.server, c.port)
|
||||
authCfgOk := false
|
||||
if (c.keyname == "" && c.keysecret == "" && c.keyalgo == "") ||
|
||||
(c.keyname != "" && c.keysecret != "" && c.keyalgo != "") {
|
||||
authCfgOk = true
|
||||
}
|
||||
if !authCfgOk {
|
||||
return nil, fmt.Errorf("Error configuring provider: when using authentication, \"key_name\", \"key_secret\" and \"key_algorithm\" should be non empty")
|
||||
}
|
||||
client.c = new(dns.Client)
|
||||
if c.keyname != "" {
|
||||
client.keyname = c.keyname
|
||||
client.keysecret = c.keysecret
|
||||
keyalgo, err := convertHMACAlgorithm(c.keyalgo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error configuring provider: %s", err)
|
||||
}
|
||||
client.keyalgo = keyalgo
|
||||
client.c.TsigSecret = map[string]string{c.keyname: c.keysecret}
|
||||
}
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// Validates and converts HMAC algorithm
|
||||
func convertHMACAlgorithm(name string) (string, error) {
|
||||
switch name {
|
||||
case "hmac-md5":
|
||||
return dns.HmacMD5, nil
|
||||
case "hmac-sha1":
|
||||
return dns.HmacSHA1, nil
|
||||
case "hmac-sha256":
|
||||
return dns.HmacSHA256, nil
|
||||
case "hmac-sha512":
|
||||
return dns.HmacSHA512, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Unknown HMAC algorithm: %s", name)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Provider returns a schema.Provider for DNS dynamic updates.
|
||||
func Provider() terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"update": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
MaxItems: 1,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"server": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("DNS_UPDATE_SERVER", nil),
|
||||
},
|
||||
"port": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 53,
|
||||
},
|
||||
"key_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("DNS_UPDATE_KEYNAME", nil),
|
||||
},
|
||||
"key_algorithm": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("DNS_UPDATE_KEYALGORITHM", nil),
|
||||
},
|
||||
"key_secret": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("DNS_UPDATE_KEYSECRET", nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"dns_a_record_set": resourceDnsARecordSet(),
|
||||
"dns_aaaa_record_set": resourceDnsAAAARecordSet(),
|
||||
"dns_cname_record": resourceDnsCnameRecord(),
|
||||
"dns_ptr_record": resourceDnsPtrRecord(),
|
||||
},
|
||||
|
||||
ConfigureFunc: configureProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func configureProvider(d *schema.ResourceData) (interface{}, error) {
|
||||
|
||||
var server, keyname, keyalgo, keysecret string
|
||||
var port int
|
||||
|
||||
// if the update block is missing, schema.EnvDefaultFunc is not called
|
||||
if v, ok := d.GetOk("update"); ok {
|
||||
update := v.([]interface{})[0].(map[string]interface{})
|
||||
if val, ok := update["port"]; ok {
|
||||
port = int(val.(int))
|
||||
}
|
||||
if val, ok := update["server"]; ok {
|
||||
server = val.(string)
|
||||
}
|
||||
if val, ok := update["key_name"]; ok {
|
||||
keyname = val.(string)
|
||||
}
|
||||
if val, ok := update["key_algorithm"]; ok {
|
||||
keyalgo = val.(string)
|
||||
}
|
||||
if val, ok := update["key_secret"]; ok {
|
||||
keysecret = val.(string)
|
||||
}
|
||||
} else {
|
||||
if len(os.Getenv("DNS_UPDATE_SERVER")) > 0 {
|
||||
server = os.Getenv("DNS_UPDATE_SERVER")
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
port = 53
|
||||
if len(os.Getenv("DNS_UPDATE_KEYNAME")) > 0 {
|
||||
keyname = os.Getenv("DNS_UPDATE_KEYNAME")
|
||||
}
|
||||
if len(os.Getenv("DNS_UPDATE_KEYALGORITHM")) > 0 {
|
||||
keyalgo = os.Getenv("DNS_UPDATE_KEYALGORITHM")
|
||||
}
|
||||
if len(os.Getenv("DNS_UPDATE_KEYSECRET")) > 0 {
|
||||
keysecret = os.Getenv("DNS_UPDATE_KEYSECRET")
|
||||
}
|
||||
}
|
||||
|
||||
config := Config{
|
||||
server: server,
|
||||
port: port,
|
||||
keyname: keyname,
|
||||
keyalgo: keyalgo,
|
||||
keysecret: keysecret,
|
||||
}
|
||||
|
||||
return config.Client()
|
||||
}
|
||||
|
||||
func getAVal(record interface{}) (string, error) {
|
||||
|
||||
recstr := record.(*dns.A).String()
|
||||
var name, ttl, class, typ, addr string
|
||||
|
||||
_, err := fmt.Sscanf(recstr, "%s\t%s\t%s\t%s\t%s", &name, &ttl, &class, &typ, &addr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing record: %s", err)
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func getAAAAVal(record interface{}) (string, error) {
|
||||
|
||||
recstr := record.(*dns.AAAA).String()
|
||||
var name, ttl, class, typ, addr string
|
||||
|
||||
_, err := fmt.Sscanf(recstr, "%s\t%s\t%s\t%s\t%s", &name, &ttl, &class, &typ, &addr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing record: %s", err)
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func getCnameVal(record interface{}) (string, error) {
|
||||
|
||||
recstr := record.(*dns.CNAME).String()
|
||||
var name, ttl, class, typ, cname string
|
||||
|
||||
_, err := fmt.Sscanf(recstr, "%s\t%s\t%s\t%s\t%s", &name, &ttl, &class, &typ, &cname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing record: %s", err)
|
||||
}
|
||||
|
||||
return cname, nil
|
||||
}
|
||||
|
||||
func getPtrVal(record interface{}) (string, error) {
|
||||
|
||||
recstr := record.(*dns.PTR).String()
|
||||
var name, ttl, class, typ, ptr string
|
||||
|
||||
_, err := fmt.Sscanf(recstr, "%s\t%s\t%s\t%s\t%s", &name, &ttl, &class, &typ, &ptr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing record: %s", err)
|
||||
}
|
||||
|
||||
return ptr, nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
func init() {
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"dns": testAccProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvider = Provider()
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
v := os.Getenv("DNS_UPDATE_SERVER")
|
||||
if v == "" {
|
||||
t.Fatal("DNS_UPDATE_SERVER must be set for acceptance tests")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func resourceDnsARecordSet() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDnsARecordSetCreate,
|
||||
Read: resourceDnsARecordSetRead,
|
||||
Update: resourceDnsARecordSetUpdate,
|
||||
Delete: resourceDnsARecordSetDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"addresses": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
"ttl": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: 3600,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsARecordSetCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error creating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
d.SetId(rec_fqdn)
|
||||
|
||||
return resourceDnsARecordSetUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceDnsARecordSetRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeA)
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
addresses := schema.NewSet(schema.HashString, nil)
|
||||
for _, record := range r.Answer {
|
||||
addr, err := getAVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
addresses.Add(addr)
|
||||
}
|
||||
if !addresses.Equal(d.Get("addresses")) {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("DNS record differs")
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsARecordSetUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
ttl := d.Get("ttl").(int)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
if d.HasChange("addresses") {
|
||||
o, n := d.GetChange("addresses")
|
||||
os := o.(*schema.Set)
|
||||
ns := n.(*schema.Set)
|
||||
remove := os.Difference(ns).List()
|
||||
add := ns.Difference(os).List()
|
||||
|
||||
// Loop through all the old addresses and remove them
|
||||
for _, addr := range remove {
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s %d A %s", rec_fqdn, ttl, addr.(string)))
|
||||
msg.Remove([]dns.RR{rr_remove})
|
||||
}
|
||||
// Loop through all the new addresses and insert them
|
||||
for _, addr := range add {
|
||||
rr_insert, _ := dns.NewRR(fmt.Sprintf("%s %d A %s", rec_fqdn, ttl, addr.(string)))
|
||||
msg.Insert([]dns.RR{rr_insert})
|
||||
}
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
addresses := ns
|
||||
d.Set("addresses", addresses)
|
||||
}
|
||||
|
||||
return resourceDnsARecordSetRead(d, meta)
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsARecordSetDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s 0 A", rec_fqdn))
|
||||
msg.RemoveRRset([]dns.RR{rr_remove})
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error deleting DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestAccDnsARecordSet_basic(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDnsARecordSetDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDnsARecordSet_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("dns_a_record_set.foo", "addresses.#", "2"),
|
||||
testAccCheckDnsARecordSetExists(t, "dns_a_record_set.foo", []interface{}{"192.168.0.2", "192.168.0.1"}),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccDnsARecordSet_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("dns_a_record_set.foo", "addresses.#", "3"),
|
||||
testAccCheckDnsARecordSetExists(t, "dns_a_record_set.foo", []interface{}{"10.0.0.3", "10.0.0.2", "10.0.0.1"}),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDnsARecordSetDestroy(s *terraform.State) error {
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "dns_a_record_set" {
|
||||
continue
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeA)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeNameError {
|
||||
return fmt.Errorf("DNS record still exists: %v", r.Rcode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDnsARecordSetExists(t *testing.T, n string, addr []interface{}) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeA)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record")
|
||||
}
|
||||
|
||||
addresses := schema.NewSet(schema.HashString, nil)
|
||||
expected := schema.NewSet(schema.HashString, addr)
|
||||
for _, record := range r.Answer {
|
||||
addr, err := getAVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
addresses.Add(addr)
|
||||
}
|
||||
if !addresses.Equal(expected) {
|
||||
return fmt.Errorf("DNS record differs: expected %v, found %v", expected, addresses)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccDnsARecordSet_basic = fmt.Sprintf(`
|
||||
resource "dns_a_record_set" "foo" {
|
||||
zone = "example.com."
|
||||
name = "foo"
|
||||
addresses = ["192.168.0.1", "192.168.0.2"]
|
||||
ttl = 300
|
||||
}`)
|
||||
|
||||
var testAccDnsARecordSet_update = fmt.Sprintf(`
|
||||
resource "dns_a_record_set" "foo" {
|
||||
zone = "example.com."
|
||||
name = "foo"
|
||||
addresses = ["10.0.0.1", "10.0.0.2", "10.0.0.3"]
|
||||
ttl = 300
|
||||
}`)
|
|
@ -0,0 +1,212 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func resourceDnsAAAARecordSet() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDnsAAAARecordSetCreate,
|
||||
Read: resourceDnsAAAARecordSetRead,
|
||||
Update: resourceDnsAAAARecordSetUpdate,
|
||||
Delete: resourceDnsAAAARecordSetDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"addresses": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
"ttl": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: 3600,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsAAAARecordSetCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error creating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
d.SetId(rec_fqdn)
|
||||
|
||||
return resourceDnsAAAARecordSetUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceDnsAAAARecordSetRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeAAAA)
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
addresses := schema.NewSet(schema.HashString, nil)
|
||||
for _, record := range r.Answer {
|
||||
addr, err := getAAAAVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
addresses.Add(addr)
|
||||
}
|
||||
if !addresses.Equal(d.Get("addresses")) {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("DNS record differs")
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsAAAARecordSetUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
ttl := d.Get("ttl").(int)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
if d.HasChange("addresses") {
|
||||
o, n := d.GetChange("addresses")
|
||||
os := o.(*schema.Set)
|
||||
ns := n.(*schema.Set)
|
||||
remove := os.Difference(ns).List()
|
||||
add := ns.Difference(os).List()
|
||||
|
||||
// Loop through all the old addresses and remove them
|
||||
for _, addr := range remove {
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s %d AAAA %s", rec_fqdn, ttl, addr.(string)))
|
||||
msg.Remove([]dns.RR{rr_remove})
|
||||
}
|
||||
// Loop through all the new addresses and insert them
|
||||
for _, addr := range add {
|
||||
rr_insert, _ := dns.NewRR(fmt.Sprintf("%s %d AAAA %s", rec_fqdn, ttl, addr.(string)))
|
||||
msg.Insert([]dns.RR{rr_insert})
|
||||
}
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
addresses := ns
|
||||
d.Set("addresses", addresses)
|
||||
}
|
||||
|
||||
return resourceDnsAAAARecordSetRead(d, meta)
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsAAAARecordSetDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s 0 AAAA", rec_fqdn))
|
||||
msg.RemoveRRset([]dns.RR{rr_remove})
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error deleting DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestAccDnsAAAARecordSet_basic(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDnsAAAARecordSetDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDnsAAAARecordSet_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("dns_aaaa_record_set.bar", "addresses.#", "2"),
|
||||
testAccCheckDnsAAAARecordSetExists(t, "dns_aaaa_record_set.bar", []interface{}{"fdd5:e282:43b8:5303:dead:beef:cafe:babe", "fdd5:e282:43b8:5303:cafe:babe:dead:beef"}),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccDnsAAAARecordSet_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("dns_aaaa_record_set.bar", "addresses.#", "2"),
|
||||
testAccCheckDnsAAAARecordSetExists(t, "dns_aaaa_record_set.bar", []interface{}{"fdd5:e282:43b8:5303:beef:dead:babe:cafe", "fdd5:e282:43b8:5303:babe:cafe:beef:dead"}),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDnsAAAARecordSetDestroy(s *terraform.State) error {
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "dns_aaaa_record_set" {
|
||||
continue
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeAAAA)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeNameError {
|
||||
return fmt.Errorf("DNS record still exists: %v", r.Rcode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDnsAAAARecordSetExists(t *testing.T, n string, addr []interface{}) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeAAAA)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record")
|
||||
}
|
||||
|
||||
addresses := schema.NewSet(schema.HashString, nil)
|
||||
expected := schema.NewSet(schema.HashString, addr)
|
||||
for _, record := range r.Answer {
|
||||
addr, err := getAAAAVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
addresses.Add(addr)
|
||||
}
|
||||
if !addresses.Equal(expected) {
|
||||
return fmt.Errorf("DNS record differs: expected %v, found %v", expected, addresses)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccDnsAAAARecordSet_basic = fmt.Sprintf(`
|
||||
resource "dns_aaaa_record_set" "bar" {
|
||||
zone = "example.com."
|
||||
name = "bar"
|
||||
addresses = ["fdd5:e282:43b8:5303:dead:beef:cafe:babe", "fdd5:e282:43b8:5303:cafe:babe:dead:beef"]
|
||||
ttl = 300
|
||||
}`)
|
||||
|
||||
var testAccDnsAAAARecordSet_update = fmt.Sprintf(`
|
||||
resource "dns_aaaa_record_set" "bar" {
|
||||
zone = "example.com."
|
||||
name = "bar"
|
||||
addresses = ["fdd5:e282:43b8:5303:beef:dead:babe:cafe", "fdd5:e282:43b8:5303:babe:cafe:beef:dead"]
|
||||
ttl = 300
|
||||
}`)
|
|
@ -0,0 +1,219 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func resourceDnsCnameRecord() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDnsCnameRecordCreate,
|
||||
Read: resourceDnsCnameRecordRead,
|
||||
Update: resourceDnsCnameRecordUpdate,
|
||||
Delete: resourceDnsCnameRecordDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"cname": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"ttl": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: 3600,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsCnameRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
rec_cname := d.Get("cname").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error creating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
if rec_cname != dns.Fqdn(rec_cname) {
|
||||
return fmt.Errorf("Error creating DNS record: \"cname\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
d.SetId(rec_fqdn)
|
||||
|
||||
return resourceDnsCnameRecordUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceDnsCnameRecordRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
rec_cname := d.Get("cname").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
if rec_cname != dns.Fqdn(rec_cname) {
|
||||
return fmt.Errorf("Error reading DNS record: \"cname\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeCNAME)
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
if len(r.Answer) > 1 {
|
||||
return fmt.Errorf("Error querying DNS record: multiple responses received")
|
||||
}
|
||||
record := r.Answer[0]
|
||||
cname, err := getCnameVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if rec_cname != cname {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("DNS record differs")
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsCnameRecordUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
rec_cname := d.Get("cname").(string)
|
||||
ttl := d.Get("ttl").(int)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
if rec_cname != dns.Fqdn(rec_cname) {
|
||||
return fmt.Errorf("Error updating DNS record: \"cname\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
if d.HasChange("cname") {
|
||||
o, n := d.GetChange("cname")
|
||||
|
||||
if o != "" {
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s %d CNAME %s", rec_fqdn, ttl, o))
|
||||
msg.Remove([]dns.RR{rr_remove})
|
||||
}
|
||||
if n != "" {
|
||||
rr_insert, _ := dns.NewRR(fmt.Sprintf("%s %d CNAME %s", rec_fqdn, ttl, n))
|
||||
msg.Insert([]dns.RR{rr_insert})
|
||||
}
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
cname := n
|
||||
d.Set("cname", cname)
|
||||
}
|
||||
|
||||
return resourceDnsCnameRecordRead(d, meta)
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsCnameRecordDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s 0 CNAME", rec_fqdn))
|
||||
msg.RemoveRRset([]dns.RR{rr_remove})
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error deleting DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestAccDnsCnameRecord_basic(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDnsCnameRecordDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDnsCnameRecord_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDnsCnameRecordExists(t, "dns_cname_record.foo", "bar.example.com."),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccDnsCnameRecord_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDnsCnameRecordExists(t, "dns_cname_record.foo", "baz.example.com."),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDnsCnameRecordDestroy(s *terraform.State) error {
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "dns_cname_record" {
|
||||
continue
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeCNAME)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeNameError {
|
||||
return fmt.Errorf("DNS record still exists: %v", r.Rcode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDnsCnameRecordExists(t *testing.T, n string, expected string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypeCNAME)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record")
|
||||
}
|
||||
|
||||
if len(r.Answer) > 1 {
|
||||
return fmt.Errorf("Error querying DNS record: multiple responses received")
|
||||
}
|
||||
record := r.Answer[0]
|
||||
cname, err := getCnameVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if expected != cname {
|
||||
return fmt.Errorf("DNS record differs: expected %v, found %v", expected, cname)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccDnsCnameRecord_basic = fmt.Sprintf(`
|
||||
resource "dns_cname_record" "foo" {
|
||||
zone = "example.com."
|
||||
name = "foo"
|
||||
cname = "bar.example.com."
|
||||
ttl = 300
|
||||
}`)
|
||||
|
||||
var testAccDnsCnameRecord_update = fmt.Sprintf(`
|
||||
resource "dns_cname_record" "foo" {
|
||||
zone = "example.com."
|
||||
name = "baz"
|
||||
cname = "baz.example.com."
|
||||
ttl = 300
|
||||
}`)
|
|
@ -0,0 +1,219 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func resourceDnsPtrRecord() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDnsPtrRecordCreate,
|
||||
Read: resourceDnsPtrRecordRead,
|
||||
Update: resourceDnsPtrRecordUpdate,
|
||||
Delete: resourceDnsPtrRecordDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"ptr": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"ttl": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: 3600,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsPtrRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
rec_ptr := d.Get("ptr").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error creating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
if rec_ptr != dns.Fqdn(rec_ptr) {
|
||||
return fmt.Errorf("Error creating DNS record: \"ptr\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
d.SetId(rec_fqdn)
|
||||
|
||||
return resourceDnsPtrRecordUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceDnsPtrRecordRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
rec_ptr := d.Get("ptr").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
if rec_ptr != dns.Fqdn(rec_ptr) {
|
||||
return fmt.Errorf("Error reading DNS record: \"ptr\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypePTR)
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
if len(r.Answer) > 1 {
|
||||
return fmt.Errorf("Error querying DNS record: multiple responses received")
|
||||
}
|
||||
record := r.Answer[0]
|
||||
ptr, err := getPtrVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if rec_ptr != ptr {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("DNS record differs")
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsPtrRecordUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
rec_ptr := d.Get("ptr").(string)
|
||||
ttl := d.Get("ttl").(int)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
if rec_ptr != dns.Fqdn(rec_ptr) {
|
||||
return fmt.Errorf("Error updating DNS record: \"ptr\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
if d.HasChange("ptr") {
|
||||
o, n := d.GetChange("ptr")
|
||||
|
||||
if o != "" {
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s %d PTR %s", rec_fqdn, ttl, o))
|
||||
msg.Remove([]dns.RR{rr_remove})
|
||||
}
|
||||
if n != "" {
|
||||
rr_insert, _ := dns.NewRR(fmt.Sprintf("%s %d PTR %s", rec_fqdn, ttl, n))
|
||||
msg.Insert([]dns.RR{rr_insert})
|
||||
}
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
d.SetId("")
|
||||
return fmt.Errorf("Error updating DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
ptr := n
|
||||
d.Set("ptr", ptr)
|
||||
}
|
||||
|
||||
return resourceDnsPtrRecordRead(d, meta)
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDnsPtrRecordDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
if meta != nil {
|
||||
|
||||
rec_name := d.Get("name").(string)
|
||||
rec_zone := d.Get("zone").(string)
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error updating DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
keyname := meta.(*DNSClient).keyname
|
||||
keyalgo := meta.(*DNSClient).keyalgo
|
||||
|
||||
msg := new(dns.Msg)
|
||||
|
||||
msg.SetUpdate(rec_zone)
|
||||
|
||||
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s 0 PTR", rec_fqdn))
|
||||
msg.RemoveRRset([]dns.RR{rr_remove})
|
||||
|
||||
if keyname != "" {
|
||||
msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix())
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error deleting DNS record: %v", r.Rcode)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("update server is not set")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestAccDnsPtrRecord_basic(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDnsPtrRecordDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDnsPtrRecord_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDnsPtrRecordExists(t, "dns_ptr_record.foo", "bar.example.com."),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccDnsPtrRecord_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDnsPtrRecordExists(t, "dns_ptr_record.foo", "baz.example.com."),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDnsPtrRecordDestroy(s *terraform.State) error {
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "dns_ptr_record" {
|
||||
continue
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypePTR)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeNameError {
|
||||
return fmt.Errorf("DNS record still exists: %v", r.Rcode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDnsPtrRecordExists(t *testing.T, n string, expected string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
rec_name := rs.Primary.Attributes["name"]
|
||||
rec_zone := rs.Primary.Attributes["zone"]
|
||||
|
||||
if rec_zone != dns.Fqdn(rec_zone) {
|
||||
return fmt.Errorf("Error reading DNS record: \"zone\" should be an FQDN")
|
||||
}
|
||||
|
||||
rec_fqdn := fmt.Sprintf("%s.%s", rec_name, rec_zone)
|
||||
|
||||
meta := testAccProvider.Meta()
|
||||
c := meta.(*DNSClient).c
|
||||
srv_addr := meta.(*DNSClient).srv_addr
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(rec_fqdn, dns.TypePTR)
|
||||
r, _, err := c.Exchange(msg, srv_addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fmt.Errorf("Error querying DNS record")
|
||||
}
|
||||
|
||||
if len(r.Answer) > 1 {
|
||||
return fmt.Errorf("Error querying DNS record: multiple responses received")
|
||||
}
|
||||
record := r.Answer[0]
|
||||
ptr, err := getPtrVal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error querying DNS record: %s", err)
|
||||
}
|
||||
if expected != ptr {
|
||||
return fmt.Errorf("DNS record differs: expected %v, found %v", expected, ptr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccDnsPtrRecord_basic = fmt.Sprintf(`
|
||||
resource "dns_ptr_record" "foo" {
|
||||
zone = "example.com."
|
||||
name = "r._dns-sd._udp"
|
||||
ptr = "bar.example.com."
|
||||
ttl = 300
|
||||
}`)
|
||||
|
||||
var testAccDnsPtrRecord_update = fmt.Sprintf(`
|
||||
resource "dns_ptr_record" "foo" {
|
||||
zone = "example.com."
|
||||
name = "r._dns-sd._udp"
|
||||
ptr = "baz.example.com."
|
||||
ttl = 300
|
||||
}`)
|
|
@ -22,6 +22,7 @@ import (
|
|||
datadogprovider "github.com/hashicorp/terraform/builtin/providers/datadog"
|
||||
digitaloceanprovider "github.com/hashicorp/terraform/builtin/providers/digitalocean"
|
||||
dmeprovider "github.com/hashicorp/terraform/builtin/providers/dme"
|
||||
dnsprovider "github.com/hashicorp/terraform/builtin/providers/dns"
|
||||
dnsimpleprovider "github.com/hashicorp/terraform/builtin/providers/dnsimple"
|
||||
dockerprovider "github.com/hashicorp/terraform/builtin/providers/docker"
|
||||
dynprovider "github.com/hashicorp/terraform/builtin/providers/dyn"
|
||||
|
@ -93,6 +94,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
|
|||
"datadog": datadogprovider.Provider,
|
||||
"digitalocean": digitaloceanprovider.Provider,
|
||||
"dme": dmeprovider.Provider,
|
||||
"dns": dnsprovider.Provider,
|
||||
"dnsimple": dnsimpleprovider.Provider,
|
||||
"docker": dockerprovider.Provider,
|
||||
"dyn": dynprovider.Provider,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
layout: "dns"
|
||||
page_title: "Provider: DNS"
|
||||
sidebar_current: "docs-dns-index"
|
||||
description: |-
|
||||
The DNS provider supports DNS updates (RFC 2136). Additionally, the provider can be configured with secret key based transaction authentication (RFC 2845).
|
||||
---
|
||||
|
||||
# DNS Provider
|
||||
|
||||
The DNS provider supports DNS updates (RFC 2136). Additionally, the provider can be configured with secret key based transaction authentication (RFC 2845).
|
||||
|
||||
Use the navigation to the left to read about the available resources.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
# Configure the DNS Provider
|
||||
provider "dns" {
|
||||
update {
|
||||
server = "192.168.0.1"
|
||||
key_name = "example.com."
|
||||
key_algorithm = "hmac-md5"
|
||||
key_secret = "3VwZXJzZWNyZXQ="
|
||||
}
|
||||
}
|
||||
|
||||
# Create a DNS A record set
|
||||
resource "dns_a_record_set" "www" {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
`update` - (Optional) When the provider is used for DNS updates, this block is required. Structure is documented below.
|
||||
|
||||
The `update` block supports the following attributes:
|
||||
|
||||
* `server` - (Required) The IPv4 address of the DNS server to send updates to.
|
||||
* `port` - (Optional) The target UDP port on the server where updates are sent to. Defaults to `53`.
|
||||
* `key_name` - (Optional) The name of the TSIG key used to sign the DNS update messages.
|
||||
* `key_algorithm` - (Optional; Required if `key_name` is set) When using TSIG authentication, the algorithm to use for HMAC. Valid values are `hmac-md5`, `hmac-sha1`, `hmac-sha256` or `hmac-sha512`.
|
||||
* `key_secret` - (Optional; Required if `key_name` is set)
|
||||
A Base64-encoded string containing the shared secret to be used for TSIG.
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
layout: "dns"
|
||||
page_title: "DNS: dns_a_record_set"
|
||||
sidebar_current: "docs-dns-record"
|
||||
description: |-
|
||||
Creates a A type DNS record set.
|
||||
---
|
||||
|
||||
# dns\_a\_record\_set
|
||||
|
||||
Creates a A type DNS record set.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "dns_a_record_set" "www" {
|
||||
zone = "example.com."
|
||||
name = "www"
|
||||
addresses = ["192.168.0.1", "192.168.0.2", "192.168.0.3"]
|
||||
ttl = 300
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `zone` - (Required) DNS zone the record set belongs to. It must be an FQDN, that is, include the trailing dot.
|
||||
* `name` - (Required) The name of the record set. The `zone` argument will be appended to this value to create the full record path.
|
||||
* `addresses` - (Required) The IPv4 addresses this record set will point to.
|
||||
* `ttl` - (Optional) The TTL of the record set. Defaults to `3600`.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `zone` - See Argument Reference above.
|
||||
* `name` - See Argument Reference above.
|
||||
* `addresses` - See Argument Reference above.
|
||||
* `ttl` - See Argument Reference above.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
layout: "dns"
|
||||
page_title: "DNS: dns_aaaa_record_set"
|
||||
sidebar_current: "docs-dns-record"
|
||||
description: |-
|
||||
Creates a AAAA type DNS record set.
|
||||
---
|
||||
|
||||
# dns\_a\_record\_set
|
||||
|
||||
Creates a AAAA type DNS record set.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "dns_aaaa_record_set" "www" {
|
||||
zone = "example.com."
|
||||
name = "www"
|
||||
addresses = ["fdd5:e282:43b8:5303:dead:beef:cafe:babe", "fdd5:e282:43b8:5303:cafe:babe:dead:beef"]
|
||||
ttl = 300
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `zone` - (Required) DNS zone the record set belongs to. It must be an FQDN, that is, include the trailing dot.
|
||||
* `name` - (Required) The name of the record set. The `zone` argument will be appended to this value to create the full record path.
|
||||
* `addresses` - (Required) The IPv6 addresses this record set will point to.
|
||||
* `ttl` - (Optional) The TTL of the record set. Defaults to `3600`.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `zone` - See Argument Reference above.
|
||||
* `name` - See Argument Reference above.
|
||||
* `addresses` - See Argument Reference above.
|
||||
* `ttl` - See Argument Reference above.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
layout: "dns"
|
||||
page_title: "DNS: dns_cname_record"
|
||||
sidebar_current: "docs-dns-record"
|
||||
description: |-
|
||||
Creates a CNAME type DNS record.
|
||||
---
|
||||
|
||||
# dns\_ptr\_record
|
||||
|
||||
Creates a CNAME type DNS record.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "dns_cname_record" "foo" {
|
||||
zone = "example.com."
|
||||
name = "foo"
|
||||
cname = "bar.example.com."
|
||||
ttl = 300
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `zone` - (Required) DNS zone the record belongs to. It must be an FQDN, that is, include the trailing dot.
|
||||
* `name` - (Required) The name of the record. The `zone` argument will be appended to this value to create the full record path.
|
||||
* `cname` - (Required) The canonical name this record will point to.
|
||||
* `ttl` - (Optional) The TTL of the record set. Defaults to `3600`.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `zone` - See Argument Reference above.
|
||||
* `name` - See Argument Reference above.
|
||||
* `cname` - See Argument Reference above.
|
||||
* `ttl` - See Argument Reference above.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
layout: "dns"
|
||||
page_title: "DNS: dns_ptr_record"
|
||||
sidebar_current: "docs-dns-record"
|
||||
description: |-
|
||||
Creates a PTR type DNS record.
|
||||
---
|
||||
|
||||
# dns\_ptr\_record
|
||||
|
||||
Creates a PTR type DNS record.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "dns_ptr_record" "dns-sd" {
|
||||
zone = "example.com."
|
||||
name = "r._dns-sd"
|
||||
ptr = "example.com."
|
||||
ttl = 300
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `zone` - (Required) DNS zone the record belongs to. It must be an FQDN, that is, include the trailing dot.
|
||||
* `name` - (Required) The name of the record. The `zone` argument will be appended to this value to create the full record path.
|
||||
* `ptr` - (Required) The canonical name this record will point to.
|
||||
* `ttl` - (Optional) The TTL of the record set. Defaults to `3600`.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `zone` - See Argument Reference above.
|
||||
* `name` - See Argument Reference above.
|
||||
* `ptr` - See Argument Reference above.
|
||||
* `ttl` - See Argument Reference above.
|
||||
|
Loading…
Reference in New Issue