From 83177db00f06c805195e4599847f04caeca2aa22 Mon Sep 17 00:00:00 2001 From: Byungjin Park Date: Fri, 7 Jun 2024 15:34:48 +0900 Subject: [PATCH] Add record-set module --- .github/labeler.yaml | 5 ++ .github/labels.yaml | 3 + README.md | 2 + modules/record-set/README.md | 55 +++++++++++++++ modules/record-set/main.tf | 58 ++++++++++++++++ modules/record-set/outputs.tf | 68 +++++++++++++++++++ modules/record-set/variables.tf | 114 ++++++++++++++++++++++++++++++++ modules/record-set/versions.tf | 10 +++ 8 files changed, 315 insertions(+) create mode 100644 modules/record-set/README.md create mode 100644 modules/record-set/main.tf create mode 100644 modules/record-set/outputs.tf create mode 100644 modules/record-set/variables.tf create mode 100644 modules/record-set/versions.tf diff --git a/.github/labeler.yaml b/.github/labeler.yaml index fbc4145..9b9c314 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -29,6 +29,11 @@ - any-glob-to-any-file: - modules/public-zone/**/* +":floppy_disk: record-set": +- changed-files: + - any-glob-to-any-file: + - modules/record-set/**/* + ":floppy_disk: registered-domain": - changed-files: - any-glob-to-any-file: diff --git a/.github/labels.yaml b/.github/labels.yaml index 81207a8..f1bfb72 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -58,6 +58,9 @@ - color: "fbca04" description: "This issue or pull request is related to public-zone module." name: ":floppy_disk: public-zone" +- color: "fbca04" + description: "This issue or pull request is related to record-set module." + name: ":floppy_disk: record-set" - color: "fbca04" description: "This issue or pull request is related to registered-domain module." name: ":floppy_disk: registered-domain" diff --git a/README.md b/README.md index 65e2689..081fd6e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Terraform module which creates Route53 related resources on AWS. - [private-ca-issued-cert](./modules/private-ca-issued-cert) - [private-zone](./modules/private-zone) - [public-zone](./modules/public-zone) +- [record-set](./modules/record-set) - [registered-domain](./modules/registered-domain) - [resolver-inbound-endpoint](./modules/resolver-inbound-endpoint) - [resolver-query-logging](./modules/resolver-query-logging) @@ -27,6 +28,7 @@ Terraform Modules from [this package](https://github.com/tedilabs/terraform-aws- - Hosted Zone - Public Hosted Zone - Private Hosted Zone + - Record Set - CIDR Collection - Resolver - Inbound Endpoint diff --git a/modules/record-set/README.md b/modules/record-set/README.md new file mode 100644 index 0000000..5924c28 --- /dev/null +++ b/modules/record-set/README.md @@ -0,0 +1,55 @@ +# record-set + +This module creates following resources. + +- `aws_route53_record` (optional) + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.51 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.47.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_route53_record.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [name](#input\_name) | (Required) The name of the record. Enter a fully qualified domain name, for example, `www.example.com`. You can use the asterisk (*) wildcard to replace the leftmost label in a domain name, for example, `*.example.com`. | `string` | n/a | yes | +| [type](#input\_type) | (Required) The DNS record type. Valid values are `A`, `AAAA`, `CAA`, `CNAME`, `DS`, `MX`, `NAPTR`, `NS`, `PTR`, `SOA`, `SPF`, `SRV` and `TXT`. | `string` | n/a | yes | +| [zone](#input\_zone) | (Required) The ID of the hosted zone to contain this record set. | `string` | n/a | yes | +| [overwrite](#input\_overwrite) | (Optional) Whether to allow creation of this record in Terraform to overwrite an existing record, if any. This does not affect the ability to update the record in Terraform and does not prevent other resources within Terraform or manual Route 53 changes outside Terraform from overwriting this record. This configuration is not recommended for most environments. Defaults to `false`. | `bool` | `false` | no | +| [records](#input\_records) | (Optional) A list of records for the record set. Each item of `records` as defined below.
(Optional) `id` - A unique ID to differentiate this record from other records with the same domain name and type. Not required if the `routing_policy` is `SIMPLE`. Defaults to `default`.
(Optional) `value` - A configuration for non-alias record with a set of the record values. You can specify more than one value for all record types except `CNAME` and `SOA`. Conflicts with `alias`.
(Optional) `alias` - A configuration for alias record. Conflicts with `value`. `alias` as defined below.
(Required) `name` - DNS domain name for a CloudFront distribution, S3 bucket, ELB, or another record set in this hosted zone.
(Required) `zone` - Hosted zone ID for a CloudFront distribution, S3 bucket, ELB, or Route 53 hosted zone.
(Optional) `evaluate_target_health` - Whether to respond to DNS queries using this record by checking the health of the alias target. Some resources have special requirements, see related part of documentation. Defaults to `true`. |
list(object({
id = optional(string, "default")
value = optional(set(string))
alias = optional(object({
name = string
zone = string
evaluate_target_health = optional(bool, true)
}))
}))
| `[]` | no | +| [routing\_policy](#input\_routing\_policy) | (Optional) The routing policy determines how Route 53 responds to queries. Defaults to `SIMPLE`. Supported routing policies are following:
`SIMPLE`
`WEIGHTED`
`GEOLOCATION`
`LATENCY`
`FAILOVER`
`MULTIVALUE_ANSWER`
`CIDR`
`GEOPROXIMITY` | `string` | `"SIMPLE"` | no | +| [ttl](#input\_ttl) | (Optional) The record cache time to live (TTL) in seconds. Defaults to `300`.
- If you're creating or updating an alias record set, omit `ttl`. Route 53 uses the value of TTL for the alias target.
- If you're associating this record set with a health check, we recommend that you specify a `ttl` of `60` seconds or less so clients respond quickly to changes in health status.
- All of the records in a group of weighted record sets must have the same value for `ttl`.
- If a group of weighted record sets includes one or more weighted alias records for which the alias target is an ELB load balancer, we recommend that you specify a `ttl` of `60` seconds for all of the non-alias weighted records that have the same name and type. Values other than `60` seconds (the TTL for load balancers) will change the effect of the values that you specify for weight. | `number` | `300` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [fqdn](#output\_fqdn) | The FQDN (Fully-qualified Domain Name) of the record. | +| [name](#output\_name) | The name of the record. | +| [records](#output\_records) | A list of records for the record set. Each item of `records` as defined below.
`id` - A unique ID to differentiate this record from other records with the same domain name and type.
`value` - A configuration for non-alias record with a list of the record values.
`alias` - A configuration for alias record. Conflicts with `value`. `alias` as defined below.
`name` - DNS domain name for a CloudFront distribution, S3 bucket, ELB, or another record set in this hosted zone.
`zone` - Hosted zone ID for a CloudFront distribution, S3 bucket, ELB, or Route 53 hosted zone.
`evaluate_target_health` - Whether to respond to DNS queries using this record by checking the health of the alias target. | +| [routing\_policy](#output\_routing\_policy) | The routing policy of the record set. | +| [ttl](#output\_ttl) | The record cache time to live (TTL) in seconds. | +| [type](#output\_type) | The DNS record type of the record set. | +| [zone](#output\_zone) | The information for Hosted Zone of the record set. | + diff --git a/modules/record-set/main.tf b/modules/record-set/main.tf new file mode 100644 index 0000000..f750718 --- /dev/null +++ b/modules/record-set/main.tf @@ -0,0 +1,58 @@ +data "aws_route53_zone" "this" { + zone_id = var.zone +} + + +################################################### +# Record Set +################################################### + +# TODO: `health_check_id` - (Optional) The health check the record should be associated with. + +# TODO: `cidr_routing_policy` - (Optional) A block indicating a routing policy based on the IP network ranges of requestors. Conflicts with any other routing policy. Documented below. +# TODO: `failover_routing_policy` - (Optional) A block indicating the routing behavior when associated health check fails. Conflicts with any other routing policy. Documented below. +# TODO: `geolocation_routing_policy` - (Optional) A block indicating a routing policy based on the geolocation of the requestor. Conflicts with any other routing policy. Documented below. +# TODO: `geoproximity_routing_policy` - (Optional) A block indicating a routing policy based on the geoproximity of the requestor. Conflicts with any other routing policy. Documented below. +# TODO: `latency_routing_policy` - (Optional) A block indicating a routing policy based on the latency between the requestor and an AWS region. Conflicts with any other routing policy. Documented below. +# TODO: `multivalue_answer_routing_policy` - (Optional) Set to true to indicate a multivalue answer routing policy. Conflicts with any other routing policy. +# TODO: `weighted_routing_policy` - (Optional) A block indicating a weighted routing policy. Conflicts with any other routing policy. Documented below. +resource "aws_route53_record" "this" { + for_each = { + for record in var.records : + record.id => record + } + + zone_id = var.zone + name = var.name + type = var.type + ttl = var.ttl + allow_overwrite = var.overwrite + + set_identifier = (var.routing_policy == "SIMPLE" + ? null + : each.value.id + ) + + ## Record + # Non-alias Record + records = each.value.value + + # Alias Record + dynamic "alias" { + for_each = each.value.alias != null ? [each.value.alias] : [] + + content { + name = alias.value.name + zone_id = alias.value.zone + evaluate_target_health = alias.value.evaluate_target_health + } + } + + + # lifecycle { + # precondition { + # condition = endswith(each.key, var.name) + # error_message = "The name of NS record must be end with the name of Hosted Zone." + # } + # } +} diff --git a/modules/record-set/outputs.tf b/modules/record-set/outputs.tf new file mode 100644 index 0000000..c8c7c9c --- /dev/null +++ b/modules/record-set/outputs.tf @@ -0,0 +1,68 @@ +output "zone" { + description = "The information for Hosted Zone of the record set." + value = { + arn = data.aws_route53_zone.this.arn + id = values(aws_route53_record.this)[0].zone_id + name = data.aws_route53_zone.this.name + } +} + +output "name" { + description = "The name of the record." + value = values(aws_route53_record.this)[0].name +} + +output "fqdn" { + description = "The FQDN (Fully-qualified Domain Name) of the record." + value = values(aws_route53_record.this)[0].fqdn +} + +output "type" { + description = "The DNS record type of the record set." + value = values(aws_route53_record.this)[0].type +} + +output "ttl" { + description = "The record cache time to live (TTL) in seconds." + value = values(aws_route53_record.this)[0].ttl +} + +output "routing_policy" { + description = "The routing policy of the record set." + value = var.routing_policy +} + +output "records" { + description = < { + id = record.set_identifier + value = record.records + alias = (one(record.alias) != null + ? { + name = record.alias[0].name + zone = record.alias[0].zone_id + evaluate_target_health = record.alias[0].evaluate_target_health + } + : null + ) + } + } +} + +# output "debug" { +# value = { +# for k, v in values(aws_route53_record.this)[0] : +# k => v +# if !contains(["zone_id", "type", "ttl", "name", "fqdn", "id", "allow_overwrite", "alias", "set_identifier", "records"], k) +# } +# } diff --git a/modules/record-set/variables.tf b/modules/record-set/variables.tf new file mode 100644 index 0000000..c1e5d29 --- /dev/null +++ b/modules/record-set/variables.tf @@ -0,0 +1,114 @@ +variable "zone" { + description = "(Required) The ID of the hosted zone to contain this record set." + type = string + nullable = false +} + +variable "name" { + description = "(Required) The name of the record. Enter a fully qualified domain name, for example, `www.example.com`. You can use the asterisk (*) wildcard to replace the leftmost label in a domain name, for example, `*.example.com`." + type = string + nullable = false +} + +variable "type" { + description = "(Required) The DNS record type. Valid values are `A`, `AAAA`, `CAA`, `CNAME`, `DS`, `MX`, `NAPTR`, `NS`, `PTR`, `SOA`, `SPF`, `SRV` and `TXT`." + type = string + nullable = false + + validation { + condition = contains(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"], var.type) + error_message = "Valid values of `type` are `A`, `AAAA`, `CAA`, `CNAME`, `DS`, `MX`, `NAPTR`, `NS`, `PTR`, `SOA`, `SPF`, `SRV` and `TXT`." + } +} + +variable "ttl" { + description = < 0, + var.ttl <= 2147483647 + ]) + error_message = "`ttl` must be greater than 0 and less than or equal to 2147483647." + } +} + +variable "overwrite" { + description = "(Optional) Whether to allow creation of this record in Terraform to overwrite an existing record, if any. This does not affect the ability to update the record in Terraform and does not prevent other resources within Terraform or manual Route 53 changes outside Terraform from overwriting this record. This configuration is not recommended for most environments. Defaults to `false`." + type = bool + default = false + nullable = false +} + +variable "routing_policy" { + description = < 0 + error_message = "At least one record must be provided." + } + validation { + condition = length(distinct(var.records[*].id)) == length(var.records) + error_message = "Each record `id` must be unique in the record set." + } + validation { + condition = alltrue([ + for record in var.records : + anytrue([ + record.value != null && record.alias == null, + record.value == null && record.alias != null, + ]) + ]) + error_message = "Each record must have either `value` or `alias`." + } +} diff --git a/modules/record-set/versions.tf b/modules/record-set/versions.tf new file mode 100644 index 0000000..a06f31d --- /dev/null +++ b/modules/record-set/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.6" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.51" + } + } +}