Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add record-set module #55

Merged
merged 1 commit into from
Jun 7, 2024
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
5 changes: 5 additions & 0 deletions .github/labeler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions .github/labels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
55 changes: 55 additions & 0 deletions modules/record-set/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# record-set

This module creates following resources.

- `aws_route53_record` (optional)

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.6 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.51 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [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 |
|------|-------------|------|---------|:--------:|
| <a name="input_name"></a> [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 |
| <a name="input_type"></a> [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 |
| <a name="input_zone"></a> [zone](#input\_zone) | (Required) The ID of the hosted zone to contain this record set. | `string` | n/a | yes |
| <a name="input_overwrite"></a> [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 |
| <a name="input_records"></a> [records](#input\_records) | (Optional) A list of records for the record set. Each item of `records` as defined below.<br> (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`.<br> (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`.<br> (Optional) `alias` - A configuration for alias record. Conflicts with `value`. `alias` as defined below.<br> (Required) `name` - DNS domain name for a CloudFront distribution, S3 bucket, ELB, or another record set in this hosted zone.<br> (Required) `zone` - Hosted zone ID for a CloudFront distribution, S3 bucket, ELB, or Route 53 hosted zone.<br> (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`. | <pre>list(object({<br> id = optional(string, "default")<br> value = optional(set(string))<br> alias = optional(object({<br> name = string<br> zone = string<br> evaluate_target_health = optional(bool, true)<br> }))<br> }))</pre> | `[]` | no |
| <a name="input_routing_policy"></a> [routing\_policy](#input\_routing\_policy) | (Optional) The routing policy determines how Route 53 responds to queries. Defaults to `SIMPLE`. Supported routing policies are following:<br> `SIMPLE`<br> `WEIGHTED`<br> `GEOLOCATION`<br> `LATENCY`<br> `FAILOVER`<br> `MULTIVALUE_ANSWER`<br> `CIDR`<br> `GEOPROXIMITY` | `string` | `"SIMPLE"` | no |
| <a name="input_ttl"></a> [ttl](#input\_ttl) | (Optional) The record cache time to live (TTL) in seconds. Defaults to `300`.<br> - If you're creating or updating an alias record set, omit `ttl`. Route 53 uses the value of TTL for the alias target.<br> - 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.<br> - All of the records in a group of weighted record sets must have the same value for `ttl`.<br> - 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 |
|------|-------------|
| <a name="output_fqdn"></a> [fqdn](#output\_fqdn) | The FQDN (Fully-qualified Domain Name) of the record. |
| <a name="output_name"></a> [name](#output\_name) | The name of the record. |
| <a name="output_records"></a> [records](#output\_records) | A list of records for the record set. Each item of `records` as defined below.<br> `id` - A unique ID to differentiate this record from other records with the same domain name and type.<br> `value` - A configuration for non-alias record with a list of the record values.<br> `alias` - A configuration for alias record. Conflicts with `value`. `alias` as defined below.<br> `name` - DNS domain name for a CloudFront distribution, S3 bucket, ELB, or another record set in this hosted zone.<br> `zone` - Hosted zone ID for a CloudFront distribution, S3 bucket, ELB, or Route 53 hosted zone.<br> `evaluate_target_health` - Whether to respond to DNS queries using this record by checking the health of the alias target. |
| <a name="output_routing_policy"></a> [routing\_policy](#output\_routing\_policy) | The routing policy of the record set. |
| <a name="output_ttl"></a> [ttl](#output\_ttl) | The record cache time to live (TTL) in seconds. |
| <a name="output_type"></a> [type](#output\_type) | The DNS record type of the record set. |
| <a name="output_zone"></a> [zone](#output\_zone) | The information for Hosted Zone of the record set. |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
58 changes: 58 additions & 0 deletions modules/record-set/main.tf
Original file line number Diff line number Diff line change
@@ -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."
# }
# }
}
68 changes: 68 additions & 0 deletions modules/record-set/outputs.tf
Original file line number Diff line number Diff line change
@@ -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 = <<EOF
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.
EOF
value = {
for id, record in aws_route53_record.this :
id => {
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)
# }
# }
114 changes: 114 additions & 0 deletions modules/record-set/variables.tf
Original file line number Diff line number Diff line change
@@ -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 = <<EOF
(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.
EOF
type = number
default = 300
nullable = false

validation {
condition = alltrue([
var.ttl > 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 = <<EOF
(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`
EOF
type = string
default = "SIMPLE"
nullable = false

validation {
condition = contains(["SIMPLE", "WEIGHTED", "GEOLOCATION", "LATENCY", "FAILOVER", "MULTIVALUE_ANSWER", "CIDR", "GEOPROXIMITY"], var.routing_policy)
error_message = "Valid values of `routing_policy` are `SIMPLE`, `WEIGHTED`, `GEOLOCATION`, `LATENCY`, `FAILOVER`, `MULTIVALUE_ANSWER`, `CIDR`, `GEOPROXIMITY`."
}
}

variable "records" {
description = <<EOF
(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`.
EOF
type = list(object({
id = optional(string, "default")
value = optional(set(string))
alias = optional(object({
name = string
zone = string
evaluate_target_health = optional(bool, true)
}))
}))
default = []
nullable = false

validation {
condition = length(var.records) > 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`."
}
}
10 changes: 10 additions & 0 deletions modules/record-set/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.6"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.51"
}
}
}
Loading