├── .editorconfig ├── .github ├── CODEOWNERS ├── dependabot.yml ├── labeler.yaml ├── labels.yaml └── workflows │ ├── pull-request-labeler.yaml │ ├── sync-labels.yaml │ ├── terraform.integration.yaml │ ├── welcome.yaml │ └── yaml.integration.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .tflint.hcl ├── .yamllint.yaml ├── LICENSE ├── README.md ├── VERSION ├── examples ├── route53-private-zone-simple │ ├── main.tf │ ├── outputs.tf │ └── versions.tf └── route53-public-zone-simple │ ├── main.tf │ ├── outputs.tf │ └── versions.tf └── modules ├── amazon-issued-cert ├── README.md ├── main.tf ├── migrations.tf ├── outputs.tf ├── resource-group.tf ├── variables.tf └── versions.tf ├── cidr-collection ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── imported-cert ├── README.md ├── main.tf ├── outputs.tf ├── resource-group.tf ├── variables.tf └── versions.tf ├── private-ca-issued-cert ├── README.md ├── main.tf ├── outputs.tf ├── resource-group.tf ├── variables.tf └── versions.tf ├── private-zone ├── README.md ├── main.tf ├── outputs.tf ├── resource-group.tf ├── variables.tf └── versions.tf ├── public-zone ├── README.md ├── main.tf ├── outputs.tf ├── records.tf ├── resource-group.tf ├── variables.tf └── versions.tf ├── record-set ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── registered-domain ├── README.md ├── main.tf ├── outputs.tf ├── resource-group.tf ├── variables.tf └── versions.tf ├── resolver-inbound-endpoint ├── README.md ├── main.tf ├── outputs.tf ├── resource-group.tf ├── security-group.tf ├── variables.tf └── versions.tf └── resolver-query-logging ├── README.md ├── main.tf ├── outputs.tf ├── ram-shares.tf ├── resource-group.tf ├── variables.tf └── versions.tf /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | max_line_length = 150 12 | 13 | [*.{tf,tfvars}] 14 | indent_size = 2 15 | indent_style = space 16 | 17 | [*.md] 18 | max_line_length = 0 19 | 20 | [COMMIT_EDITMSG] 21 | max_line_length = 0 22 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @posquit0 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | 8 | - package-ecosystem: terraform 9 | directories: 10 | - /modules/* 11 | schedule: 12 | interval: weekly 13 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | # Modules 2 | ":floppy_disk: amazon-issued-cert": 3 | - changed-files: 4 | - any-glob-to-any-file: 5 | - modules/amazon-issued-cert/**/* 6 | 7 | ":floppy_disk: cidr-collection": 8 | - changed-files: 9 | - any-glob-to-any-file: 10 | - modules/cidr-collection/**/* 11 | 12 | ":floppy_disk: imported-cert": 13 | - changed-files: 14 | - any-glob-to-any-file: 15 | - modules/imported-cert/**/* 16 | 17 | ":floppy_disk: private-ca-issued-cert": 18 | - changed-files: 19 | - any-glob-to-any-file: 20 | - modules/private-ca-issued-cert/**/* 21 | 22 | ":floppy_disk: private-zone": 23 | - changed-files: 24 | - any-glob-to-any-file: 25 | - modules/private-zone/**/* 26 | 27 | ":floppy_disk: public-zone": 28 | - changed-files: 29 | - any-glob-to-any-file: 30 | - modules/public-zone/**/* 31 | 32 | ":floppy_disk: record-set": 33 | - changed-files: 34 | - any-glob-to-any-file: 35 | - modules/record-set/**/* 36 | 37 | ":floppy_disk: registered-domain": 38 | - changed-files: 39 | - any-glob-to-any-file: 40 | - modules/registered-domain/**/* 41 | 42 | ":floppy_disk: resolver-inbound-endpoint": 43 | - changed-files: 44 | - any-glob-to-any-file: 45 | - modules/resolver-inbound-endpoint/**/* 46 | 47 | ":floppy_disk: resolver-query-logging": 48 | - changed-files: 49 | - any-glob-to-any-file: 50 | - modules/resolver-query-logging/**/* 51 | -------------------------------------------------------------------------------- /.github/labels.yaml: -------------------------------------------------------------------------------- 1 | # Warning 2 | - color: "ee0701" 3 | description: "Categorize bug reports." 4 | name: ":warning: bug" 5 | - color: "ee0701" 6 | description: "Categorize vulnerability reports." 7 | name: ":warning: vulnerability" 8 | 9 | # Highlight 10 | - color: "0e8a16" 11 | description: "Good for newcomers." 12 | name: ":fire: good first issue" 13 | - color: "0e8a16" 14 | description: "Extra attention is needed." 15 | name: ":fire: help wanted" 16 | 17 | # Cancel 18 | - color: "b60205" 19 | description: "This issue or pull request already exists." 20 | name: ":pray: duplicate" 21 | - color: "b60205" 22 | description: "This will not be worked on." 23 | name: ":pray: wontfix" 24 | 25 | # Size 26 | - color: "cfd3d7" 27 | description: "Extra Small size issue or PR." 28 | name: "size/XS" 29 | - color: "cfd3d7" 30 | description: "Small size issue or PR." 31 | name: "size/S" 32 | - color: "cfd3d7" 33 | description: "Medium size issue or PR." 34 | name: "size/M" 35 | - color: "cfd3d7" 36 | description: "Large size issue or PR." 37 | name: "size/L" 38 | - color: "cfd3d7" 39 | description: "Extra Large size issue or PR." 40 | name: "size/XL" 41 | 42 | # Modules 43 | - color: "fbca04" 44 | description: "This issue or pull request is related to amazon-issued-cert module." 45 | name: ":floppy_disk: amazon-issued-cert" 46 | - color: "fbca04" 47 | description: "This issue or pull request is related to cidr-collection module." 48 | name: ":floppy_disk: cidr-collection" 49 | - color: "fbca04" 50 | description: "This issue or pull request is related to imported-cert module." 51 | name: ":floppy_disk: imported-cert" 52 | - color: "fbca04" 53 | description: "This issue or pull request is related to private-ca-issued-cert module." 54 | name: ":floppy_disk: private-ca-issued-cert" 55 | - color: "fbca04" 56 | description: "This issue or pull request is related to private-zone module." 57 | name: ":floppy_disk: private-zone" 58 | - color: "fbca04" 59 | description: "This issue or pull request is related to public-zone module." 60 | name: ":floppy_disk: public-zone" 61 | - color: "fbca04" 62 | description: "This issue or pull request is related to record-set module." 63 | name: ":floppy_disk: record-set" 64 | - color: "fbca04" 65 | description: "This issue or pull request is related to registered-domain module." 66 | name: ":floppy_disk: registered-domain" 67 | - color: "fbca04" 68 | description: "This issue or pull request is related to resolver-inbound-endpoint module." 69 | name: ":floppy_disk: resolver-inbound-endpoint" 70 | - color: "fbca04" 71 | description: "This issue or pull request is related to resolver-query-logging module." 72 | name: ":floppy_disk: resolver-query-logging" 73 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-labeler.yaml: -------------------------------------------------------------------------------- 1 | name: Label Pull Requests 2 | 3 | on: 4 | - pull_request_target 5 | 6 | jobs: 7 | label-pr: 8 | runs-on: ubuntu-latest 9 | 10 | permissions: 11 | contents: read 12 | pull-requests: write 13 | 14 | steps: 15 | - name: Add Labels for PR 16 | uses: actions/labeler@v5 17 | with: 18 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 19 | configuration-path: .github/labeler.yaml 20 | dot: true 21 | sync-labels: true 22 | 23 | - name: Add PR Size Labels for PR 24 | uses: codelytv/pr-size-labeler@v1 25 | with: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | xs_label: 'size/XS' 28 | xs_max_size: '20' 29 | s_label: 'size/S' 30 | s_max_size: '50' 31 | m_label: 'size/M' 32 | m_max_size: '150' 33 | l_label: 'size/L' 34 | l_max_size: '300' 35 | xl_label: 'size/XL' 36 | fail_if_xl: 'false' 37 | message_if_xl: > 38 | 'This PR has too many changes. 39 | Please make sure you are NOT addressing multiple issues with one PR.' 40 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yaml: -------------------------------------------------------------------------------- 1 | name: Sync labels 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - .github/labels.yaml 9 | workflow_dispatch: {} 10 | 11 | jobs: 12 | sync-labels: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Sync labels 20 | uses: crazy-max/ghaction-github-labeler@v5 21 | with: 22 | github-otken: ${{ secrets.GITHUB_TOKEN }} 23 | yaml-file: .github/labels.yaml 24 | skip-delete: false 25 | dry-run: false 26 | # exclude: | 27 | -------------------------------------------------------------------------------- /.github/workflows/terraform.integration.yaml: -------------------------------------------------------------------------------- 1 | name: Integration (Terraform) 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: {} 8 | 9 | concurrency: 10 | group: terraform-integration-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | changed: 15 | name: Filter Changed Files and Directories 16 | runs-on: ubuntu-latest 17 | 18 | outputs: 19 | changed: ${{ steps.set-outputs.outputs.changed }} 20 | modified: ${{ steps.set-outputs.outputs.modified }} 21 | changed_files: ${{ steps.set-outputs.outputs.changed_files }} 22 | modified_files: ${{ steps.set-outputs.outputs.modified_files }} 23 | changed_directories: ${{ steps.set-outputs.outputs.changed_directories }} 24 | modified_directories: ${{ steps.set-outputs.outputs.modified_directories }} 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | 32 | - name: Get Changed Files 33 | id: changed-files 34 | uses: tj-actions/changed-files@v44 35 | with: 36 | files: | 37 | modules/** 38 | examples/** 39 | json: true 40 | 41 | - name: Get Changed Directories 42 | id: changed-directories 43 | uses: tj-actions/changed-files@v44 44 | with: 45 | files: | 46 | modules/** 47 | examples/** 48 | dir_names: "true" 49 | dir_names_max_depth: 2 50 | json: true 51 | 52 | - name: Set outputs 53 | id: set-outputs 54 | run: | 55 | echo "changed=${{ steps.changed-directories.outputs.any_changed }}" >> $GITHUB_OUTPUT 56 | echo "modified=${{ steps.changed-directories.outputs.any_modified }}" >> $GITHUB_OUTPUT 57 | 58 | echo "changed_files=${{ steps.changed-files.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 59 | echo "modified_files=${{ steps.changed-files.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 60 | 61 | echo "changed_directories=${{ steps.changed-directories.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 62 | echo "modified_directories=${{ steps.changed-directories.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 63 | 64 | 65 | terraform: 66 | name: Lint (terraform) 67 | needs: 68 | - changed 69 | if: ${{ needs.changed.outputs.modified == 'true' }} 70 | uses: tedilabs/.github/.github/workflows/terraform.terraform.yaml@main 71 | 72 | strategy: 73 | matrix: 74 | path: ${{ fromJson(needs.changed.outputs.modified_directories) }} 75 | 76 | with: 77 | terraform_target_dir: ${{ matrix.path }} 78 | terraform_version: latest 79 | terraform_host: app.terraform.io 80 | secrets: 81 | gh_token: ${{ secrets.GITHUB_TOKEN }} 82 | token: ${{ secrets.GITHUB_TOKEN }} 83 | terraform_token: ${{ secrets.TERRAFORM_TOKEN }} 84 | 85 | 86 | tflint: 87 | name: Lint (tflint) 88 | needs: 89 | - changed 90 | if: ${{ needs.changed.outputs.modified == 'true' }} 91 | uses: tedilabs/.github/.github/workflows/terraform.tflint.yaml@main 92 | 93 | strategy: 94 | matrix: 95 | path: ${{ fromJson(needs.changed.outputs.modified_directories) }} 96 | 97 | with: 98 | tflint_version: latest 99 | tflint_config_file: .tflint.hcl 100 | tflint_target_dir: ${{ matrix.path }} 101 | tflint_recursive_enabled: false 102 | tflint_terraform_init_enabled: true 103 | terraform_version: latest 104 | terraform_host: app.terraform.io 105 | secrets: 106 | gh_token: ${{ secrets.GITHUB_TOKEN }} 107 | token: ${{ secrets.GITHUB_TOKEN }} 108 | terraform_token: ${{ secrets.TERRAFORM_TOKEN }} 109 | -------------------------------------------------------------------------------- /.github/workflows/welcome.yaml: -------------------------------------------------------------------------------- 1 | name: Welcome for First Issue or Pull Request 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | issues: 8 | types: 9 | - opened 10 | 11 | jobs: 12 | welcome: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Welcome for First Issue or Pull Request 17 | uses: actions/first-interaction@v1 18 | with: 19 | repo-token: ${{ secrets.GITHUB_TOKEN }} 20 | issue-message: | 21 | ### :wave: Welcome! Looks like this is your first issue. 22 | 23 | Hey, thanks for your contribution! Please give us a bit of time to review it. 😄 24 | 25 | **Be sure to follow the issue template!** 26 | pr-message: | 27 | ### :wave: Welcome! Looks like this is your first pull request. 28 | 29 | Hey, thanks for your contribution! Please give us a bit of time to review it. 😄 30 | 31 | **Please check out our contributing guidelines.** 32 | -------------------------------------------------------------------------------- /.github/workflows/yaml.integration.yaml: -------------------------------------------------------------------------------- 1 | name: Integration (YAML) 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: {} 8 | 9 | concurrency: 10 | group: yaml-integration-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | changed: 15 | name: Filter Changed Files and Directories 16 | runs-on: ubuntu-latest 17 | 18 | outputs: 19 | changed: ${{ steps.set-outputs.outputs.changed }} 20 | modified: ${{ steps.set-outputs.outputs.modified }} 21 | changed_files: ${{ steps.set-outputs.outputs.changed_files }} 22 | modified_files: ${{ steps.set-outputs.outputs.modified_files }} 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Get Changed Files 31 | id: changed-files 32 | uses: tj-actions/changed-files@v44 33 | with: 34 | files: | 35 | **/*.yaml 36 | **/*.yml 37 | json: true 38 | 39 | - name: Set outputs 40 | id: set-outputs 41 | run: | 42 | echo "changed=${{ steps.changed-files.outputs.any_changed }}" >> $GITHUB_OUTPUT 43 | echo "modified=${{ steps.changed-files.outputs.any_modified }}" >> $GITHUB_OUTPUT 44 | 45 | echo "changed_files=${{ steps.changed-files.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 46 | echo "modified_files=${{ steps.changed-files.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 47 | 48 | lint: 49 | name: Lint (yamllint) 50 | needs: 51 | - changed 52 | if: ${{ needs.changed.outputs.modified == 'true' }} 53 | uses: tedilabs/.github/.github/workflows/yaml.yamllint.yaml@main 54 | 55 | with: 56 | yamllint_version: latest 57 | yamllint_config_file: .yamllint.yaml 58 | yamllint_target_dir: ./ 59 | secrets: 60 | token: ${{ secrets.GITHUB_TOKEN }} 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | # General 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | 30 | ### Terraform ### 31 | # Lock file 32 | .terraform.lock.hcl 33 | 34 | # Local .terraform directories 35 | **/.terraform/* 36 | 37 | # .tfstate files 38 | *.tfstate 39 | *.tfstate.* 40 | 41 | # Crash log files 42 | crash.log 43 | 44 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 45 | # .tfvars files are managed as part of configuration and so should be included in 46 | # version control. 47 | # 48 | # example.tfvars 49 | 50 | # Ignore override files as they are usually used to override resources locally and so 51 | # are not checked in 52 | override.tf 53 | override.tf.json 54 | *_override.tf 55 | *_override.tf.json 56 | 57 | # Include override files you do wish to add to version control using negated pattern 58 | # !example_override.tf 59 | 60 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 61 | # example: *tfplan* 62 | 63 | 64 | ### Vim ### 65 | # Swap 66 | [._]*.s[a-v][a-z] 67 | !*.svg # comment out if you don't need vector files 68 | [._]*.sw[a-p] 69 | [._]s[a-rt-v][a-z] 70 | [._]ss[a-gi-z] 71 | [._]sw[a-p] 72 | 73 | # Session 74 | Session.vim 75 | Sessionx.vim 76 | 77 | # Temporary 78 | .netrwhist 79 | *~ 80 | # Auto-generated tag files 81 | tags 82 | # Persistent undo 83 | [._]*.un~ 84 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_install_hook_types: 2 | - pre-commit 3 | - commit-msg 4 | 5 | repos: 6 | - repo: https://github.com/antonbabenko/pre-commit-terraform 7 | rev: v1.97.4 8 | hooks: 9 | - id: terraform_fmt 10 | name: (terraform) Format .tf files with `terraform fmt` 11 | args: 12 | - --args=-diff 13 | - id: terraform_validate 14 | name: (terraform) Check with `terraform validate` 15 | args: 16 | - --hook-config=--retry-once-with-cleanup=true 17 | - --tf-init-args=-upgrade 18 | - id: terraform_tflint 19 | name: (terraform) Check with `tflint` 20 | args: 21 | - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl 22 | files: ^modules/ 23 | - id: terraform_docs 24 | name: (terraform) Generate docs with `terraform-docs` 25 | args: ["--args=--sort-by required"] 26 | 27 | - repo: https://github.com/adrienverge/yamllint 28 | rev: v1.36.2 29 | hooks: 30 | - id: yamllint 31 | name: (yaml) Check with `yamllint` 32 | 33 | - repo: https://github.com/compilerla/conventional-pre-commit 34 | rev: v4.0.0 35 | hooks: 36 | - id: conventional-pre-commit 37 | name: (commit-message) Check conventional commit 38 | stages: [commit-msg] 39 | args: [] 40 | -------------------------------------------------------------------------------- /.tflint.hcl: -------------------------------------------------------------------------------- 1 | config { 2 | plugin_dir = "~/.tflint.d/plugins" 3 | 4 | format = "compact" 5 | call_module_type = "local" 6 | force = false 7 | disabled_by_default = false 8 | 9 | ignore_module = {} 10 | } 11 | 12 | 13 | ################################################### 14 | # Rule Sets - Terraform 15 | ################################################### 16 | 17 | plugin "terraform" { 18 | enabled = true 19 | preset = "recommended" 20 | } 21 | 22 | rule "terraform_comment_syntax" { 23 | enabled = true 24 | } 25 | 26 | rule "terraform_documented_variables" { 27 | enabled = true 28 | } 29 | 30 | rule "terraform_documented_outputs" { 31 | enabled = true 32 | } 33 | 34 | rule "terraform_naming_convention" { 35 | enabled = true 36 | format = "snake_case" 37 | 38 | custom_formats = { 39 | extended_snake_case = { 40 | description = "Extended snake_case Format which allows double underscore like `a__b`." 41 | regex = "^[a-z][a-z0-9]+([_]{1,2}[a-z0-9]+)*$" 42 | } 43 | } 44 | 45 | module { 46 | format = "extended_snake_case" 47 | } 48 | 49 | resource { 50 | format = "extended_snake_case" 51 | } 52 | 53 | data { 54 | format = "extended_snake_case" 55 | } 56 | } 57 | 58 | rule "terraform_unused_declarations" { 59 | enabled = false 60 | } 61 | 62 | rule "terraform_unused_required_providers" { 63 | enabled = true 64 | } 65 | 66 | 67 | ################################################### 68 | # Rule Sets - AWS 69 | ################################################### 70 | 71 | plugin "aws" { 72 | source = "github.com/terraform-linters/tflint-ruleset-aws" 73 | version = "0.38.0" 74 | 75 | enabled = true 76 | deep_check = false 77 | } 78 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | yaml-files: 2 | - '*.yaml' 3 | - '*.yml' 4 | 5 | rules: 6 | braces: 7 | min-spaces-inside: 0 8 | max-spaces-inside: 1 9 | min-spaces-inside-empty: 0 10 | max-spaces-inside-empty: 0 11 | brackets: 12 | min-spaces-inside: 0 13 | max-spaces-inside: 1 14 | min-spaces-inside-empty: 0 15 | max-spaces-inside-empty: 0 16 | colons: 17 | max-spaces-before: 0 18 | max-spaces-after: 1 19 | commas: 20 | max-spaces-before: 0 21 | comments: 22 | level: warning 23 | require-starting-space: true 24 | min-spaces-from-content: 1 25 | comments-indentation: disable 26 | document-end: disable 27 | document-start: disable 28 | empty-lines: 29 | level: warning 30 | max: 2 31 | max-start: 0 32 | max-end: 1 33 | empty-values: 34 | forbid-in-block-mappings: true 35 | forbid-in-flow-mappings: true 36 | hyphens: 37 | max-spaces-after: 1 38 | indentation: 39 | spaces: consistent 40 | indent-sequences: false 41 | key-duplicates: enable 42 | key-ordering: disable 43 | line-length: disable 44 | new-line-at-end-of-file: enable 45 | # Use UNIX new line characters `\n` instead of DOS new line characters `\r\n` 46 | new-lines: 47 | type: unix 48 | octal-values: disable 49 | quoted-strings: 50 | quote-type: any 51 | required: false 52 | trailing-spaces: enable 53 | truthy: disable 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-domain 2 | 3 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/tedilabs/terraform-aws-domain?color=blue&sort=semver&style=flat-square) 4 | ![GitHub](https://img.shields.io/github/license/tedilabs/terraform-aws-domain?color=blue&style=flat-square) 5 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)](https://github.com/pre-commit/pre-commit) 6 | 7 | Terraform module which creates Route53 related resources on AWS. 8 | 9 | - [amazon-issued-cert](./modules/amazon-issued-cert) 10 | - [cidr-collection](./modules/cidr-collection/) 11 | - [imported-cert](./modules/imported-cert) 12 | - [private-ca-issued-cert](./modules/private-ca-issued-cert) 13 | - [private-zone](./modules/private-zone) 14 | - [public-zone](./modules/public-zone) 15 | - [record-set](./modules/record-set) 16 | - [registered-domain](./modules/registered-domain) 17 | - [resolver-inbound-endpoint](./modules/resolver-inbound-endpoint) 18 | - [resolver-query-logging](./modules/resolver-query-logging) 19 | 20 | 21 | ## Target AWS Services 22 | 23 | Terraform Modules from [this package](https://github.com/tedilabs/terraform-aws-domain) were written to manage the following AWS Services with Terraform. 24 | 25 | - **Route53** 26 | - Registrar 27 | - Registered Domain 28 | - Hosted Zone 29 | - Public Hosted Zone 30 | - Private Hosted Zone 31 | - Record Set 32 | - CIDR Collection 33 | - Resolver 34 | - Inbound Endpoint 35 | - Outbound Endpoint (comming soon!) 36 | - Query Logging Configuration 37 | - **ACM (AWS Certificate Manager)** 38 | - Amazon issued Certificate 39 | - Private CA issued Certificate 40 | - Imported Certificate 41 | 42 | 43 | ## Examples 44 | 45 | ### Route53 46 | 47 | - [route53-private-zone-simple](./examples/route53-private-zone-simple/) 48 | - [route53-public-zone-simple](./examples/route53-public-zone-simple/) 49 | 50 | 51 | ## Self Promotion 52 | 53 | Like this project? Follow the repository on [GitHub](https://github.com/tedilabs/terraform-aws-domain). And if you're feeling especially charitable, follow **[posquit0](https://github.com/posquit0)** on GitHub. 54 | 55 | 56 | ## License 57 | 58 | Provided under the terms of the [Apache License](LICENSE). 59 | 60 | Copyright © 2021-2023, [Byungjin Park](https://www.posquit0.com). 61 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.16.0 2 | -------------------------------------------------------------------------------- /examples/route53-private-zone-simple/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | data "aws_vpc" "default" { 6 | default = true 7 | } 8 | 9 | 10 | ################################################### 11 | # Route53 Private Hosted Zone 12 | ################################################### 13 | 14 | module "zone" { 15 | source = "../../modules/private-zone/" 16 | # source = "tedilabs/domain/aws//modules/private-zone" 17 | # version = "~> 0.2.0" 18 | 19 | name = "mycompany.com" 20 | 21 | primary_vpc_association = { 22 | vpc_id = data.aws_vpc.default.id 23 | } 24 | 25 | tags = { 26 | "project" = "terraform-aws-domain-examples" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/route53-private-zone-simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "zone" { 2 | description = "The Route53 Hosted Zone." 3 | value = module.zone 4 | } 5 | -------------------------------------------------------------------------------- /examples/route53-private-zone-simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.6" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/route53-public-zone-simple/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # Route53 Public Hosted Zone 8 | ################################################### 9 | 10 | module "zone" { 11 | source = "../../modules/public-zone/" 12 | # source = "tedilabs/domain/aws//modules/public-zone" 13 | # version = "~> 0.2.0" 14 | 15 | name = "mycompany.com" 16 | 17 | tags = { 18 | "project" = "terraform-aws-domain-examples" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/route53-public-zone-simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "zone" { 2 | description = "The Route53 Hosted Zone." 3 | value = module.zone 4 | } 5 | -------------------------------------------------------------------------------- /examples/route53-public-zone-simple/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.6" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/amazon-issued-cert/README.md: -------------------------------------------------------------------------------- 1 | # amazon-issued-cert 2 | 3 | This module creates following resources. 4 | 5 | - `aws_acm_certificate` 6 | - `aws_acm_certificate_validation` (Optional) 7 | - `aws_route53_record` (Optional) 8 | 9 | 10 | ## Requirements 11 | 12 | | Name | Version | 13 | |------|---------| 14 | | [terraform](#requirement\_terraform) | >= 1.6 | 15 | | [aws](#requirement\_aws) | >= 4.58 | 16 | 17 | ## Providers 18 | 19 | | Name | Version | 20 | |------|---------| 21 | | [aws](#provider\_aws) | 5.26.0 | 22 | 23 | ## Modules 24 | 25 | | Name | Source | Version | 26 | |------|--------|---------| 27 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 28 | 29 | ## Resources 30 | 31 | | Name | Type | 32 | |------|------| 33 | | [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource | 34 | | [aws_acm_certificate_validation.dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource | 35 | | [aws_acm_certificate_validation.email](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource | 36 | | [aws_route53_record.validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | 37 | 38 | ## Inputs 39 | 40 | | Name | Description | Type | Default | Required | 41 | |------|-------------|------|---------|:--------:| 42 | | [domain\_name](#input\_domain\_name) | (Required) A domain name for which the certificate should be issued. FQDN (Fully qualified domain name), such as `www.example.com`, that you want to secure with an ACM certificate. Use an asterisk (*) to create a wildcard certificate that protects several sites in the same domain. | `string` | n/a | yes | 43 | | [name](#input\_name) | (Required) The name of the certificate. | `string` | n/a | yes | 44 | | [certificate\_transparency\_logging\_enabled](#input\_certificate\_transparency\_logging\_enabled) | (Optional) Whether to add the certificate to a certificate transparency log. Transparency makes it possible to detect SSL/TLS certificates that have been mistakenly or maliciously issued. Certificates that have not been logged typically produce an error message in a browser. Defaults to `true`. | `bool` | `true` | no | 45 | | [dns\_validation](#input\_dns\_validation) | (Optional) The configuration for the DNS validation. `dns_validation` as defined below.
(Optional) `enabled` - Whether to process DNS validation by creating the necessary domain records in the module. Defaults to `false`.
(Optional) `managed_zones` - List of Hosted Zones to automatically manage the records for DNS validation as a map. The key is the name of Hosted Zone. The value is the ID of Hosted Zone. |
object({
enabled = optional(bool, false)
managed_zones = optional(map(string), {})
})
| `{}` | no | 46 | | [email\_validation](#input\_email\_validation) | (Optional) The configuration for the Email validation. `email_validation` as defined below.
(Optional) `enabled` - Whether to process Email validation by waiting the manual approval. Defaults to `false`. |
object({
enabled = optional(bool, false)
})
| `{}` | no | 47 | | [key\_algorithm](#input\_key\_algorithm) | (Optional) The algorithm of the public and private key pair that your Amazon issued certificate uses to encrypt data. RSA is the default key algorithm for ACM certificates. Elliptic Curve Digital Signature Algorithm (ECDSA) keys are smaller, offering security comparable to RSA keys but with greater computing efficiency. However, ECDSA is not supported by all network clients. Some AWS services may require RSA keys, or only support ECDSA keys of a particular size, while others allow the use of either RSA and ECDSA keys to ensure that compatibility is not broken. Supported values are `RSA_1024`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `ECDSA_P256`, `ECDSA_P384`, `ECDSA_P521`. Defaults to `RSA_2048`. | `string` | `"RSA_2048"` | no | 48 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 49 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 50 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 51 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 52 | | [subject\_alternative\_names](#input\_subject\_alternative\_names) | (Optional) A list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate. | `list(string)` | `[]` | no | 53 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 54 | | [validation\_method](#input\_validation\_method) | (Optional) Which method to use for validation. Valid values are `DNS` or `EMAIL`. Only support `DNS` validation method in this module. | `string` | `"DNS"` | no | 55 | 56 | ## Outputs 57 | 58 | | Name | Description | 59 | |------|-------------| 60 | | [arn](#output\_arn) | The ARN of the certificate. | 61 | | [certificate\_transparency\_logging\_enabled](#output\_certificate\_transparency\_logging\_enabled) | Whether or not the certificate transparency logging is enabled. | 62 | | [domain\_name](#output\_domain\_name) | The domain name for which the certificate is issued. | 63 | | [effective\_date](#output\_effective\_date) | Effective date and time of the certificate. Start of the validity period of the certificate. | 64 | | [expiration\_date](#output\_expiration\_date) | Expiration date and time of the certificate. | 65 | | [id](#output\_id) | The ID of the certificate. | 66 | | [key\_algorithm](#output\_key\_algorithm) | The algorithm of the public and private key pair to encrypt data. | 67 | | [name](#output\_name) | The name of the certificate. | 68 | | [renewal](#output\_renewal) | The configuration for the certificate renewal.
`eligibility` - Whether the certificate is eligible for managed renewal.
`summary` - The information about the status of ACM's managed renewal for the certificate. | 69 | | [status](#output\_status) | Status of the certificate. | 70 | | [subject\_alternative\_names](#output\_subject\_alternative\_names) | The list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate. | 71 | | [type](#output\_type) | The type of the certificate. | 72 | | [validation](#output\_validation) | The configuration for the certificate validation.
`method` - The method to use to validate the domain ownership for requesting a public certificate.
`domain_records` - A map of domain validation records which can be used to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if `validation.method` is `DNS`.
`emails` - A list of addresses that received a validation E-Mail. Only set if `validation.method` is `EMAIL`. | 73 | 74 | -------------------------------------------------------------------------------- /modules/amazon-issued-cert/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | locals { 18 | key_algorithms = { 19 | "RSA_1024" = "RSA_1024" 20 | "RSA_2048" = "RSA_2048" 21 | "RSA_3072" = "RSA_3072" 22 | "RSA_4096" = "RSA_4096" 23 | "ECDSA_P256" = "EC_prime256v1" 24 | "ECDSA_P384" = "EC_secp384r1" 25 | "ECDSA_P521" = "EC_secp521r1" 26 | } 27 | } 28 | 29 | 30 | ################################################### 31 | # ACM Certificate 32 | ################################################### 33 | 34 | # INFO: Not supported attributes 35 | # - `certificate_authority_arn` 36 | # - `certificate_body` 37 | # - `certificate_chain` 38 | # - `private_key` 39 | # TODO: Support `EMAIL` validation method 40 | # - `validation_options` 41 | resource "aws_acm_certificate" "this" { 42 | domain_name = var.domain_name 43 | subject_alternative_names = var.subject_alternative_names 44 | 45 | key_algorithm = local.key_algorithms[var.key_algorithm] 46 | 47 | options { 48 | certificate_transparency_logging_preference = var.certificate_transparency_logging_enabled ? "ENABLED" : "DISABLED" 49 | } 50 | 51 | validation_method = var.validation_method 52 | 53 | tags = merge( 54 | { 55 | "Name" = local.metadata.name 56 | }, 57 | local.module_tags, 58 | var.tags, 59 | ) 60 | 61 | lifecycle { 62 | create_before_destroy = true 63 | } 64 | } 65 | 66 | 67 | ################################################### 68 | # DNS Validation 69 | ################################################### 70 | 71 | locals { 72 | subject_names = concat([var.domain_name], var.subject_alternative_names) 73 | 74 | dns_validation_enabled = var.validation_method == "DNS" && var.dns_validation.enabled 75 | dns_validation_records = { 76 | for record in aws_acm_certificate.this.domain_validation_options : 77 | record.domain_name => { 78 | name = record.resource_record_name 79 | type = record.resource_record_type 80 | value = record.resource_record_value 81 | } 82 | } 83 | } 84 | 85 | resource "aws_route53_record" "validation" { 86 | for_each = toset(local.dns_validation_enabled ? local.subject_names : []) 87 | 88 | zone_id = var.dns_validation.managed_zones[replace(each.value, "*.", "")] 89 | name = local.dns_validation_records[each.value].name 90 | type = local.dns_validation_records[each.value].type 91 | records = [local.dns_validation_records[each.value].value] 92 | 93 | ttl = 60 94 | allow_overwrite = true 95 | } 96 | 97 | resource "aws_acm_certificate_validation" "dns" { 98 | count = local.dns_validation_enabled ? 1 : 0 99 | 100 | certificate_arn = aws_acm_certificate.this.arn 101 | validation_record_fqdns = values(aws_route53_record.validation)[*].fqdn 102 | } 103 | 104 | 105 | ################################################### 106 | # Email Validation 107 | ################################################### 108 | 109 | locals { 110 | email_validation_enabled = var.validation_method == "EMAIL" && var.email_validation.enabled 111 | } 112 | 113 | resource "aws_acm_certificate_validation" "email" { 114 | count = local.email_validation_enabled ? 1 : 0 115 | 116 | certificate_arn = aws_acm_certificate.this.arn 117 | } 118 | -------------------------------------------------------------------------------- /modules/amazon-issued-cert/migrations.tf: -------------------------------------------------------------------------------- 1 | # INFO: [2023-11-17] Change the name of `aws_acm_certificate_validation` resource for DNS validation 2 | moved { 3 | from = aws_acm_certificate_validation.this 4 | to = aws_acm_certificate_validation.dns 5 | } 6 | -------------------------------------------------------------------------------- /modules/amazon-issued-cert/outputs.tf: -------------------------------------------------------------------------------- 1 | output "name" { 2 | description = "The name of the certificate." 3 | value = var.name 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the certificate." 8 | value = aws_acm_certificate.this.id 9 | } 10 | 11 | output "arn" { 12 | description = "The ARN of the certificate." 13 | value = aws_acm_certificate.this.arn 14 | } 15 | 16 | output "status" { 17 | description = "Status of the certificate." 18 | value = aws_acm_certificate.this.status 19 | } 20 | 21 | output "type" { 22 | description = "The type of the certificate." 23 | value = aws_acm_certificate.this.type 24 | } 25 | 26 | output "domain_name" { 27 | description = "The domain name for which the certificate is issued." 28 | value = aws_acm_certificate.this.domain_name 29 | } 30 | 31 | output "subject_alternative_names" { 32 | description = "The list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate." 33 | value = aws_acm_certificate.this.subject_alternative_names 34 | } 35 | 36 | output "key_algorithm" { 37 | description = "The algorithm of the public and private key pair to encrypt data." 38 | value = { 39 | for k, v in local.key_algorithms : 40 | v => k 41 | }[aws_acm_certificate.this.key_algorithm] 42 | } 43 | 44 | output "certificate_transparency_logging_enabled" { 45 | description = "Whether or not the certificate transparency logging is enabled." 46 | value = var.certificate_transparency_logging_enabled 47 | } 48 | 49 | output "effective_date" { 50 | description = "Effective date and time of the certificate. Start of the validity period of the certificate." 51 | value = aws_acm_certificate.this.not_before 52 | } 53 | 54 | output "expiration_date" { 55 | description = "Expiration date and time of the certificate." 56 | value = aws_acm_certificate.this.not_after 57 | } 58 | 59 | output "validation" { 60 | description = < { 72 | name = option.resource_record_name 73 | type = option.resource_record_type 74 | value = option.resource_record_value 75 | } 76 | } 77 | : {} 78 | ) 79 | emails = (aws_acm_certificate.this.validation_method == "EMAIL" 80 | ? aws_acm_certificate.this.validation_emails 81 | : [] 82 | ) 83 | } 84 | } 85 | 86 | output "renewal" { 87 | description = < 9 | ## Requirements 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [terraform](#requirement\_terraform) | >= 1.6 | 14 | | [aws](#requirement\_aws) | >= 5.14 | 15 | 16 | ## Providers 17 | 18 | | Name | Version | 19 | |------|---------| 20 | | [aws](#provider\_aws) | 5.25.0 | 21 | 22 | ## Modules 23 | 24 | No modules. 25 | 26 | ## Resources 27 | 28 | | Name | Type | 29 | |------|------| 30 | | [aws_route53_cidr_collection.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_cidr_collection) | resource | 31 | | [aws_route53_cidr_location.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_cidr_location) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [name](#input\_name) | (Required) The name for the CIDR collection. | `string` | n/a | yes | 38 | | [locations](#input\_locations) | (Optional) A configurations for locations of the CIDR collection. Each key is the CIDR location name. Each value is a set of the location CIDR blocks. | `map(set(string))` | `{}` | no | 39 | 40 | ## Outputs 41 | 42 | | Name | Description | 43 | |------|-------------| 44 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of the CIDR collection. | 45 | | [id](#output\_id) | The CIDR collection ID. | 46 | | [locations](#output\_locations) | A map of locations for the CIDR collection. | 47 | | [name](#output\_name) | The name of the CIDR collection. | 48 | | [version](#output\_version) | The version of the CIDR collection. | 49 | 50 | -------------------------------------------------------------------------------- /modules/cidr-collection/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } 15 | } 16 | 17 | 18 | ################################################### 19 | # CIDR Collection 20 | ################################################### 21 | 22 | resource "aws_route53_cidr_collection" "this" { 23 | name = var.name 24 | } 25 | 26 | 27 | ################################################### 28 | # Locations for CIDR Collection 29 | ################################################### 30 | 31 | resource "aws_route53_cidr_location" "this" { 32 | for_each = var.locations 33 | 34 | cidr_collection_id = aws_route53_cidr_collection.this.id 35 | 36 | name = each.key 37 | cidr_blocks = each.value 38 | } 39 | -------------------------------------------------------------------------------- /modules/cidr-collection/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The Amazon Resource Name (ARN) of the CIDR collection." 3 | value = aws_route53_cidr_collection.this.arn 4 | } 5 | 6 | output "id" { 7 | description = "The CIDR collection ID." 8 | value = aws_route53_cidr_collection.this.id 9 | } 10 | 11 | output "name" { 12 | description = "The name of the CIDR collection." 13 | value = aws_route53_cidr_collection.this.name 14 | } 15 | 16 | output "version" { 17 | description = "The version of the CIDR collection." 18 | value = aws_route53_cidr_collection.this.version 19 | } 20 | 21 | output "locations" { 22 | description = "A map of locations for the CIDR collection." 23 | value = { 24 | for name, location in aws_route53_cidr_location.this : 25 | name => location.cidr_blocks 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/cidr-collection/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name for the CIDR collection." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "locations" { 8 | description = < 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.5 | 13 | | [aws](#requirement\_aws) | >= 4.27 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [aws](#provider\_aws) | 5.26.0 | 20 | 21 | ## Modules 22 | 23 | | Name | Source | Version | 24 | |------|--------|---------| 25 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |------|------| 31 | | [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [certificate\_body](#input\_certificate\_body) | (Required) Certificate's PEM-formatted public key. | `string` | n/a | yes | 38 | | [name](#input\_name) | (Required) The name of the certificate. | `string` | n/a | yes | 39 | | [private\_key](#input\_private\_key) | (Required) Certificate's PEM-formatted private key. | `string` | n/a | yes | 40 | | [certificate\_chain](#input\_certificate\_chain) | (Optional) Certificate's PEM-formatted chain. | `string` | `null` | no | 41 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 42 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 43 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 44 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 45 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 46 | 47 | ## Outputs 48 | 49 | | Name | Description | 50 | |------|-------------| 51 | | [arn](#output\_arn) | The ARN of the certificate. | 52 | | [domain\_name](#output\_domain\_name) | The domain name for which the certificate is issued. | 53 | | [effective\_date](#output\_effective\_date) | Effective date and time of the certificate. Start of the validity period of the certificate. | 54 | | [expiration\_date](#output\_expiration\_date) | Expiration date and time of the certificate. | 55 | | [id](#output\_id) | The ID of the certificate. | 56 | | [name](#output\_name) | The name of the certificate. | 57 | | [status](#output\_status) | Status of the certificate. | 58 | | [subject\_alternative\_names](#output\_subject\_alternative\_names) | The list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate. | 59 | | [type](#output\_type) | The type of the certificate. | 60 | 61 | -------------------------------------------------------------------------------- /modules/imported-cert/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # ACM Certificate 20 | ################################################### 21 | 22 | # INFO: Not supported attributes 23 | # - `certificate_authority_arn` 24 | # - `domain_name` 25 | # - `key_algorithm` 26 | # - `options` 27 | # - `subject_alternative_names` 28 | # - `validation_method` 29 | # - `validation_option` 30 | resource "aws_acm_certificate" "this" { 31 | private_key = var.private_key 32 | certificate_body = var.certificate_body 33 | certificate_chain = var.certificate_chain 34 | 35 | tags = merge( 36 | { 37 | "Name" = local.metadata.name 38 | }, 39 | local.module_tags, 40 | var.tags, 41 | ) 42 | 43 | lifecycle { 44 | create_before_destroy = true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/imported-cert/outputs.tf: -------------------------------------------------------------------------------- 1 | output "name" { 2 | description = "The name of the certificate." 3 | value = var.name 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the certificate." 8 | value = aws_acm_certificate.this.id 9 | } 10 | 11 | output "arn" { 12 | description = "The ARN of the certificate." 13 | value = aws_acm_certificate.this.arn 14 | } 15 | 16 | output "status" { 17 | description = "Status of the certificate." 18 | value = aws_acm_certificate.this.status 19 | } 20 | 21 | output "type" { 22 | description = "The type of the certificate." 23 | value = aws_acm_certificate.this.type 24 | } 25 | 26 | output "domain_name" { 27 | description = "The domain name for which the certificate is issued." 28 | value = aws_acm_certificate.this.domain_name 29 | } 30 | 31 | output "subject_alternative_names" { 32 | description = "The list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate." 33 | value = aws_acm_certificate.this.subject_alternative_names 34 | } 35 | 36 | output "effective_date" { 37 | description = "Effective date and time of the certificate. Start of the validity period of the certificate." 38 | value = aws_acm_certificate.this.not_before 39 | } 40 | 41 | output "expiration_date" { 42 | description = "Expiration date and time of the certificate." 43 | value = aws_acm_certificate.this.not_after 44 | } 45 | -------------------------------------------------------------------------------- /modules/imported-cert/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/imported-cert/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the certificate." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "private_key" { 8 | description = "(Required) Certificate's PEM-formatted private key." 9 | type = string 10 | nullable = false 11 | } 12 | 13 | variable "certificate_body" { 14 | description = "(Required) Certificate's PEM-formatted public key." 15 | type = string 16 | nullable = false 17 | } 18 | 19 | variable "certificate_chain" { 20 | description = "(Optional) Certificate's PEM-formatted chain." 21 | type = string 22 | default = null 23 | nullable = true 24 | } 25 | 26 | variable "tags" { 27 | description = "(Optional) A map of tags to add to all resources." 28 | type = map(string) 29 | default = {} 30 | nullable = false 31 | } 32 | 33 | variable "module_tags_enabled" { 34 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 35 | type = bool 36 | default = true 37 | nullable = false 38 | } 39 | 40 | 41 | ################################################### 42 | # Resource Group 43 | ################################################### 44 | 45 | variable "resource_group_enabled" { 46 | description = "(Optional) Whether to create Resource Group to find and group AWS resources which are created by this module." 47 | type = bool 48 | default = true 49 | nullable = false 50 | } 51 | 52 | variable "resource_group_name" { 53 | description = "(Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`." 54 | type = string 55 | default = "" 56 | nullable = false 57 | } 58 | 59 | variable "resource_group_description" { 60 | description = "(Optional) The description of Resource Group." 61 | type = string 62 | default = "Managed by Terraform." 63 | nullable = false 64 | } 65 | -------------------------------------------------------------------------------- /modules/imported-cert/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.27" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/private-ca-issued-cert/README.md: -------------------------------------------------------------------------------- 1 | # private-ca-issued-cert 2 | 3 | This module creates following resources. 4 | 5 | - `aws_acm_certificate` 6 | 7 | 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.5 | 13 | | [aws](#requirement\_aws) | >= 4.27 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [aws](#provider\_aws) | 5.26.0 | 20 | 21 | ## Modules 22 | 23 | | Name | Source | Version | 24 | |------|--------|---------| 25 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |------|------| 31 | | [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [certificate\_authority](#input\_certificate\_authority) | (Required) The ARN (Amazon Resource Name) of the AWS PCA (Private Certificate Authority) that will be used to issue the certificate. | `string` | n/a | yes | 38 | | [domain\_name](#input\_domain\_name) | (Required) A domain name for which the certificate should be issued. FQDN (Fully qualified domain name), such as `www.example.com`, that you want to secure with an ACM certificate. Use an asterisk (*) to create a wildcard certificate that protects several sites in the same domain. | `string` | n/a | yes | 39 | | [name](#input\_name) | (Required) The name of the certificate. | `string` | n/a | yes | 40 | | [key\_algorithm](#input\_key\_algorithm) | (Optional) The algorithm of the public and private key pair that your Amazon issued certificate uses to encrypt data. RSA is the default key algorithm for ACM certificates. Elliptic Curve Digital Signature Algorithm (ECDSA) keys are smaller, offering security comparable to RSA keys but with greater computing efficiency. However, ECDSA is not supported by all network clients. Some AWS services may require RSA keys, or only support ECDSA keys of a particular size, while others allow the use of either RSA and ECDSA keys to ensure that compatibility is not broken. Supported values are `RSA_1024`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `ECDSA_P256`, `ECDSA_P384`, `ECDSA_P521`. Defaults to `RSA_2048`. | `string` | `"RSA_2048"` | no | 41 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 42 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 43 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 44 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 45 | | [subject\_alternative\_names](#input\_subject\_alternative\_names) | (Optional) A list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate. | `list(string)` | `[]` | no | 46 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 47 | 48 | ## Outputs 49 | 50 | | Name | Description | 51 | |------|-------------| 52 | | [arn](#output\_arn) | The ARN of the certificate. | 53 | | [certificate\_authority](#output\_certificate\_authority) | The ARN (Amazon Resource Name) of the AWS PCA (Private Certificate Authority). | 54 | | [domain\_name](#output\_domain\_name) | The domain name for which the certificate is issued. | 55 | | [effective\_date](#output\_effective\_date) | Effective date and time of the certificate. Start of the validity period of the certificate. | 56 | | [expiration\_date](#output\_expiration\_date) | Expiration date and time of the certificate. | 57 | | [id](#output\_id) | The ID of the certificate. | 58 | | [key\_algorithm](#output\_key\_algorithm) | The algorithm of the public and private key pair to encrypt data. | 59 | | [name](#output\_name) | The name of the certificate. | 60 | | [status](#output\_status) | Status of the certificate. | 61 | | [subject\_alternative\_names](#output\_subject\_alternative\_names) | The list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate. | 62 | | [type](#output\_type) | The type of the certificate. | 63 | 64 | -------------------------------------------------------------------------------- /modules/private-ca-issued-cert/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | locals { 18 | key_algorithms = { 19 | "RSA_1024" = "RSA_1024" 20 | "RSA_2048" = "RSA_2048" 21 | "RSA_3072" = "RSA_3072" 22 | "RSA_4096" = "RSA_4096" 23 | "ECDSA_P256" = "EC_prime256v1" 24 | "ECDSA_P384" = "EC_secp384r1" 25 | "ECDSA_P521" = "EC_secp521r1" 26 | } 27 | } 28 | 29 | 30 | ################################################### 31 | # ACM Certificate 32 | ################################################### 33 | 34 | # INFO: Not supported attributes 35 | # - `certificate_body` 36 | # - `certificate_chain` 37 | # - `private_key` 38 | # - `validation_method` 39 | # - `validation_options` 40 | resource "aws_acm_certificate" "this" { 41 | certificate_authority_arn = var.certificate_authority 42 | 43 | domain_name = var.domain_name 44 | subject_alternative_names = var.subject_alternative_names 45 | 46 | key_algorithm = local.key_algorithms[var.key_algorithm] 47 | 48 | tags = merge( 49 | { 50 | "Name" = local.metadata.name 51 | }, 52 | local.module_tags, 53 | var.tags, 54 | ) 55 | 56 | lifecycle { 57 | create_before_destroy = true 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /modules/private-ca-issued-cert/outputs.tf: -------------------------------------------------------------------------------- 1 | output "name" { 2 | description = "The name of the certificate." 3 | value = var.name 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the certificate." 8 | value = aws_acm_certificate.this.id 9 | } 10 | 11 | output "arn" { 12 | description = "The ARN of the certificate." 13 | value = aws_acm_certificate.this.arn 14 | } 15 | 16 | output "status" { 17 | description = "Status of the certificate." 18 | value = aws_acm_certificate.this.status 19 | } 20 | 21 | output "type" { 22 | description = "The type of the certificate." 23 | value = aws_acm_certificate.this.type 24 | } 25 | 26 | output "domain_name" { 27 | description = "The domain name for which the certificate is issued." 28 | value = aws_acm_certificate.this.domain_name 29 | } 30 | 31 | output "subject_alternative_names" { 32 | description = "The list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate." 33 | value = aws_acm_certificate.this.subject_alternative_names 34 | } 35 | 36 | output "key_algorithm" { 37 | description = "The algorithm of the public and private key pair to encrypt data." 38 | value = { 39 | for k, v in local.key_algorithms : 40 | v => k 41 | }[aws_acm_certificate.this.key_algorithm] 42 | } 43 | 44 | output "certificate_authority" { 45 | description = "The ARN (Amazon Resource Name) of the AWS PCA (Private Certificate Authority)." 46 | value = aws_acm_certificate.this.certificate_authority_arn 47 | } 48 | 49 | output "effective_date" { 50 | description = "Effective date and time of the certificate. Start of the validity period of the certificate." 51 | value = aws_acm_certificate.this.not_before 52 | } 53 | 54 | output "expiration_date" { 55 | description = "Expiration date and time of the certificate." 56 | value = aws_acm_certificate.this.not_after 57 | } 58 | -------------------------------------------------------------------------------- /modules/private-ca-issued-cert/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/private-ca-issued-cert/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the certificate." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "domain_name" { 8 | description = "(Required) A domain name for which the certificate should be issued. FQDN (Fully qualified domain name), such as `www.example.com`, that you want to secure with an ACM certificate. Use an asterisk (*) to create a wildcard certificate that protects several sites in the same domain." 9 | type = string 10 | nullable = false 11 | } 12 | 13 | variable "subject_alternative_names" { 14 | description = "(Optional) A list of additional FQDNs (Fully qualified domain names) to be included in SANs of the issued certificate." 15 | type = list(string) 16 | default = [] 17 | nullable = false 18 | } 19 | 20 | variable "key_algorithm" { 21 | description = "(Optional) The algorithm of the public and private key pair that your Amazon issued certificate uses to encrypt data. RSA is the default key algorithm for ACM certificates. Elliptic Curve Digital Signature Algorithm (ECDSA) keys are smaller, offering security comparable to RSA keys but with greater computing efficiency. However, ECDSA is not supported by all network clients. Some AWS services may require RSA keys, or only support ECDSA keys of a particular size, while others allow the use of either RSA and ECDSA keys to ensure that compatibility is not broken. Supported values are `RSA_1024`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `ECDSA_P256`, `ECDSA_P384`, `ECDSA_P521`. Defaults to `RSA_2048`." 22 | type = string 23 | default = "RSA_2048" 24 | nullable = false 25 | 26 | validation { 27 | condition = contains(["RSA_1024", "RSA_2048", "RSA_3072", "RSA_4096", "ECDSA_P256", "ECDSA_P384", "ECDSA_P521"], var.key_algorithm) 28 | error_message = "Valid values for `key_algorithm` are `RSA_1024`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `ECDSA_P256`, `ECDSA_P384`, `ECDSA_P521`." 29 | } 30 | } 31 | 32 | variable "certificate_authority" { 33 | description = "(Required) The ARN (Amazon Resource Name) of the AWS PCA (Private Certificate Authority) that will be used to issue the certificate." 34 | type = string 35 | nullable = false 36 | } 37 | 38 | variable "tags" { 39 | description = "(Optional) A map of tags to add to all resources." 40 | type = map(string) 41 | default = {} 42 | nullable = false 43 | } 44 | 45 | variable "module_tags_enabled" { 46 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 47 | type = bool 48 | default = true 49 | nullable = false 50 | } 51 | 52 | 53 | ################################################### 54 | # Resource Group 55 | ################################################### 56 | 57 | variable "resource_group_enabled" { 58 | description = "(Optional) Whether to create Resource Group to find and group AWS resources which are created by this module." 59 | type = bool 60 | default = true 61 | nullable = false 62 | } 63 | 64 | variable "resource_group_name" { 65 | description = "(Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`." 66 | type = string 67 | default = "" 68 | nullable = false 69 | } 70 | 71 | variable "resource_group_description" { 72 | description = "(Optional) The description of Resource Group." 73 | type = string 74 | default = "Managed by Terraform." 75 | nullable = false 76 | } 77 | -------------------------------------------------------------------------------- /modules/private-ca-issued-cert/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.27" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/private-zone/README.md: -------------------------------------------------------------------------------- 1 | # private-zone 2 | 3 | This module creates following resources. 4 | 5 | - `aws_route53_zone` 6 | - `aws_route53_zone_vpc_association` (optional) 7 | - `aws_route53_vpc_association_authorization` (optional) 8 | 9 | 10 | ## Requirements 11 | 12 | | Name | Version | 13 | |------|---------| 14 | | [terraform](#requirement\_terraform) | >= 1.6 | 15 | | [aws](#requirement\_aws) | >= 5.51 | 16 | 17 | ## Providers 18 | 19 | | Name | Version | 20 | |------|---------| 21 | | [aws](#provider\_aws) | 5.51.1 | 22 | 23 | ## Modules 24 | 25 | | Name | Source | Version | 26 | |------|--------|---------| 27 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 28 | 29 | ## Resources 30 | 31 | | Name | Type | 32 | |------|------| 33 | | [aws_route53_vpc_association_authorization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_vpc_association_authorization) | resource | 34 | | [aws_route53_zone.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource | 35 | | [aws_route53_zone_association.secondary](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone_association) | resource | 36 | 37 | ## Inputs 38 | 39 | | Name | Description | Type | Default | Required | 40 | |------|-------------|------|---------|:--------:| 41 | | [name](#input\_name) | (Required) The name of the Hosted Zone. | `string` | n/a | yes | 42 | | [primary\_vpc\_association](#input\_primary\_vpc\_association) | (Required) The Primary VPC to associate with the private hosted zone. `primary_vpc_association` block as defined below.
(Required) `vpc_id` - The ID of the VPC to associate with the private Hosted Zone.
(Optional) `region` - The region of the VPC to associate. Defaults to the region of the AWS provider. |
object({
region = optional(string)
vpc_id = string
})
| n/a | yes | 43 | | [cross\_account\_vpc\_association\_authorizations](#input\_cross\_account\_vpc\_association\_authorizations) | (Optional) A list of authorizations for a VPC in a peer account to be associated with the Route53 Hosted Zone. Each block of `cross_account_vpc_association_authorizations` as defined below.
(Required) `vpc_id` - The ID of the VPC to authorize for association with the private Hosted Zone.
(Optional) `region` - The region of the VPC to authorize. Defaults to the region of the AWS provider. |
list(object({
region = optional(string)
vpc_id = string
}))
| `[]` | no | 44 | | [description](#input\_description) | (Optional) A description for the Hosted Zone. | `string` | `"Managed by Terraform."` | no | 45 | | [force\_destroy](#input\_force\_destroy) | (Optional) Whether to destroy all records (possibly managed outside of Terraform) in the zone when destroying the zone. Defaults to `false`. | `bool` | `false` | no | 46 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 47 | | [namespace](#input\_namespace) | (Optional) The namespace of the Hosted Zone. Just for categorising overlapped hosted zones. Defaults to `default`. | `string` | `"default"` | no | 48 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 49 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 50 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 51 | | [secondary\_vpc\_associations](#input\_secondary\_vpc\_associations) | (Optional) A list of secondary VPCs to associate with the private hosted zone. Each
block of `secondary_vpc_associations` as defined below.
(Required) `vpc_id` - The ID of the VPC to associate with the private Hosted Zone.
(Optional) `region` - The region of the VPC to associate. Defaults to the region of the AWS provider. |
list(object({
region = optional(string)
vpc_id = string
}))
| `[]` | no | 52 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 53 | 54 | ## Outputs 55 | 56 | | Name | Description | 57 | |------|-------------| 58 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of the Hosted Zone. | 59 | | [cross\_account\_vpc\_association\_authorizations](#output\_cross\_account\_vpc\_association\_authorizations) | A list of authorized VPCs in cross accounts to associate with a private Hosted Zone. | 60 | | [description](#output\_description) | A description for the Hosted Zone. | 61 | | [id](#output\_id) | The Hosted Zone ID. This can be referenced by zone records. | 62 | | [name](#output\_name) | The name of the Hosted Zone. | 63 | | [name\_servers](#output\_name\_servers) | A list of name servers in associated (or default) delegation set. | 64 | | [namespace](#output\_namespace) | The namespace of the Hosted Zone. | 65 | | [primary\_name\_server](#output\_primary\_name\_server) | The Route 53 name server that created the SOA record. | 66 | | [vpc\_associations](#output\_vpc\_associations) | A list of associated VPCs with a private Hosted Zone. | 67 | 68 | -------------------------------------------------------------------------------- /modules/private-zone/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = "${var.namespace}/${var.name}" 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # Private Hosted Zone 20 | ################################################### 21 | 22 | # INFO: Not supported attributes 23 | # - `delegation_set_id` 24 | resource "aws_route53_zone" "private" { 25 | name = var.name 26 | comment = var.description 27 | force_destroy = var.force_destroy 28 | 29 | vpc { 30 | vpc_region = var.primary_vpc_association.region 31 | vpc_id = var.primary_vpc_association.vpc_id 32 | } 33 | 34 | tags = merge( 35 | { 36 | "Name" = local.metadata.name 37 | }, 38 | local.module_tags, 39 | var.tags, 40 | ) 41 | 42 | lifecycle { 43 | ignore_changes = [vpc] 44 | } 45 | } 46 | 47 | 48 | ################################################### 49 | # Authorizations for VPC Associations 50 | ################################################### 51 | 52 | resource "aws_route53_vpc_association_authorization" "this" { 53 | for_each = { 54 | for authorization in var.cross_account_vpc_association_authorizations : 55 | authorization.vpc_id => authorization 56 | } 57 | 58 | zone_id = aws_route53_zone.private.zone_id 59 | 60 | vpc_region = each.value.region 61 | vpc_id = each.value.vpc_id 62 | } 63 | 64 | 65 | ################################################### 66 | # VPC Associations 67 | ################################################### 68 | 69 | resource "aws_route53_zone_association" "secondary" { 70 | for_each = { 71 | for vpc_association in var.secondary_vpc_associations : 72 | vpc_association.vpc_id => vpc_association 73 | } 74 | 75 | zone_id = aws_route53_zone.private.zone_id 76 | 77 | vpc_region = each.value.region 78 | vpc_id = each.value.vpc_id 79 | 80 | depends_on = [ 81 | aws_route53_vpc_association_authorization.this 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /modules/private-zone/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The Amazon Resource Name (ARN) of the Hosted Zone." 3 | value = aws_route53_zone.private.arn 4 | } 5 | 6 | output "id" { 7 | description = "The Hosted Zone ID. This can be referenced by zone records." 8 | value = aws_route53_zone.private.id 9 | } 10 | 11 | output "name" { 12 | description = "The name of the Hosted Zone." 13 | value = aws_route53_zone.private.name 14 | } 15 | 16 | output "namespace" { 17 | description = "The namespace of the Hosted Zone." 18 | value = var.namespace 19 | } 20 | 21 | output "description" { 22 | description = "A description for the Hosted Zone." 23 | value = aws_route53_zone.private.comment 24 | } 25 | 26 | output "primary_name_server" { 27 | description = "The Route 53 name server that created the SOA record." 28 | value = aws_route53_zone.private.primary_name_server 29 | } 30 | 31 | output "name_servers" { 32 | description = "A list of name servers in associated (or default) delegation set." 33 | value = aws_route53_zone.private.name_servers 34 | } 35 | 36 | output "vpc_associations" { 37 | description = "A list of associated VPCs with a private Hosted Zone." 38 | value = [ 39 | for association in aws_route53_zone.private.vpc : { 40 | region = association.vpc_region 41 | vpc_id = association.vpc_id 42 | } 43 | ] 44 | } 45 | 46 | output "cross_account_vpc_association_authorizations" { 47 | description = "A list of authorized VPCs in cross accounts to associate with a private Hosted Zone." 48 | value = [ 49 | for authorization in values(aws_route53_vpc_association_authorization.this) : { 50 | region = authorization.vpc_region 51 | vpc_id = authorization.vpc_id 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /modules/private-zone/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/private-zone/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the Hosted Zone." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "namespace" { 8 | description = "(Optional) The namespace of the Hosted Zone. Just for categorising overlapped hosted zones. Defaults to `default`." 9 | type = string 10 | default = "default" 11 | nullable = false 12 | } 13 | 14 | variable "description" { 15 | description = "(Optional) A description for the Hosted Zone." 16 | type = string 17 | default = "Managed by Terraform." 18 | nullable = false 19 | } 20 | 21 | variable "force_destroy" { 22 | description = "(Optional) Whether to destroy all records (possibly managed outside of Terraform) in the zone when destroying the zone. Defaults to `false`." 23 | type = bool 24 | default = false 25 | nullable = false 26 | } 27 | 28 | variable "cross_account_vpc_association_authorizations" { 29 | description = < 10 | ## Requirements 11 | 12 | | Name | Version | 13 | |------|---------| 14 | | [terraform](#requirement\_terraform) | >= 1.6 | 15 | | [aws](#requirement\_aws) | >= 5.51 | 16 | 17 | ## Providers 18 | 19 | | Name | Version | 20 | |------|---------| 21 | | [aws](#provider\_aws) | 5.47.0 | 22 | 23 | ## Modules 24 | 25 | | Name | Source | Version | 26 | |------|--------|---------| 27 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 28 | 29 | ## Resources 30 | 31 | | Name | Type | 32 | |------|------| 33 | | [aws_route53_query_log.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_query_log) | resource | 34 | | [aws_route53_record.ns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | 35 | | [aws_route53_zone.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource | 36 | 37 | ## Inputs 38 | 39 | | Name | Description | Type | Default | Required | 40 | |------|-------------|------|---------|:--------:| 41 | | [name](#input\_name) | (Required) The name of the Hosted Zone. | `string` | n/a | yes | 42 | | [delegation\_set](#input\_delegation\_set) | (Optional) The ID of the reusable delegation set whose NS records you want to assign to the Hosted Zone. | `string` | `null` | no | 43 | | [description](#input\_description) | (Optional) A description for the Hosted Zone. | `string` | `"Managed by Terraform."` | no | 44 | | [force\_destroy](#input\_force\_destroy) | (Optional) Whether to destroy all records (possibly managed outside of Terraform) in the zone when destroying the zone. Defaults to `false`. | `bool` | `false` | no | 45 | | [logging](#input\_logging) | (Optional) The configuration of Route53 query logging. `logging` as defined below.
(Optional) `cloudwatch` - A configuration to define where the execution history events are logged. `cloudwatch` as defined below.
(Optional) `enabled` - Whether to enable or disable Route53 query logging.
(Optional) `log_group` - The ARN (Amazon Resource Name) of the CloudWatch Log Group. The CloudWatch log group must be in the `us-east-1` region. A permissive CloudWatch log resource policy must be in place. |
object({
cloudwatch = optional(object({
enabled = optional(bool, false)
log_group = optional(string, "")
}), {})
})
| `{}` | no | 46 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 47 | | [namespace](#input\_namespace) | (Optional) The namespace of the Hosted Zone. Just for categorising overlapped hosted zones. Defaults to `default`. | `string` | `"default"` | no | 48 | | [ns\_records](#input\_ns\_records) | (Optional) A map of `NS` records for the zone. Each key of the map is the record name. Each value of `ns_records` as defined below.
(Required) `values` - A list of the record values
(Optional) `ttl` - The TTL of the record. Defaults to `300`. |
map(object({
values = list(string)
ttl = optional(number, 300)
}))
| `{}` | no | 49 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 50 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 51 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 52 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 53 | 54 | ## Outputs 55 | 56 | | Name | Description | 57 | |------|-------------| 58 | | [arn](#output\_arn) | The Amazon Resource Name (ARN) of the Hosted Zone. | 59 | | [delegation\_set](#output\_delegation\_set) | The ID of the assigned delegation set. | 60 | | [description](#output\_description) | A description for the Hosted Zone. | 61 | | [id](#output\_id) | The Hosted Zone ID. This can be referenced by zone records. | 62 | | [logging](#output\_logging) | A configuration for query logging of the Route53 Hosted Zone.
`cloudwatch` - The configuration for Route53 query logs to CloudWatch Logs. | 63 | | [name](#output\_name) | The name of the Hosted Zone. | 64 | | [name\_servers](#output\_name\_servers) | A list of name servers in associated (or default) delegation set. | 65 | | [namespace](#output\_namespace) | The namespace of the Hosted Zone. | 66 | | [ns\_records](#output\_ns\_records) | A map of `NS` records for the zone. Each key of the map is the record name.
`values` - A list of the record values
`ttl` - The TTL of the record. | 67 | | [primary\_name\_server](#output\_primary\_name\_server) | The Route 53 name server that created the SOA record. | 68 | 69 | -------------------------------------------------------------------------------- /modules/public-zone/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = "${var.namespace}/${var.name}" 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # Public Hosted Zone 20 | ################################################### 21 | 22 | # INFO: Not supported attributes 23 | # - `vpc` 24 | resource "aws_route53_zone" "public" { 25 | name = var.name 26 | comment = var.description 27 | force_destroy = var.force_destroy 28 | 29 | delegation_set_id = var.delegation_set 30 | 31 | tags = merge( 32 | { 33 | "Name" = local.metadata.name 34 | }, 35 | local.module_tags, 36 | var.tags, 37 | ) 38 | } 39 | 40 | 41 | ################################################### 42 | # Query Logging for Public Hosted Zone 43 | ################################################### 44 | 45 | resource "aws_route53_query_log" "this" { 46 | count = var.logging.cloudwatch.enabled ? 1 : 0 47 | 48 | zone_id = aws_route53_zone.public.zone_id 49 | 50 | cloudwatch_log_group_arn = var.logging.cloudwatch.log_group 51 | } 52 | -------------------------------------------------------------------------------- /modules/public-zone/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The Amazon Resource Name (ARN) of the Hosted Zone." 3 | value = aws_route53_zone.public.arn 4 | } 5 | 6 | output "id" { 7 | description = "The Hosted Zone ID. This can be referenced by zone records." 8 | value = aws_route53_zone.public.id 9 | } 10 | 11 | output "name" { 12 | description = "The name of the Hosted Zone." 13 | value = aws_route53_zone.public.name 14 | } 15 | 16 | output "namespace" { 17 | description = "The namespace of the Hosted Zone." 18 | value = var.namespace 19 | } 20 | 21 | output "description" { 22 | description = "A description for the Hosted Zone." 23 | value = aws_route53_zone.public.comment 24 | } 25 | 26 | output "primary_name_server" { 27 | description = "The Route 53 name server that created the SOA record." 28 | value = aws_route53_zone.public.primary_name_server 29 | } 30 | 31 | output "name_servers" { 32 | description = "A list of name servers in associated (or default) delegation set." 33 | value = aws_route53_zone.public.name_servers 34 | } 35 | 36 | output "delegation_set" { 37 | description = "The ID of the assigned delegation set." 38 | value = aws_route53_zone.public.delegation_set_id 39 | } 40 | 41 | output "logging" { 42 | description = < 0 49 | log_group = one(aws_route53_query_log.this[*].cloudwatch_log_group_arn) 50 | } 51 | } 52 | } 53 | 54 | output "ns_records" { 55 | description = < { 63 | values = record.records 64 | ttl = record.ttl 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/public-zone/records.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # NS Records 3 | ################################################### 4 | 5 | resource "aws_route53_record" "ns" { 6 | for_each = var.ns_records 7 | 8 | zone_id = aws_route53_zone.public.zone_id 9 | 10 | type = "NS" 11 | name = each.key 12 | ttl = each.value.ttl 13 | 14 | records = each.value.values 15 | 16 | # To manage auto-created `NS` record for the Hosted Zone 17 | allow_overwrite = each.key == var.name 18 | 19 | lifecycle { 20 | precondition { 21 | condition = endswith(each.key, var.name) 22 | error_message = "The name of NS record must be end with the name of Hosted Zone." 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /modules/public-zone/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/public-zone/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the Hosted Zone." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "namespace" { 8 | description = "(Optional) The namespace of the Hosted Zone. Just for categorising overlapped hosted zones. Defaults to `default`." 9 | type = string 10 | default = "default" 11 | nullable = false 12 | } 13 | 14 | variable "description" { 15 | description = "(Optional) A description for the Hosted Zone." 16 | type = string 17 | default = "Managed by Terraform." 18 | nullable = false 19 | } 20 | 21 | variable "force_destroy" { 22 | description = "(Optional) Whether to destroy all records (possibly managed outside of Terraform) in the zone when destroying the zone. Defaults to `false`." 23 | type = bool 24 | default = false 25 | nullable = false 26 | } 27 | 28 | variable "delegation_set" { 29 | description = "(Optional) The ID of the reusable delegation set whose NS records you want to assign to the Hosted Zone." 30 | type = string 31 | default = null 32 | } 33 | 34 | variable "logging" { 35 | description = < 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.6 | 13 | | [aws](#requirement\_aws) | >= 5.51 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [aws](#provider\_aws) | 5.47.0 | 20 | 21 | ## Modules 22 | 23 | No modules. 24 | 25 | ## Resources 26 | 27 | | Name | Type | 28 | |------|------| 29 | | [aws_route53_record.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | 30 | | [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | 31 | 32 | ## Inputs 33 | 34 | | Name | Description | Type | Default | Required | 35 | |------|-------------|------|---------|:--------:| 36 | | [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 | 37 | | [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 | 38 | | [zone](#input\_zone) | (Required) The ID of the hosted zone to contain this record set. | `string` | n/a | yes | 39 | | [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 | 40 | | [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 list 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(list(string))
alias = optional(object({
name = string
zone = string
evaluate_target_health = optional(bool, true)
}))
}))
| `[]` | no | 41 | | [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 | 42 | | [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 | 43 | 44 | ## Outputs 45 | 46 | | Name | Description | 47 | |------|-------------| 48 | | [fqdn](#output\_fqdn) | The FQDN (Fully-qualified Domain Name) of the record. | 49 | | [name](#output\_name) | The name of the record. | 50 | | [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. | 51 | | [routing\_policy](#output\_routing\_policy) | The routing policy of the record set. | 52 | | [ttl](#output\_ttl) | The record cache time to live (TTL) in seconds. | 53 | | [type](#output\_type) | The DNS record type of the record set. | 54 | | [zone](#output\_zone) | The information for Hosted Zone of the record set. | 55 | 56 | -------------------------------------------------------------------------------- /modules/record-set/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_route53_zone" "this" { 2 | zone_id = var.zone 3 | } 4 | 5 | 6 | ################################################### 7 | # Record Set 8 | ################################################### 9 | 10 | # TODO: `health_check_id` - (Optional) The health check the record should be associated with. 11 | 12 | # 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. 13 | # TODO: `failover_routing_policy` - (Optional) A block indicating the routing behavior when associated health check fails. Conflicts with any other routing policy. Documented below. 14 | # 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. 15 | # 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. 16 | # 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. 17 | # TODO: `multivalue_answer_routing_policy` - (Optional) Set to true to indicate a multivalue answer routing policy. Conflicts with any other routing policy. 18 | # TODO: `weighted_routing_policy` - (Optional) A block indicating a weighted routing policy. Conflicts with any other routing policy. Documented below. 19 | resource "aws_route53_record" "this" { 20 | for_each = { 21 | for record in var.records : 22 | record.id => record 23 | } 24 | 25 | zone_id = var.zone 26 | name = var.name 27 | type = var.type 28 | ttl = each.value.alias != null ? null : var.ttl 29 | allow_overwrite = var.overwrite 30 | 31 | set_identifier = (var.routing_policy == "SIMPLE" 32 | ? null 33 | : each.value.id 34 | ) 35 | 36 | ## Record 37 | # Non-alias Record 38 | records = (var.type == "TXT" 39 | ? [ 40 | for v in each.value.value : 41 | join("\"\"", [ 42 | for idx in range(0, length(v), 255) : 43 | substr(v, idx, 255) 44 | ]) 45 | ] 46 | : each.value.value 47 | ) 48 | 49 | # Alias Record 50 | dynamic "alias" { 51 | for_each = each.value.alias != null ? [each.value.alias] : [] 52 | 53 | content { 54 | name = alias.value.name 55 | zone_id = alias.value.zone 56 | evaluate_target_health = alias.value.evaluate_target_health 57 | } 58 | } 59 | 60 | 61 | # lifecycle { 62 | # precondition { 63 | # condition = endswith(each.key, var.name) 64 | # error_message = "The name of NS record must be end with the name of Hosted Zone." 65 | # } 66 | # } 67 | } 68 | -------------------------------------------------------------------------------- /modules/record-set/outputs.tf: -------------------------------------------------------------------------------- 1 | output "zone" { 2 | description = "The information for Hosted Zone of the record set." 3 | value = { 4 | arn = data.aws_route53_zone.this.arn 5 | id = values(aws_route53_record.this)[0].zone_id 6 | name = data.aws_route53_zone.this.name 7 | } 8 | } 9 | 10 | output "name" { 11 | description = "The name of the record." 12 | value = values(aws_route53_record.this)[0].name 13 | } 14 | 15 | output "fqdn" { 16 | description = "The FQDN (Fully-qualified Domain Name) of the record." 17 | value = values(aws_route53_record.this)[0].fqdn 18 | } 19 | 20 | output "type" { 21 | description = "The DNS record type of the record set." 22 | value = values(aws_route53_record.this)[0].type 23 | } 24 | 25 | output "ttl" { 26 | description = "The record cache time to live (TTL) in seconds." 27 | value = values(aws_route53_record.this)[0].ttl 28 | } 29 | 30 | output "routing_policy" { 31 | description = "The routing policy of the record set." 32 | value = var.routing_policy 33 | } 34 | 35 | output "records" { 36 | description = < { 48 | id = record.set_identifier == "" ? null : record.set_identifier 49 | value = record.records 50 | alias = (one(record.alias) != null 51 | ? { 52 | name = record.alias[0].name 53 | zone = record.alias[0].zone_id 54 | evaluate_target_health = record.alias[0].evaluate_target_health 55 | } 56 | : null 57 | ) 58 | } 59 | } 60 | } 61 | 62 | # output "debug" { 63 | # value = { 64 | # for k, v in values(aws_route53_record.this)[0] : 65 | # k => v 66 | # if !contains(["zone_id", "type", "ttl", "name", "fqdn", "id", "allow_overwrite", "alias", "set_identifier", "records"], k) 67 | # } 68 | # } 69 | -------------------------------------------------------------------------------- /modules/record-set/variables.tf: -------------------------------------------------------------------------------- 1 | variable "zone" { 2 | description = "(Required) The ID of the hosted zone to contain this record set." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "name" { 8 | 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`." 9 | type = string 10 | nullable = false 11 | } 12 | 13 | variable "type" { 14 | description = "(Required) The DNS record type. Valid values are `A`, `AAAA`, `CAA`, `CNAME`, `DS`, `MX`, `NAPTR`, `NS`, `PTR`, `SOA`, `SPF`, `SRV` and `TXT`." 15 | type = string 16 | nullable = false 17 | 18 | validation { 19 | condition = contains(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"], var.type) 20 | error_message = "Valid values of `type` are `A`, `AAAA`, `CAA`, `CNAME`, `DS`, `MX`, `NAPTR`, `NS`, `PTR`, `SOA`, `SPF`, `SRV` and `TXT`." 21 | } 22 | } 23 | 24 | variable "ttl" { 25 | description = < 0, 39 | var.ttl <= 2147483647 40 | ]) 41 | error_message = "`ttl` must be greater than 0 and less than or equal to 2147483647." 42 | } 43 | } 44 | 45 | variable "overwrite" { 46 | 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`." 47 | type = bool 48 | default = false 49 | nullable = false 50 | } 51 | 52 | variable "routing_policy" { 53 | description = < 0 98 | error_message = "At least one record must be provided." 99 | } 100 | validation { 101 | condition = length(distinct(var.records[*].id)) == length(var.records) 102 | error_message = "Each record `id` must be unique in the record set." 103 | } 104 | validation { 105 | condition = alltrue([ 106 | for record in var.records : 107 | anytrue([ 108 | record.value != null && record.alias == null, 109 | record.value == null && record.alias != null, 110 | ]) 111 | ]) 112 | error_message = "Each record must have either `value` or `alias`." 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /modules/record-set/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.6" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.51" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/registered-domain/README.md: -------------------------------------------------------------------------------- 1 | # registered-domain 2 | 3 | This module creates following resources. 4 | 5 | - `aws_route53domains_registered_domain` 6 | 7 | 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.6 | 13 | | [aws](#requirement\_aws) | >= 5.41 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [aws](#provider\_aws) | 5.47.0 | 20 | 21 | ## Modules 22 | 23 | | Name | Source | Version | 24 | |------|--------|---------| 25 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |------|------| 31 | | [aws_route53domains_registered_domain.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53domains_registered_domain) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [name](#input\_name) | (Required) The name of the registred domain. | `string` | n/a | yes | 38 | | [admin\_contact](#input\_admin\_contact) | (Optional) The configuration of the domain administrative contact. `admin_contact` as defined below.
(Optional) `type` - Whether the contact is a person, company, association, or public organization. Valid values are `PERSON`, `COMPANY`, `ASSOCIATION`, or `PUBLIC_BODY`, `RESELLER`. Defaults to `PERSON`.
(Optional) `organization` - The name of the organization for contact types other than `PERSON`.
(Optional) `first_name` - First name of contact.
(Optional) `last_name` - Last name of contact.
(Optional) `email` - The email address of the contact.
(Optional) `phone` - The phone number of the contact. Phone number must be specified in the format `+[country dialing code].[number including any area code]`.
(Optional) `fax` - The fax number of the contact. Fax number must be specified in the format `+[country dialing code].[number including any area code]`.

(Optional) `country_code` - The ISO-3166 two-letter country code for the contact address.
(Optional) `state` - The state or province of the contact's city.
(Optional) `city` - The city of the contact's address.
(Optional) `address_line_1` - The first line of the contact address.
(Optional) `address_line_2` - The second line of the contact address, if any.
(Optional) `postal_code` - The zip or postal code of the contact's address.

(Optional) `extra_params` - A key-value map of parameters required by certain top-level domains.
(Optional) `privacy_protection_enabled` - Whether domain contact information is concealed from WHOIS queries. Defaults to `true`. |
object({
type = optional(string, "PERSON")
organization = optional(string)
first_name = optional(string)
last_name = optional(string)
email = optional(string)
phone = optional(string)
fax = optional(string)

country_code = optional(string)
state = optional(string)
city = optional(string)
address_line_1 = optional(string)
address_line_2 = optional(string)
postal_code = optional(string)

extra_params = optional(map(string), {})
privacy_protection_enabled = optional(bool, true)
})
| `{}` | no | 39 | | [auto\_renew\_enabled](#input\_auto\_renew\_enabled) | (Optional) Whether the domain registration is set to renew automatically. Defaults to `true`. | `bool` | `true` | no | 40 | | [billing\_contact](#input\_billing\_contact) | (Optional) The configuration of the domain billing contact. `billing_contact` as defined below.
(Optional) `type` - Whether the contact is a person, company, association, or public organization. Valid values are `PERSON`, `COMPANY`, `ASSOCIATION`, or `PUBLIC_BODY`, `RESELLER`. Defaults to `PERSON`.
(Optional) `organization` - The name of the organization for contact types other than `PERSON`.
(Optional) `first_name` - First name of contact.
(Optional) `last_name` - Last name of contact.
(Optional) `email` - The email address of the contact.
(Optional) `phone` - The phone number of the contact. Phone number must be specified in the format `+[country dialing code].[number including any area code]`.
(Optional) `fax` - The fax number of the contact. Fax number must be specified in the format `+[country dialing code].[number including any area code]`.

(Optional) `country_code` - The ISO-3166 two-letter country code for the contact address.
(Optional) `state` - The state or province of the contact's city.
(Optional) `city` - The city of the contact's address.
(Optional) `address_line_1` - The first line of the contact address.
(Optional) `address_line_2` - The second line of the contact address, if any.
(Optional) `postal_code` - The zip or postal code of the contact's address.

(Optional) `extra_params` - A key-value map of parameters required by certain top-level domains.
(Optional) `privacy_protection_enabled` - Whether domain contact information is concealed from WHOIS queries. Defaults to `true`. |
object({
type = optional(string, "PERSON")
organization = optional(string)
first_name = optional(string)
last_name = optional(string)
email = optional(string)
phone = optional(string)
fax = optional(string)

country_code = optional(string)
state = optional(string)
city = optional(string)
address_line_1 = optional(string)
address_line_2 = optional(string)
postal_code = optional(string)

extra_params = optional(map(string), {})
privacy_protection_enabled = optional(bool, true)
})
| `{}` | no | 41 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 42 | | [name\_servers](#input\_name\_servers) | (Optional) A set of fully qualified host name of name servers for the registered domain. | `set(string)` | `[]` | no | 43 | | [registrant\_contact](#input\_registrant\_contact) | (Optional) The configuration of the domain registrant contact. `registrant_contact` as defined below.
(Optional) `type` - Whether the contact is a person, company, association, or public organization. Valid values are `PERSON`, `COMPANY`, `ASSOCIATION`, or `PUBLIC_BODY`, `RESELLER`. Defaults to `PERSON`.
(Optional) `organization` - The name of the organization for contact types other than `PERSON`.
(Optional) `first_name` - First name of contact.
(Optional) `last_name` - Last name of contact.
(Optional) `email` - The email address of the contact.
(Optional) `phone` - The phone number of the contact. Phone number must be specified in the format `+[country dialing code].[number including any area code]`.
(Optional) `fax` - The fax number of the contact. Fax number must be specified in the format `+[country dialing code].[number including any area code]`.

(Optional) `country_code` - The ISO-3166 two-letter country code for the contact address.
(Optional) `state` - The state or province of the contact's city.
(Optional) `city` - The city of the contact's address.
(Optional) `address_line_1` - The first line of the contact address.
(Optional) `address_line_2` - The second line of the contact address, if any.
(Optional) `postal_code` - The zip or postal code of the contact's address.

(Optional) `extra_params` - A key-value map of parameters required by certain top-level domains.
(Optional) `privacy_protection_enabled` - Whether domain contact information is concealed from WHOIS queries. Defaults to `true`. |
object({
type = optional(string, "PERSON")
organization = optional(string)
first_name = optional(string)
last_name = optional(string)
email = optional(string)
phone = optional(string)
fax = optional(string)

country_code = optional(string)
state = optional(string)
city = optional(string)
address_line_1 = optional(string)
address_line_2 = optional(string)
postal_code = optional(string)

extra_params = optional(map(string), {})
privacy_protection_enabled = optional(bool, true)
})
| `{}` | no | 44 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 45 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 46 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 47 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 48 | | [technical\_contact](#input\_technical\_contact) | (Optional) The configuration of the domain technical contact. `technical_contact` as defined below.
(Optional) `type` - Whether the contact is a person, company, association, or public organization. Valid values are `PERSON`, `COMPANY`, `ASSOCIATION`, or `PUBLIC_BODY`, `RESELLER`. Defaults to `PERSON`.
(Optional) `organization` - The name of the organization for contact types other than `PERSON`.
(Optional) `first_name` - First name of contact.
(Optional) `last_name` - Last name of contact.
(Optional) `email` - The email address of the contact.
(Optional) `phone` - The phone number of the contact. Phone number must be specified in the format `+[country dialing code].[number including any area code]`.
(Optional) `fax` - The fax number of the contact. Fax number must be specified in the format `+[country dialing code].[number including any area code]`.

(Optional) `country_code` - The ISO-3166 two-letter country code for the contact address.
(Optional) `state` - The state or province of the contact's city.
(Optional) `city` - The city of the contact's address.
(Optional) `address_line_1` - The first line of the contact address.
(Optional) `address_line_2` - The second line of the contact address, if any.
(Optional) `postal_code` - The zip or postal code of the contact's address.

(Optional) `extra_params` - A key-value map of parameters required by certain top-level domains.
(Optional) `privacy_protection_enabled` - Whether domain contact information is concealed from WHOIS queries. Defaults to `true`. |
object({
type = optional(string, "PERSON")
organization = optional(string)
first_name = optional(string)
last_name = optional(string)
email = optional(string)
phone = optional(string)
fax = optional(string)

country_code = optional(string)
state = optional(string)
city = optional(string)
address_line_1 = optional(string)
address_line_2 = optional(string)
postal_code = optional(string)

extra_params = optional(map(string), {})
privacy_protection_enabled = optional(bool, true)
})
| `{}` | no | 49 | | [transfer\_lock\_enabled](#input\_transfer\_lock\_enabled) | (Optional) Whether the domain is locked for transfer. Defaults to `true`. Following top-level domains do not support transfer lock: `.fi`. | `bool` | `true` | no | 50 | 51 | ## Outputs 52 | 53 | | Name | Description | 54 | |------|-------------| 55 | | [abuse\_contact](#output\_abuse\_contact) | The contact informations to report incorrect contact information for a domain, to report that the domain is being used to send spam, to report that someone is cybersquatting on a domain name, or report some other type of abuse. | 56 | | [admin\_contact](#output\_admin\_contact) | The configuration of the domain administrative contact. | 57 | | [auto\_renew\_enabled](#output\_auto\_renew\_enabled) | Whether the domain registration is set to renew automatically. | 58 | | [billing\_contact](#output\_billing\_contact) | The configuration of the domain billing contact. | 59 | | [created\_at](#output\_created\_at) | The date when the domain was created as found in the response to a WHOIS query. | 60 | | [expired\_at](#output\_expired\_at) | The date when the registration for the domain is set to expire. | 61 | | [id](#output\_id) | The ID of the registered domain. | 62 | | [name](#output\_name) | The name of the registered domain. | 63 | | [name\_servers](#output\_name\_servers) | A list of name servers for the registered domain. | 64 | | [registrant\_contact](#output\_registrant\_contact) | The configuration of the domain registrant contact. | 65 | | [registrar](#output\_registrar) | The informations about the registrar for the domain. | 66 | | [reseller](#output\_reseller) | Reseller of the domain. | 67 | | [status\_codes](#output\_status\_codes) | A list of domain name status codes. | 68 | | [technical\_contact](#output\_technical\_contact) | The configuration of the domain technical contact. | 69 | | [transfer\_lock\_enabled](#output\_transfer\_lock\_enabled) | Whether the domain is locked for transfer. | 70 | | [updated\_at](#output\_updated\_at) | The last updated date of the domain as found in the response to a WHOIS query. | 71 | | [whois\_server](#output\_whois\_server) | The fully qualified name of the WHOIS server that can answer the WHOIS query for the domain. | 72 | 73 | -------------------------------------------------------------------------------- /modules/registered-domain/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | locals { 18 | transfer_lock_unsupported_tlds = [".fi"] 19 | } 20 | 21 | ################################################### 22 | # Registred Domain in Route53 Registry 23 | ################################################### 24 | 25 | # INFO: Not supported attributes 26 | # - `name_server.glue_ips` 27 | resource "aws_route53domains_registered_domain" "this" { 28 | domain_name = var.name 29 | 30 | auto_renew = var.auto_renew_enabled 31 | transfer_lock = (anytrue([ 32 | for tld in local.transfer_lock_unsupported_tlds : 33 | endswith(var.name, tld)] 34 | ) ? false : var.transfer_lock_enabled) 35 | 36 | 37 | ## Name Servers 38 | dynamic "name_server" { 39 | for_each = var.name_servers 40 | 41 | content { 42 | name = name_server.value 43 | } 44 | } 45 | 46 | 47 | ## Privacy Protection 48 | admin_privacy = var.admin_contact.privacy_protection_enabled 49 | # billing_privacy = var.privacy_protection_enabled 50 | registrant_privacy = var.registrant_contact.privacy_protection_enabled 51 | tech_privacy = var.technical_contact.privacy_protection_enabled 52 | 53 | 54 | ## Contacts 55 | admin_contact { 56 | contact_type = var.admin_contact.type 57 | organization_name = var.admin_contact.organization 58 | first_name = var.admin_contact.first_name 59 | last_name = var.admin_contact.last_name 60 | email = var.admin_contact.email 61 | phone_number = var.admin_contact.phone 62 | fax = var.admin_contact.fax 63 | 64 | country_code = var.admin_contact.country_code 65 | state = var.admin_contact.state 66 | city = var.admin_contact.city 67 | address_line_1 = var.admin_contact.address_line_1 68 | address_line_2 = var.admin_contact.address_line_2 69 | zip_code = var.admin_contact.postal_code 70 | 71 | extra_params = var.admin_contact.extra_params 72 | } 73 | billing_contact { 74 | contact_type = var.billing_contact.type 75 | organization_name = var.billing_contact.organization 76 | first_name = var.billing_contact.first_name 77 | last_name = var.billing_contact.last_name 78 | email = var.billing_contact.email 79 | phone_number = var.billing_contact.phone 80 | fax = var.billing_contact.fax 81 | 82 | country_code = var.billing_contact.country_code 83 | state = var.billing_contact.state 84 | city = var.billing_contact.city 85 | address_line_1 = var.billing_contact.address_line_1 86 | address_line_2 = var.billing_contact.address_line_2 87 | zip_code = var.billing_contact.postal_code 88 | 89 | extra_params = var.admin_contact.extra_params 90 | } 91 | registrant_contact { 92 | contact_type = var.registrant_contact.type 93 | organization_name = var.registrant_contact.organization 94 | first_name = var.registrant_contact.first_name 95 | last_name = var.registrant_contact.last_name 96 | email = var.registrant_contact.email 97 | phone_number = var.registrant_contact.phone 98 | fax = var.registrant_contact.fax 99 | 100 | country_code = var.registrant_contact.country_code 101 | state = var.registrant_contact.state 102 | city = var.registrant_contact.city 103 | address_line_1 = var.registrant_contact.address_line_1 104 | address_line_2 = var.registrant_contact.address_line_2 105 | zip_code = var.registrant_contact.postal_code 106 | 107 | extra_params = var.admin_contact.extra_params 108 | } 109 | tech_contact { 110 | contact_type = var.technical_contact.type 111 | organization_name = var.technical_contact.organization 112 | first_name = var.technical_contact.first_name 113 | last_name = var.technical_contact.last_name 114 | email = var.technical_contact.email 115 | phone_number = var.technical_contact.phone 116 | fax = var.technical_contact.fax 117 | 118 | country_code = var.technical_contact.country_code 119 | state = var.technical_contact.state 120 | city = var.technical_contact.city 121 | address_line_1 = var.technical_contact.address_line_1 122 | address_line_2 = var.technical_contact.address_line_2 123 | zip_code = var.technical_contact.postal_code 124 | 125 | extra_params = var.admin_contact.extra_params 126 | } 127 | 128 | 129 | tags = merge( 130 | { 131 | "Name" = local.metadata.name 132 | }, 133 | local.module_tags, 134 | var.tags, 135 | ) 136 | } 137 | -------------------------------------------------------------------------------- /modules/registered-domain/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of the registered domain." 3 | value = aws_route53domains_registered_domain.this.id 4 | } 5 | 6 | output "name" { 7 | description = "The name of the registered domain." 8 | value = aws_route53domains_registered_domain.this.domain_name 9 | } 10 | 11 | output "auto_renew_enabled" { 12 | description = "Whether the domain registration is set to renew automatically." 13 | value = aws_route53domains_registered_domain.this.auto_renew 14 | } 15 | 16 | output "transfer_lock_enabled" { 17 | description = "Whether the domain is locked for transfer." 18 | value = aws_route53domains_registered_domain.this.transfer_lock 19 | } 20 | 21 | output "name_servers" { 22 | description = "A list of name servers for the registered domain." 23 | value = aws_route53domains_registered_domain.this.name_server[*].name 24 | } 25 | 26 | output "admin_contact" { 27 | description = "The configuration of the domain administrative contact." 28 | value = { 29 | type = one(aws_route53domains_registered_domain.this.admin_contact[*].contact_type) 30 | organization = one(aws_route53domains_registered_domain.this.admin_contact[*].organization_name) 31 | first_name = one(aws_route53domains_registered_domain.this.admin_contact[*].first_name) 32 | last_name = one(aws_route53domains_registered_domain.this.admin_contact[*].last_name) 33 | email = one(aws_route53domains_registered_domain.this.admin_contact[*].email) 34 | phone = one(aws_route53domains_registered_domain.this.admin_contact[*].phone_number) 35 | fax = one(aws_route53domains_registered_domain.this.admin_contact[*].fax) 36 | 37 | country_code = one(aws_route53domains_registered_domain.this.admin_contact[*].country_code) 38 | state = one(aws_route53domains_registered_domain.this.admin_contact[*].state) 39 | city = one(aws_route53domains_registered_domain.this.admin_contact[*].city) 40 | address_line_1 = one(aws_route53domains_registered_domain.this.admin_contact[*].address_line_1) 41 | address_line_2 = one(aws_route53domains_registered_domain.this.admin_contact[*].address_line_2) 42 | postal_code = one(aws_route53domains_registered_domain.this.admin_contact[*].zip_code) 43 | 44 | extra_prams = one(aws_route53domains_registered_domain.this.admin_contact[*].extra_params) 45 | privacy_protection_enabled = aws_route53domains_registered_domain.this.admin_privacy 46 | } 47 | } 48 | 49 | output "billing_contact" { 50 | description = "The configuration of the domain billing contact." 51 | value = { 52 | type = one(aws_route53domains_registered_domain.this.billing_contact[*].contact_type) 53 | organization = one(aws_route53domains_registered_domain.this.billing_contact[*].organization_name) 54 | first_name = one(aws_route53domains_registered_domain.this.billing_contact[*].first_name) 55 | last_name = one(aws_route53domains_registered_domain.this.billing_contact[*].last_name) 56 | email = one(aws_route53domains_registered_domain.this.billing_contact[*].email) 57 | phone = one(aws_route53domains_registered_domain.this.billing_contact[*].phone_number) 58 | fax = one(aws_route53domains_registered_domain.this.billing_contact[*].fax) 59 | 60 | country_code = one(aws_route53domains_registered_domain.this.billing_contact[*].country_code) 61 | state = one(aws_route53domains_registered_domain.this.billing_contact[*].state) 62 | city = one(aws_route53domains_registered_domain.this.billing_contact[*].city) 63 | address_line_1 = one(aws_route53domains_registered_domain.this.billing_contact[*].address_line_1) 64 | address_line_2 = one(aws_route53domains_registered_domain.this.billing_contact[*].address_line_2) 65 | postal_code = one(aws_route53domains_registered_domain.this.billing_contact[*].zip_code) 66 | 67 | extra_prams = one(aws_route53domains_registered_domain.this.billing_contact[*].extra_params) 68 | privacy_protection_enabled = aws_route53domains_registered_domain.this.billing_privacy 69 | } 70 | } 71 | 72 | output "registrant_contact" { 73 | description = "The configuration of the domain registrant contact." 74 | value = { 75 | type = one(aws_route53domains_registered_domain.this.registrant_contact[*].contact_type) 76 | organization = one(aws_route53domains_registered_domain.this.registrant_contact[*].organization_name) 77 | first_name = one(aws_route53domains_registered_domain.this.registrant_contact[*].first_name) 78 | last_name = one(aws_route53domains_registered_domain.this.registrant_contact[*].last_name) 79 | email = one(aws_route53domains_registered_domain.this.registrant_contact[*].email) 80 | phone = one(aws_route53domains_registered_domain.this.registrant_contact[*].phone_number) 81 | fax = one(aws_route53domains_registered_domain.this.registrant_contact[*].fax) 82 | 83 | country_code = one(aws_route53domains_registered_domain.this.registrant_contact[*].country_code) 84 | state = one(aws_route53domains_registered_domain.this.registrant_contact[*].state) 85 | city = one(aws_route53domains_registered_domain.this.registrant_contact[*].city) 86 | address_line_1 = one(aws_route53domains_registered_domain.this.registrant_contact[*].address_line_1) 87 | address_line_2 = one(aws_route53domains_registered_domain.this.registrant_contact[*].address_line_2) 88 | postal_code = one(aws_route53domains_registered_domain.this.registrant_contact[*].zip_code) 89 | 90 | extra_prams = one(aws_route53domains_registered_domain.this.registrant_contact[*].extra_params) 91 | privacy_protection_enabled = aws_route53domains_registered_domain.this.registrant_privacy 92 | } 93 | } 94 | 95 | output "technical_contact" { 96 | description = "The configuration of the domain technical contact." 97 | value = { 98 | type = one(aws_route53domains_registered_domain.this.tech_contact[*].contact_type) 99 | organization = one(aws_route53domains_registered_domain.this.tech_contact[*].organization_name) 100 | first_name = one(aws_route53domains_registered_domain.this.tech_contact[*].first_name) 101 | last_name = one(aws_route53domains_registered_domain.this.tech_contact[*].last_name) 102 | email = one(aws_route53domains_registered_domain.this.tech_contact[*].email) 103 | phone = one(aws_route53domains_registered_domain.this.tech_contact[*].phone_number) 104 | fax = one(aws_route53domains_registered_domain.this.tech_contact[*].fax) 105 | 106 | country_code = one(aws_route53domains_registered_domain.this.tech_contact[*].country_code) 107 | state = one(aws_route53domains_registered_domain.this.tech_contact[*].state) 108 | city = one(aws_route53domains_registered_domain.this.tech_contact[*].city) 109 | address_line_1 = one(aws_route53domains_registered_domain.this.tech_contact[*].address_line_1) 110 | address_line_2 = one(aws_route53domains_registered_domain.this.tech_contact[*].address_line_2) 111 | postal_code = one(aws_route53domains_registered_domain.this.tech_contact[*].zip_code) 112 | 113 | extra_prams = one(aws_route53domains_registered_domain.this.tech_contact[*].extra_params) 114 | privacy_protection_enabled = aws_route53domains_registered_domain.this.tech_privacy 115 | } 116 | } 117 | 118 | output "abuse_contact" { 119 | description = "The contact informations to report incorrect contact information for a domain, to report that the domain is being used to send spam, to report that someone is cybersquatting on a domain name, or report some other type of abuse." 120 | value = { 121 | email = aws_route53domains_registered_domain.this.abuse_contact_email 122 | phoen = aws_route53domains_registered_domain.this.abuse_contact_phone 123 | } 124 | } 125 | 126 | output "registrar" { 127 | description = "The informations about the registrar for the domain." 128 | value = { 129 | name = aws_route53domains_registered_domain.this.registrar_name 130 | url = aws_route53domains_registered_domain.this.registrar_url 131 | } 132 | } 133 | 134 | output "reseller" { 135 | description = "Reseller of the domain." 136 | value = aws_route53domains_registered_domain.this.reseller 137 | } 138 | 139 | output "whois_server" { 140 | description = "The fully qualified name of the WHOIS server that can answer the WHOIS query for the domain." 141 | value = aws_route53domains_registered_domain.this.whois_server 142 | } 143 | 144 | output "status_codes" { 145 | description = "A list of domain name status codes." 146 | value = aws_route53domains_registered_domain.this.status_list 147 | } 148 | 149 | output "created_at" { 150 | description = "The date when the domain was created as found in the response to a WHOIS query." 151 | value = aws_route53domains_registered_domain.this.creation_date 152 | } 153 | 154 | output "expired_at" { 155 | description = "The date when the registration for the domain is set to expire." 156 | value = aws_route53domains_registered_domain.this.expiration_date 157 | } 158 | 159 | output "updated_at" { 160 | description = "The last updated date of the domain as found in the response to a WHOIS query." 161 | value = aws_route53domains_registered_domain.this.updated_date 162 | } 163 | 164 | # output "debug" { 165 | # value = { 166 | # for k, v in aws_route53domains_registered_domain.this : 167 | # k => v 168 | # if !contains(["id", "domain_name", "tags", "tags_all", "auto_renew", "transfer_lock", "timeouts", "creation_date", "expiration_date", "updated_date", "registrar_name", "registrar_url", "whois_server", "reseller", "abuse_contact_email", "abuse_contact_phone", "admin_contact", "registrant_contact", "tech_contact", "admin_privacy", "registrant_privacy", "tech_privacy", "billing_contact", "billing_privacy", "status_list"], k) 169 | # } 170 | # } 171 | -------------------------------------------------------------------------------- /modules/registered-domain/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/registered-domain/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.6" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.41" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/README.md: -------------------------------------------------------------------------------- 1 | # resolver-inbound-endpoint 2 | 3 | This module creates following resources. 4 | 5 | - `aws_route53_resolver_endpoint` 6 | - `aws_security_group` 7 | - `aws_security_group_rule` 8 | 9 | 10 | ## Requirements 11 | 12 | | Name | Version | 13 | |------|---------| 14 | | [terraform](#requirement\_terraform) | >= 1.10 | 15 | | [assert](#requirement\_assert) | >= 0.15 | 16 | | [aws](#requirement\_aws) | >= 5.72 | 17 | 18 | ## Providers 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [aws](#provider\_aws) | 5.94.1 | 23 | 24 | ## Modules 25 | 26 | | Name | Source | Version | 27 | |------|--------|---------| 28 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 29 | | [security\_group](#module\_security\_group) | tedilabs/network/aws//modules/security-group | ~> 0.32.0 | 30 | 31 | ## Resources 32 | 33 | | Name | Type | 34 | |------|------| 35 | | [aws_route53_resolver_endpoint.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_endpoint) | resource | 36 | | [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | 37 | 38 | ## Inputs 39 | 40 | | Name | Description | Type | Default | Required | 41 | |------|-------------|------|---------|:--------:| 42 | | [ip\_allocations](#input\_ip\_allocations) | (Optional) The configuration for IP allocations of the Route53 Resolver Inbound Endpoint. Select at least two Availability Zone and one subnet for each zone. Each item of `ip_allocations` block as defined below.
(Required) `subnet` - The ID of the subnet that contains the IP address. You can specify only one subnet per Availability Zone.
(Optional) `ipv4_address` - IPv4 address from the provided subnet. Defaults to be randomly configured by Amazon.
(Optional) `ipv6_address` - IPv6 address from the provided subnet. Defaults to be randomly configured by Amazon. |
list(object({
subnet = string
ipv4_address = optional(string)
ipv6_address = optional(string)
}))
| n/a | yes | 43 | | [name](#input\_name) | (Required) The name of the Route53 Resolver Inbound Endpoint. | `string` | n/a | yes | 44 | | [default\_security\_group](#input\_default\_security\_group) | (Optional) The configuration of the default security group for the Route53 Resolver Inbound Endpoint. `default_security_group` block as defined below.
(Optional) `enabled` - Whether to use the default security group. Defaults to `true`.
(Optional) `name` - The name of the default security group. If not provided, the Route53 Resolver Inbound Endpoint name is used for the name of security group.
(Optional) `description` - The description of the default security group.
(Optional) `ingress_rules` - A list of ingress rules in a security group. Defaults to `[]`. Each block of `ingress_rules` as defined below.
(Required) `id` - The ID of the ingress rule. This value is only used internally within Terraform code.
(Optional) `description` - The description of the rule.
(Required) `protocol` - The protocol to match. Note that if `protocol` is set to `-1`, it translates to all protocols, all port ranges, and `from_port` and `to_port` values should not be defined.
(Required) `from_port` - The start of port range for the protocols.
(Required) `to_port` - The end of port range for the protocols.
(Optional) `ipv4_cidrs` - The IPv4 network ranges to allow, in CIDR notation.
(Optional) `ipv6_cidrs` - The IPv6 network ranges to allow, in CIDR notation.
(Optional) `prefix_lists` - The prefix list IDs to allow.
(Optional) `security_groups` - The source security group IDs to allow.
(Optional) `self` - Whether the security group itself will be added as a source to this ingress rule.
(Optional) `egress_rules` - A list of egress rules in a security group. Defaults to `[]`. Each block of `egress_rules` as defined below.
(Required) `id` - The ID of the egress rule. This value is only used internally within Terraform code.
(Optional) `description` - The description of the rule.
(Required) `protocol` - The protocol to match. Note that if `protocol` is set to `-1`, it translates to all protocols, all port ranges, and `from_port` and `to_port` values should not be defined.
(Required) `from_port` - The start of port range for the protocols.
(Required) `to_port` - The end of port range for the protocols.
(Optional) `ipv4_cidrs` - The IPv4 network ranges to allow, in CIDR notation.
(Optional) `ipv6_cidrs` - The IPv6 network ranges to allow, in CIDR notation.
(Optional) `prefix_lists` - The prefix list IDs to allow.
(Optional) `security_groups` - The source security group IDs to allow.
(Optional) `self` - Whether the security group itself will be added as a source to this ingress rule.
(Optional) `ingress_ipv4_cidrs` - A list of IPv4 CIDR ranges to allow to query to the endpoint. Defaults to `[]`."
(Optional) `istener_ingress_ipv6_cidrs` - A list of IPv6 CIDR ranges to allow to query to the endpoint. Defaults to `[]`."
(Optional) `ingress_prefix_lists` - A list of prefix list IDs for AWS services to allow to query to the endpoint. Defaults to `[]`."
(Optional) `ingress_security_groups` - A list of security group IDs to allow to query to the endpoint. Defaults to `[]`." |
object({
enabled = optional(bool, true)
name = optional(string)
description = optional(string, "Managed by Terraform.")
ingress_rules = optional(
list(object({
id = string
description = optional(string, "Managed by Terraform.")
protocol = string
from_port = number
to_port = number
ipv4_cidrs = optional(list(string), [])
ipv6_cidrs = optional(list(string), [])
prefix_lists = optional(list(string), [])
security_groups = optional(list(string), [])
self = optional(bool, false)
})),
[]
)
egress_rules = optional(
list(object({
id = string
description = optional(string, "Managed by Terraform.")
protocol = string
from_port = number
to_port = number
ipv4_cidrs = optional(list(string), [])
ipv6_cidrs = optional(list(string), [])
prefix_lists = optional(list(string), [])
security_groups = optional(list(string), [])
self = optional(bool, false)
})),
[]
)
ingress_ipv4_cidrs = optional(list(string), [])
ingress_ipv6_cidrs = optional(list(string), [])
ingress_prefix_lists = optional(list(string), [])
ingress_security_groups = optional(list(string), [])
})
| `{}` | no | 45 | | [ip\_address\_type](#input\_ip\_address\_type) | (Optional) The type of IP addresses used by the Route53 Resolver Inbound Endpoint. Valid values are `IPV4`, `IPV6`, `DUALSTACK`. Defaults to `IPV4`. | `string` | `"IPV4"` | no | 46 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 47 | | [protocols](#input\_protocols) | (Optional) A set of protocols to use for the Route53 Resolver Inbound Endpoint. The protocols determine how data is transmitted to this endpoint. Valid values are `DoH`, `Do53`, or `DoH-FIPS`. Defaults to `["Do53"]`.
`Do53`: DNS over port 53. The data is relayed by using the Route 53 Resolver without additional encryption. While the data cannot be read by external parties, it can be viewed within the AWS networks. Uses either UDP or TCP to send the packets. Do53 is primarily used for traffic within and between Amazon VPCs.
`DoH`: The data is transmitted over an encrypted HTTPS session. DoH adds an added level of security where data can't be decrypted by unauthorized users, and cannot be read by anyone except the intended recipient.
`DoH-FIPS`: The data is transmitted over an encrypted HTTPS session that is compliant with the FIPS 140-2 cryptographic standard. Supported for inbound endpoints only. For more information, see FIPS PUB 140-2 | `set(string)` |
[
"Do53"
]
| no | 48 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 49 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 50 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 51 | | [security\_groups](#input\_security\_groups) | (Optional) A list of security group IDs to assign to the Route53 Resolver Inbound Endpoint. | `list(string)` | `[]` | no | 52 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 53 | 54 | ## Outputs 55 | 56 | | Name | Description | 57 | |------|-------------| 58 | | [arn](#output\_arn) | The ARN of the Route53 Resolver Endpoint. | 59 | | [default\_security\_group](#output\_default\_security\_group) | The default security group ID of the Route53 Resolver Endpoint. | 60 | | [direction](#output\_direction) | The direction of DNS queries to or from the Route 53 Resolver endpoint. | 61 | | [id](#output\_id) | The ID of the Route53 Resolver Endpoint. | 62 | | [ip\_address\_type](#output\_ip\_address\_type) | The type of IP addresses used by the Route53 Resolver Inbound Endpoint. | 63 | | [ip\_allocations](#output\_ip\_allocations) | The configuration for IP allocations of the Route53 Resolver Inbound Endpoint. | 64 | | [ipv4\_addresses](#output\_ipv4\_addresses) | A set of IPv4 addresses in your VPC that you want DNS queries to pass through on the way from your network to your VPCs. | 65 | | [ipv6\_addresses](#output\_ipv6\_addresses) | A set of IPv6 addresses in your VPC that you want DNS queries to pass through on the way from your network to your VPCs. | 66 | | [name](#output\_name) | The name of the Route53 Resolver Endpoint. | 67 | | [protocols](#output\_protocols) | A set of protocols to use for the Route53 Resolver Inbound Endpoint. | 68 | | [security\_groups](#output\_security\_groups) | A set of security group IDs which is assigned to the Route53 Resolver Endpoint. | 69 | | [subnets](#output\_subnets) | A set of the ID of subnets that IP addresses of resolver endpoint are allocated in. | 70 | | [vpc\_id](#output\_vpc\_id) | The ID of the VPC that you want to create the resolver endpoint in. | 71 | 72 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # Inbound Endpoint for Route53 Resolver 20 | ################################################### 21 | 22 | resource "aws_route53_resolver_endpoint" "this" { 23 | direction = "INBOUND" 24 | name = local.metadata.name 25 | 26 | protocols = var.protocols 27 | security_group_ids = local.security_groups 28 | 29 | resolver_endpoint_type = var.ip_address_type 30 | dynamic "ip_address" { 31 | for_each = var.ip_allocations 32 | 33 | content { 34 | subnet_id = ip_address.value.subnet 35 | ip = ip_address.value.ipv4_address 36 | ipv6 = ip_address.value.ipv6_address 37 | } 38 | } 39 | 40 | tags = merge( 41 | { 42 | "Name" = local.metadata.name 43 | }, 44 | local.module_tags, 45 | var.tags, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of the Route53 Resolver Endpoint." 3 | value = aws_route53_resolver_endpoint.this.id 4 | } 5 | 6 | output "arn" { 7 | description = "The ARN of the Route53 Resolver Endpoint." 8 | value = aws_route53_resolver_endpoint.this.arn 9 | } 10 | 11 | output "name" { 12 | description = "The name of the Route53 Resolver Endpoint." 13 | value = aws_route53_resolver_endpoint.this.name 14 | } 15 | 16 | output "direction" { 17 | description = "The direction of DNS queries to or from the Route 53 Resolver endpoint." 18 | value = aws_route53_resolver_endpoint.this.direction 19 | } 20 | 21 | output "protocols" { 22 | description = "A set of protocols to use for the Route53 Resolver Inbound Endpoint." 23 | value = aws_route53_resolver_endpoint.this.protocols 24 | } 25 | 26 | output "vpc_id" { 27 | description = "The ID of the VPC that you want to create the resolver endpoint in." 28 | value = aws_route53_resolver_endpoint.this.host_vpc_id 29 | } 30 | 31 | output "ip_address_type" { 32 | description = "The type of IP addresses used by the Route53 Resolver Inbound Endpoint." 33 | value = aws_route53_resolver_endpoint.this.resolver_endpoint_type 34 | } 35 | 36 | output "ip_allocations" { 37 | description = "The configuration for IP allocations of the Route53 Resolver Inbound Endpoint." 38 | value = { 39 | for ip_allocation in aws_route53_resolver_endpoint.this.ip_address : 40 | data.aws_subnet.this[ip_allocation.subnet_id].availability_zone_id => { 41 | az_id = data.aws_subnet.this[ip_allocation.subnet_id].availability_zone_id 42 | subnet = ip_allocation.subnet_id 43 | id = ip_allocation.ip_id 44 | ipv4_address = ip_allocation.ip 45 | ipv6_address = ip_allocation.ipv6 46 | } 47 | } 48 | } 49 | 50 | output "ipv4_addresses" { 51 | description = "A set of IPv4 addresses in your VPC that you want DNS queries to pass through on the way from your network to your VPCs." 52 | value = toset(aws_route53_resolver_endpoint.this.ip_address[*].ip) 53 | } 54 | 55 | output "ipv6_addresses" { 56 | description = "A set of IPv6 addresses in your VPC that you want DNS queries to pass through on the way from your network to your VPCs." 57 | value = toset(aws_route53_resolver_endpoint.this.ip_address[*].ipv6) 58 | } 59 | 60 | output "default_security_group" { 61 | description = "The default security group ID of the Route53 Resolver Endpoint." 62 | value = one(module.security_group[*].id) 63 | } 64 | 65 | output "security_groups" { 66 | description = "A set of security group IDs which is assigned to the Route53 Resolver Endpoint." 67 | value = aws_route53_resolver_endpoint.this.security_group_ids 68 | } 69 | 70 | output "subnets" { 71 | description = "A set of the ID of subnets that IP addresses of resolver endpoint are allocated in." 72 | value = toset(aws_route53_resolver_endpoint.this.ip_address[*].subnet_id) 73 | } 74 | 75 | # output "debug" { 76 | # value = { 77 | # for k, v in aws_route53_resolver_endpoint.this : 78 | # k => v 79 | # if !contains(["id", "arn", "direction", "name", "host_vpc_id", "security_group_ids", "protocols", "resolver_endpoint_type", "tags", "tags_all", "ip_address", "timeouts"], k) 80 | # } 81 | # } 82 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/security-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | security_groups = concat( 3 | (var.default_security_group.enabled 4 | ? module.security_group[*].id 5 | : [] 6 | ), 7 | var.security_groups, 8 | ) 9 | } 10 | 11 | data "aws_subnet" "this" { 12 | for_each = toset(var.ip_allocations[*].subnet) 13 | 14 | id = each.value 15 | } 16 | 17 | 18 | ################################################### 19 | # Security Group 20 | ################################################### 21 | 22 | module "security_group" { 23 | source = "tedilabs/network/aws//modules/security-group" 24 | version = "~> 0.32.0" 25 | 26 | count = var.default_security_group.enabled ? 1 : 0 27 | 28 | name = coalesce(var.default_security_group.name, local.metadata.name) 29 | description = var.default_security_group.description 30 | vpc_id = values(data.aws_subnet.this)[0].vpc_id 31 | 32 | ingress_rules = concat( 33 | var.default_security_group.ingress_rules, 34 | [ 35 | for protocol in ["tcp", "udp"] : { 36 | id = "dns-${protocol}" 37 | description = "Allow to query to Route53 Resolver Inbound Endpoint." 38 | protocol = protocol 39 | from_port = 53 40 | to_port = 53 41 | 42 | ipv4_cidrs = var.default_security_group.ingress_ipv4_cidrs 43 | ipv6_cidrs = var.default_security_group.ingress_ipv6_cidrs 44 | prefix_lists = var.default_security_group.ingress_prefix_lists 45 | security_groups = var.default_security_group.ingress_security_groups 46 | } 47 | if anytrue([ 48 | length(var.default_security_group.ingress_ipv4_cidrs) > 0, 49 | length(var.default_security_group.ingress_ipv6_cidrs) > 0, 50 | length(var.default_security_group.ingress_prefix_lists) > 0, 51 | length(var.default_security_group.ingress_security_groups) > 0, 52 | ]) 53 | ] 54 | ) 55 | egress_rules = concat( 56 | var.default_security_group.egress_rules, 57 | ) 58 | 59 | revoke_rules_on_delete = true 60 | resource_group_enabled = false 61 | module_tags_enabled = false 62 | 63 | tags = merge( 64 | local.module_tags, 65 | var.tags, 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the Route53 Resolver Inbound Endpoint." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "protocols" { 8 | description = <= 2 138 | error_message = "To improve reliability, Resolver requires that you specify two IP addresses for DNS queries. We recommend that you specify IP addresses in two different Availability Zones. After you add the first two IP addresses, you can optionally add more in the same or different Availability Zones." 139 | } 140 | validation { 141 | condition = alltrue([ 142 | for ip_allocation in var.ip_allocations : 143 | (ip_allocation.ipv4_address != null 144 | ? provider::assert::ipv4(ip_allocation.ipv4_address) 145 | : true 146 | ) 147 | ]) 148 | error_message = "Value of `ipv4_address` is not valid IPv4 address." 149 | } 150 | validation { 151 | condition = alltrue([ 152 | for ip_allocation in var.ip_allocations : 153 | (ip_allocation.ipv6_address != null 154 | ? provider::assert::ipv6(ip_allocation.ipv6_address) 155 | : true 156 | ) 157 | ]) 158 | error_message = "Value of `ipv6_address` is not valid IPv6 address." 159 | } 160 | } 161 | 162 | variable "tags" { 163 | description = "(Optional) A map of tags to add to all resources." 164 | type = map(string) 165 | default = {} 166 | nullable = false 167 | } 168 | 169 | variable "module_tags_enabled" { 170 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 171 | type = bool 172 | default = true 173 | nullable = false 174 | } 175 | 176 | 177 | ################################################### 178 | # Resource Group 179 | ################################################### 180 | 181 | variable "resource_group_enabled" { 182 | description = "(Optional) Whether to create Resource Group to find and group AWS resources which are created by this module." 183 | type = bool 184 | default = true 185 | nullable = false 186 | } 187 | 188 | variable "resource_group_name" { 189 | description = "(Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`." 190 | type = string 191 | default = "" 192 | nullable = false 193 | } 194 | 195 | variable "resource_group_description" { 196 | description = "(Optional) The description of Resource Group." 197 | type = string 198 | default = "Managed by Terraform." 199 | nullable = false 200 | } 201 | -------------------------------------------------------------------------------- /modules/resolver-inbound-endpoint/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.10" 3 | 4 | required_providers { 5 | assert = { 6 | source = "hashicorp/assert" 7 | version = ">= 0.15" 8 | } 9 | aws = { 10 | source = "hashicorp/aws" 11 | version = ">= 5.72" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/resolver-query-logging/README.md: -------------------------------------------------------------------------------- 1 | # resolver-query-logging 2 | 3 | This module creates following resources. 4 | 5 | - `aws_route53_resolver_query_log_config` 6 | - `aws_route53_resolver_query_log_config_association` (optional) 7 | - `aws_ram_resource_share` (optional) 8 | - `aws_ram_principal_association` (optional) 9 | - `aws_ram_resource_association` (optional) 10 | 11 | 12 | ## Requirements 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [terraform](#requirement\_terraform) | >= 1.9 | 17 | | [aws](#requirement\_aws) | >= 5.72 | 18 | 19 | ## Providers 20 | 21 | | Name | Version | 22 | |------|---------| 23 | | [aws](#provider\_aws) | 5.94.1 | 24 | 25 | ## Modules 26 | 27 | | Name | Source | Version | 28 | |------|--------|---------| 29 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 | 30 | | [share](#module\_share) | tedilabs/account/aws//modules/ram-share | ~> 0.22.0 | 31 | 32 | ## Resources 33 | 34 | | Name | Type | 35 | |------|------| 36 | | [aws_route53_resolver_query_log_config.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_query_log_config) | resource | 37 | | [aws_route53_resolver_query_log_config_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_query_log_config_association) | resource | 38 | 39 | ## Inputs 40 | 41 | | Name | Description | Type | Default | Required | 42 | |------|-------------|------|---------|:--------:| 43 | | [destination](#input\_destination) | (Required) The ARN of the resource that you want Route 53 Resolver to send query logs. You can send query logs to an S3 bucket, a CloudWatch Logs log group, or a Kinesis Data Firehose delivery stream. | `string` | n/a | yes | 44 | | [name](#input\_name) | (Required) The name of the Route 53 Resolver query logging configuration. | `string` | n/a | yes | 45 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 46 | | [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | 47 | | [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | 48 | | [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | 49 | | [shares](#input\_shares) | (Optional) A list of resource shares via RAM (Resource Access Manager). |
list(object({
name = optional(string)

permissions = optional(set(string), ["AWSRAMDefaultPermissionResolverQueryLogConfig"])

external_principals_allowed = optional(bool, false)
principals = optional(set(string), [])

tags = optional(map(string), {})
}))
| `[]` | no | 50 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 51 | | [vpc\_associations](#input\_vpc\_associations) | (Optional) A list of VPC IDs that you want this query logging configuration to log queries for. | `list(string)` | `[]` | no | 52 | 53 | ## Outputs 54 | 55 | | Name | Description | 56 | |------|-------------| 57 | | [arn](#output\_arn) | The ARN of the Route 53 Resolver query logging configuration. | 58 | | [destination](#output\_destination) | The ARN of the resource that Route 53 Resolver send query logs. This can be S3 bucket, CloudWatch Logs log group, or Kinesis Data Firehose delivery stream. | 59 | | [id](#output\_id) | The ID of the Route 53 Resolver query logging configuration. | 60 | | [name](#output\_name) | The name of the Route 53 Resolver query logging configuration. | 61 | | [owner\_id](#output\_owner\_id) | The AWS Account ID the account that created the query logging configuration. | 62 | | [sharing](#output\_sharing) | The configuration for sharing of the Route53 Resolver query logging configuration.
`status` - An indication of whether the query logging configuration is shared with other AWS accounts, or was shared with the current account by another AWS account. Sharing is configured through AWS Resource Access Manager (AWS RAM). Values are `NOT_SHARED`, `SHARED_BY_ME` or `SHARED_WITH_ME`.
`shares` - The list of resource shares via RAM (Resource Access Manager). | 63 | | [vpc\_associations](#output\_vpc\_associations) | A list of associated VPC IDs to query logging configuration. | 64 | 65 | -------------------------------------------------------------------------------- /modules/resolver-query-logging/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-domain" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | resource "aws_route53_resolver_query_log_config" "this" { 18 | name = local.metadata.name 19 | destination_arn = var.destination 20 | 21 | tags = merge( 22 | { 23 | "Name" = local.metadata.name 24 | }, 25 | local.module_tags, 26 | var.tags, 27 | ) 28 | } 29 | 30 | resource "aws_route53_resolver_query_log_config_association" "this" { 31 | for_each = toset(var.vpc_associations) 32 | 33 | resolver_query_log_config_id = aws_route53_resolver_query_log_config.this.id 34 | resource_id = each.value 35 | } 36 | -------------------------------------------------------------------------------- /modules/resolver-query-logging/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of the Route 53 Resolver query logging configuration." 3 | value = aws_route53_resolver_query_log_config.this.id 4 | } 5 | 6 | output "arn" { 7 | description = "The ARN of the Route 53 Resolver query logging configuration." 8 | value = aws_route53_resolver_query_log_config.this.arn 9 | } 10 | 11 | output "owner_id" { 12 | description = "The AWS Account ID the account that created the query logging configuration." 13 | value = aws_route53_resolver_query_log_config.this.owner_id 14 | } 15 | 16 | output "name" { 17 | description = "The name of the Route 53 Resolver query logging configuration." 18 | value = aws_route53_resolver_query_log_config.this.name 19 | } 20 | 21 | output "vpc_associations" { 22 | description = "A list of associated VPC IDs to query logging configuration." 23 | value = keys(aws_route53_resolver_query_log_config_association.this) 24 | } 25 | 26 | output "destination" { 27 | description = "The ARN of the resource that Route 53 Resolver send query logs. This can be S3 bucket, CloudWatch Logs log group, or Kinesis Data Firehose delivery stream." 28 | value = aws_route53_resolver_query_log_config.this.destination_arn 29 | } 30 | 31 | output "sharing" { 32 | description = < share 12 | } 13 | 14 | name = "route53-resolver.query-log-config.${var.name}.${each.key}" 15 | 16 | resources = [ 17 | aws_route53_resolver_query_log_config.this.arn, 18 | ] 19 | permissions = each.value.permissions 20 | 21 | external_principals_allowed = each.value.external_principals_allowed 22 | principals = each.value.principals 23 | 24 | resource_group_enabled = false 25 | module_tags_enabled = false 26 | 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | each.value.tags, 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /modules/resolver-query-logging/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group_name != "" 3 | ? var.resource_group_name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.10.0" 16 | 17 | count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group_description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/resolver-query-logging/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the Route 53 Resolver query logging configuration." 3 | type = string 4 | } 5 | 6 | variable "destination" { 7 | description = "(Required) The ARN of the resource that you want Route 53 Resolver to send query logs. You can send query logs to an S3 bucket, a CloudWatch Logs log group, or a Kinesis Data Firehose delivery stream." 8 | type = string 9 | } 10 | 11 | variable "vpc_associations" { 12 | description = "(Optional) A list of VPC IDs that you want this query logging configuration to log queries for." 13 | type = list(string) 14 | default = [] 15 | nullable = false 16 | } 17 | 18 | variable "tags" { 19 | description = "(Optional) A map of tags to add to all resources." 20 | type = map(string) 21 | default = {} 22 | nullable = false 23 | } 24 | 25 | variable "module_tags_enabled" { 26 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 27 | type = bool 28 | default = true 29 | nullable = false 30 | } 31 | 32 | 33 | ################################################### 34 | # Resource Group 35 | ################################################### 36 | 37 | variable "resource_group_enabled" { 38 | description = "(Optional) Whether to create Resource Group to find and group AWS resources which are created by this module." 39 | type = bool 40 | default = true 41 | nullable = false 42 | } 43 | 44 | variable "resource_group_name" { 45 | description = "(Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`." 46 | type = string 47 | default = "" 48 | nullable = false 49 | } 50 | 51 | variable "resource_group_description" { 52 | description = "(Optional) The description of Resource Group." 53 | type = string 54 | default = "Managed by Terraform." 55 | nullable = false 56 | } 57 | 58 | 59 | ################################################### 60 | # Resource Sharing by RAM (Resource Access Manager) 61 | ################################################### 62 | 63 | variable "shares" { 64 | description = "(Optional) A list of resource shares via RAM (Resource Access Manager)." 65 | type = list(object({ 66 | name = optional(string) 67 | 68 | permissions = optional(set(string), ["AWSRAMDefaultPermissionResolverQueryLogConfig"]) 69 | 70 | external_principals_allowed = optional(bool, false) 71 | principals = optional(set(string), []) 72 | 73 | tags = optional(map(string), {}) 74 | })) 75 | default = [] 76 | nullable = false 77 | } 78 | -------------------------------------------------------------------------------- /modules/resolver-query-logging/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.9" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 5.72" 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------