├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── examples ├── private-domains │ ├── README.md │ └── main.tf ├── public-domains │ ├── README.md │ └── main.tf └── public-subdomains │ ├── README.md │ └── main.tf ├── locals.tf ├── main.tf ├── outputs.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | terraform.tfstate 3 | *.tfstate* 4 | terraform.tfvars 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ### 4 | ### Travis settings 5 | ### 6 | sudo: required 7 | services: 8 | - docker 9 | 10 | 11 | ### 12 | ### Installation 13 | ### 14 | before_install: true 15 | install: true 16 | 17 | ### 18 | ### Linting 19 | ### 20 | before_script: 21 | - make lint 22 | - make gen 23 | - git diff --quiet || { echo "Build Changes"; git diff; git status; false; } 24 | 25 | 26 | ### 27 | ### Testing 28 | ### 29 | script: 30 | - make test 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 cytopia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq (,) 2 | .error This Makefile requires GNU Make. 3 | endif 4 | 5 | CURRENT_DIR = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 6 | TF_EXAMPLES = $(sort $(dir $(wildcard $(CURRENT_DIR)examples/*/))) 7 | 8 | 9 | # ------------------------------------------------------------------------------------------------- 10 | # Image versions 11 | # ------------------------------------------------------------------------------------------------- 12 | TF_VERSION = light 13 | TFDOCS_VERSION = latest 14 | FL_VERSION = 0.4 15 | 16 | 17 | # ------------------------------------------------------------------------------------------------- 18 | # Image settings 19 | # ------------------------------------------------------------------------------------------------- 20 | # Adjust your delimiter here or overwrite via make arguments 21 | DELIM_START = 22 | DELIM_CLOSE = 23 | # What arguments to append to terraform-docs command 24 | TFDOCS_ARGS = --sort-by-required 25 | 26 | FL_IGNORE = .git/,.github/,*.terraform/ 27 | 28 | # ------------------------------------------------------------------------------------------------- 29 | # Default target 30 | # ------------------------------------------------------------------------------------------------- 31 | help: 32 | @echo "gen Generate terraform-docs output and replace in README.md's" 33 | @echo "lint Static source code analysis" 34 | @echo "test Integration tests" 35 | 36 | 37 | # ------------------------------------------------------------------------------------------------- 38 | # Standard targets 39 | # ------------------------------------------------------------------------------------------------- 40 | gen: _pull-tfdocs 41 | @echo "################################################################################" 42 | @echo "# Terraform-docs generate" 43 | @echo "################################################################################" 44 | @$(MAKE) --no-print-directory _gen-main 45 | @$(MAKE) --no-print-directory _gen-examples 46 | 47 | lint: 48 | @# Lint all Terraform files 49 | @echo "################################################################################" 50 | @echo "# Linting" 51 | @echo "################################################################################" 52 | $(MAKE) --no-print-directory _lint-files 53 | $(MAKE) --no-print-directory _lint-fmt 54 | 55 | @if docker run -it --rm -v "$(CURRENT_DIR):/t:ro" --workdir "/t" hashicorp/terraform:light \ 56 | fmt -check=true -diff=true -write=false -list=true .; then \ 57 | echo "OK"; \ 58 | else \ 59 | echo "Failed"; \ 60 | exit 1; \ 61 | fi; 62 | @echo 63 | 64 | 65 | test: _pull-tf 66 | @$(foreach example,\ 67 | $(TF_EXAMPLES),\ 68 | DOCKER_PATH="/t/examples/$(notdir $(patsubst %/,%,$(example)))"; \ 69 | echo "################################################################################"; \ 70 | echo "# Terraform init: $${DOCKER_PATH}"; \ 71 | echo "################################################################################"; \ 72 | if docker run -it --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" hashicorp/terraform:light \ 73 | init \ 74 | -verify-plugins=true \ 75 | -lock=false \ 76 | -upgrade=true \ 77 | -reconfigure \ 78 | -input=false \ 79 | -get-plugins=true \ 80 | -get=true \ 81 | .; then \ 82 | echo "OK"; \ 83 | else \ 84 | echo "Failed"; \ 85 | docker run -it --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" --entrypoint=rm hashicorp/terraform:light -rf .terraform/ || true; \ 86 | exit 1; \ 87 | fi; \ 88 | echo; \ 89 | ) 90 | @$(foreach example,\ 91 | $(TF_EXAMPLES),\ 92 | DOCKER_PATH="/t/examples/$(notdir $(patsubst %/,%,$(example)))"; \ 93 | echo "################################################################################"; \ 94 | echo "# Terraform validate: $${DOCKER_PATH}"; \ 95 | echo "################################################################################"; \ 96 | if docker run -it --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" hashicorp/terraform:light \ 97 | validate \ 98 | .; then \ 99 | echo "OK"; \ 100 | docker run -it --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" --entrypoint=rm hashicorp/terraform:light -rf .terraform/ || true; \ 101 | else \ 102 | echo "Failed"; \ 103 | docker run -it --rm -v "$(CURRENT_DIR):/t" --workdir "$${DOCKER_PATH}" --entrypoint=rm hashicorp/terraform:light -rf .terraform/ || true; \ 104 | exit 1; \ 105 | fi; \ 106 | echo; \ 107 | ) 108 | 109 | 110 | # ------------------------------------------------------------------------------------------------- 111 | # Helper Targets 112 | # ------------------------------------------------------------------------------------------------- 113 | _gen-main: 114 | @echo "------------------------------------------------------------" 115 | @echo "# Main module" 116 | @echo "------------------------------------------------------------" 117 | @if docker run --rm \ 118 | -v $(CURRENT_DIR):/data \ 119 | -e DELIM_START='$(DELIM_START)' \ 120 | -e DELIM_CLOSE='$(DELIM_CLOSE)' \ 121 | cytopia/terraform-docs:$(TFDOCS_VERSION) \ 122 | terraform-docs-replace-012 $(TFDOCS_ARGS) md README.md; then \ 123 | echo "OK"; \ 124 | else \ 125 | echo "Failed"; \ 126 | exit 1; \ 127 | fi 128 | 129 | _gen-examples: 130 | @$(foreach example,\ 131 | $(TF_EXAMPLES),\ 132 | DOCKER_PATH="examples/$(notdir $(patsubst %/,%,$(example)))"; \ 133 | echo "------------------------------------------------------------"; \ 134 | echo "# $${DOCKER_PATH}"; \ 135 | echo "------------------------------------------------------------"; \ 136 | if docker run --rm \ 137 | -v $(CURRENT_DIR):/data \ 138 | -e DELIM_START='$(DELIM_START)' \ 139 | -e DELIM_CLOSE='$(DELIM_CLOSE)' \ 140 | cytopia/terraform-docs:$(TFDOCS_VERSION) \ 141 | terraform-docs-replace-012 $(TFDOCS_ARGS) md $${DOCKER_PATH}/README.md; then \ 142 | echo "OK"; \ 143 | else \ 144 | echo "Failed"; \ 145 | exit 1; \ 146 | fi; \ 147 | ) 148 | 149 | _lint-files: _pull-fl 150 | @# Basic file linting 151 | @echo "################################################################################" 152 | @echo "# File-lint" 153 | @echo "################################################################################" 154 | @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-cr --text --ignore '$(FL_IGNORE)' --path . 155 | @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-crlf --text --ignore '$(FL_IGNORE)' --path . 156 | @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-single-newline --text --ignore '$(FL_IGNORE)' --path . 157 | @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-space --text --ignore '$(FL_IGNORE)' --path . 158 | @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8 --text --ignore '$(FL_IGNORE)' --path . 159 | @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8-bom --text --ignore '$(FL_IGNORE)' --path . 160 | 161 | _lint-fmt: _pull-tf 162 | @# Lint all Terraform files 163 | @echo "################################################################################" 164 | @echo "# Terraform fmt" 165 | @echo "################################################################################" 166 | @echo 167 | @echo "------------------------------------------------------------" 168 | @echo "# *.tf files" 169 | @echo "------------------------------------------------------------" 170 | @if docker run -it --rm -v "$(CURRENT_DIR):/t:ro" --workdir "/t" hashicorp/terraform:$(TF_VERSION) \ 171 | fmt -check=true -diff=true -write=false -list=true .; then \ 172 | echo "OK"; \ 173 | else \ 174 | echo "Failed"; \ 175 | exit 1; \ 176 | fi; 177 | @echo 178 | @echo "------------------------------------------------------------" 179 | @echo "# *.tfvars files" 180 | @echo "------------------------------------------------------------" 181 | @if docker run --rm --entrypoint=/bin/sh -v "$(CURRENT_DIR):/t:ro" --workdir "/t" hashicorp/terraform:$(TF_VERSION) \ 182 | -c "find . -name '*.tfvars' -type f -print0 | xargs -0 -n1 terraform fmt -check=true -write=false -diff=true -list=true"; then \ 183 | echo "OK"; \ 184 | else \ 185 | echo "Failed"; \ 186 | exit 1; \ 187 | fi; 188 | @echo 189 | 190 | _pull-tf: 191 | docker pull hashicorp/terraform:$(TF_VERSION) 192 | 193 | _pull-tfdocs: 194 | docker pull cytopia/terraform-docs:$(TFDOCS_VERSION) 195 | 196 | _pull-fl: 197 | docker pull cytopia/file-lint:$(FL_VERSION) 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform module: AWS Route53 Zone 2 | 3 | **[Usage](#usage)** | 4 | **[Tagging](#resource-tagging)** | 5 | **[Importing](#importing-existing-resources)** | 6 | **[Examples](#examples)** | 7 | **[Requirements](#requirements)** | 8 | **[Providers](#providers)** | 9 | **[Inputs](#inputs)** | 10 | **[Outputs](#outputs)** | 11 | **[License](#license)** 12 | 13 | [![Build Status](https://travis-ci.org/cytopia/terraform-aws-route53-zone.svg?branch=master)](https://travis-ci.org/cytopia/terraform-aws-route53-zone) 14 | [![Tag](https://img.shields.io/github/tag/cytopia/terraform-aws-route53-zone.svg)](https://github.com/cytopia/terraform-aws-route53-zone/releases) 15 | [![Terraform](https://img.shields.io/badge/Terraform--registry-aws--route53--zone-brightgreen.svg)](https://registry.terraform.io/modules/cytopia/route53-zone/aws/) 16 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 17 | 18 | This Terraform module is able to create an arbitrary number of **delegation sets**, **public** and **private** hosted zones for root and delegated domains. 19 | 20 | **Public** hosted zones can be created with or without a delegation set. 21 | **Private** hosted zones will always have the default VPC from the current region attached, but can optionally also attach more VPCs from any region. 22 | 23 | When adding delegated secondary zones, the **NS records** are added automatically to their corresponding root zone. 24 | The only thing you need to choose, is the TTL (in seconds), of those NS records, per item. 25 | 26 | 27 | ## Usage 28 | 29 | ```hcl 30 | module "public_zone" { 31 | source = "github.com/cytopia/terraform-aws-route53-zone?ref=v1.0.0" 32 | 33 | # Create as many delegation sets by reference name as are required. 34 | delegation_sets = [ 35 | "root-zone", 36 | "sub-zone", 37 | ] 38 | 39 | # Specify your root zones. 40 | # Tld or subdomain of any level to make this your starting point on the current AWS account. 41 | # If delegation set is null, it will use AWS defaults. otherwise specify the delegation set 42 | # by reference name as defined in 'delegation_sets' above. 43 | public_root_zones = [ 44 | { 45 | name = "example.com", 46 | delegation_set = "root-zone", 47 | }, 48 | { 49 | name = "example.org", 50 | delegation_set = null, 51 | }, 52 | ] 53 | 54 | # Specify your delegated secondary zones (must have their parents defined in 'public_zones') 55 | # If delegation set is null, it will use AWS defaults. otherwise specify the delegation set 56 | # by reference name as defined in 'delegation_sets' above. 57 | # Specify your own name servers or use an empty list to use AWS defaults. 58 | public_delegated_secondary_zones = [ 59 | { 60 | name = "internal.example.org", 61 | parent = "example.org", 62 | ns_ttl = 30, 63 | ns_list = [], 64 | delegation_set = null, 65 | }, 66 | { 67 | name = "private.example.org", 68 | parent = "example.org", 69 | ns_ttl = 30, 70 | ns_list = ["1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"], 71 | delegation_set = "sub-zone", 72 | }, 73 | ] 74 | 75 | # Specify your private zones. 76 | # All private zones will be attached to the default VPC of the current region. 77 | # Optionally also attach more VPCs by id and region. 78 | private_root_zones = [ 79 | { 80 | name = "private.loc", 81 | vpc_ids = [], 82 | }, 83 | { 84 | name = "private.local", 85 | vpc_ids = [{"id": "vpc-xxxxxxxxxx", "region": "eu-central-1"}], 86 | }, 87 | ] 88 | 89 | # A set of default tags to add to all managed resources. 90 | # The 'Name' tag will be added automatically with the value of the domain of each item. 91 | tags = { 92 | Environment = "prod" 93 | Infrastructure = "core" 94 | Owner = "terraform" 95 | Project = "route53-zone" 96 | } 97 | 98 | # The default comment to add to all managed resources. 99 | comment = "Managed by Terraform" 100 | } 101 | ``` 102 | 103 | 104 | ## Resource tagging 105 | 106 | This module will add certain tags to specific resources by default. The `tags` variable extends these and adds additional tags to the resources. 107 | 108 | | Tags | Condition | Description | 109 | |---------------------|---------------------------------------------------|------------------------| 110 | | `Name` | Always on all zones | Name of domain | 111 | | `Parent` | On public delegated zones | Name of parent domain | 112 | | `DelegationSetId` | On public zones which are using a delegation set | Name of delegation set | 113 | | `DelegationSetName` | On public zones which are using a delegation set | ID of delegation set | 114 | 115 | 116 | ## Importing existing resources 117 | 118 | In case you have existing resources and want to import them into this module, use the following commands: 119 | 120 | ### Delegation sets 121 | ```bash 122 | # List available delegation sets 123 | aws route53 list-reusable-delegation-sets 124 | 125 | # Define them in tfvars 126 | delegation_sets = [ 127 | "", # <- If a delegation set is nameless, use an empty string 128 | "deleg1", 129 | ] 130 | 131 | # Import them 132 | terraform import 'aws_route53_delegation_set.delegation_sets[""]' 133 | terraform import 'aws_route53_delegation_set.delegation_sets["deleg1"]' 134 | ``` 135 | 136 | ### Zones 137 | ```bash 138 | # Public root zone 139 | terraform import 'aws_route53_zone.public_root_zones["www.example.com"]' 140 | 141 | # Private root zone 142 | terraform import 'aws_route53_zone.private_root_zones["private.example.com"]' 143 | ``` 144 | 145 | ### Secondary zones 146 | Secondary zones will create NS records in their parent zone. So in order to import them, you will 147 | first have to identify the currently existing NS record in the parent zone, import it 148 | and then import the secondary zone. 149 | ```bash 150 | # List records in parent zone 151 | aws route53 list-resource-record-sets --hosted-zone-id 152 | 153 | # Import NS record (from parent zone) 154 | # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record#import 155 | terraform import 'aws_route53_record.public_delegated_secondary_ns_records["sub.www.example.com"]' _sub.www.example.com_NS 156 | 157 | # Import Sub zone 158 | terraform import 'aws_route53_zone.public_delegated_secondary_zones["sub.www.example.com"]' 159 | ``` 160 | 161 | 162 | ## Examples 163 | 164 | * [private-domains](examples/private-domains) 165 | * [public-domains](examples/public-domains) 166 | * [public-subdomains](examples/public-subdomains) 167 | 168 | 169 | 170 | ## Requirements 171 | 172 | No requirements. 173 | 174 | ## Providers 175 | 176 | | Name | Version | 177 | |------|---------| 178 | | aws | n/a | 179 | 180 | ## Inputs 181 | 182 | | Name | Description | Type | Default | Required | 183 | |------|-------------|------|---------|:--------:| 184 | | comment | Default comment to add to all resources. | `string` | `"Managed by Terraform"` | no | 185 | | delegation\_sets | A list of delegation sets to create. You only need to specify the alias names that can then be referenced by other variables in this module via this unique name. A delegation set is a set of four authoritative name servers that you can use with more than one hosted zone. By default, Route 53 assigns a random selection of name servers to each new hosted zone. To make it easier to migrate DNS service to Route 53 for a large number of domains, you can create a reusable delegation set and then associate the reusable delegation set with new hosted zones. | `list(string)` | `[]` | no | 186 | | private\_root\_zones | Private Route53 root zone (also allows subdomain if this is your root starting point). Note, by default the default VPC will always be attached, even if vpc\_ids or vpc\_tags are empty. |
list(object({
name = string,
vpc_ids = list(object({
id = string,
region = string,
})),
}))
| `[]` | no | 187 | | public\_delegated\_secondary\_zones | A list of public Route53 delegated secondary zones. Each item must specify its 'parent' by name, which must match the name defined in the 'public\_root\_zones' variables and must also be exactly one level deeper than the corresponding root zone item. By doing so, this module will automatically add nameservers into the root zone to create the delegation. You can also attach a delegation\_set to this zone by its reference name (if it has been defined in the 'delegation\_sets' list) or set it to 'null' to use no delegation set. Additionally you can also define your own name servers for this zone by specifying them in the `ns_list` list or just leave the list empty to use AWS default name server. |
list(object({
name = string,
parent = string,
ns_ttl = number,
ns_list = list(string),
delegation_set = string,
}))
| `[]` | no | 188 | | public\_root\_zones | A list of public Route53 root zones. A 'root zone' can be anything from a tld to any level of subdomain, if and only if this is your root starting point for this (sub-)domain on the current AWS account. You can also attach a delegation\_set to this root zone by its reference name (if it has been defined in the 'delegation\_sets' list) or set it to 'null' to use no delegation set. |
list(object({
name = string,
delegation_set = string,
}))
| `[]` | no | 189 | | tags | Default tags to additionally apply to all resources. | `map` | `{}` | no | 190 | 191 | ## Outputs 192 | 193 | | Name | Description | 194 | |------|-------------| 195 | | delegation\_sets | Created delegation sets. | 196 | | private\_root\_zones | Created private root zones. | 197 | | public\_delegated\_secondary\_ns\_records | Created NS records in your root zone for delegated secondary zones. | 198 | | public\_delegated\_secondary\_zones | Created public delegated secondary zones. | 199 | | public\_root\_zones | Created public root zones. | 200 | 201 | 202 | 203 | 204 | ## Example output 205 | 206 | ```bash 207 | $ terraform output 208 | ``` 209 | ``` 210 | Outputs: 211 | 212 | delegation_sets = { 213 | "dg0" = { 214 | "id" = "N0XXXXXXXXXXXXXXXXXXX" 215 | "name_servers" = [ 216 | "ns-1.awsdns-44.org", 217 | "ns-2.awsdns-54.co.uk", 218 | "ns-3.awsdns-14.net", 219 | "ns-4.awsdns-01.com", 220 | ] 221 | "reference_name" = "dg0" 222 | } 223 | "dg1" = { 224 | "id" = "N1XXXXXXXXXXXXXXXXXXX" 225 | "name_servers" = [ 226 | "ns-5.awsdns-44.org", 227 | "ns-6.awsdns-54.co.uk", 228 | "ns-7.awsdns-14.net", 229 | "ns-8.awsdns-01.com", 230 | ] 231 | "reference_name" = "dg1" 232 | } 233 | } 234 | public_root_zones = { 235 | "example.org" = { 236 | "comment" = "Managed by Terraform" 237 | "delegation_set_id" = "N0XXXXXXXXXXXXXXXXXXX" 238 | "force_destroy" = false 239 | "id" = "Z0YYYYYYYYYYYYYYYY" 240 | "name" = "example.org." 241 | "name_servers" = [ 242 | "ns-1.awsdns-44.org", 243 | "ns-2.awsdns-54.co.uk", 244 | "ns-3.awsdns-14.net", 245 | "ns-4.awsdns-01.com", 246 | ] 247 | "tags" = { 248 | "DelegationSetId" = "N0XXXXXXXXXXXXXXXXXXX" 249 | "DelegationSetName" = "dg0" 250 | "Name" = "example.org" 251 | } 252 | "vpc" = [] 253 | "zone_id" = "Z0YYYYYYYYYYYYYYYY" 254 | } 255 | public_delegated_secondary_ns_records = { 256 | "internal.example.org" = { 257 | "alias" = [] 258 | "failover_routing_policy" = [] 259 | "fqdn" = "internal.example.org" 260 | "geolocation_routing_policy" = [] 261 | "id" = "Z1YYYYYYYYYYYYYYYY_internal.example.org_NS" 262 | "latency_routing_policy" = [] 263 | "name" = "internal.example.org" 264 | "records" = [ 265 | "ns-5.awsdns-44.org", 266 | "ns-6.awsdns-54.co.uk", 267 | "ns-7.awsdns-14.net", 268 | "ns-8.awsdns-01.com", 269 | ] 270 | "ttl" = 30 271 | "type" = "NS" 272 | "weighted_routing_policy" = [] 273 | "zone_id" = "Z1YYYYYYYYYYYYYYYY" 274 | } 275 | } 276 | public_delegated_secondary_zones = { 277 | "internal.example.org" = { 278 | "comment" = "Managed by Terraform" 279 | "delegation_set_id" = "N1XXXXXXXXXXXXXXXXXXX" 280 | "force_destroy" = false 281 | "id" = "Z1YYYYYYYYYYYYYYYY" 282 | "name" = "internal.example.org." 283 | "name_servers" = [ 284 | "ns-5.awsdns-44.org", 285 | "ns-6.awsdns-54.co.uk", 286 | "ns-7.awsdns-14.net", 287 | "ns-8.awsdns-01.com", 288 | ] 289 | "tags" = { 290 | "DelegationSetId" = "N1XXXXXXXXXXXXXXXXXXX" 291 | "DelegationSetName" = "dg1" 292 | "Name" = "internal.example.org" 293 | "Root" = "example.org" 294 | } 295 | "vpc" = [] 296 | "zone_id" = "Z1YYYYYYYYYYYYYYYY" 297 | } 298 | ``` 299 | 300 | 301 | ## Authors 302 | 303 | Module managed by [cytopia](https://github.com/cytopia). 304 | 305 | 306 | ## License 307 | 308 | [MIT License](LICENSE) 309 | 310 | Copyright (c) 2018 [cytopia](https://github.com/cytopia) 311 | -------------------------------------------------------------------------------- /examples/private-domains/README.md: -------------------------------------------------------------------------------- 1 | # Private domains example 2 | 3 | ## Usage 4 | 5 | To run this example you need to execute: 6 | 7 | ```bash 8 | $ terraform init 9 | $ terraform plan 10 | $ terraform apply 11 | ``` 12 | 13 | Note that this example may create resources which cost money. Run terraform destroy when you don't need these resources. 14 | 15 | 16 | 17 | ## Requirements 18 | 19 | No requirements. 20 | 21 | ## Providers 22 | 23 | No provider. 24 | 25 | ## Inputs 26 | 27 | No input. 28 | 29 | ## Outputs 30 | 31 | No output. 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/private-domains/main.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------- 2 | # AWS Settings 3 | # ------------------------------------------------------------------------------------------------- 4 | provider "aws" { 5 | region = "eu-central-1" 6 | } 7 | 8 | # ------------------------------------------------------------------------------------------------- 9 | # Modules Settings 10 | # ------------------------------------------------------------------------------------------------- 11 | module "aws_route53zone" { 12 | source = "../.." 13 | 14 | delegation_sets = [] 15 | 16 | # Add private zones. 17 | # NOTE: They are always attached to the default vpc of the current region 18 | private_root_zones = [ 19 | { 20 | name = "private.loc", 21 | vpc_ids = [], 22 | }, 23 | { 24 | name = "private.local", 25 | vpc_ids = [{"id": "vpc-xxxxxxxxxx", "region": "eu-central-1"}], 26 | }, 27 | ] 28 | 29 | comment = "Managed by Terraform" 30 | 31 | tags = { 32 | Environment = "example" 33 | Owner = "terraform" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/public-domains/README.md: -------------------------------------------------------------------------------- 1 | # Public domains example 2 | 3 | ## Usage 4 | 5 | To run this example you need to execute: 6 | 7 | ```bash 8 | $ terraform init 9 | $ terraform plan 10 | $ terraform apply 11 | ``` 12 | 13 | Note that this example may create resources which cost money. Run terraform destroy when you don't need these resources. 14 | 15 | 16 | 17 | ## Requirements 18 | 19 | No requirements. 20 | 21 | ## Providers 22 | 23 | No provider. 24 | 25 | ## Inputs 26 | 27 | No input. 28 | 29 | ## Outputs 30 | 31 | No output. 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/public-domains/main.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------- 2 | # AWS Settings 3 | # ------------------------------------------------------------------------------------------------- 4 | provider "aws" { 5 | region = "eu-central-1" 6 | } 7 | 8 | # ------------------------------------------------------------------------------------------------- 9 | # Modules Settings 10 | # ------------------------------------------------------------------------------------------------- 11 | module "aws_route53zone" { 12 | source = "../.." 13 | 14 | delegation_sets = [] 15 | 16 | public_root_zones = [ 17 | { 18 | name = "example.com", 19 | delegation_set = null, 20 | }, 21 | { 22 | name = "example.org", 23 | delegation_set = null, 24 | }, 25 | ] 26 | 27 | comment = "Managed by Terraform" 28 | 29 | tags = { 30 | Environment = "example" 31 | Owner = "terraform" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/public-subdomains/README.md: -------------------------------------------------------------------------------- 1 | # Public subdomains example 2 | 3 | ## Usage 4 | 5 | To run this example you need to execute: 6 | 7 | ```bash 8 | $ terraform init 9 | $ terraform plan 10 | $ terraform apply 11 | ``` 12 | 13 | Note that this example may create resources which cost money. Run terraform destroy when you don't need these resources. 14 | 15 | 16 | 17 | ## Requirements 18 | 19 | No requirements. 20 | 21 | ## Providers 22 | 23 | No provider. 24 | 25 | ## Inputs 26 | 27 | No input. 28 | 29 | ## Outputs 30 | 31 | No output. 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/public-subdomains/main.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------- 2 | # AWS Settings 3 | # ------------------------------------------------------------------------------------------------- 4 | provider "aws" { 5 | region = "eu-central-1" 6 | } 7 | 8 | # ------------------------------------------------------------------------------------------------- 9 | # Modules Settings 10 | # ------------------------------------------------------------------------------------------------- 11 | module "aws_route53zone" { 12 | source = "../.." 13 | 14 | delegation_sets = [] 15 | 16 | public_root_zones = [ 17 | { 18 | name = "example.com", 19 | delegation_set = "root-zones", 20 | }, 21 | { 22 | name = "example.org", 23 | delegation_set = null, 24 | }, 25 | ] 26 | 27 | # If delegation set is null, it will use AWS defaults. 28 | # Specify your own nameserver or use an empty list to use AWS defaults. 29 | public_delegated_secondary_zones = [ 30 | { 31 | name = "internal.example.org", 32 | parent = "example.org", 33 | ns_ttl = 30, 34 | ns_list = [], 35 | delegation_set = null, 36 | }, 37 | { 38 | name = "private.example.org", 39 | parent = "example.org", 40 | ns_ttl = 30, 41 | ns_list = ["1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"], 42 | delegation_set = null, 43 | }, 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /locals.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------- 2 | # Public root zones 3 | # ------------------------------------------------------------------------------------------------- 4 | locals { 5 | # Transforms from: 6 | # ---------------- 7 | # var.public_root_zones = [ 8 | # { 9 | # name = "example1.tld", 10 | # delegation_set = "deleg-name", 11 | # }, 12 | # { 13 | # name = "example2.tld", 14 | # delegation_set = null, 15 | # }, 16 | # ] 17 | # 18 | # Transforms into: 19 | # ---------------- 20 | # local.public_root_zones = { 21 | # "example1.tld" { 22 | # "name" = "example1.tld" 23 | # "deleg_id" = "N0XXXXXXXXXXXXX" 24 | # "deleg_name" = "deleg-name" 25 | # }, 26 | # "example2.tld" { 27 | # "name" = "example2.tld" 28 | # "deleg_id" = null 29 | # "deleg_name" = null 30 | # }, 31 | # } 32 | public_root_zones = { 33 | for zone in var.public_root_zones : zone.name => { 34 | name = zone.name 35 | deleg_id = zone.delegation_set != null ? aws_route53_delegation_set.delegation_sets[zone.delegation_set]["id"] : null 36 | deleg_name = zone.delegation_set 37 | } 38 | } 39 | } 40 | 41 | 42 | # ------------------------------------------------------------------------------------------------- 43 | # Public secondary zones 44 | # ------------------------------------------------------------------------------------------------- 45 | locals { 46 | # Transforms from: 47 | # ---------------- 48 | # var.public_delegated_secondary_zones = [ 49 | # { 50 | # name = "intranet.example.tld", 51 | # root = "example.tld", 52 | # nameservers = [], 53 | # ns_ttl = 30, 54 | # delegation_set = "deleg-name", 55 | # }, 56 | # { 57 | # name = "private.example.tld", 58 | # root = "example.tld", 59 | # nameservers = ["1,1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"], 60 | # delegation_set = null, 61 | # ns_ttl = 30, 62 | # }, 63 | # ] 64 | # 65 | # Transforms into: 66 | # ---------------- 67 | # local.public_delegated_secondary_zones = { 68 | # "intranet.example1.tld" { 69 | # "name" = "intranet.example.tld" 70 | # "parent" = "example.tld", 71 | # "deleg_id" = "N0XXXXXXXXXXXXX" 72 | # "deleg_name" = "deleg-name" 73 | # }, 74 | # "private.example1.tld" { 75 | # "name" = "private.example.tld" 76 | # "parent" = "example.tld", 77 | # "deleg_id" = null 78 | # "deleg_name" = "" 79 | # }, 80 | # } 81 | # local.public_delegated_secondary_default_ns_records = { 82 | # "intranet.example1.tld" { 83 | # "name" = "intranet.example.tld" 84 | # "parent" = "example.tld", 85 | # }, 86 | # } 87 | # local.public_delegated_secondary_custom_ns_records = { 88 | # "private.example1.tld" { 89 | # "name" = "private.example.tld" 90 | # "parent" = "example.tld", 91 | # "nameservers" = ["1,1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"], 92 | # "ns_ttl" = 30, 93 | # }, 94 | # } 95 | public_delegated_secondary_zones = { 96 | for zone in var.public_delegated_secondary_zones : zone.name => { 97 | name = zone.name 98 | parent = zone.parent 99 | deleg_id = zone.delegation_set != null ? aws_route53_delegation_set.delegation_sets[zone.delegation_set]["id"] : null 100 | deleg_name = zone.delegation_set 101 | } 102 | } 103 | public_delegated_secondary_ns_records = { 104 | for zone in var.public_delegated_secondary_zones : zone.name => { 105 | name = zone.name 106 | parent = zone.parent 107 | ns_ttl = zone.ns_ttl 108 | ns_list = length(zone.ns_list) == 0 ? aws_route53_zone.public_delegated_secondary_zones[zone.name]["name_servers"] : zone.ns_list 109 | } 110 | } 111 | } 112 | 113 | 114 | # ------------------------------------------------------------------------------------------------- 115 | # Private root zones 116 | # ------------------------------------------------------------------------------------------------- 117 | locals { 118 | # Transforms from: 119 | # ---------------- 120 | # var.private_root_zones = [ 121 | # { 122 | # name = "example1.tld", 123 | # vpc_ids = [{"id"="vpc-11111", "region"="eu-central"}], 124 | # }, 125 | # { 126 | # name = "example2.tld", 127 | # vpc_ids = [{"id"="vpc-11111", "region"="eu-central"}], 128 | # }, 129 | # ] 130 | # 131 | # Transforms into: 132 | # ---------------- 133 | # local.private_root_zones = { 134 | # "example1.tld" { 135 | # "name" = "example1.tld" 136 | # vpc_ids = [{"id"="vpc-11111", "region"="eu-central"}], 137 | # }, 138 | # "example2.tld" { 139 | # "name" = "example2.tld" 140 | # vpc_ids = [{"id"="vpc-11111", "region"="eu-central"}], 141 | # }, 142 | private_root_zones = { 143 | for zone in var.private_root_zones : zone.name => { 144 | name = zone.name 145 | vpc_ids = zone.vpc_ids 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------- 2 | # Delegation sets 3 | # ------------------------------------------------------------------------------------------------- 4 | resource "aws_route53_delegation_set" "delegation_sets" { 5 | for_each = { for val in var.delegation_sets : val => val } 6 | 7 | reference_name = each.value 8 | } 9 | 10 | 11 | # ------------------------------------------------------------------------------------------------- 12 | # Public root zones 13 | # ------------------------------------------------------------------------------------------------- 14 | resource "aws_route53_zone" "public_root_zones" { 15 | for_each = local.public_root_zones 16 | 17 | name = each.value.name 18 | comment = var.comment 19 | 20 | delegation_set_id = each.value.deleg_id 21 | 22 | tags = merge( 23 | map("Name", each.value.name), 24 | map("DelegationSetId", each.value.deleg_id), 25 | map("DelegationSetName", each.value.deleg_name), 26 | var.tags 27 | ) 28 | 29 | depends_on = [aws_route53_delegation_set.delegation_sets] 30 | } 31 | 32 | 33 | # ------------------------------------------------------------------------------------------------- 34 | # Public secondary zones 35 | # ------------------------------------------------------------------------------------------------- 36 | resource "aws_route53_zone" "public_delegated_secondary_zones" { 37 | for_each = local.public_delegated_secondary_zones 38 | 39 | name = each.value.name 40 | comment = var.comment 41 | 42 | delegation_set_id = each.value.deleg_id 43 | 44 | tags = merge( 45 | map("Name", each.value.name), 46 | map("Parent", each.value.parent), 47 | map("DelegationSetId", each.value.deleg_id), 48 | map("DelegationSetName", each.value.deleg_name), 49 | var.tags 50 | ) 51 | 52 | depends_on = [aws_route53_zone.public_root_zones] 53 | } 54 | 55 | # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone#public-subdomain-zone 56 | resource "aws_route53_record" "public_delegated_secondary_ns_records" { 57 | for_each = local.public_delegated_secondary_ns_records 58 | 59 | zone_id = aws_route53_zone.public_root_zones[each.value.parent]["id"] 60 | name = each.value.name 61 | type = "NS" 62 | ttl = each.value.ns_ttl 63 | records = formatlist("%s.", each.value.ns_list) 64 | 65 | depends_on = [aws_route53_zone.public_delegated_secondary_zones] 66 | } 67 | 68 | 69 | # ------------------------------------------------------------------------------------------------- 70 | # Private root zones 71 | # ------------------------------------------------------------------------------------------------- 72 | data "aws_vpc" "default" { 73 | default = true 74 | } 75 | data "aws_region" "current" {} 76 | 77 | resource "aws_route53_zone" "private_root_zones" { 78 | for_each = local.private_root_zones 79 | 80 | name = each.value.name 81 | comment = var.comment 82 | 83 | dynamic "vpc" { 84 | for_each = { for vpc in concat([{ "id" = data.aws_vpc.default.id, "region" = data.aws_region.current.name }], each.value.vpc_ids) : vpc.id => vpc } 85 | content { 86 | vpc_id = vpc.value.id 87 | vpc_region = vpc.value.region 88 | } 89 | } 90 | 91 | tags = merge( 92 | map("Name", each.value.name), 93 | var.tags 94 | ) 95 | 96 | depends_on = [data.aws_vpc.default, data.aws_region.current] 97 | } 98 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | # 2 | # Note: local_* values are for debugging purposes 3 | # 4 | 5 | # ------------------------------------------------------------------------------------------------- 6 | # Route53 Delegation Sets 7 | # ------------------------------------------------------------------------------------------------- 8 | output "delegation_sets" { 9 | value = aws_route53_delegation_set.delegation_sets 10 | description = "Created delegation sets." 11 | } 12 | 13 | 14 | # ------------------------------------------------------------------------------------------------- 15 | # Public Route53 Root Zones 16 | # ------------------------------------------------------------------------------------------------- 17 | #output "local_public_root_zones" { 18 | # value = local.public_root_zones 19 | # description = "Transformed public root zones." 20 | #} 21 | 22 | output "public_root_zones" { 23 | value = aws_route53_zone.public_root_zones 24 | description = "Created public root zones." 25 | } 26 | 27 | 28 | # ------------------------------------------------------------------------------------------------- 29 | # Public Route53 Subdomain Zones (secondary) 30 | # ------------------------------------------------------------------------------------------------- 31 | #output "local_public_delegated_secondary_zones" { 32 | # value = local.public_delegated_secondary_zones 33 | # description = "Transformed public secondary zones." 34 | #} 35 | 36 | #output "local_public_delegated_secondary_ns_records" { 37 | # value = local.public_delegated_secondary_ns_records 38 | # description = "Transformed public secondary ns records." 39 | #} 40 | 41 | output "public_delegated_secondary_zones" { 42 | value = aws_route53_zone.public_delegated_secondary_zones 43 | description = "Created public delegated secondary zones." 44 | } 45 | 46 | output "public_delegated_secondary_ns_records" { 47 | value = aws_route53_record.public_delegated_secondary_ns_records 48 | description = "Created NS records in your root zone for delegated secondary zones." 49 | } 50 | 51 | 52 | # ------------------------------------------------------------------------------------------------- 53 | # Private Route53 Root Zones 54 | # ------------------------------------------------------------------------------------------------- 55 | #output "local_private_root_zones" { 56 | # value = local.private_root_zones 57 | # description = "Transformed private root zones." 58 | #} 59 | 60 | output "private_root_zones" { 61 | value = aws_route53_zone.private_root_zones 62 | description = "Created private root zones." 63 | } 64 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------------- 2 | # Optional Public Zone Variables 3 | # ------------------------------------------------------------------------------------------------- 4 | variable "delegation_sets" { 5 | type = list(string) 6 | default = [] 7 | description = "A list of delegation sets to create. You only need to specify the alias names that can then be referenced by other variables in this module via this unique name. A delegation set is a set of four authoritative name servers that you can use with more than one hosted zone. By default, Route 53 assigns a random selection of name servers to each new hosted zone. To make it easier to migrate DNS service to Route 53 for a large number of domains, you can create a reusable delegation set and then associate the reusable delegation set with new hosted zones." 8 | } 9 | 10 | variable "public_root_zones" { 11 | type = list(object({ 12 | name = string, 13 | delegation_set = string, 14 | })) 15 | default = [] 16 | description = "A list of public Route53 root zones. A 'root zone' can be anything from a tld to any level of subdomain, if and only if this is your root starting point for this (sub-)domain on the current AWS account. You can also attach a delegation_set to this root zone by its reference name (if it has been defined in the 'delegation_sets' list) or set it to 'null' to use no delegation set." 17 | } 18 | 19 | variable "public_delegated_secondary_zones" { 20 | type = list(object({ 21 | name = string, 22 | parent = string, 23 | ns_ttl = number, 24 | ns_list = list(string), 25 | delegation_set = string, 26 | })) 27 | default = [] 28 | description = "A list of public Route53 delegated secondary zones. Each item must specify its 'parent' by name, which must match the name defined in the 'public_root_zones' variables and must also be exactly one level deeper than the corresponding root zone item. By doing so, this module will automatically add nameservers into the root zone to create the delegation. You can also attach a delegation_set to this zone by its reference name (if it has been defined in the 'delegation_sets' list) or set it to 'null' to use no delegation set. Additionally you can also define your own name servers for this zone by specifying them in the `ns_list` list or just leave the list empty to use AWS default name server." 29 | } 30 | 31 | 32 | # ------------------------------------------------------------------------------------------------- 33 | # Optional Private Zone Variables 34 | # ------------------------------------------------------------------------------------------------- 35 | variable "private_root_zones" { 36 | type = list(object({ 37 | name = string, 38 | vpc_ids = list(object({ 39 | id = string, 40 | region = string, 41 | })), 42 | })) 43 | default = [] 44 | description = "Private Route53 root zone (also allows subdomain if this is your root starting point). Note, by default the default VPC will always be attached, even if vpc_ids or vpc_tags are empty." 45 | } 46 | 47 | 48 | # ------------------------------------------------------------------------------------------------- 49 | # Optional Misc Variables 50 | # ------------------------------------------------------------------------------------------------- 51 | variable "tags" { 52 | type = map 53 | default = {} 54 | description = "Default tags to additionally apply to all resources." 55 | } 56 | 57 | variable "comment" { 58 | type = string 59 | default = "Managed by Terraform" 60 | description = "Default comment to add to all resources." 61 | } 62 | --------------------------------------------------------------------------------