Terraform and NREC: Part V - DNS Management¶
Last changed: 2024-09-17
This document is an introduction on how to create a DNS zone and DNS records in Openstack using Terraform.
The files used in this document can be downloaded:
The examples in this document have been tested and verified with Terraform version 1.9.5:
Terraform v1.9.5
on linux_amd64
+ provider registry.terraform.io/terraform-provider-openstack/openstack v2.1.0
Creating a DNS zone¶
It is quite easy to create a DNS zone using Terraform. Consider the part of static.tf below. It is a single resource declaration needed to create a zone.
Important
The DNS service expects the zone name to be a fully qualified domain name, which means that the name of the zone provided in the resource declaration must end with a dot “.”. Omitting the trailing dot will result in an error.
This is correct:
name = "test.com."
This is incorrect and will not work:
name = "test.com"
In this example we create a zone “test.com”:
1# Create zone
2resource "openstack_dns_zone_v2" "test_com" {
3 name = "test.com."
4 email = "trondham@uio.no"
5 description = "An example zone"
6}
This is all that is needed. You may add additional parameters, most commonly TTL, if you need to set a TTL value other than the default (3600).
Creating DNS records¶
In this example we create 3 records in the “test.com” zone:
An A record which poinst to a single IPv4 address for “test01.test.com”
An AAAA record which points to a single IPv6 address for “test01.test.com”
A CNAME record (alias) “www” which points to “test01.test.com”
The record resources are specified in the static.tf file below:
1# Create A record
2resource "openstack_dns_recordset_v2" "A_test01_test_com" {
3 zone_id = openstack_dns_zone_v2.test_com.id
4 name = "test01.test.com."
5 description = "An example record set"
6 type = "A"
7 records = [ "10.0.0.1" ]
8}
9
10# Create AAAA record
11resource "openstack_dns_recordset_v2" "AAAA_test01_test_com" {
12 zone_id = openstack_dns_zone_v2.test_com.id
13 name = "test01.test.com."
14 description = "An example record set"
15 type = "AAAA"
16 records = [ "2001:700:2:8200::226c" ]
17}
18
19# Create CNAME record
20resource "openstack_dns_recordset_v2" "CNAME_www_test_com" {
21 zone_id = openstack_dns_zone_v2.test_com.id
22 name = "www.test.com."
23 description = "An example record set"
24 type = "CNAME"
25 records = [ "test01.test.com." ]
26}
Important
The DNS service expects the record name to be a fully qualified domain name, which means that the name of the record provided in the resource declaration must end with a dot “.”. Omitting the trailing dot will result in an error. This is correct:
name = "app-01.test.com."
This is incorrect and will not work:
name = "app-01.test.com"
This also applies to the records list in case of a CNAME, as show in the example above.
Apply and check¶
Running terraform apply creates the zone, as well as the three records we specified:
$ terraform apply
...
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
We can check that the authoritative name servers have our zone and records by querying one of them them directly:
$ host www.test.com. ns1.nrec.no
Using domain server:
Name: ns1.nrec.no
Address: 158.37.63.251#53
Aliases:
www.test.com is an alias for test01.test.com.
test01.test.com has address 10.0.0.1
test01.test.com has IPv6 address 2001:700:2:8200::226c
As always, you can use terraform destroy to remove the created resources:
$ terraform destroy
...
Destroy complete! Resources: 4 destroyed.
Dynamically add DNS records¶
The previous examples show how to add a zone and create records within that zone. What if the zone already exists, and how do we automatically add a DNS record for an instance when the instance is created? We’ll answer those questions here.
First, let’s consider how to add records to an already existing
zone. The problem here is that we need to know the ID of the zone. We
can manually fetch the ID from the output of openstack zone list
and hard code the ID into our Terraform config, but there is a more
dynamic and flexible way to do this. In order to fetch the needed
metadata for our zone we use a data
directive in Terraform:
1# DNS zone
2variable "zone_name" {
3 default = "mytestzone.com"
4}
5# Create a zone
6data "openstack_dns_zone_v2" "myzone" {
7 name = "${var.zone_name}."
8}
In this example, we have a resource declaration for instances that creates an arbitrary number of instances. In our example, we create 2 instances:
1# How many instances to create
2variable "node_count" {
3 default = "2"
4}
5
6# Create instances
7resource "openstack_compute_instance_v2" "testserver" {
8 region = var.region
9 count = var.node_count
10 name = "${var.region}-test-${count.index}"
11 image_name = "GOLD Alma Linux 9"
12 flavor_name = "m1.small"
13
14 key_pair = "mykey"
15 security_groups = [ "default" ]
16
17 network {
18 name = "dualStack"
19 }
20
21 lifecycle {
22 ignore_changes = [image_name,image_id]
23 }
24}
Finally, in order to create DNS records for our instances we need to reference the name and IP of the instances. Notice the usage of the data variable to reference the zone ID (highlighted):
1# Create records for A (IPv4)
2resource "openstack_dns_recordset_v2" "a_record" {
3 zone_id = data.openstack_dns_zone_v2.myzone.id
4 count = var.node_count
5 name = "${openstack_compute_instance_v2.testserver[count.index].name}.${var.zone_name}."
6 type = "A"
7 records = [ "${openstack_compute_instance_v2.testserver[count.index].access_ip_v4}" ]
8}
9
10# Create records for AAAA (IPv6)
11resource "openstack_dns_recordset_v2" "aaaa_record" {
12 zone_id = data.openstack_dns_zone_v2.myzone.id
13 count = var.node_count
14 name = "${openstack_compute_instance_v2.testserver[count.index].name}.${var.zone_name}."
15 type = "AAAA"
16 records = [ trim(openstack_compute_instance_v2.testserver[count.index].access_ip_v6, "[]") ]
17}
In this example, we create both A (IPv4) and AAAA (IPv6) records for our instances, since we specified the “dualStack” network for the instance resources.
After running terraform apply
we can use the CLI command
openstack recordset list
to verify that the DNS records have been
created:
$ openstack recordset list mytestzone.com. -c name -c type -c records
+----------------------------+-------+-------------------------------------------------------------+
| name | type | records |
+----------------------------+-------+-------------------------------------------------------------+
| mytestzone.com. | SOA | ns2.nrec.no. foo.bar.com. 1575885141 3519 600 86400 3600 |
| mytestzone.com. | NS | ns1.nrec.no. |
| | | ns2.nrec.no. |
| bgo-test-1.mytestzone.com. | A | 158.39.74.137 |
| bgo-test-0.mytestzone.com. | AAAA | 2001:700:2:8300::21d3 |
| bgo-test-1.mytestzone.com. | AAAA | 2001:700:2:8300::207e |
| bgo-test-0.mytestzone.com. | A | 158.39.77.244 |
+----------------------------+-------+-------------------------------------------------------------+
And we can check that they exist in DNS by querying the authoritative name servers:
$ host bgo-test-1.mytestzone.com. ns1.nrec.no
Using domain server:
Name: ns1.nrec.no
Address: 158.37.63.251#53
Aliases:
bgo-test-1.mytestzone.com has address 158.39.74.137
bgo-test-1.mytestzone.com has IPv6 address 2001:700:2:8300::207e
Complete example¶
A complete listing of the example files used in this document is provided below.
1# Define required providers
2terraform {
3 required_version = ">= 1.0"
4 required_providers {
5 openstack = {
6 source = "terraform-provider-openstack/openstack"
7 }
8 }
9}
10
11# Configure the OpenStack Provider
12# Empty means using environment variables "OS_*". More info:
13# https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs
14provider "openstack" {}
15
16# Create zone
17resource "openstack_dns_zone_v2" "test_com" {
18 name = "test.com."
19 email = "trondham@uio.no"
20 description = "An example zone"
21}
22
23# Create A record
24resource "openstack_dns_recordset_v2" "A_test01_test_com" {
25 zone_id = openstack_dns_zone_v2.test_com.id
26 name = "test01.test.com."
27 description = "An example record set"
28 type = "A"
29 records = [ "10.0.0.1" ]
30}
31
32# Create AAAA record
33resource "openstack_dns_recordset_v2" "AAAA_test01_test_com" {
34 zone_id = openstack_dns_zone_v2.test_com.id
35 name = "test01.test.com."
36 description = "An example record set"
37 type = "AAAA"
38 records = [ "2001:700:2:8200::226c" ]
39}
40
41# Create CNAME record
42resource "openstack_dns_recordset_v2" "CNAME_www_test_com" {
43 zone_id = openstack_dns_zone_v2.test_com.id
44 name = "www.test.com."
45 description = "An example record set"
46 type = "CNAME"
47 records = [ "test01.test.com." ]
48}
1# Define required providers
2terraform {
3 required_version = ">= 1.0"
4 required_providers {
5 openstack = {
6 source = "terraform-provider-openstack/openstack"
7 }
8 }
9}
10
11# Configure the OpenStack Provider
12# Empty means using environment variables "OS_*". More info:
13# https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs
14provider "openstack" {}
15
16# DNS zone
17variable "zone_name" {
18 default = "mytestzone.com"
19}
20
21# Region in which to create instances
22variable "region" {
23 default = "bgo"
24}
25
26# How many instances to create
27variable "node_count" {
28 default = "2"
29}
30
31# Create instances
32resource "openstack_compute_instance_v2" "testserver" {
33 region = var.region
34 count = var.node_count
35 name = "${var.region}-test-${count.index}"
36 image_name = "GOLD Alma Linux 9"
37 flavor_name = "m1.small"
38
39 key_pair = "mykey"
40 security_groups = [ "default" ]
41
42 network {
43 name = "dualStack"
44 }
45
46 lifecycle {
47 ignore_changes = [image_name,image_id]
48 }
49}
50
51# Create a zone
52data "openstack_dns_zone_v2" "myzone" {
53 name = "${var.zone_name}."
54}
55
56# Create records for A (IPv4)
57resource "openstack_dns_recordset_v2" "a_record" {
58 zone_id = data.openstack_dns_zone_v2.myzone.id
59 count = var.node_count
60 name = "${openstack_compute_instance_v2.testserver[count.index].name}.${var.zone_name}."
61 type = "A"
62 records = [ "${openstack_compute_instance_v2.testserver[count.index].access_ip_v4}" ]
63}
64
65# Create records for AAAA (IPv6)
66resource "openstack_dns_recordset_v2" "aaaa_record" {
67 zone_id = data.openstack_dns_zone_v2.myzone.id
68 count = var.node_count
69 name = "${openstack_compute_instance_v2.testserver[count.index].name}.${var.zone_name}."
70 type = "AAAA"
71 records = [ trim(openstack_compute_instance_v2.testserver[count.index].access_ip_v6, "[]") ]
72}