feature(iac): add dns module #2
5 changed files with 321 additions and 0 deletions
160
dns/README.md
Normal file
160
dns/README.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Terraform STACKIT DNS Zone and Record Set Module
|
||||
|
||||
This module allows you to declaratively manage DNS zones and their associated record sets in STACKIT.
|
||||
|
||||
It supports:
|
||||
|
||||
- Creating one or more new DNS zones.
|
||||
- Using pre-existing DNS zones by providing a `zone_id`.
|
||||
- Creating one or more record sets within any managed zone.
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here is an example of how to use the module to create one or many new zones and use another pre-existing one.
|
||||
|
||||
```terraform
|
||||
terraform {
|
||||
required_providers {
|
||||
stackit = {
|
||||
source = "stackitcloud/stackit"
|
||||
version = "~> 0.59.0"
|
||||
}
|
||||
}
|
||||
backend "s3" {
|
||||
endpoints = {
|
||||
s3 = "https://object.storage.eu01.onstackit.cloud"
|
||||
}
|
||||
bucket = "test-bucket"
|
||||
key = "state/test/dns"
|
||||
region = "eu01"
|
||||
skip_region_validation = true
|
||||
skip_metadata_api_check = true
|
||||
skip_credentials_validation = true
|
||||
skip_requesting_account_id = true
|
||||
skip_s3_checksum = true
|
||||
use_path_style = true
|
||||
}
|
||||
}
|
||||
|
||||
provider "stackit" {
|
||||
# Configure your provider credentials, i.e.:
|
||||
default_region = local.region
|
||||
enable_beta_resources = true
|
||||
service_account_key_path = "sa_key.json"
|
||||
}
|
||||
|
||||
module "dns" {
|
||||
source = "./path/to/your/module" # Or a Git URL git::https://commerce-platform.git.onstackit.cloud/commerce-platform-public//terraform-modules/dns
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
|
||||
zones = {
|
||||
# EXAMPLE 1: Create multiple zones with multiple A records
|
||||
"primary_domain" = {
|
||||
name = "first domain"
|
||||
dns_name = "test-b.stackit.rocks"
|
||||
record_sets = {
|
||||
"qa-test" = {
|
||||
name = "qa-test"
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
records = ["192.0.1.1", "192.0.1.2"]
|
||||
}
|
||||
"beta-test" = {
|
||||
name = "beta-test"
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
records = ["192.0.2.10", "192.0.2.20", "192.0.2.30"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"secondary_domain" = {
|
||||
name = "second domain"
|
||||
dns_name = "test-a.stackit.rocks"
|
||||
record_sets = {
|
||||
"alpha-records" = {
|
||||
name = "alpha-test"
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
records = ["192.10.2.10", "192.10.2.20", "192.10.2.30"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
# EXAMPLE 2: Use a pre-existing zone and add a new TXT and A record to it
|
||||
"existing_domain" = {
|
||||
zone_id = "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"
|
||||
record_sets = {
|
||||
"spf_txt" = {
|
||||
name = "@"
|
||||
type = "TXT"
|
||||
records = ["v=spf1 mx -all"]
|
||||
ttl = 7200
|
||||
comment = "this is a test txt record"
|
||||
},
|
||||
"spf_cname" = {
|
||||
name = "exampledomain"
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
records = ["192.0.29.1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
# EXAMPLE 3: Create a new zone with no initial records
|
||||
"empty_domain" = {
|
||||
name = "My Empty Domain"
|
||||
dns_name = "empty-example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
| Variable Name | Description | Type | Required |
|
||||
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- |
|
||||
| `project_id` | STACKIT project ID to which the DNS resources are associated. | `string` | Yes |
|
||||
| `zones` | A map of DNS zones to be created or managed. Each zone contains a `name`, `dns_name`, and `record_sets` map. | `map` | Yes |
|
||||
| `record_sets` | A map of DNS record sets to create within this zone. Each record set contains a `name`, `type`, `records`, `ttl`, `comment`, and `active` attribute. | `map` | Optional |
|
||||
|
||||
### Values for zones
|
||||
|
||||
| Key | Description | Type | Required |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ----------------------------------- |
|
||||
| `zone_id` | The ID of an existing DNS zone to manage. If provided, the module will use the existing zone. If not provided, a new zone will be created. | `string` | Optional |
|
||||
| `name` | The descriptive name of the DNS zone. | `string` | Optional |
|
||||
| `dns_name` | The DNS name of the DNS zone. | `string` | Optional |
|
||||
| `contact_email` | The contact email for the DNS zone. | `string` | Optional |
|
||||
| `description` | A description of the DNS zone. | `string` | Optional |
|
||||
| `acl` | The Access Control List (ACL) for the DNS zone. | `string` | Optional |
|
||||
| `active` | Whether the DNS zone is active or not. | `bool` | Optional (currently non-functional) |
|
||||
| `default_ttl` | The default Time-to-Live (TTL) for records in the DNS zone. | `number` | Optional |
|
||||
| `expire_time` | The expiration time for the DNS zone. | `number` | Optional |
|
||||
| `is_reverse_zone` | Whether the DNS zone is a reverse zone or not. | `bool` | Optional |
|
||||
| `negative_cache` | The negative cache duration for the DNS zone. | `number` | Optional |
|
||||
| `primaries` | A list of primary name servers for the DNS zone. | `list(string)` | Optional |
|
||||
| `refresh_time` | The refresh time for the DNS zone. | `number` | Optional |
|
||||
| `retry_time` | The retry time for the DNS zone. | `number` | Optional |
|
||||
| `type` | The type of the DNS zone. Defaults to "primary" if not provided. | `string` | Optional |
|
||||
|
||||
### Values for record_sets
|
||||
|
||||
| Key | Description | Type | Required |
|
||||
| --------- | ------------------------------------------------------------- | -------------- | ----------------------------------- |
|
||||
| `name` | The name of the DNS record set. | `string` | Yes |
|
||||
| `type` | The type of the DNS record set. | `string` | Yes |
|
||||
| `records` | A list of DNS records for the record set. | `list(string)` | Yes |
|
||||
| `ttl` | The Time-to-Live (TTL) for the DNS records in the record set. | `number` | Optional |
|
||||
| `comment` | A comment for the DNS record set. | `string` | Optional |
|
||||
| `active` | Whether the DNS record set is active or not. | `bool` | Optional (currently non-functional) |
|
||||
|
||||
## Outputs
|
||||
|
||||
| Name | Description |
|
||||
| ------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `zones` | A map of all managed DNS zone objects, including those created and those referenced by ID. |
|
||||
| `record_sets` | A map of all created DNS record set objects. |
|
||||
|
||||
## Notes
|
||||
|
||||
Setting a zone or record to inactive by using `active = false` is currently not possible due to a bug in the provider. It is active by default.
|
||||
94
dns/dns.tf
Normal file
94
dns/dns.tf
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# main.tf
|
||||
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
# LOCAL VARIABLES
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
|
||||
locals {
|
||||
# Create a map of zones to be created (where zone_id is not specified)
|
||||
zones_to_create = { for k, v in var.zones : k => v if try(v.zone_id, null) == null }
|
||||
|
||||
# Create a map of zones to be referenced via data source (where zone_id is specified)
|
||||
zones_to_read = { for k, v in var.zones : k => v if try(v.zone_id, null) != null }
|
||||
|
||||
# Merge the created resources and data sources into a single, unified map.
|
||||
# This allows record sets to reference a zone regardless of whether it was created or read.
|
||||
all_zones = merge(
|
||||
{
|
||||
for k, zone in stackit_dns_zone.this : k => zone
|
||||
},
|
||||
{
|
||||
for k, zone in data.stackit_dns_zone.this : k => zone
|
||||
}
|
||||
)
|
||||
|
||||
# Flatten the nested record_sets structure into a single list, making it easy to iterate with for_each.
|
||||
# Each item in the list retains a reference to its parent zone key.
|
||||
flat_record_sets = flatten([
|
||||
for zone_key, zone_config in var.zones : [
|
||||
for record_key, record_config in try(zone_config.record_sets, {}) : {
|
||||
zone_key = zone_key
|
||||
record_key = record_key
|
||||
name = record_config.name
|
||||
type = record_config.type
|
||||
records = record_config.records
|
||||
ttl = try(record_config.ttl, null)
|
||||
comment = try(record_config.comment, null)
|
||||
active = try(record_config.active, null)
|
||||
}
|
||||
]
|
||||
])
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
# DNS ZONE RESOURCES (CREATE OR READ)
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
|
||||
# Create new DNS zones for configurations that do not have a zone_id
|
||||
resource "stackit_dns_zone" "this" {
|
||||
for_each = local.zones_to_create
|
||||
|
||||
project_id = var.project_id
|
||||
name = each.value.name
|
||||
dns_name = each.value.dns_name
|
||||
contact_email = try(each.value.contact_email, null)
|
||||
description = try(each.value.description, null)
|
||||
acl = try(each.value.acl, null)
|
||||
active = try(each.value.active, null)
|
||||
default_ttl = try(each.value.default_ttl, null)
|
||||
expire_time = try(each.value.expire_time, null)
|
||||
is_reverse_zone = try(each.value.is_reverse_zone, null)
|
||||
negative_cache = try(each.value.negative_cache, null)
|
||||
primaries = try(each.value.primaries, null)
|
||||
refresh_time = try(each.value.refresh_time, null)
|
||||
retry_time = try(each.value.retry_time, null)
|
||||
type = try(each.value.type, "primary")
|
||||
}
|
||||
|
||||
# Read existing DNS zones for configurations that provide a zone_id
|
||||
data "stackit_dns_zone" "this" {
|
||||
for_each = local.zones_to_read
|
||||
|
||||
project_id = var.project_id
|
||||
zone_id = each.value.zone_id
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
# DNS RECORD SET RESOURCES
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
|
||||
resource "stackit_dns_record_set" "this" {
|
||||
# The key is a unique combination of the zone and record keys for a stable address.
|
||||
for_each = { for record in local.flat_record_sets : "${record.zone_key}.${record.record_key}" => record }
|
||||
|
||||
project_id = var.project_id
|
||||
# Look up the correct zone_id from the unified 'all_zones' map
|
||||
zone_id = local.all_zones[each.value.zone_key].zone_id
|
||||
|
||||
name = each.value.name
|
||||
type = each.value.type
|
||||
records = each.value.records
|
||||
ttl = each.value.ttl
|
||||
comment = each.value.comment
|
||||
active = each.value.active
|
||||
}
|
||||
11
dns/outputs.tf
Normal file
11
dns/outputs.tf
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# outputs.tf
|
||||
|
||||
output "zones" {
|
||||
description = "A map of all managed DNS zone objects, including those created and those referenced by ID."
|
||||
value = local.all_zones
|
||||
}
|
||||
|
||||
output "record_sets" {
|
||||
description = "A map of all created DNS record set objects."
|
||||
value = stackit_dns_record_set.this
|
||||
}
|
||||
8
dns/providers.tf
Normal file
8
dns/providers.tf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
stackit = {
|
||||
source = "stackitcloud/stackit"
|
||||
version = "~> 0.61.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
48
dns/variables.tf
Normal file
48
dns/variables.tf
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# variables.tf
|
||||
|
||||
variable "project_id" {
|
||||
type = string
|
||||
description = "STACKIT project ID to which the DNS resources are associated."
|
||||
}
|
||||
|
||||
variable "zones" {
|
||||
type = map(object({
|
||||
# If zone_id is provided, the module will use the existing zone.
|
||||
# Otherwise, a new zone will be created using the attributes below.
|
||||
zone_id = optional(string)
|
||||
|
||||
# Required attributes for new zones
|
||||
name = optional(string)
|
||||
dns_name = optional(string)
|
||||
|
||||
# Optional attributes for new zones
|
||||
contact_email = optional(string)
|
||||
description = optional(string)
|
||||
acl = optional(string)
|
||||
active = optional(bool)
|
||||
default_ttl = optional(number)
|
||||
expire_time = optional(number)
|
||||
is_reverse_zone = optional(bool)
|
||||
negative_cache = optional(number)
|
||||
primaries = optional(list(string))
|
||||
refresh_time = optional(number)
|
||||
retry_time = optional(number)
|
||||
type = optional(string, "primary")
|
||||
|
||||
# A map of DNS record sets to create within this zone.
|
||||
# The key is a logical name for the record set (e.g., "www", "mx_record").
|
||||
record_sets = optional(map(object({
|
||||
# Required record set attributes
|
||||
name = string
|
||||
type = string
|
||||
records = list(string)
|
||||
|
||||
# Optional record set attributes
|
||||
ttl = optional(number)
|
||||
comment = optional(string)
|
||||
active = optional(bool, true)
|
||||
})), {})
|
||||
}))
|
||||
description = "A map of DNS zones to manage. The key is a logical name for the zone."
|
||||
default = {}
|
||||
}
|
||||
Loading…
Reference in a new issue