Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
98 changes: 50 additions & 48 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,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."
}
}
Expand Down