Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,8 @@ To attach access management tags to resources in this module, you need the follo
| <a name="input_dns_instance_name"></a> [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 |
| <a name="input_dns_location"></a> [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 |
| <a name="input_dns_plan"></a> [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 |
| <a name="input_dns_records"></a> [dns\_records](#input\_dns\_records) | List of DNS records to be created. | <pre>list(object({<br/> name = string<br/> type = string<br/> ttl = number<br/> rdata = string<br/> preference = optional(number, null)<br/> service = optional(string, null)<br/> protocol = optional(string, null)<br/> priority = optional(number, null)<br/> weight = optional(number, null)<br/> port = optional(number, null)<br/> }))</pre> | `[]` | no |
| <a name="input_dns_zone_description"></a> [dns\_zone\_description](#input\_dns\_zone\_description) | The description of the DNS zone. | `string` | `"Default DNS Zone"` | no |
| <a name="input_dns_zone_label"></a> [dns\_zone\_label](#input\_dns\_zone\_label) | Label associated with the DNS zone. | `string` | `"dns-zone"` | no |
| <a name="input_dns_zone_name"></a> [dns\_zone\_name](#input\_dns\_zone\_name) | The name of the DNS zone to be created. | `string` | `null` | no |
| <a name="input_dns_records"></a> [dns\_records](#input\_dns\_records) | List of DNS records to be created. | <pre>map(list(object({<br/> name = string<br/> type = string<br/> ttl = number<br/> rdata = string<br/> preference = optional(number, null)<br/> service = optional(string, null)<br/> protocol = optional(string, null)<br/> priority = optional(number, null)<br/> weight = optional(number, null)<br/> port = optional(number, null)<br/> })))</pre> | `{}` | no |
| <a name="input_dns_zones"></a> [dns\_zones](#input\_dns\_zones) | List of the DNS zone to be created. | <pre>list(object({<br/> name = string<br/> description = optional(string)<br/> label = optional(string, "dns-zone")<br/> }))</pre> | `[]` | no |
| <a name="input_enable_hub"></a> [enable\_hub](#input\_enable\_hub) | Indicates whether this VPC is enabled as a DNS name resolution hub. | `bool` | `false` | no |
| <a name="input_enable_hub_vpc_crn"></a> [enable\_hub\_vpc\_crn](#input\_enable\_hub\_vpc\_crn) | Indicates whether Hub VPC CRN is passed. | `bool` | `false` | no |
| <a name="input_enable_hub_vpc_id"></a> [enable\_hub\_vpc\_id](#input\_enable\_hub\_vpc\_id) | Indicates whether Hub VPC ID is passed. | `bool` | `false` | no |
Expand Down
6 changes: 5 additions & 1 deletion examples/hub-spoke-delegated-resolver/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down
2 changes: 1 addition & 1 deletion examples/vpc-with-dns/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
26 changes: 18 additions & 8 deletions examples/vpc-with-dns/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -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"
}
]
}
30 changes: 21 additions & 9 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -361,21 +361,21 @@ 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
}

##############################################################################
# DNS PERMITTED NETWORK
##############################################################################

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"
}
Expand All @@ -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

Expand All @@ -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]
}
}

##############################################################################
Expand Down
6 changes: 3 additions & 3 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
22 changes: 14 additions & 8 deletions tests/pr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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")
Expand Down
100 changes: 53 additions & 47 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -767,30 +762,41 @@ 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([
length(var.dns_zones) == length(keys(var.dns_records)),
alltrue([for k in var.dns_zones : contains(keys(var.dns_records), k.name)]),
alltrue([for k in keys(var.dns_records) : contains([for zone in var.dns_zones : zone.name], k)])
])
error_message = "The values in DNS names in 'dns_zones' must match exactly the keys of 'dns_records'."
}

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))])
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."
}

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."
}
}
Expand Down