diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..ce0ebcb
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+max_line_length = 80
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = 0
+trim_trailing_whitespace = false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..0f731c1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,36 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+## Describe the bug
+
+
+
+## Versions
+
+
+
+- Terraform:
+- Provider:
+- Module:
+
+## Reproduction
+
+
+
+## Expected behavior
+
+
+
+## Actual behavior
+
+
+
+## Additional context
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..951e0c8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,24 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: enhancement
+assignees: ''
+
+---
+
+## Is your feature request related to a problem? Please describe.
+
+
+
+## Describe the solution you'd like
+
+
+
+## Describe alternatives you've considered
+
+
+
+## Additional context
+
+
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..126baa5
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,41 @@
+name: CI
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+jobs:
+ pre-commit-checks:
+ name: Pre-commit checks
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Terraform min/max versions
+ id: minMax
+ uses: clowdhaus/terraform-min-max@v1.0.4
+ - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }}
+ uses: clowdhaus/terraform-composite-actions/pre-commit@v1.4.1
+ with:
+ terraform-version: ${{ steps.minMax.outputs.maxVersion }}
+ terraform-docs-version: v0.16.0
+ validate-examples:
+ name: Validate examples
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ shell: bash
+ working-directory: examples
+ steps:
+ - uses: hashicorp/setup-terraform@v1
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Check examples
+ env:
+ EXAMPLES: simple
+ run: |
+ for EXAMPLE in ${EXAMPLES}
+ do
+ echo "Validating $EXAMPLE"...
+ cd $EXAMPLE && terraform init && terraform validate && cd -
+ done
diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml
new file mode 100644
index 0000000..e1e02b2
--- /dev/null
+++ b/.github/workflows/release-please.yml
@@ -0,0 +1,12 @@
+on:
+ push:
+ branches:
+ - main
+name: release-please
+jobs:
+ release-please:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: google-github-actions/release-please-action@v3
+ with:
+ release-type: terraform-module
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a85eada
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,40 @@
+.terraform.lock.hcl
+
+#####################
+# Default gitignore #
+#####################
+
+# Local .terraform directories
+**/.terraform/*
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+
+# Exclude all .tfvars files, which are likely to contain sentitive data, such as
+# password, private keys, and other secrets. These should not be part of version
+# control as they are data points which are potentially sensitive and subject
+# to change depending on the environment.
+#
+*.tfvars
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+#
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..7f6597f
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,12 @@
+repos:
+ - repo: https://github.com/antonbabenko/pre-commit-terraform
+ rev: v1.76.0
+ hooks:
+ - id: terraform_fmt
+ - id: terraform_docs
+ args:
+ - --args=--config=.terraform-docs.yml
+ - id: terraform_tflint
+ exclude: "test/"
+ args:
+ - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl
diff --git a/.terraform-docs.yml b/.terraform-docs.yml
new file mode 100644
index 0000000..8369b4a
--- /dev/null
+++ b/.terraform-docs.yml
@@ -0,0 +1,16 @@
+formatter: "markdown table"
+
+sections:
+ show:
+ - requirements
+ - providers
+ - inputs
+ - outputs
+
+sort:
+ enabled: true
+ by: required
+
+settings:
+ default: false
+ lockfile: false
diff --git a/.tflint.hcl b/.tflint.hcl
new file mode 100644
index 0000000..714b601
--- /dev/null
+++ b/.tflint.hcl
@@ -0,0 +1,53 @@
+config {
+ module = false
+ force = false
+ disabled_by_default = false
+}
+
+rule "terraform_deprecated_interpolation" {
+ enabled = true
+}
+
+rule "terraform_deprecated_index" {
+ enabled = true
+}
+
+rule "terraform_unused_declarations" {
+ enabled = true
+}
+
+rule "terraform_comment_syntax" {
+ enabled = true
+}
+
+rule "terraform_documented_outputs" {
+ enabled = true
+}
+
+rule "terraform_documented_variables" {
+ enabled = true
+}
+
+rule "terraform_typed_variables" {
+ enabled = true
+}
+
+rule "terraform_module_pinned_source" {
+ enabled = true
+}
+
+rule "terraform_required_version" {
+ enabled = true
+}
+
+rule "terraform_required_providers" {
+ enabled = true
+}
+
+rule "terraform_standard_module_structure" {
+ enabled = true
+}
+
+rule "terraform_workspace_remote" {
+ enabled = true
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7e51950
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Takashi Nozawa
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..18aad7e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# terraform-aws-parked-domain-baseline
+
+[![Github Actions](https://github.com/nozaq/terraform-aws-parked-domain-baseline/actions/workflows/main.yml/badge.svg)](https://github.com/nozaq/terraform-aws-parked-domain-baseline/actions/workflows/main.yml)
+[![Releases](https://img.shields.io/github/v/release/nozaq/terraform-aws-parked-domain-baseline)](https://github.com/nozaq/terraform-aws-parked-domain-baseline/releases/latest)
+
+[Terraform Module Registry](https://registry.terraform.io/modules/nozaq/parked-domain-baseline/aws)
+
+A terraform module to set up DNS records to harden the parked(unused) domain using AWS Route53.
+
+Domains should be protected for email spoofing even if they are not intended to be actively used.
+This module configures DNS records to protect such domain based on [M3AAWG Protecting Parked Domains Best Common Practices].
+
+## Features
+
+This module creates the following DNS records.
+
+- Null MX record([RFC 7505]) to indicate the domain does not accept any email.
+- SPF record to indicate no IP is authorized to send email on behalf of this domain.
+- DMARC record to enforce receiving domains to reject any email forging this domain.
+- Optionally adds `rua` tag in the DMARC record to receive aggregate feedback reports via email.
+- Optionally creates Null MX and DMARC records for wildcard subdomains as well as the root domain(enabled by default).
+
+## Usage
+
+```hcl
+provider "aws" {
+}
+
+data "aws_route53_zone" "this" {
+ name = "example.com"
+}
+
+module "parked_domain" {
+ source = "nozaq/parked-domain-baseline/aws"
+
+ zone_id = data.aws_route53_zone.this.zone_id
+ ttl = 86400 # One day
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [aws](#requirement\_aws) | >= 4.40 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 4.40 |
+
+## Inputs
+
+| Name | Description | Type | Required |
+|------|-------------|------|:--------:|
+| [aggregate\_feedback\_email](#input\_aggregate\_feedback\_email) | The email address to which aggregate feedback is to be sent. | `string` | no |
+| [include\_subdomains](#input\_include\_subdomains) | Configure all subdomains as well as the root domain. | `bool` | no |
+| [ttl](#input\_ttl) | The TTL of the DNS records. | `number` | no |
+| [zone\_id](#input\_zone\_id) | The DNS zone ID to add the records to. Either zone\_name or zone\_id need to be given. | `string` | no |
+
+## Outputs
+
+No outputs.
+
+
+[M3AAWG Protecting Parked Domains Best Common Practices]: https://www.m3aawg.org/sites/default/files/m3aawg_parked_domains_bcp-2022-06.pdf
+[RFC 7505]: https://datatracker.ietf.org/doc/rfc7505/
diff --git a/examples/simple/main.tf b/examples/simple/main.tf
new file mode 100644
index 0000000..9af08be
--- /dev/null
+++ b/examples/simple/main.tf
@@ -0,0 +1,24 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.40"
+ }
+ }
+
+ required_version = ">= 1.3"
+}
+
+provider "aws" {
+}
+
+data "aws_route53_zone" "this" {
+ name = var.zone_name
+}
+
+module "parked_domain" {
+ source = "../../"
+
+ zone_id = data.aws_route53_zone.this.zone_id
+ ttl = 86400 # One day
+}
diff --git a/examples/simple/outputs.tf b/examples/simple/outputs.tf
new file mode 100644
index 0000000..e69de29
diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf
new file mode 100644
index 0000000..5c48f82
--- /dev/null
+++ b/examples/simple/variables.tf
@@ -0,0 +1,4 @@
+variable "zone_name" {
+ description = "Hosted Zone name in which DNS records are create."
+ type = string
+}
diff --git a/main.tf b/main.tf
new file mode 100644
index 0000000..f600387
--- /dev/null
+++ b/main.tf
@@ -0,0 +1,58 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.40"
+ }
+ }
+ required_version = ">= 1.3"
+}
+
+# Null MX record specified in RFC 7505
+# https://datatracker.ietf.org/doc/rfc7505/
+resource "aws_route53_record" "mx_root" {
+ zone_id = var.zone_id
+ type = "MX"
+ name = ""
+ records = ["0 ."]
+ ttl = var.ttl
+}
+
+resource "aws_route53_record" "mx_subdomains" {
+ count = var.include_subdomains ? 1 : 0
+
+ zone_id = var.zone_id
+ type = "MX"
+ name = "*"
+ records = ["0 ."]
+ ttl = var.ttl
+}
+
+# Ensure all SPF validations to fail for the root domain.
+resource "aws_route53_record" "spf_root" {
+ zone_id = var.zone_id
+ type = "TXT"
+ name = ""
+ records = ["v=spf1 -all"]
+ ttl = var.ttl
+}
+
+# Ensure all SPF validations to fail for subdomains.
+resource "aws_route53_record" "spf_subdomains" {
+ count = var.include_subdomains ? 1 : 0
+
+ zone_id = var.zone_id
+ type = "TXT"
+ name = "*"
+ records = ["v=spf1 -all"]
+ ttl = var.ttl
+}
+
+# Advise receivers to reject emails when DMARC alignment fails.
+resource "aws_route53_record" "dmarc" {
+ zone_id = var.zone_id
+ type = "TXT"
+ name = "_dmarc"
+ records = [var.aggregate_feedback_email != "" ? "v=DMARC1; p=reject; rua=${var.aggregate_feedback_email}" : "v=DMARC1; p=reject;"]
+ ttl = var.ttl
+}
diff --git a/outputs.tf b/outputs.tf
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/outputs.tf
@@ -0,0 +1 @@
+
diff --git a/variables.tf b/variables.tf
new file mode 100644
index 0000000..473b6d2
--- /dev/null
+++ b/variables.tf
@@ -0,0 +1,23 @@
+variable "zone_id" {
+ description = "The DNS zone ID to add the records to. Either zone_name or zone_id need to be given."
+ type = string
+ default = ""
+}
+
+variable "ttl" {
+ description = "The TTL of the DNS records."
+ type = number
+ default = 1
+}
+
+variable "aggregate_feedback_email" {
+ description = "The email address to which aggregate feedback is to be sent."
+ type = string
+ default = ""
+}
+
+variable "include_subdomains" {
+ description = "Configure all subdomains as well as the root domain."
+ type = bool
+ default = true
+}