Skip to content

Commit

Permalink
Merge pull request #48 from answerdigital/route53
Browse files Browse the repository at this point in the history
Route53 module from our infra repo
  • Loading branch information
cmbuckley authored Apr 21, 2023
2 parents 5274baf + 143d85b commit 1a3912f
Show file tree
Hide file tree
Showing 12 changed files with 414 additions and 15 deletions.
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ updates:
update-types:
- "version-update:semver-patch"
- "version-update:semver-minor"
- package-ecosystem: terraform
directory: /modules/aws/route53
schedule:
interval: daily
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-patch"
- "version-update:semver-minor"
- package-ecosystem: terraform
directory: /modules/aws/vpc
schedule:
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,22 @@
# terraform-modules
The repo for AnswerDigital Terraform modules

The repo for Answer Digital shared Terraform modules.

## Using these modules

You can use these modules in your own terraform projects as follows:

```hcl
module "ec2_setup" {
source = "github.com/answerdigital/terraform-modules//modules/aws/ec2?ref=v2"
}
```

Notice the double `//` between the repository URL and the path to the module.
For further information please see the [terraform documentation](https://developer.hashicorp.com/terraform/language/modules/sources#modules-in-package-sub-directories).

Versions are shared across all modules. You can choose a specific tag (e.g. `?ref=v2.0.0`) or to get the latest changes within a major version, use `?ref=v2` which will get the latest v2.x.x release.

## Documentation

Further documentation can be found in the [wiki](https://github.com/answerdigital/terraform-modules/wiki).
5 changes: 1 addition & 4 deletions modules/aws/ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ This Terraform module will produce an EC2 instance which can be accessed via ssh

# Example Usage

Below is an example of how you would call the `ec2_instance` module in your
terraform code. Note that when calling it directly from the GitHub repository you can specify a
version by appending the below source reference with `?ref=v2` for the latest v2.x.x release
(for further information please see [here](https://developer.hashicorp.com/terraform/language/modules/sources#modules-in-package-sub-directories)).
Below is an example of how you would call the `ec2` module in your terraform code.

Here we also give an example of a bash script used to install docker on `Amazon linux`, then create a .env file on the instance and finally run a chosen docker image with the .env file.

Expand Down
6 changes: 1 addition & 5 deletions modules/aws/rds_serverless_cluster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,7 @@ These secrets are also set as outputs of the module and can be referenced throug

# Example Usage

Below is an example of how you would call the `rds_serverless_cluster` module in your
terraform code. Note that when calling it directly from the GitHub repository you can specify a
version by appending the below source reference with `?ref=v2` for the latest v2.x.x release
(for further information please see
[here](https://developer.hashicorp.com/terraform/language/modules/sources#modules-in-package-sub-directories)).
Below is an example of how you would call the `rds_serverless_cluster` module in your terraform code.

```hcl
module "rds_cluster_setup" {
Expand Down
109 changes: 109 additions & 0 deletions modules/aws/route53/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Terraform Route53 Module

This Terraform module manages a zone and multiple records in Route53.
The module also simplifies a few boilerplate records at the apex for security purposes.

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 4.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | ~> 4.0 |

## Resources

| Name | Type |
|------|------|
| [aws_route53_record.apex_txt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.caa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.dns_record](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.name_servers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.redirect_record_apex](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.redirect_record_extra](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.redirect_record_mx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.redirect_record_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_zone.dns_zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource |
| [aws_route53_zone.redirect_zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource |
| [aws_s3_bucket.redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_acl.redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [aws_s3_bucket_website_configuration.redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_alias_mx"></a> [alias\_mx](#input\_alias\_mx) | List of alias domains that should have the same MX records as the canonical domain. | `list(string)` | `[]` | no |
| <a name="input_alias_records"></a> [alias\_records](#input\_alias\_records) | List of DNS records for alias domains. The top-level keys should match entries in the `aliases`<br> list. The second-level map should match the same structure as `records`. | <pre>map(map(object({<br> name = optional(string)<br> ttl = optional(string)<br> type = string<br> records = list(string)<br> })))</pre> | `{}` | no |
| <a name="input_alias_redirect_protocol"></a> [alias\_redirect\_protocol](#input\_alias\_redirect\_protocol) | Protocol to use when redirecting to the canonical domain. Valid values: `http`, `https`. | `string` | `"https"` | no |
| <a name="input_aliases"></a> [aliases](#input\_aliases) | List of alias domains that should redirect to the canonical domain. | `list(string)` | `[]` | no |
| <a name="input_apex_txt"></a> [apex\_txt](#input\_apex\_txt) | List of TXT records to be added at the apex. | `list(string)` | `[]` | no |
| <a name="input_caa_issuers"></a> [caa\_issuers](#input\_caa\_issuers) | List of CAs that can issue certificates. | `list(string)` | <pre>[<br> "amazon.com"<br>]</pre> | no |
| <a name="input_canonical_mx_record"></a> [canonical\_mx\_record](#input\_canonical\_mx\_record) | The name of the MX record on the canonical domain. | `string` | `"apex_mx"` | no |
| <a name="input_comment"></a> [comment](#input\_comment) | A comment for the hosted zone. Defaults to 'Managed by Terraform'. | `string` | `null` | no |
| <a name="input_default_ttl"></a> [default\_ttl](#input\_default\_ttl) | Default TTL for DNS records. | `number` | `86400` | no |
| <a name="input_domain"></a> [domain](#input\_domain) | The top-level domain name to hold the records. | `string` | n/a | yes |
| <a name="input_records"></a> [records](#input\_records) | List of DNS records for the domain.<br><br> • `name` - (Optional) The name of the record. Defaults to the domain (i.e. an apex record).<br> • `ttl` - (Optional) The TTL of the record. Defaults to `default_ttl`.<br> • `type` - (Required) The record type.<br> • `records` - (Required) A string list of records. | <pre>map(object({<br> name = optional(string)<br> ttl = optional(string)<br> type = string<br> records = list(string)<br> }))</pre> | n/a | yes |
| <a name="input_security_contact"></a> [security\_contact](#input\_security\_contact) | Security contact for the domain. Defaults to 'security@DOMAIN', where `DOMAIN` is the top-level domain name. | `string` | `null` | no |
| <a name="input_spf"></a> [spf](#input\_spf) | List of SPF directives for the domain. | `list(string)` | `[]` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Tags for the hosted zone. | `map(any)` | `{}` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_zone_arn"></a> [zone\_arn](#output\_zone\_arn) | The Amazon Resource Name (ARN) of the Hosted Zone. |
| <a name="output_zone_id"></a> [zone\_id](#output\_zone\_id) | The Hosted Zone ID. |
<!-- END_TF_DOCS -->

# Example Usage

Below is a simple example for an example.com zone with a single subdomain record.

```terraform
module "example_com" {
source = "github.com/answerdigital/terraform-modules//modules/aws/route53?ref=v2"
domain = "example.com"
records = {
www = {
name = "www"
type = "A"
records = ["1.2.3.4"]
}
}
spf = [
"include:_spf.google.com"
]
}
```

If the domain is a canonical domain with aliases (e.g. brand protection domains), alias
domains can be added here. The alias domains are configured with an S3 bucket to redirect
the bare domain and www subdomain to the canonical domain.


```terraform
module "example_com" {
source = "github.com/answerdigital/terraform-modules//modules/aws/route53?ref=v2"
domain = "example.com"
aliases = [
"example.org" # example.org and www.example.org will redirect to example.com
]
alias_records = {
"example.org" = {
foo = {
name = "foo"
type = "A"
records = ["8.7.6.5"]
}
}
}
}
```
78 changes: 78 additions & 0 deletions modules/aws/route53/aliases.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
resource "aws_s3_bucket" "redirect" {
for_each = toset(concat(var.aliases, [for a in var.aliases : "www.${a}"]))
bucket = each.key
}

resource "aws_s3_bucket_acl" "redirect" {
for_each = toset(concat(var.aliases, [for a in var.aliases : "www.${a}"]))
bucket = aws_s3_bucket.redirect[each.key].bucket
acl = "private"
}

resource "aws_s3_bucket_website_configuration" "redirect" {
for_each = toset(concat(var.aliases, [for a in var.aliases : "www.${a}"]))
bucket = aws_s3_bucket.redirect[each.key].bucket

redirect_all_requests_to {
host_name = var.domain
protocol = var.alias_redirect_protocol
}
}

resource "aws_route53_zone" "redirect_zone" {
for_each = toset(var.aliases)
name = each.key
tags = var.tags
}

resource "aws_route53_record" "redirect_record_apex" {
for_each = toset(var.aliases)

zone_id = aws_route53_zone.redirect_zone[each.key].zone_id
name = each.key
type = "A"
allow_overwrite = true

alias {
zone_id = aws_s3_bucket.redirect[each.key].hosted_zone_id
name = aws_s3_bucket_website_configuration.redirect[each.key].website_domain
evaluate_target_health = false
}
}

resource "aws_route53_record" "redirect_record_www" {
for_each = toset(var.aliases)

zone_id = aws_route53_zone.redirect_zone[each.key].zone_id
name = "www"
type = "A"
allow_overwrite = true

alias {
zone_id = aws_s3_bucket.redirect["www.${each.key}"].hosted_zone_id
name = aws_s3_bucket_website_configuration.redirect["www.${each.key}"].website_domain
evaluate_target_health = false
}
}

resource "aws_route53_record" "redirect_record_mx" {
for_each = toset(var.alias_mx)

zone_id = aws_route53_zone.redirect_zone[each.key].zone_id
allow_overwrite = true
name = each.key
type = "MX"
ttl = aws_route53_record.dns_record[var.canonical_mx_record].ttl
records = aws_route53_record.dns_record[var.canonical_mx_record].records
}

resource "aws_route53_record" "redirect_record_extra" {
for_each = { for r in local.alias_records_list : "${r.zone}_${r.key}" => r }

zone_id = aws_route53_zone.redirect_zone[each.value.zone].zone_id
allow_overwrite = true
name = each.value.name
type = each.value.type
ttl = each.value.ttl
records = each.value.records
}
51 changes: 51 additions & 0 deletions modules/aws/route53/dns.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
resource "aws_route53_zone" "dns_zone" {
name = var.domain
comment = var.comment
tags = var.tags
}

resource "aws_route53_record" "name_servers" {
zone_id = aws_route53_zone.dns_zone.zone_id
allow_overwrite = true
name = var.domain
ttl = var.default_ttl
type = "NS"
records = aws_route53_zone.dns_zone.name_servers
}

resource "aws_route53_record" "dns_record" {
for_each = var.records

zone_id = aws_route53_zone.dns_zone.zone_id
allow_overwrite = true
name = each.value.name != null ? each.value.name : var.domain
ttl = each.value.ttl != null ? each.value.ttl : var.default_ttl
type = each.value.type
records = [
for r in each.value.records : length(r) > 255 ? join("\"\"", [
for c in chunklist(split("", r), 255) : join("", c)
]) : r
]
}

resource "aws_route53_record" "apex_txt" {
zone_id = aws_route53_zone.dns_zone.zone_id
name = var.domain
ttl = var.default_ttl
type = "TXT"
records = concat(var.apex_txt, [
format("security_contact=mailto:%s", local.security_contact),
replace("v=spf1 ${join(" ", var.spf)} -all", " ", " ")
])
}

resource "aws_route53_record" "caa" {
zone_id = aws_route53_zone.dns_zone.zone_id
name = var.domain
ttl = var.default_ttl
type = "CAA"
records = flatten([
[for issuer in var.caa_issuers : format("0 issue \"%s\"", issuer)],
format("0 iodef \"mailto:%s\"", local.security_contact)
])
}
16 changes: 16 additions & 0 deletions modules/aws/route53/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
locals {
security_contact = var.security_contact != null ? var.security_contact : format("security@%s", var.domain)

alias_records_list = flatten([
for zone, records in var.alias_records : [
for key, record in records : {
zone = zone
key = key
name = record.name != null ? record.name : zone
ttl = record.ttl != null ? record.ttl : var.default_ttl
type = record.type
records = record.records
}
]
])
}
9 changes: 9 additions & 0 deletions modules/aws/route53/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "zone_arn" {
value = aws_route53_zone.dns_zone.arn
description = "The Amazon Resource Name (ARN) of the Hosted Zone."
}

output "zone_id" {
value = aws_route53_zone.dns_zone.zone_id
description = "The Hosted Zone ID."
}
Loading

0 comments on commit 1a3912f

Please sign in to comment.