diff --git a/README.md b/README.md index a481c24a..dc843a3f 100644 --- a/README.md +++ b/README.md @@ -214,10 +214,8 @@ To attach access management tags to resources in this module, you need the follo | [dns\_instance\_name](#input\_dns\_instance\_name) | The name to give the provisioned DNS instance. If not set, the module generates a name based on the `prefix` and `name` variables. | `string` | `null` | no | | [dns\_location](#input\_dns\_location) | The target location or environment for the DNS instance created to host the custom resolver in a hub-spoke DNS resolution topology. Only used if enable\_hub is true and skip\_custom\_resolver\_hub\_creation is false (defaults). | `string` | `"global"` | no | | [dns\_plan](#input\_dns\_plan) | The plan for the DNS resource instance created to host the custom resolver in a hub-spoke DNS resolution topology. Only used if enable\_hub is true and skip\_custom\_resolver\_hub\_creation is false (defaults). | `string` | `"standard-dns"` | no | -| [dns\_records](#input\_dns\_records) | List of DNS records to be created. |
list(object({
name = string
type = string
ttl = number
rdata = string
preference = optional(number, null)
service = optional(string, null)
protocol = optional(string, null)
priority = optional(number, null)
weight = optional(number, null)
port = optional(number, null)
})) | `[]` | no |
-| [dns\_zone\_description](#input\_dns\_zone\_description) | The description of the DNS zone. | `string` | `"Default DNS Zone"` | no |
-| [dns\_zone\_label](#input\_dns\_zone\_label) | Label associated with the DNS zone. | `string` | `"dns-zone"` | no |
-| [dns\_zone\_name](#input\_dns\_zone\_name) | The name of the DNS zone to be created. | `string` | `null` | no |
+| [dns\_records](#input\_dns\_records) | List of DNS records to be created. | map(list(object({
name = string
type = string
ttl = number
rdata = string
preference = optional(number, null)
service = optional(string, null)
protocol = optional(string, null)
priority = optional(number, null)
weight = optional(number, null)
port = optional(number, null)
}))) | `{}` | no |
+| [dns\_zones](#input\_dns\_zones) | List of the DNS zone to be created. | list(object({
name = string
description = optional(string)
label = optional(string, "dns-zone")
})) | `[]` | no |
| [enable\_hub](#input\_enable\_hub) | Indicates whether this VPC is enabled as a DNS name resolution hub. | `bool` | `false` | no |
| [enable\_hub\_vpc\_crn](#input\_enable\_hub\_vpc\_crn) | Indicates whether Hub VPC CRN is passed. | `bool` | `false` | no |
| [enable\_hub\_vpc\_id](#input\_enable\_hub\_vpc\_id) | Indicates whether Hub VPC ID is passed. | `bool` | `false` | no |
diff --git a/examples/hub-spoke-delegated-resolver/main.tf b/examples/hub-spoke-delegated-resolver/main.tf
index 7005039a..1ef8eecd 100644
--- a/examples/hub-spoke-delegated-resolver/main.tf
+++ b/examples/hub-spoke-delegated-resolver/main.tf
@@ -36,7 +36,11 @@ module "hub_vpc" {
prefix = "${var.prefix}-hub"
tags = var.resource_tags
enable_hub = true
- dns_zone_name = "hnsexample.com"
+ dns_zones = [
+ {
+ name = "hnsexample.com"
+ }
+ ]
subnets = {
zone-1 = [
{
diff --git a/examples/vpc-with-dns/main.tf b/examples/vpc-with-dns/main.tf
index ce9ae17a..da867f48 100644
--- a/examples/vpc-with-dns/main.tf
+++ b/examples/vpc-with-dns/main.tf
@@ -46,7 +46,7 @@ module "slz_vpc" {
prefix = var.prefix
tags = var.resource_tags
enable_hub = true
- dns_zone_name = var.dns_zone_name
+ dns_zones = var.dns_zones
dns_records = var.dns_records
subnets = local.subnets
}
diff --git a/examples/vpc-with-dns/variables.tf b/examples/vpc-with-dns/variables.tf
index 3861e898..bbc19994 100644
--- a/examples/vpc-with-dns/variables.tf
+++ b/examples/vpc-with-dns/variables.tf
@@ -36,7 +36,7 @@ variable "resource_tags" {
variable "dns_records" {
description = "List of DNS records to create"
- type = list(object({
+ type = map(list(object({
name = string
type = string
rdata = string
@@ -47,8 +47,8 @@ variable "dns_records" {
protocol = optional(string)
service = optional(string)
weight = optional(number)
- }))
- default = [
+ })))
+ default = { "dns-example.com" = [
{
name = "testA"
type = "A"
@@ -77,11 +77,21 @@ variable "dns_records" {
rdata = "textinformation"
ttl = 900
}
- ]
+ ]
+ }
}
-variable "dns_zone_name" {
- description = "The name of the DNS zone to be created."
- type = string
- default = "dns-example.com"
+variable "dns_zones" {
+ description = "The DNS zones to be created."
+ type = list(object({
+ name = string
+ description = optional(string)
+ label = optional(string, "dns-zone")
+ }))
+ default = [
+ {
+ name = "dns-example.com"
+ description = "Example DNS zone"
+ }
+ ]
}
diff --git a/main.tf b/main.tf
index b48a4911..c9f21c0b 100644
--- a/main.tf
+++ b/main.tf
@@ -361,11 +361,11 @@ resource "ibm_is_flow_log" "flow_logs" {
###############################################################################
resource "ibm_dns_zone" "dns_zone" {
- count = var.enable_hub && !var.skip_custom_resolver_hub_creation && alltrue([var.dns_zone_name != null, var.dns_zone_name != ""]) ? 1 : 0
- name = var.dns_zone_name
+ for_each = var.enable_hub && !var.skip_custom_resolver_hub_creation ? { for zone in var.dns_zones : zone.name => zone } : {}
+ name = each.key
instance_id = var.use_existing_dns_instance ? var.existing_dns_instance_id : ibm_resource_instance.dns_instance_hub[0].guid
- description = var.dns_zone_description
- label = var.dns_zone_label
+ description = each.value.description == null ? "Hosted zone for ${each.key}" : each.value.description
+ label = each.value.label
}
##############################################################################
@@ -373,9 +373,9 @@ resource "ibm_dns_zone" "dns_zone" {
##############################################################################
resource "ibm_dns_permitted_network" "dns_permitted_network" {
- count = var.enable_hub && !var.skip_custom_resolver_hub_creation ? 1 : 0
+ for_each = var.enable_hub && !var.skip_custom_resolver_hub_creation ? ibm_dns_zone.dns_zone : {}
instance_id = var.use_existing_dns_instance ? var.existing_dns_instance_id : ibm_resource_instance.dns_instance_hub[0].guid
- zone_id = ibm_dns_zone.dns_zone[0].zone_id
+ zone_id = each.value.zone_id
vpc_crn = local.vpc_crn
type = "vpc"
}
@@ -384,10 +384,19 @@ resource "ibm_dns_permitted_network" "dns_permitted_network" {
# DNS Records
##############################################################################
+locals {
+ dns_records = flatten([
+ for key, value in var.dns_records : [
+ for idx, record in value : merge(record, { identifier = "${key}-${idx}", dns_zone = (key) })
+ ]
+ ])
+
+}
+
resource "ibm_dns_resource_record" "dns_record" {
- for_each = length(ibm_dns_zone.dns_zone) > 0 ? { for idx, record in var.dns_records : idx => record } : {}
+ for_each = length(ibm_dns_zone.dns_zone) > 0 ? { for record in local.dns_records : record.identifier => record } : {}
instance_id = var.use_existing_dns_instance ? var.existing_dns_instance_id : ibm_resource_instance.dns_instance_hub[0].guid
- zone_id = ibm_dns_zone.dns_zone[0].zone_id
+ zone_id = ibm_dns_zone.dns_zone[each.value.dns_zone].zone_id
name = each.value.name
type = each.value.type
@@ -407,7 +416,10 @@ resource "ibm_dns_resource_record" "dns_record" {
}
locals {
- record_ids = [for record in ibm_dns_resource_record.dns_record : element(split("/", record.id), 2)]
+ record_ids = {
+ for k in distinct([for d in local.dns_records : d.dns_zone]) :
+ k => [for d in local.dns_records : element(split("/", ibm_dns_resource_record.dns_record[d.identifier].id), 2) if d.dns_zone == k]
+ }
}
##############################################################################
diff --git a/outputs.tf b/outputs.tf
index 815e8af7..71d16eff 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -180,17 +180,17 @@ output "dns_custom_resolver_id" {
## DNS Zone and Records
output "dns_zone_state" {
description = "The state of the DNS zone."
- value = length(ibm_dns_zone.dns_zone) > 0 ? ibm_dns_zone.dns_zone[0].state : null
+ value = length(ibm_dns_zone.dns_zone) > 0 ? [for zone in var.dns_zones : { (zone.name) = ibm_dns_zone.dns_zone[zone.name].state }] : null
}
output "dns_zone_id" {
description = "The ID of the DNS zone."
- value = length(ibm_dns_zone.dns_zone) > 0 ? ibm_dns_zone.dns_zone[0].zone_id : null
+ value = length(ibm_dns_zone.dns_zone) > 0 ? [for zone in var.dns_zones : { (zone.name) = ibm_dns_zone.dns_zone[zone.name].zone_id }] : null
}
output "dns_zone" {
description = "A map representing DNS zone information."
- value = length(ibm_dns_zone.dns_zone) > 0 ? ibm_dns_zone.dns_zone[0] : null
+ value = length(ibm_dns_zone.dns_zone) > 0 ? [for zone in ibm_dns_zone.dns_zone : zone] : null
}
output "dns_record_ids" {
diff --git a/tests/pr_test.go b/tests/pr_test.go
index b024fd86..174d5977 100644
--- a/tests/pr_test.go
+++ b/tests/pr_test.go
@@ -42,13 +42,19 @@ const terraformVersion = "terraform_v1.10" // This should match the version in t
var permanentResources map[string]interface{}
// To verify DNS records creation
-var dnsRecordsMap = []map[string]interface{}{
- {"name": "testA", "type": "A", "rdata": "1.2.3.4", "ttl": 3600},
- {"name": "testAAAA", "type": "AAAA", "rdata": "2001:0db8:0012:0001:3c5e:7354:0000:5db5"},
- {"name": "testCNAME", "type": "CNAME", "rdata": "test.com"},
- {"name": "testTXT", "type": "TXT", "rdata": "textinformation", "ttl": 900},
- {"name": "testMX", "type": "MX", "rdata": "mailserver.test.com", "preference": 10},
- {"name": "testSRV", "type": "SRV", "rdata": "tester.com", "priority": 100, "weight": 100, "port": 8000, "service": "_sip", "protocol": "udp"},
+var dnsRecordsMap = map[string][]map[string]interface{}{
+ "slz.com": {
+ {"name": "testA", "type": "A", "rdata": "1.2.3.4", "ttl": 3600},
+ {"name": "testAAAA", "type": "AAAA", "rdata": "2001:0db8:0012:0001:3c5e:7354:0000:5db5"},
+ {"name": "testCNAME", "type": "CNAME", "rdata": "test.com"},
+ {"name": "testTXT", "type": "TXT", "rdata": "textinformation", "ttl": 900},
+ {"name": "testMX", "type": "MX", "rdata": "mailserver.test.com", "preference": 10},
+ {"name": "testSRV", "type": "SRV", "rdata": "tester.com", "priority": 100, "weight": 100, "port": 8000, "service": "_sip", "protocol": "udp"},
+ }}
+
+// To verify DNS zone creation
+var dnsZoneMap = []map[string]interface{}{
+ {"name": "slz.com"},
}
func TestMain(m *testing.M) {
@@ -192,7 +198,7 @@ func TestRunVpcWithDnsExample(t *testing.T) {
options.TerraformVars["dns_records"] = dnsRecordsMap
options.TerraformVars["name"] = "test-dns"
- options.TerraformVars["dns_zone_name"] = "slz.com"
+ options.TerraformVars["dns_zones"] = dnsZoneMap
output, err := options.RunTestConsistency()
assert.Nil(t, err, "This should not have errored")
assert.NotNil(t, output, "Expected some output")
diff --git a/variables.tf b/variables.tf
index 4af7460c..3424ca75 100644
--- a/variables.tf
+++ b/variables.tf
@@ -714,49 +714,44 @@ variable "dns_plan" {
}
}
-variable "dns_zone_name" {
- description = "The name of the DNS zone to be created."
- default = null
- type = string
+variable "dns_zones" {
+ description = "List of the DNS zone to be created."
+ type = list(object({
+ name = string
+ description = optional(string)
+ label = optional(string, "dns-zone")
+ }))
+ nullable = false
+ default = []
validation {
- condition = var.enable_hub && !var.skip_custom_resolver_hub_creation ? alltrue([var.dns_zone_name != null, var.dns_zone_name != ""]) : true
- error_message = "dns_zone_name must not be null or empty when enable_hub is true and skip_custom_resolver_hub_creation is false."
+ condition = var.enable_hub && !var.skip_custom_resolver_hub_creation ? length(var.dns_zones) != 0 : true
+ error_message = "dns_zones must not be empty list when enable_hub is true and skip_custom_resolver_hub_creation is false."
}
validation {
- condition = var.dns_zone_name == null ? true : !contains([
- "ibm.com",
- "softlayer.com",
- "bluemix.net",
- "softlayer.local",
- "mybluemix.net",
- "networklayer.com",
- "ibmcloud.com",
- "pdnsibm.net",
- "appdomain.cloud",
- "compass.cobaltiron.com"
- ], var.dns_zone_name)
-
+ condition = alltrue([
+ for zone in var.dns_zones :
+ !contains([
+ "ibm.com",
+ "softlayer.com",
+ "bluemix.net",
+ "softlayer.local",
+ "mybluemix.net",
+ "networklayer.com",
+ "ibmcloud.com",
+ "pdnsibm.net",
+ "appdomain.cloud",
+ "compass.cobaltiron.com"
+ ], zone.name)
+ ])
error_message = "The specified DNS zone name is not permitted. Please choose a different domain name. [Learn more](https://cloud.ibm.com/docs/dns-svcs?topic=dns-svcs-managing-dns-zones&interface=ui#restricted-dns-zone-names)"
}
}
-variable "dns_zone_description" {
- description = "The description of the DNS zone."
- type = string
- default = "Default DNS Zone"
-}
-
-variable "dns_zone_label" {
- description = "Label associated with the DNS zone."
- type = string
- default = "dns-zone"
-}
-
variable "dns_records" {
description = "List of DNS records to be created."
- type = list(object({
+ type = map(list(object({
name = string
type = string
ttl = number
@@ -767,30 +762,37 @@ variable "dns_records" {
priority = optional(number, null)
weight = optional(number, null)
port = optional(number, null)
- }))
- default = []
+ })))
+ nullable = false
+ default = {}
+
+ validation {
+ condition = length(var.dns_records) == 0 || alltrue([for k in keys(var.dns_records) : contains([for zone in var.dns_zones : zone.name], k)])
+ error_message = "The keys of 'dns_records' must match DNS names in 'dns_zones'."
+ }
+
validation {
- condition = length(var.dns_records) == 0 || alltrue([for record in var.dns_records != null ? var.dns_records : [] : (contains(["A", "AAAA", "CNAME", "MX", "PTR", "TXT", "SRV"], record.type))])
- error_message = "Invalid domain resource record type is provided."
+ condition = length(var.dns_records) == 0 || alltrue(flatten([for key, record in var.dns_records : [for value in record : (contains(["A", "AAAA", "CNAME", "MX", "PTR", "TXT", "SRV"], value.type))]]))
+ error_message = "Invalid domain resource record type is provided. Allowed values are 'A', 'AAAA', 'CNAME', 'MX', 'PTR', 'TXT', 'SRV'."
}
validation {
- condition = length(var.dns_records) == 0 || alltrue([
- for record in var.dns_records == null ? [] : var.dns_records : (
- record.type != "SRV" || (
- record.protocol != null && record.port != null &&
- record.service != null && record.priority != null && record.weight != null
+ condition = length(var.dns_records) == 0 || alltrue(flatten([
+ for key, record in var.dns_records : [for value in record : (
+ value.type != "SRV" || (
+ value.protocol != null && value.port != null &&
+ value.service != null && value.priority != null && value.weight != null
)
- )
- ])
+ )
+ ]]))
error_message = "Invalid SRV record configuration. For 'SRV' records, 'protocol' , 'service', 'priority', 'port' and 'weight' values must be provided."
}
validation {
- condition = length(var.dns_records) == 0 || alltrue([
- for record in var.dns_records == null ? [] : var.dns_records : (
- record.type != "MX" || record.preference != null
- )
- ])
+ condition = length(var.dns_records) == 0 || alltrue(flatten([
+ for key, record in var.dns_records : [for value in record : (
+ value.type != "MX" || value.preference != null
+ )
+ ]]))
error_message = "Invalid MX record configuration. For 'MX' records, value for 'preference' must be provided."
}
}