├── .editorconfig ├── .github └── workflows │ ├── lock.yml │ ├── pr-title.yml │ ├── pre-commit.yml │ ├── release.yml │ └── stale-actions.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .releaserc.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── UPGRADE-4.0.md ├── docs ├── README.md └── images │ ├── cluster.png │ ├── complete.png │ ├── service.png │ └── task.png ├── examples ├── README.md ├── complete │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── ec2-autoscaling │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── fargate │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── main.tf ├── modules ├── cluster │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── container-definition │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── service │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── outputs.tf ├── variables.tf ├── versions.tf └── wrappers ├── README.md ├── cluster ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── container-definition ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── main.tf ├── outputs.tf ├── service ├── README.md ├── main.tf ├── outputs.tf ├── variables.tf └── versions.tf ├── variables.tf └── versions.tf /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.{tf,tfvars}] 18 | indent_size = 2 19 | indent_style = space 20 | 21 | [*.md] 22 | max_line_length = 0 23 | trim_trailing_whitespace = false 24 | 25 | [Makefile] 26 | tab_width = 2 27 | indent_style = tab 28 | 29 | [COMMIT_EDITMSG] 30 | max_line_length = 0 31 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '50 1 * * *' 6 | 7 | jobs: 8 | lock: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dessant/lock-threads@v5 12 | with: 13 | github-token: ${{ secrets.GITHUB_TOKEN }} 14 | issue-comment: > 15 | I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. 16 | If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. 17 | issue-inactive-days: '30' 18 | pr-comment: > 19 | I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. 20 | If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. 21 | pr-inactive-days: '30' 22 | -------------------------------------------------------------------------------- /.github/workflows/pr-title.yml: -------------------------------------------------------------------------------- 1 | name: 'Validate PR title' 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Please look up the latest version from 16 | # https://github.com/amannn/action-semantic-pull-request/releases 17 | - uses: amannn/action-semantic-pull-request@v5.5.3 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | # Configure which types are allowed. 22 | # Default: https://github.com/commitizen/conventional-commit-types 23 | types: | 24 | fix 25 | feat 26 | docs 27 | ci 28 | chore 29 | # Configure that a scope must always be provided. 30 | requireScope: false 31 | # Configure additional validation for the subject based on a regex. 32 | # This example ensures the subject starts with an uppercase character. 33 | subjectPattern: ^[A-Z].+$ 34 | # If `subjectPattern` is configured, you can use this property to override 35 | # the default error message that is shown when the pattern doesn't match. 36 | # The variables `subject` and `title` can be used within the message. 37 | subjectPatternError: | 38 | The subject "{subject}" found in the pull request title "{title}" 39 | didn't match the configured pattern. Please ensure that the subject 40 | starts with an uppercase character. 41 | # For work-in-progress PRs you can typically use draft pull requests 42 | # from Github. However, private repositories on the free plan don't have 43 | # this option and therefore this action allows you to opt-in to using the 44 | # special "[WIP]" prefix to indicate this state. This will avoid the 45 | # validation of the PR title and the pull request checks remain pending. 46 | # Note that a second check will be reported if this is enabled. 47 | wip: true 48 | # When using "Squash and merge" on a PR with only one commit, GitHub 49 | # will suggest using that commit message instead of the PR title for the 50 | # merge commit, and it's easy to commit this by mistake. Enable this option 51 | # to also validate the commit message for one commit PRs. 52 | validateSingleCommit: false 53 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - master 8 | 9 | env: 10 | TERRAFORM_DOCS_VERSION: v0.19.0 11 | TFLINT_VERSION: v0.53.0 12 | 13 | jobs: 14 | collectInputs: 15 | name: Collect workflow inputs 16 | runs-on: ubuntu-latest 17 | outputs: 18 | directories: ${{ steps.dirs.outputs.directories }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Get root directories 24 | id: dirs 25 | uses: clowdhaus/terraform-composite-actions/directories@v1.9.0 26 | 27 | preCommitMinVersions: 28 | name: Min TF pre-commit 29 | needs: collectInputs 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} 34 | steps: 35 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 36 | - name: Delete huge unnecessary tools folder 37 | run: | 38 | rm -rf /opt/hostedtoolcache/CodeQL 39 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk 40 | rm -rf /opt/hostedtoolcache/Ruby 41 | rm -rf /opt/hostedtoolcache/go 42 | 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | 46 | - name: Terraform min/max versions 47 | id: minMax 48 | uses: clowdhaus/terraform-min-max@v1.3.1 49 | with: 50 | directory: ${{ matrix.directory }} 51 | 52 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 53 | # Run only validate pre-commit check on min version supported 54 | if: ${{ matrix.directory != '.' }} 55 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 56 | with: 57 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 58 | tflint-version: ${{ env.TFLINT_VERSION }} 59 | args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*' 60 | 61 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 62 | # Run only validate pre-commit check on min version supported 63 | if: ${{ matrix.directory == '.' }} 64 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 65 | with: 66 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 67 | tflint-version: ${{ env.TFLINT_VERSION }} 68 | args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)' 69 | 70 | preCommitMaxVersion: 71 | name: Max TF pre-commit 72 | runs-on: ubuntu-latest 73 | needs: collectInputs 74 | steps: 75 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 76 | - name: Delete huge unnecessary tools folder 77 | run: | 78 | rm -rf /opt/hostedtoolcache/CodeQL 79 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk 80 | rm -rf /opt/hostedtoolcache/Ruby 81 | rm -rf /opt/hostedtoolcache/go 82 | 83 | - name: Checkout 84 | uses: actions/checkout@v4 85 | with: 86 | ref: ${{ github.event.pull_request.head.ref }} 87 | repository: ${{github.event.pull_request.head.repo.full_name}} 88 | 89 | - name: Terraform min/max versions 90 | id: minMax 91 | uses: clowdhaus/terraform-min-max@v1.3.1 92 | 93 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} 94 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1 95 | with: 96 | terraform-version: ${{ steps.minMax.outputs.maxVersion }} 97 | tflint-version: ${{ env.TFLINT_VERSION }} 98 | terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }} 99 | install-hcledit: true 100 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | paths: 10 | - '**/*.tpl' 11 | - '**/*.py' 12 | - '**/*.tf' 13 | - '.github/workflows/release.yml' 14 | 15 | jobs: 16 | release: 17 | name: Release 18 | runs-on: ubuntu-latest 19 | # Skip running release workflow on forks 20 | if: github.repository_owner == 'terraform-aws-modules' 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | with: 25 | persist-credentials: false 26 | fetch-depth: 0 27 | 28 | - name: Release 29 | uses: cycjimmy/semantic-release-action@v4 30 | with: 31 | semantic_version: 23.0.2 32 | extra_plugins: | 33 | @semantic-release/changelog@6.0.3 34 | @semantic-release/git@10.0.1 35 | conventional-changelog-conventionalcommits@7.0.2 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/stale-actions.yaml: -------------------------------------------------------------------------------- 1 | name: 'Mark or close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | # Staling issues and PR's 14 | days-before-stale: 30 15 | stale-issue-label: stale 16 | stale-pr-label: stale 17 | stale-issue-message: | 18 | This issue has been automatically marked as stale because it has been open 30 days 19 | with no activity. Remove stale label or comment or this issue will be closed in 10 days 20 | stale-pr-message: | 21 | This PR has been automatically marked as stale because it has been open 30 days 22 | with no activity. Remove stale label or comment or this PR will be closed in 10 days 23 | # Not stale if have this labels or part of milestone 24 | exempt-issue-labels: bug,wip,on-hold 25 | exempt-pr-labels: bug,wip,on-hold 26 | exempt-all-milestones: true 27 | # Close issue operations 28 | # Label will be automatically removed if the issues are no longer closed nor locked. 29 | days-before-close: 10 30 | delete-branch: true 31 | close-issue-message: This issue was automatically closed because of stale in 10 days 32 | close-pr-message: This PR was automatically closed because of stale in 10 days 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # Terraform lockfile 5 | .terraform.lock.hcl 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Crash log files 12 | crash.log 13 | 14 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 15 | # password, private keys, and other secrets. These should not be part of version 16 | # control as they are data points which are potentially sensitive and subject 17 | # to change depending on the environment. 18 | *.tfvars 19 | 20 | # Ignore override files as they are usually used to override resources locally and so 21 | # are not checked in 22 | override.tf 23 | override.tf.json 24 | *_override.tf 25 | *_override.tf.json 26 | 27 | # Ignore CLI configuration files 28 | .terraformrc 29 | terraform.rc 30 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.99.0 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_wrapper_module_for_each 7 | - id: terraform_docs 8 | args: 9 | - '--args=--lockfile=false' 10 | - id: terraform_tflint 11 | args: 12 | - '--args=--only=terraform_deprecated_interpolation' 13 | - '--args=--only=terraform_deprecated_index' 14 | - '--args=--only=terraform_unused_declarations' 15 | - '--args=--only=terraform_comment_syntax' 16 | - '--args=--only=terraform_documented_outputs' 17 | - '--args=--only=terraform_documented_variables' 18 | - '--args=--only=terraform_typed_variables' 19 | - '--args=--only=terraform_module_pinned_source' 20 | - '--args=--only=terraform_naming_convention' 21 | - '--args=--only=terraform_required_version' 22 | - '--args=--only=terraform_required_providers' 23 | - '--args=--only=terraform_standard_module_structure' 24 | - '--args=--only=terraform_workspace_remote' 25 | - id: terraform_validate 26 | - repo: https://github.com/pre-commit/pre-commit-hooks 27 | rev: v5.0.0 28 | hooks: 29 | - id: check-merge-conflict 30 | - id: end-of-file-fixer 31 | - id: trailing-whitespace 32 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master" 5 | ], 6 | "ci": false, 7 | "plugins": [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | "preset": "conventionalcommits" 12 | } 13 | ], 14 | [ 15 | "@semantic-release/release-notes-generator", 16 | { 17 | "preset": "conventionalcommits" 18 | } 19 | ], 20 | [ 21 | "@semantic-release/github", 22 | { 23 | "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", 24 | "labels": false, 25 | "releasedLabels": false 26 | } 27 | ], 28 | [ 29 | "@semantic-release/changelog", 30 | { 31 | "changelogFile": "CHANGELOG.md", 32 | "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." 33 | } 34 | ], 35 | [ 36 | "@semantic-release/git", 37 | { 38 | "assets": [ 39 | "CHANGELOG.md" 40 | ], 41 | "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 42 | } 43 | ] 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /UPGRADE-4.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from v3.x to v4.x 2 | 3 | Please consult the `examples` directory for reference example configurations. If you find a bug, please open an issue with supporting configuration to reproduce. 4 | 5 | ## List of backwards incompatible changes 6 | 7 | - Minimum supported version of Terraform AWS provider updated to v4.6 to support the latest resources utilized 8 | - Minimum supported version of Terraform updated to v1.0 9 | - `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/complete`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. 10 | - The `container_insights` and `capacity_providers` variables have been replaced by new variables - see below for more details 11 | 12 | ## Additional changes 13 | 14 | ### Added 15 | 16 | - Support for `aws_ecs_capacity_provider` has been added to the module 17 | 18 | ### Modified 19 | 20 | - The `container_insights` variable has been replaced with the `cluster_settings` variable which allows users to enable/disable container insights and also allows for not specifying at all for regions where container insights is currently not supported. 21 | - The `capacity_providers` variable has been replaced with `fargate_capacity_providers`and `autoscaling_capacity_providers`. This allows users to specify either Fargate based capacity providers, EC2 AutoScaling Group capacity providers, or both. 22 | - Previously `capacity_providers` and `default_capacity_provider_strategy` usage looked like: 23 | ```hcl 24 | capacity_providers = ["FARGATE", "FARGATE_SPOT"] 25 | 26 | default_capacity_provider_strategy = [{ 27 | capacity_provider = "FARGATE" 28 | weight = 50 29 | base = 20 30 | }, { 31 | capacity_provider = "FARGATE_SPOT" 32 | weight = 50 33 | }] 34 | ``` 35 | Where the current equivalent now looks like: 36 | ```hcl 37 | fargate_capacity_providers = { 38 | FARGATE = { 39 | default_capacity_provider_strategy = { 40 | weight = 50 41 | base = 20 42 | } 43 | } 44 | FARGATE_SPOT = { 45 | default_capacity_provider_strategy = { 46 | weight = 50 47 | } 48 | } 49 | } 50 | ``` 51 | - Previously `capacity_providers` accepted the name of an AutoScaling Group created externally; this is now replaced by the usage of `autoscaling_capacity_providers` which incorporates the usage of the newly added support for `aws_ecs_capacity_provider` 52 | 53 | ### Removed 54 | 55 | - `ecs-instance-profile` sub-module has been removed; this functionality is available through the [`terraform-aws-modules/terraform-aws-autoscaling`](https://github.com/terraform-aws-modules/terraform-aws-autoscaling) module starting with version [v6.5.0](https://github.com/terraform-aws-modules/terraform-aws-autoscaling/pull/194). Please see the [`examples/complete`](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) example for a demonstration on how to use and integrate with the `terraform-aws-autoscaling` module. 56 | 57 | ### Variable and output changes 58 | 59 | 1. Removed variables: 60 | 61 | - `default_capacity_provider_strategy` is now incorporated into the `fargate_capacity_providers` and `autoscaling_capacity_providers` variables. 62 | 63 | 2. Renamed variables: 64 | 65 | - `create_ecs` -> `create` 66 | - `name` -> `cluster_name` 67 | 68 | 3. Added variables: 69 | 70 | - `cluster_configuration` has been added under a dynamic block with all current attributes supported 71 | 72 | 4. Removed outputs: 73 | 74 | - `ecs_cluster_name` 75 | 76 | 5. Renamed outputs: 77 | 78 | - `ecs_cluster_id` -> `cluster_id` 79 | - `ecs_cluster_arn` -> `cluster_arn` 80 | 81 | 6. Added outputs: 82 | 83 | - `cluster_capacity_providers` 84 | - `autoscaling_capacity_providers` 85 | 86 | ## Upgrade Migrations 87 | 88 | ### Before v3.x Example 89 | 90 | ```hcl 91 | module "ecs" { 92 | source = "terraform-aws-modules/ecs/aws" 93 | version = "3.5.0" 94 | 95 | name = "example" 96 | container_insights = true 97 | 98 | capacity_providers = ["FARGATE", "FARGATE_SPOT", aws_ecs_capacity_provider.prov1.name] 99 | 100 | default_capacity_provider_strategy = [{ 101 | capacity_provider = aws_ecs_capacity_provider.prov1.name 102 | weight = "1" 103 | }] 104 | } 105 | 106 | module "ec2_profile" { 107 | source = "terraform-aws-modules/ecs/aws//modules/ecs-instance-profile" 108 | version = "3.5.0" 109 | 110 | name = local.name 111 | } 112 | 113 | resource "aws_ecs_capacity_provider" "prov1" { 114 | name = "prov1" 115 | 116 | auto_scaling_group_provider { 117 | auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn 118 | } 119 | } 120 | ``` 121 | 122 | ### After v4.x Example 123 | 124 | ```hcl 125 | module "ecs" { 126 | source = "terraform-aws-modules/ecs/aws" 127 | version = "4.0.0" 128 | 129 | cluster_name = "example" 130 | 131 | fargate_capacity_providers = { 132 | FARGATE = {} 133 | FARGATE_SPOT = {} 134 | } 135 | 136 | autoscaling_capacity_providers = { 137 | prov1 = { 138 | auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn 139 | default_capacity_provider_strategy = { 140 | weight = 1 141 | } 142 | } 143 | } 144 | } 145 | 146 | module "ec2_profile" { 147 | source = "terraform-aws-modules/ecs/aws//modules/ecs-instance-profile" 148 | # Users can pin and stay on v3.5.0 until they able to use the IAM instance 149 | # profile provided through the autoscaling group module 150 | version = "3.5.0" 151 | 152 | name = "example 153 | } 154 | ``` 155 | 156 | ### Diff of Before vs After 157 | 158 | ```diff 159 | - resource "aws_ecs_capacity_provider" "prov1" { 160 | - name = "prov1" 161 | - 162 | - auto_scaling_group_provider { 163 | - auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn 164 | - } 165 | - } 166 | 167 | module "ecs" { 168 | source = "terraform-aws-modules/ecs/aws" 169 | - version = "3.5.0" 170 | + version = "4.0.0" 171 | 172 | - name = "example" 173 | + cluster_name = "example" 174 | 175 | - container_insights = true 176 | + # On by default now 177 | 178 | - capacity_providers = ["FARGATE", "FARGATE_SPOT", aws_ecs_capacity_provider.prov1.name] 179 | - default_capacity_provider_strategy = [{ 180 | - capacity_provider = aws_ecs_capacity_provider.prov1.name 181 | - weight = "1" 182 | - }] 183 | 184 | + fargate_capacity_providers = { 185 | + FARGATE = {} 186 | + FARGATE_SPOT = {} 187 | + } 188 | 189 | + autoscaling_capacity_providers = { 190 | + prov1 = { 191 | + auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn 192 | + default_capacity_provider_strategy = { 193 | + weight = 1 194 | + } 195 | + } 196 | + } 197 | } 198 | ``` 199 | 200 | ### State Move Commands 201 | 202 | In conjunction with the changes above, users can elect to move their external capacity provider(s) under this module using the following move command. Command is shown using the values from the example shown above, please update to suit your configuration names: 203 | 204 | ```sh 205 | # Cluster 206 | terraform state mv 'aws_ecs_capacity_provider.prov1' 'module.ecs.aws_ecs_capacity_provider.this["prov1"]' 207 | ``` 208 | -------------------------------------------------------------------------------- /docs/images/cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-ecs/7570dbfcbab7ab2141155927d424b7b3f4305b4f/docs/images/cluster.png -------------------------------------------------------------------------------- /docs/images/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-ecs/7570dbfcbab7ab2141155927d424b7b3f4305b4f/docs/images/complete.png -------------------------------------------------------------------------------- /docs/images/service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-ecs/7570dbfcbab7ab2141155927d424b7b3f4305b4f/docs/images/service.png -------------------------------------------------------------------------------- /docs/images/task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-ecs/7570dbfcbab7ab2141155927d424b7b3f4305b4f/docs/images/task.png -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Please note - the examples provided serve two primary means: 4 | 5 | 1. Show users working examples of the various ways in which the module can be configured and features supported 6 | 2. A means of testing/validating module changes 7 | 8 | Please do not mistake the examples provided as "best practices". It is up to users to consult the AWS service documentation for best practices, usage recommendations, etc. 9 | -------------------------------------------------------------------------------- /examples/complete/README.md: -------------------------------------------------------------------------------- 1 | # ECS Cluster Complete 2 | 3 | Configuration in this directory creates: 4 | 5 | - ECS cluster using Fargate (on-demand and spot) capacity providers 6 | - Example ECS service that utilizes 7 | - AWS Firelens using FluentBit sidecar container definition 8 | - Service connect configuration 9 | - Load balancer target group attachment 10 | - Security group for access to the example service 11 | 12 | ## Usage 13 | 14 | To run this example you need to execute: 15 | 16 | ```bash 17 | $ terraform init 18 | $ terraform plan 19 | $ terraform apply 20 | ``` 21 | 22 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 23 | 24 | 25 | ## Requirements 26 | 27 | | Name | Version | 28 | |------|---------| 29 | | [terraform](#requirement\_terraform) | >= 1.0 | 30 | | [aws](#requirement\_aws) | >= 4.66.1 | 31 | 32 | ## Providers 33 | 34 | | Name | Version | 35 | |------|---------| 36 | | [aws](#provider\_aws) | >= 4.66.1 | 37 | 38 | ## Modules 39 | 40 | | Name | Source | Version | 41 | |------|--------|---------| 42 | | [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 9.0 | 43 | | [ecs](#module\_ecs) | ../../ | n/a | 44 | | [ecs\_cluster\_disabled](#module\_ecs\_cluster\_disabled) | ../../modules/cluster | n/a | 45 | | [ecs\_disabled](#module\_ecs\_disabled) | ../../ | n/a | 46 | | [service\_disabled](#module\_service\_disabled) | ../../modules/service | n/a | 47 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | 48 | 49 | ## Resources 50 | 51 | | Name | Type | 52 | |------|------| 53 | | [aws_service_discovery_http_namespace.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/service_discovery_http_namespace) | resource | 54 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 55 | | [aws_ssm_parameter.fluentbit](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | 56 | 57 | ## Inputs 58 | 59 | No inputs. 60 | 61 | ## Outputs 62 | 63 | | Name | Description | 64 | |------|-------------| 65 | | [alb\_dns\_name](#output\_alb\_dns\_name) | The DNS name of the load balancer | 66 | | [cluster\_arn](#output\_cluster\_arn) | ARN that identifies the cluster | 67 | | [cluster\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | 68 | | [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes | 69 | | [cluster\_id](#output\_cluster\_id) | ID that identifies the cluster | 70 | | [cluster\_name](#output\_cluster\_name) | Name that identifies the cluster | 71 | | [services](#output\_services) | Map of services created and their attributes | 72 | 73 | 74 | ## License 75 | 76 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE). 77 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | region = "eu-west-1" 9 | name = "ex-${basename(path.cwd)}" 10 | 11 | vpc_cidr = "10.0.0.0/16" 12 | azs = slice(data.aws_availability_zones.available.names, 0, 3) 13 | 14 | container_name = "ecsdemo-frontend" 15 | container_port = 3000 16 | 17 | tags = { 18 | Name = local.name 19 | Example = local.name 20 | Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" 21 | } 22 | } 23 | 24 | ################################################################################ 25 | # Cluster 26 | ################################################################################ 27 | 28 | module "ecs" { 29 | source = "../../" 30 | 31 | cluster_name = local.name 32 | 33 | # Capacity provider 34 | fargate_capacity_providers = { 35 | FARGATE = { 36 | default_capacity_provider_strategy = { 37 | weight = 50 38 | base = 20 39 | } 40 | } 41 | FARGATE_SPOT = { 42 | default_capacity_provider_strategy = { 43 | weight = 50 44 | } 45 | } 46 | } 47 | 48 | services = { 49 | ecsdemo-frontend = { 50 | cpu = 1024 51 | memory = 4096 52 | 53 | # Container definition(s) 54 | container_definitions = { 55 | 56 | fluent-bit = { 57 | cpu = 512 58 | memory = 1024 59 | essential = true 60 | image = nonsensitive(data.aws_ssm_parameter.fluentbit.value) 61 | firelens_configuration = { 62 | type = "fluentbit" 63 | } 64 | memory_reservation = 50 65 | } 66 | 67 | (local.container_name) = { 68 | cpu = 512 69 | memory = 1024 70 | essential = true 71 | image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50" 72 | 73 | health_check = { 74 | command = ["CMD-SHELL", "curl -f http://localhost:${local.container_port}/health || exit 1"] 75 | } 76 | 77 | port_mappings = [ 78 | { 79 | name = local.container_name 80 | containerPort = local.container_port 81 | hostPort = local.container_port 82 | protocol = "tcp" 83 | } 84 | ] 85 | 86 | # Example image used requires access to write to root filesystem 87 | readonly_root_filesystem = false 88 | 89 | dependencies = [{ 90 | containerName = "fluent-bit" 91 | condition = "START" 92 | }] 93 | 94 | enable_cloudwatch_logging = false 95 | log_configuration = { 96 | logDriver = "awsfirelens" 97 | options = { 98 | Name = "firehose" 99 | region = local.region 100 | delivery_stream = "my-stream" 101 | log-driver-buffer-limit = "2097152" 102 | } 103 | } 104 | memory_reservation = 100 105 | } 106 | } 107 | 108 | service_connect_configuration = { 109 | namespace = aws_service_discovery_http_namespace.this.arn 110 | service = { 111 | client_alias = { 112 | port = local.container_port 113 | dns_name = local.container_name 114 | } 115 | port_name = local.container_name 116 | discovery_name = local.container_name 117 | } 118 | } 119 | 120 | load_balancer = { 121 | service = { 122 | target_group_arn = module.alb.target_groups["ex_ecs"].arn 123 | container_name = local.container_name 124 | container_port = local.container_port 125 | } 126 | } 127 | 128 | tasks_iam_role_name = "${local.name}-tasks" 129 | tasks_iam_role_description = "Example tasks IAM role for ${local.name}" 130 | tasks_iam_role_policies = { 131 | ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" 132 | } 133 | tasks_iam_role_statements = [ 134 | { 135 | actions = ["s3:List*"] 136 | resources = ["arn:aws:s3:::*"] 137 | } 138 | ] 139 | 140 | subnet_ids = module.vpc.private_subnets 141 | security_group_rules = { 142 | alb_ingress_3000 = { 143 | type = "ingress" 144 | from_port = local.container_port 145 | to_port = local.container_port 146 | protocol = "tcp" 147 | description = "Service port" 148 | source_security_group_id = module.alb.security_group_id 149 | } 150 | egress_all = { 151 | type = "egress" 152 | from_port = 0 153 | to_port = 0 154 | protocol = "-1" 155 | cidr_blocks = ["0.0.0.0/0"] 156 | } 157 | } 158 | } 159 | } 160 | 161 | tags = local.tags 162 | } 163 | 164 | module "ecs_disabled" { 165 | source = "../../" 166 | 167 | create = false 168 | } 169 | 170 | module "ecs_cluster_disabled" { 171 | source = "../../modules/cluster" 172 | 173 | create = false 174 | } 175 | 176 | module "service_disabled" { 177 | source = "../../modules/service" 178 | 179 | create = false 180 | } 181 | 182 | ################################################################################ 183 | # Supporting Resources 184 | ################################################################################ 185 | 186 | data "aws_ssm_parameter" "fluentbit" { 187 | name = "/aws/service/aws-for-fluent-bit/stable" 188 | } 189 | 190 | resource "aws_service_discovery_http_namespace" "this" { 191 | name = local.name 192 | description = "CloudMap namespace for ${local.name}" 193 | tags = local.tags 194 | } 195 | 196 | module "alb" { 197 | source = "terraform-aws-modules/alb/aws" 198 | version = "~> 9.0" 199 | 200 | name = local.name 201 | 202 | load_balancer_type = "application" 203 | 204 | vpc_id = module.vpc.vpc_id 205 | subnets = module.vpc.public_subnets 206 | 207 | # For example only 208 | enable_deletion_protection = false 209 | 210 | # Security Group 211 | security_group_ingress_rules = { 212 | all_http = { 213 | from_port = 80 214 | to_port = 80 215 | ip_protocol = "tcp" 216 | cidr_ipv4 = "0.0.0.0/0" 217 | } 218 | } 219 | security_group_egress_rules = { 220 | all = { 221 | ip_protocol = "-1" 222 | cidr_ipv4 = module.vpc.vpc_cidr_block 223 | } 224 | } 225 | 226 | listeners = { 227 | ex_http = { 228 | port = 80 229 | protocol = "HTTP" 230 | 231 | forward = { 232 | target_group_key = "ex_ecs" 233 | } 234 | } 235 | } 236 | 237 | target_groups = { 238 | ex_ecs = { 239 | backend_protocol = "HTTP" 240 | backend_port = local.container_port 241 | target_type = "ip" 242 | deregistration_delay = 5 243 | load_balancing_cross_zone_enabled = true 244 | 245 | health_check = { 246 | enabled = true 247 | healthy_threshold = 5 248 | interval = 30 249 | matcher = "200" 250 | path = "/" 251 | port = "traffic-port" 252 | protocol = "HTTP" 253 | timeout = 5 254 | unhealthy_threshold = 2 255 | } 256 | 257 | # Theres nothing to attach here in this definition. Instead, 258 | # ECS will attach the IPs of the tasks to this target group 259 | create_attachment = false 260 | } 261 | } 262 | 263 | tags = local.tags 264 | } 265 | 266 | module "vpc" { 267 | source = "terraform-aws-modules/vpc/aws" 268 | version = "~> 5.0" 269 | 270 | name = local.name 271 | cidr = local.vpc_cidr 272 | 273 | azs = local.azs 274 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] 275 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] 276 | 277 | enable_nat_gateway = true 278 | single_nat_gateway = true 279 | 280 | tags = local.tags 281 | } 282 | -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Cluster 3 | ################################################################################ 4 | 5 | output "cluster_arn" { 6 | description = "ARN that identifies the cluster" 7 | value = module.ecs.cluster_arn 8 | } 9 | 10 | output "cluster_id" { 11 | description = "ID that identifies the cluster" 12 | value = module.ecs.cluster_id 13 | } 14 | 15 | output "cluster_name" { 16 | description = "Name that identifies the cluster" 17 | value = module.ecs.cluster_name 18 | } 19 | 20 | output "cluster_capacity_providers" { 21 | description = "Map of cluster capacity providers attributes" 22 | value = module.ecs.cluster_capacity_providers 23 | } 24 | 25 | output "cluster_autoscaling_capacity_providers" { 26 | description = "Map of capacity providers created and their attributes" 27 | value = module.ecs.autoscaling_capacity_providers 28 | } 29 | 30 | ################################################################################ 31 | # Service(s) 32 | ################################################################################ 33 | 34 | output "services" { 35 | description = "Map of services created and their attributes" 36 | value = module.ecs.services 37 | } 38 | 39 | ################################################################################ 40 | # Application Load Balancer 41 | ################################################################################ 42 | 43 | output "alb_dns_name" { 44 | description = "The DNS name of the load balancer" 45 | value = module.alb.dns_name 46 | } 47 | -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-ecs/7570dbfcbab7ab2141155927d424b7b3f4305b4f/examples/complete/variables.tf -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/ec2-autoscaling/README.md: -------------------------------------------------------------------------------- 1 | # ECS Cluster w/ EC2 Autoscaling 2 | 3 | Configuration in this directory creates: 4 | 5 | - ECS cluster using EC2 autoscaling groups 6 | - Autoscaling groups with IAM instance profile to be used by ECS cluster 7 | - Example ECS service that utilizes 8 | - Mounts a host volume into the container definition 9 | - Load balancer target group attachment 10 | - Security group for access to the example service 11 | 12 | ## Usage 13 | 14 | To run this example you need to execute: 15 | 16 | ```bash 17 | $ terraform init 18 | $ terraform plan 19 | $ terraform apply 20 | ``` 21 | 22 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 23 | 24 | 25 | ## Requirements 26 | 27 | | Name | Version | 28 | |------|---------| 29 | | [terraform](#requirement\_terraform) | >= 1.0 | 30 | | [aws](#requirement\_aws) | >= 4.66.1 | 31 | 32 | ## Providers 33 | 34 | | Name | Version | 35 | |------|---------| 36 | | [aws](#provider\_aws) | >= 4.66.1 | 37 | 38 | ## Modules 39 | 40 | | Name | Source | Version | 41 | |------|--------|---------| 42 | | [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 9.0 | 43 | | [autoscaling](#module\_autoscaling) | terraform-aws-modules/autoscaling/aws | ~> 6.5 | 44 | | [autoscaling\_sg](#module\_autoscaling\_sg) | terraform-aws-modules/security-group/aws | ~> 5.0 | 45 | | [ecs\_cluster](#module\_ecs\_cluster) | ../../modules/cluster | n/a | 46 | | [ecs\_service](#module\_ecs\_service) | ../../modules/service | n/a | 47 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | 48 | 49 | ## Resources 50 | 51 | | Name | Type | 52 | |------|------| 53 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 54 | | [aws_ssm_parameter.ecs_optimized_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | 55 | 56 | ## Inputs 57 | 58 | No inputs. 59 | 60 | ## Outputs 61 | 62 | | Name | Description | 63 | |------|-------------| 64 | | [alb\_dns\_name](#output\_alb\_dns\_name) | The DNS name of the load balancer | 65 | | [cluster\_arn](#output\_cluster\_arn) | ARN that identifies the cluster | 66 | | [cluster\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | 67 | | [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes | 68 | | [cluster\_id](#output\_cluster\_id) | ID that identifies the cluster | 69 | | [cluster\_name](#output\_cluster\_name) | Name that identifies the cluster | 70 | | [service\_autoscaling\_policies](#output\_service\_autoscaling\_policies) | Map of autoscaling policies and their attributes | 71 | | [service\_autoscaling\_scheduled\_actions](#output\_service\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes | 72 | | [service\_container\_definitions](#output\_service\_container\_definitions) | Container definitions | 73 | | [service\_iam\_role\_arn](#output\_service\_iam\_role\_arn) | Service IAM role ARN | 74 | | [service\_iam\_role\_name](#output\_service\_iam\_role\_name) | Service IAM role name | 75 | | [service\_iam\_role\_unique\_id](#output\_service\_iam\_role\_unique\_id) | Stable and unique string identifying the service IAM role | 76 | | [service\_id](#output\_service\_id) | ARN that identifies the service | 77 | | [service\_name](#output\_service\_name) | Name of the service | 78 | | [service\_task\_definition\_arn](#output\_service\_task\_definition\_arn) | Full ARN of the Task Definition (including both `family` and `revision`) | 79 | | [service\_task\_definition\_revision](#output\_service\_task\_definition\_revision) | Revision of the task in a particular family | 80 | | [service\_task\_exec\_iam\_role\_arn](#output\_service\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN | 81 | | [service\_task\_exec\_iam\_role\_name](#output\_service\_task\_exec\_iam\_role\_name) | Task execution IAM role name | 82 | | [service\_task\_exec\_iam\_role\_unique\_id](#output\_service\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role | 83 | | [service\_task\_set\_arn](#output\_service\_task\_set\_arn) | The Amazon Resource Name (ARN) that identifies the task set | 84 | | [service\_task\_set\_id](#output\_service\_task\_set\_id) | The ID of the task set | 85 | | [service\_task\_set\_stability\_status](#output\_service\_task\_set\_stability\_status) | The stability status. This indicates whether the task set has reached a steady state | 86 | | [service\_task\_set\_status](#output\_service\_task\_set\_status) | The status of the task set | 87 | | [service\_tasks\_iam\_role\_arn](#output\_service\_tasks\_iam\_role\_arn) | Tasks IAM role ARN | 88 | | [service\_tasks\_iam\_role\_name](#output\_service\_tasks\_iam\_role\_name) | Tasks IAM role name | 89 | | [service\_tasks\_iam\_role\_unique\_id](#output\_service\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role | 90 | 91 | 92 | ## License 93 | 94 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE). 95 | -------------------------------------------------------------------------------- /examples/ec2-autoscaling/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | region = "eu-west-1" 9 | name = "ex-${basename(path.cwd)}" 10 | 11 | vpc_cidr = "10.0.0.0/16" 12 | azs = slice(data.aws_availability_zones.available.names, 0, 3) 13 | 14 | container_name = "ecs-sample" 15 | container_port = 80 16 | 17 | tags = { 18 | Name = local.name 19 | Example = local.name 20 | Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" 21 | } 22 | } 23 | 24 | ################################################################################ 25 | # Cluster 26 | ################################################################################ 27 | 28 | module "ecs_cluster" { 29 | source = "../../modules/cluster" 30 | 31 | cluster_name = local.name 32 | 33 | # Capacity provider - autoscaling groups 34 | default_capacity_provider_use_fargate = false 35 | autoscaling_capacity_providers = { 36 | # On-demand instances 37 | ex_1 = { 38 | auto_scaling_group_arn = module.autoscaling["ex_1"].autoscaling_group_arn 39 | managed_termination_protection = "ENABLED" 40 | 41 | managed_scaling = { 42 | maximum_scaling_step_size = 5 43 | minimum_scaling_step_size = 1 44 | status = "ENABLED" 45 | target_capacity = 60 46 | } 47 | 48 | default_capacity_provider_strategy = { 49 | weight = 60 50 | base = 20 51 | } 52 | } 53 | # Spot instances 54 | ex_2 = { 55 | auto_scaling_group_arn = module.autoscaling["ex_2"].autoscaling_group_arn 56 | managed_termination_protection = "ENABLED" 57 | 58 | managed_scaling = { 59 | maximum_scaling_step_size = 15 60 | minimum_scaling_step_size = 5 61 | status = "ENABLED" 62 | target_capacity = 90 63 | } 64 | 65 | default_capacity_provider_strategy = { 66 | weight = 40 67 | } 68 | } 69 | } 70 | 71 | tags = local.tags 72 | } 73 | 74 | ################################################################################ 75 | # Service 76 | ################################################################################ 77 | 78 | module "ecs_service" { 79 | source = "../../modules/service" 80 | 81 | # Service 82 | name = local.name 83 | cluster_arn = module.ecs_cluster.arn 84 | 85 | # Task Definition 86 | requires_compatibilities = ["EC2"] 87 | capacity_provider_strategy = { 88 | # On-demand instances 89 | ex_1 = { 90 | capacity_provider = module.ecs_cluster.autoscaling_capacity_providers["ex_1"].name 91 | weight = 1 92 | base = 1 93 | } 94 | } 95 | 96 | volume = { 97 | my-vol = {} 98 | } 99 | 100 | # Container definition(s) 101 | container_definitions = { 102 | (local.container_name) = { 103 | image = "public.ecr.aws/ecs-sample-image/amazon-ecs-sample:latest" 104 | port_mappings = [ 105 | { 106 | name = local.container_name 107 | containerPort = local.container_port 108 | protocol = "tcp" 109 | } 110 | ] 111 | 112 | mount_points = [ 113 | { 114 | sourceVolume = "my-vol", 115 | containerPath = "/var/www/my-vol" 116 | } 117 | ] 118 | 119 | entry_point = ["/usr/sbin/apache2", "-D", "FOREGROUND"] 120 | 121 | # Example image used requires access to write to root filesystem 122 | readonly_root_filesystem = false 123 | 124 | enable_cloudwatch_logging = true 125 | create_cloudwatch_log_group = true 126 | cloudwatch_log_group_name = "/aws/ecs/${local.name}/${local.container_name}" 127 | cloudwatch_log_group_retention_in_days = 7 128 | 129 | log_configuration = { 130 | logDriver = "awslogs" 131 | } 132 | } 133 | } 134 | 135 | load_balancer = { 136 | service = { 137 | target_group_arn = module.alb.target_groups["ex_ecs"].arn 138 | container_name = local.container_name 139 | container_port = local.container_port 140 | } 141 | } 142 | 143 | subnet_ids = module.vpc.private_subnets 144 | security_group_rules = { 145 | alb_http_ingress = { 146 | type = "ingress" 147 | from_port = local.container_port 148 | to_port = local.container_port 149 | protocol = "tcp" 150 | description = "Service port" 151 | source_security_group_id = module.alb.security_group_id 152 | } 153 | } 154 | 155 | tags = local.tags 156 | } 157 | 158 | ################################################################################ 159 | # Supporting Resources 160 | ################################################################################ 161 | 162 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux 163 | data "aws_ssm_parameter" "ecs_optimized_ami" { 164 | name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended" 165 | } 166 | 167 | module "alb" { 168 | source = "terraform-aws-modules/alb/aws" 169 | version = "~> 9.0" 170 | 171 | name = local.name 172 | 173 | load_balancer_type = "application" 174 | 175 | vpc_id = module.vpc.vpc_id 176 | subnets = module.vpc.public_subnets 177 | 178 | # For example only 179 | enable_deletion_protection = false 180 | 181 | # Security Group 182 | security_group_ingress_rules = { 183 | all_http = { 184 | from_port = 80 185 | to_port = 80 186 | ip_protocol = "tcp" 187 | cidr_ipv4 = "0.0.0.0/0" 188 | } 189 | } 190 | security_group_egress_rules = { 191 | all = { 192 | ip_protocol = "-1" 193 | cidr_ipv4 = module.vpc.vpc_cidr_block 194 | } 195 | } 196 | 197 | listeners = { 198 | ex_http = { 199 | port = 80 200 | protocol = "HTTP" 201 | 202 | forward = { 203 | target_group_key = "ex_ecs" 204 | } 205 | } 206 | } 207 | 208 | target_groups = { 209 | ex_ecs = { 210 | backend_protocol = "HTTP" 211 | backend_port = local.container_port 212 | target_type = "ip" 213 | deregistration_delay = 5 214 | load_balancing_cross_zone_enabled = true 215 | 216 | health_check = { 217 | enabled = true 218 | healthy_threshold = 5 219 | interval = 30 220 | matcher = "200" 221 | path = "/" 222 | port = "traffic-port" 223 | protocol = "HTTP" 224 | timeout = 5 225 | unhealthy_threshold = 2 226 | } 227 | 228 | # Theres nothing to attach here in this definition. Instead, 229 | # ECS will attach the IPs of the tasks to this target group 230 | create_attachment = false 231 | } 232 | } 233 | 234 | tags = local.tags 235 | } 236 | 237 | module "autoscaling" { 238 | source = "terraform-aws-modules/autoscaling/aws" 239 | version = "~> 6.5" 240 | 241 | for_each = { 242 | # On-demand instances 243 | ex_1 = { 244 | instance_type = "t3.large" 245 | use_mixed_instances_policy = false 246 | mixed_instances_policy = {} 247 | user_data = <<-EOT 248 | #!/bin/bash 249 | 250 | cat <<'EOF' >> /etc/ecs/ecs.config 251 | ECS_CLUSTER=${local.name} 252 | ECS_LOGLEVEL=debug 253 | ECS_CONTAINER_INSTANCE_TAGS=${jsonencode(local.tags)} 254 | ECS_ENABLE_TASK_IAM_ROLE=true 255 | EOF 256 | EOT 257 | } 258 | # Spot instances 259 | ex_2 = { 260 | instance_type = "t3.medium" 261 | use_mixed_instances_policy = true 262 | mixed_instances_policy = { 263 | instances_distribution = { 264 | on_demand_base_capacity = 0 265 | on_demand_percentage_above_base_capacity = 0 266 | spot_allocation_strategy = "price-capacity-optimized" 267 | } 268 | 269 | override = [ 270 | { 271 | instance_type = "m4.large" 272 | weighted_capacity = "2" 273 | }, 274 | { 275 | instance_type = "t3.large" 276 | weighted_capacity = "1" 277 | }, 278 | ] 279 | } 280 | user_data = <<-EOT 281 | #!/bin/bash 282 | 283 | cat <<'EOF' >> /etc/ecs/ecs.config 284 | ECS_CLUSTER=${local.name} 285 | ECS_LOGLEVEL=debug 286 | ECS_CONTAINER_INSTANCE_TAGS=${jsonencode(local.tags)} 287 | ECS_ENABLE_TASK_IAM_ROLE=true 288 | ECS_ENABLE_SPOT_INSTANCE_DRAINING=true 289 | EOF 290 | EOT 291 | } 292 | } 293 | 294 | name = "${local.name}-${each.key}" 295 | 296 | image_id = jsondecode(data.aws_ssm_parameter.ecs_optimized_ami.value)["image_id"] 297 | instance_type = each.value.instance_type 298 | 299 | security_groups = [module.autoscaling_sg.security_group_id] 300 | user_data = base64encode(each.value.user_data) 301 | ignore_desired_capacity_changes = true 302 | 303 | create_iam_instance_profile = true 304 | iam_role_name = local.name 305 | iam_role_description = "ECS role for ${local.name}" 306 | iam_role_policies = { 307 | AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" 308 | AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" 309 | } 310 | 311 | vpc_zone_identifier = module.vpc.private_subnets 312 | health_check_type = "EC2" 313 | min_size = 1 314 | max_size = 5 315 | desired_capacity = 2 316 | 317 | # https://github.com/hashicorp/terraform-provider-aws/issues/12582 318 | autoscaling_group_tags = { 319 | AmazonECSManaged = true 320 | } 321 | 322 | # Required for managed_termination_protection = "ENABLED" 323 | protect_from_scale_in = true 324 | 325 | # Spot instances 326 | use_mixed_instances_policy = each.value.use_mixed_instances_policy 327 | mixed_instances_policy = each.value.mixed_instances_policy 328 | 329 | tags = local.tags 330 | } 331 | 332 | module "autoscaling_sg" { 333 | source = "terraform-aws-modules/security-group/aws" 334 | version = "~> 5.0" 335 | 336 | name = local.name 337 | description = "Autoscaling group security group" 338 | vpc_id = module.vpc.vpc_id 339 | 340 | computed_ingress_with_source_security_group_id = [ 341 | { 342 | rule = "http-80-tcp" 343 | source_security_group_id = module.alb.security_group_id 344 | } 345 | ] 346 | number_of_computed_ingress_with_source_security_group_id = 1 347 | 348 | egress_rules = ["all-all"] 349 | 350 | tags = local.tags 351 | } 352 | 353 | module "vpc" { 354 | source = "terraform-aws-modules/vpc/aws" 355 | version = "~> 5.0" 356 | 357 | name = local.name 358 | cidr = local.vpc_cidr 359 | 360 | azs = local.azs 361 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] 362 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] 363 | 364 | enable_nat_gateway = true 365 | single_nat_gateway = true 366 | 367 | tags = local.tags 368 | } 369 | -------------------------------------------------------------------------------- /examples/ec2-autoscaling/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Cluster 3 | ################################################################################ 4 | 5 | output "cluster_arn" { 6 | description = "ARN that identifies the cluster" 7 | value = module.ecs_cluster.arn 8 | } 9 | 10 | output "cluster_id" { 11 | description = "ID that identifies the cluster" 12 | value = module.ecs_cluster.id 13 | } 14 | 15 | output "cluster_name" { 16 | description = "Name that identifies the cluster" 17 | value = module.ecs_cluster.name 18 | } 19 | 20 | output "cluster_capacity_providers" { 21 | description = "Map of cluster capacity providers attributes" 22 | value = module.ecs_cluster.cluster_capacity_providers 23 | } 24 | 25 | output "cluster_autoscaling_capacity_providers" { 26 | description = "Map of capacity providers created and their attributes" 27 | value = module.ecs_cluster.autoscaling_capacity_providers 28 | } 29 | 30 | ################################################################################ 31 | # Service 32 | ################################################################################ 33 | 34 | output "service_id" { 35 | description = "ARN that identifies the service" 36 | value = module.ecs_service.id 37 | } 38 | 39 | output "service_name" { 40 | description = "Name of the service" 41 | value = module.ecs_service.name 42 | } 43 | 44 | output "service_iam_role_name" { 45 | description = "Service IAM role name" 46 | value = module.ecs_service.iam_role_name 47 | } 48 | 49 | output "service_iam_role_arn" { 50 | description = "Service IAM role ARN" 51 | value = module.ecs_service.iam_role_arn 52 | } 53 | 54 | output "service_iam_role_unique_id" { 55 | description = "Stable and unique string identifying the service IAM role" 56 | value = module.ecs_service.iam_role_unique_id 57 | } 58 | 59 | output "service_container_definitions" { 60 | description = "Container definitions" 61 | value = module.ecs_service.container_definitions 62 | } 63 | 64 | output "service_task_definition_arn" { 65 | description = "Full ARN of the Task Definition (including both `family` and `revision`)" 66 | value = module.ecs_service.task_definition_arn 67 | } 68 | 69 | output "service_task_definition_revision" { 70 | description = "Revision of the task in a particular family" 71 | value = module.ecs_service.task_definition_revision 72 | } 73 | 74 | output "service_task_exec_iam_role_name" { 75 | description = "Task execution IAM role name" 76 | value = module.ecs_service.task_exec_iam_role_name 77 | } 78 | 79 | output "service_task_exec_iam_role_arn" { 80 | description = "Task execution IAM role ARN" 81 | value = module.ecs_service.task_exec_iam_role_arn 82 | } 83 | 84 | output "service_task_exec_iam_role_unique_id" { 85 | description = "Stable and unique string identifying the task execution IAM role" 86 | value = module.ecs_service.task_exec_iam_role_unique_id 87 | } 88 | 89 | output "service_tasks_iam_role_name" { 90 | description = "Tasks IAM role name" 91 | value = module.ecs_service.tasks_iam_role_name 92 | } 93 | 94 | output "service_tasks_iam_role_arn" { 95 | description = "Tasks IAM role ARN" 96 | value = module.ecs_service.tasks_iam_role_arn 97 | } 98 | 99 | output "service_tasks_iam_role_unique_id" { 100 | description = "Stable and unique string identifying the tasks IAM role" 101 | value = module.ecs_service.tasks_iam_role_unique_id 102 | } 103 | 104 | output "service_task_set_id" { 105 | description = "The ID of the task set" 106 | value = module.ecs_service.task_set_id 107 | } 108 | 109 | output "service_task_set_arn" { 110 | description = "The Amazon Resource Name (ARN) that identifies the task set" 111 | value = module.ecs_service.task_set_arn 112 | } 113 | 114 | output "service_task_set_stability_status" { 115 | description = "The stability status. This indicates whether the task set has reached a steady state" 116 | value = module.ecs_service.task_set_stability_status 117 | } 118 | 119 | output "service_task_set_status" { 120 | description = "The status of the task set" 121 | value = module.ecs_service.task_set_status 122 | } 123 | 124 | output "service_autoscaling_policies" { 125 | description = "Map of autoscaling policies and their attributes" 126 | value = module.ecs_service.autoscaling_policies 127 | } 128 | 129 | output "service_autoscaling_scheduled_actions" { 130 | description = "Map of autoscaling scheduled actions and their attributes" 131 | value = module.ecs_service.autoscaling_scheduled_actions 132 | } 133 | 134 | ################################################################################ 135 | # Application Load Balancer 136 | ################################################################################ 137 | 138 | output "alb_dns_name" { 139 | description = "The DNS name of the load balancer" 140 | value = module.alb.dns_name 141 | } 142 | -------------------------------------------------------------------------------- /examples/ec2-autoscaling/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-ecs/7570dbfcbab7ab2141155927d424b7b3f4305b4f/examples/ec2-autoscaling/variables.tf -------------------------------------------------------------------------------- /examples/ec2-autoscaling/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/fargate/README.md: -------------------------------------------------------------------------------- 1 | # ECS Clusters w/ Fargate 2 | 3 | Configuration in this directory creates: 4 | 5 | - ECS cluster using Fargate (on-demand and spot) capacity providers 6 | - Example ECS service that utilizes 7 | - AWS Firelens using FluentBit sidecar container definition 8 | - Service connect configuration 9 | - Load balancer target group attachment 10 | - Security group for access to the example service 11 | 12 | ## Usage 13 | 14 | To run this example you need to execute: 15 | 16 | ```bash 17 | $ terraform init 18 | $ terraform plan 19 | $ terraform apply 20 | ``` 21 | 22 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 23 | 24 | 25 | ## Requirements 26 | 27 | | Name | Version | 28 | |------|---------| 29 | | [terraform](#requirement\_terraform) | >= 1.0 | 30 | | [aws](#requirement\_aws) | >= 4.66.1 | 31 | 32 | ## Providers 33 | 34 | | Name | Version | 35 | |------|---------| 36 | | [aws](#provider\_aws) | >= 4.66.1 | 37 | 38 | ## Modules 39 | 40 | | Name | Source | Version | 41 | |------|--------|---------| 42 | | [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 9.0 | 43 | | [ecs\_cluster](#module\_ecs\_cluster) | ../../modules/cluster | n/a | 44 | | [ecs\_service](#module\_ecs\_service) | ../../modules/service | n/a | 45 | | [ecs\_task\_definition](#module\_ecs\_task\_definition) | ../../modules/service | n/a | 46 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | 47 | 48 | ## Resources 49 | 50 | | Name | Type | 51 | |------|------| 52 | | [aws_service_discovery_http_namespace.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/service_discovery_http_namespace) | resource | 53 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 54 | | [aws_ssm_parameter.fluentbit](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | 55 | 56 | ## Inputs 57 | 58 | No inputs. 59 | 60 | ## Outputs 61 | 62 | | Name | Description | 63 | |------|-------------| 64 | | [cluster\_arn](#output\_cluster\_arn) | ARN that identifies the cluster | 65 | | [cluster\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes | 66 | | [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes | 67 | | [cluster\_id](#output\_cluster\_id) | ID that identifies the cluster | 68 | | [cluster\_name](#output\_cluster\_name) | Name that identifies the cluster | 69 | | [service\_autoscaling\_policies](#output\_service\_autoscaling\_policies) | Map of autoscaling policies and their attributes | 70 | | [service\_autoscaling\_scheduled\_actions](#output\_service\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes | 71 | | [service\_container\_definitions](#output\_service\_container\_definitions) | Container definitions | 72 | | [service\_iam\_role\_arn](#output\_service\_iam\_role\_arn) | Service IAM role ARN | 73 | | [service\_iam\_role\_name](#output\_service\_iam\_role\_name) | Service IAM role name | 74 | | [service\_iam\_role\_unique\_id](#output\_service\_iam\_role\_unique\_id) | Stable and unique string identifying the service IAM role | 75 | | [service\_id](#output\_service\_id) | ARN that identifies the service | 76 | | [service\_name](#output\_service\_name) | Name of the service | 77 | | [service\_security\_group\_arn](#output\_service\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group | 78 | | [service\_security\_group\_id](#output\_service\_security\_group\_id) | ID of the security group | 79 | | [service\_task\_definition\_arn](#output\_service\_task\_definition\_arn) | Full ARN of the Task Definition (including both `family` and `revision`) | 80 | | [service\_task\_definition\_family](#output\_service\_task\_definition\_family) | The unique name of the task definition | 81 | | [service\_task\_definition\_family\_revision](#output\_service\_task\_definition\_family\_revision) | The family and revision (family:revision) of the task definition | 82 | | [service\_task\_definition\_revision](#output\_service\_task\_definition\_revision) | Revision of the task in a particular family | 83 | | [service\_task\_exec\_iam\_role\_arn](#output\_service\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN | 84 | | [service\_task\_exec\_iam\_role\_name](#output\_service\_task\_exec\_iam\_role\_name) | Task execution IAM role name | 85 | | [service\_task\_exec\_iam\_role\_unique\_id](#output\_service\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role | 86 | | [service\_task\_set\_arn](#output\_service\_task\_set\_arn) | The Amazon Resource Name (ARN) that identifies the task set | 87 | | [service\_task\_set\_id](#output\_service\_task\_set\_id) | The ID of the task set | 88 | | [service\_task\_set\_stability\_status](#output\_service\_task\_set\_stability\_status) | The stability status. This indicates whether the task set has reached a steady state | 89 | | [service\_task\_set\_status](#output\_service\_task\_set\_status) | The status of the task set | 90 | | [service\_tasks\_iam\_role\_arn](#output\_service\_tasks\_iam\_role\_arn) | Tasks IAM role ARN | 91 | | [service\_tasks\_iam\_role\_name](#output\_service\_tasks\_iam\_role\_name) | Tasks IAM role name | 92 | | [service\_tasks\_iam\_role\_unique\_id](#output\_service\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role | 93 | | [task\_definition\_run\_task\_command](#output\_task\_definition\_run\_task\_command) | awscli command to run the standalone task | 94 | 95 | 96 | ## License 97 | 98 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE). 99 | -------------------------------------------------------------------------------- /examples/fargate/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | region = "eu-west-1" 9 | name = "ex-${basename(path.cwd)}" 10 | 11 | vpc_cidr = "10.0.0.0/16" 12 | azs = slice(data.aws_availability_zones.available.names, 0, 3) 13 | 14 | container_name = "ecsdemo-frontend" 15 | container_port = 3000 16 | 17 | tags = { 18 | Name = local.name 19 | Example = local.name 20 | Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs" 21 | } 22 | } 23 | 24 | ################################################################################ 25 | # Cluster 26 | ################################################################################ 27 | 28 | module "ecs_cluster" { 29 | source = "../../modules/cluster" 30 | 31 | cluster_name = local.name 32 | 33 | # Capacity provider 34 | fargate_capacity_providers = { 35 | FARGATE = { 36 | default_capacity_provider_strategy = { 37 | weight = 50 38 | base = 20 39 | } 40 | } 41 | FARGATE_SPOT = { 42 | default_capacity_provider_strategy = { 43 | weight = 50 44 | } 45 | } 46 | } 47 | 48 | tags = local.tags 49 | } 50 | 51 | ################################################################################ 52 | # Service 53 | ################################################################################ 54 | 55 | module "ecs_service" { 56 | source = "../../modules/service" 57 | 58 | name = local.name 59 | cluster_arn = module.ecs_cluster.arn 60 | 61 | cpu = 1024 62 | memory = 4096 63 | 64 | # Enables ECS Exec 65 | enable_execute_command = true 66 | 67 | # Container definition(s) 68 | container_definitions = { 69 | 70 | fluent-bit = { 71 | cpu = 512 72 | memory = 1024 73 | essential = true 74 | image = nonsensitive(data.aws_ssm_parameter.fluentbit.value) 75 | firelens_configuration = { 76 | type = "fluentbit" 77 | } 78 | memory_reservation = 50 79 | user = "0" 80 | } 81 | 82 | (local.container_name) = { 83 | cpu = 512 84 | memory = 1024 85 | essential = true 86 | image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50" 87 | port_mappings = [ 88 | { 89 | name = local.container_name 90 | containerPort = local.container_port 91 | hostPort = local.container_port 92 | protocol = "tcp" 93 | } 94 | ] 95 | 96 | # Example image used requires access to write to root filesystem 97 | readonly_root_filesystem = false 98 | 99 | dependencies = [{ 100 | containerName = "fluent-bit" 101 | condition = "START" 102 | }] 103 | 104 | enable_cloudwatch_logging = false 105 | log_configuration = { 106 | logDriver = "awsfirelens" 107 | options = { 108 | Name = "firehose" 109 | region = local.region 110 | delivery_stream = "my-stream" 111 | log-driver-buffer-limit = "2097152" 112 | } 113 | } 114 | 115 | linux_parameters = { 116 | capabilities = { 117 | add = [] 118 | drop = [ 119 | "NET_RAW" 120 | ] 121 | } 122 | } 123 | 124 | # Not required for fluent-bit, just an example 125 | volumes_from = [{ 126 | sourceContainer = "fluent-bit" 127 | readOnly = false 128 | }] 129 | 130 | memory_reservation = 100 131 | } 132 | } 133 | 134 | service_connect_configuration = { 135 | namespace = aws_service_discovery_http_namespace.this.arn 136 | service = { 137 | client_alias = { 138 | port = local.container_port 139 | dns_name = local.container_name 140 | } 141 | port_name = local.container_name 142 | discovery_name = local.container_name 143 | } 144 | } 145 | 146 | load_balancer = { 147 | service = { 148 | target_group_arn = module.alb.target_groups["ex_ecs"].arn 149 | container_name = local.container_name 150 | container_port = local.container_port 151 | } 152 | } 153 | 154 | subnet_ids = module.vpc.private_subnets 155 | security_group_rules = { 156 | alb_ingress_3000 = { 157 | type = "ingress" 158 | from_port = local.container_port 159 | to_port = local.container_port 160 | protocol = "tcp" 161 | description = "Service port" 162 | source_security_group_id = module.alb.security_group_id 163 | } 164 | egress_all = { 165 | type = "egress" 166 | from_port = 0 167 | to_port = 0 168 | protocol = "-1" 169 | cidr_blocks = ["0.0.0.0/0"] 170 | } 171 | } 172 | 173 | service_tags = { 174 | "ServiceTag" = "Tag on service level" 175 | } 176 | 177 | tags = local.tags 178 | } 179 | 180 | ################################################################################ 181 | # Standalone Task Definition (w/o Service) 182 | ################################################################################ 183 | 184 | module "ecs_task_definition" { 185 | source = "../../modules/service" 186 | 187 | # Service 188 | name = "${local.name}-standalone" 189 | cluster_arn = module.ecs_cluster.arn 190 | create_service = false 191 | 192 | # Task Definition 193 | volume = { 194 | ex-vol = {} 195 | } 196 | 197 | runtime_platform = { 198 | cpu_architecture = "ARM64" 199 | operating_system_family = "LINUX" 200 | } 201 | 202 | # Container definition(s) 203 | container_definitions = { 204 | al2023 = { 205 | image = "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal" 206 | 207 | mount_points = [ 208 | { 209 | sourceVolume = "ex-vol", 210 | containerPath = "/var/www/ex-vol" 211 | } 212 | ] 213 | 214 | command = ["echo hello world"] 215 | entrypoint = ["/usr/bin/sh", "-c"] 216 | } 217 | } 218 | 219 | subnet_ids = module.vpc.private_subnets 220 | 221 | security_group_rules = { 222 | egress_all = { 223 | type = "egress" 224 | from_port = 0 225 | to_port = 0 226 | protocol = "-1" 227 | cidr_blocks = ["0.0.0.0/0"] 228 | } 229 | } 230 | 231 | tags = local.tags 232 | } 233 | 234 | ################################################################################ 235 | # Supporting Resources 236 | ################################################################################ 237 | 238 | data "aws_ssm_parameter" "fluentbit" { 239 | name = "/aws/service/aws-for-fluent-bit/stable" 240 | } 241 | 242 | resource "aws_service_discovery_http_namespace" "this" { 243 | name = local.name 244 | description = "CloudMap namespace for ${local.name}" 245 | tags = local.tags 246 | } 247 | 248 | module "alb" { 249 | source = "terraform-aws-modules/alb/aws" 250 | version = "~> 9.0" 251 | 252 | name = local.name 253 | 254 | load_balancer_type = "application" 255 | 256 | vpc_id = module.vpc.vpc_id 257 | subnets = module.vpc.public_subnets 258 | 259 | # For example only 260 | enable_deletion_protection = false 261 | 262 | # Security Group 263 | security_group_ingress_rules = { 264 | all_http = { 265 | from_port = 80 266 | to_port = 80 267 | ip_protocol = "tcp" 268 | cidr_ipv4 = "0.0.0.0/0" 269 | } 270 | } 271 | security_group_egress_rules = { 272 | all = { 273 | ip_protocol = "-1" 274 | cidr_ipv4 = module.vpc.vpc_cidr_block 275 | } 276 | } 277 | 278 | listeners = { 279 | ex_http = { 280 | port = 80 281 | protocol = "HTTP" 282 | 283 | forward = { 284 | target_group_key = "ex_ecs" 285 | } 286 | } 287 | } 288 | 289 | target_groups = { 290 | ex_ecs = { 291 | backend_protocol = "HTTP" 292 | backend_port = local.container_port 293 | target_type = "ip" 294 | deregistration_delay = 5 295 | load_balancing_cross_zone_enabled = true 296 | 297 | health_check = { 298 | enabled = true 299 | healthy_threshold = 5 300 | interval = 30 301 | matcher = "200" 302 | path = "/" 303 | port = "traffic-port" 304 | protocol = "HTTP" 305 | timeout = 5 306 | unhealthy_threshold = 2 307 | } 308 | 309 | # There's nothing to attach here in this definition. Instead, 310 | # ECS will attach the IPs of the tasks to this target group 311 | create_attachment = false 312 | } 313 | } 314 | 315 | tags = local.tags 316 | } 317 | 318 | module "vpc" { 319 | source = "terraform-aws-modules/vpc/aws" 320 | version = "~> 5.0" 321 | 322 | name = local.name 323 | cidr = local.vpc_cidr 324 | 325 | azs = local.azs 326 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] 327 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] 328 | 329 | enable_nat_gateway = true 330 | single_nat_gateway = true 331 | 332 | tags = local.tags 333 | } 334 | -------------------------------------------------------------------------------- /examples/fargate/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Cluster 3 | ################################################################################ 4 | 5 | output "cluster_arn" { 6 | description = "ARN that identifies the cluster" 7 | value = module.ecs_cluster.arn 8 | } 9 | 10 | output "cluster_id" { 11 | description = "ID that identifies the cluster" 12 | value = module.ecs_cluster.id 13 | } 14 | 15 | output "cluster_name" { 16 | description = "Name that identifies the cluster" 17 | value = module.ecs_cluster.name 18 | } 19 | 20 | output "cluster_capacity_providers" { 21 | description = "Map of cluster capacity providers attributes" 22 | value = module.ecs_cluster.cluster_capacity_providers 23 | } 24 | 25 | output "cluster_autoscaling_capacity_providers" { 26 | description = "Map of capacity providers created and their attributes" 27 | value = module.ecs_cluster.autoscaling_capacity_providers 28 | } 29 | 30 | ################################################################################ 31 | # Service 32 | ################################################################################ 33 | 34 | output "service_id" { 35 | description = "ARN that identifies the service" 36 | value = module.ecs_service.id 37 | } 38 | 39 | output "service_name" { 40 | description = "Name of the service" 41 | value = module.ecs_service.name 42 | } 43 | 44 | output "service_iam_role_name" { 45 | description = "Service IAM role name" 46 | value = module.ecs_service.iam_role_name 47 | } 48 | 49 | output "service_iam_role_arn" { 50 | description = "Service IAM role ARN" 51 | value = module.ecs_service.iam_role_arn 52 | } 53 | 54 | output "service_iam_role_unique_id" { 55 | description = "Stable and unique string identifying the service IAM role" 56 | value = module.ecs_service.iam_role_unique_id 57 | } 58 | 59 | output "service_container_definitions" { 60 | description = "Container definitions" 61 | value = module.ecs_service.container_definitions 62 | } 63 | 64 | output "service_task_definition_arn" { 65 | description = "Full ARN of the Task Definition (including both `family` and `revision`)" 66 | value = module.ecs_service.task_definition_arn 67 | } 68 | 69 | output "service_task_definition_revision" { 70 | description = "Revision of the task in a particular family" 71 | value = module.ecs_service.task_definition_revision 72 | } 73 | 74 | output "service_task_definition_family" { 75 | description = "The unique name of the task definition" 76 | value = module.ecs_service.task_definition_family 77 | } 78 | 79 | output "service_task_definition_family_revision" { 80 | description = "The family and revision (family:revision) of the task definition" 81 | value = module.ecs_service.task_definition_family_revision 82 | } 83 | 84 | output "service_task_exec_iam_role_name" { 85 | description = "Task execution IAM role name" 86 | value = module.ecs_service.task_exec_iam_role_name 87 | } 88 | 89 | output "service_task_exec_iam_role_arn" { 90 | description = "Task execution IAM role ARN" 91 | value = module.ecs_service.task_exec_iam_role_arn 92 | } 93 | 94 | output "service_task_exec_iam_role_unique_id" { 95 | description = "Stable and unique string identifying the task execution IAM role" 96 | value = module.ecs_service.task_exec_iam_role_unique_id 97 | } 98 | 99 | output "service_tasks_iam_role_name" { 100 | description = "Tasks IAM role name" 101 | value = module.ecs_service.tasks_iam_role_name 102 | } 103 | 104 | output "service_tasks_iam_role_arn" { 105 | description = "Tasks IAM role ARN" 106 | value = module.ecs_service.tasks_iam_role_arn 107 | } 108 | 109 | output "service_tasks_iam_role_unique_id" { 110 | description = "Stable and unique string identifying the tasks IAM role" 111 | value = module.ecs_service.tasks_iam_role_unique_id 112 | } 113 | 114 | output "service_task_set_id" { 115 | description = "The ID of the task set" 116 | value = module.ecs_service.task_set_id 117 | } 118 | 119 | output "service_task_set_arn" { 120 | description = "The Amazon Resource Name (ARN) that identifies the task set" 121 | value = module.ecs_service.task_set_arn 122 | } 123 | 124 | output "service_task_set_stability_status" { 125 | description = "The stability status. This indicates whether the task set has reached a steady state" 126 | value = module.ecs_service.task_set_stability_status 127 | } 128 | 129 | output "service_task_set_status" { 130 | description = "The status of the task set" 131 | value = module.ecs_service.task_set_status 132 | } 133 | 134 | output "service_autoscaling_policies" { 135 | description = "Map of autoscaling policies and their attributes" 136 | value = module.ecs_service.autoscaling_policies 137 | } 138 | 139 | output "service_autoscaling_scheduled_actions" { 140 | description = "Map of autoscaling scheduled actions and their attributes" 141 | value = module.ecs_service.autoscaling_scheduled_actions 142 | } 143 | 144 | output "service_security_group_arn" { 145 | description = "Amazon Resource Name (ARN) of the security group" 146 | value = module.ecs_service.security_group_arn 147 | } 148 | 149 | output "service_security_group_id" { 150 | description = "ID of the security group" 151 | value = module.ecs_service.security_group_id 152 | } 153 | 154 | ################################################################################ 155 | # Standalone Task Definition (w/o Service) 156 | ################################################################################ 157 | 158 | output "task_definition_run_task_command" { 159 | description = "awscli command to run the standalone task" 160 | value = < v if var.create } 55 | 56 | create = try(each.value.create, true) 57 | create_service = try(each.value.create_service, true) 58 | 59 | # Service 60 | ignore_task_definition_changes = try(each.value.ignore_task_definition_changes, false) 61 | alarms = try(each.value.alarms, {}) 62 | capacity_provider_strategy = try(each.value.capacity_provider_strategy, {}) 63 | cluster_arn = module.cluster.arn 64 | deployment_circuit_breaker = try(each.value.deployment_circuit_breaker, {}) 65 | deployment_controller = try(each.value.deployment_controller, {}) 66 | deployment_maximum_percent = try(each.value.deployment_maximum_percent, 200) 67 | deployment_minimum_healthy_percent = try(each.value.deployment_minimum_healthy_percent, 66) 68 | desired_count = try(each.value.desired_count, 1) 69 | enable_ecs_managed_tags = try(each.value.enable_ecs_managed_tags, true) 70 | enable_execute_command = try(each.value.enable_execute_command, false) 71 | force_new_deployment = try(each.value.force_new_deployment, true) 72 | health_check_grace_period_seconds = try(each.value.health_check_grace_period_seconds, null) 73 | launch_type = try(each.value.launch_type, "FARGATE") 74 | load_balancer = lookup(each.value, "load_balancer", {}) 75 | name = try(each.value.name, each.key) 76 | assign_public_ip = try(each.value.assign_public_ip, false) 77 | security_group_ids = lookup(each.value, "security_group_ids", []) 78 | subnet_ids = lookup(each.value, "subnet_ids", []) 79 | ordered_placement_strategy = try(each.value.ordered_placement_strategy, {}) 80 | placement_constraints = try(each.value.placement_constraints, {}) 81 | platform_version = try(each.value.platform_version, null) 82 | propagate_tags = try(each.value.propagate_tags, null) 83 | scheduling_strategy = try(each.value.scheduling_strategy, null) 84 | service_connect_configuration = lookup(each.value, "service_connect_configuration", {}) 85 | service_registries = lookup(each.value, "service_registries", {}) 86 | timeouts = try(each.value.timeouts, {}) 87 | triggers = try(each.value.triggers, {}) 88 | wait_for_steady_state = try(each.value.wait_for_steady_state, null) 89 | 90 | # Service IAM role 91 | create_iam_role = try(each.value.create_iam_role, true) 92 | iam_role_arn = lookup(each.value, "iam_role_arn", null) 93 | iam_role_name = try(each.value.iam_role_name, null) 94 | iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, true) 95 | iam_role_path = try(each.value.iam_role_path, null) 96 | iam_role_description = try(each.value.iam_role_description, null) 97 | iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, null) 98 | iam_role_tags = try(each.value.iam_role_tags, {}) 99 | iam_role_statements = lookup(each.value, "iam_role_statements", {}) 100 | 101 | # Task definition 102 | create_task_definition = try(each.value.create_task_definition, true) 103 | task_definition_arn = lookup(each.value, "task_definition_arn", null) 104 | container_definitions = try(each.value.container_definitions, {}) 105 | container_definition_defaults = try(each.value.container_definition_defaults, {}) 106 | cpu = try(each.value.cpu, 1024) 107 | ephemeral_storage = try(each.value.ephemeral_storage, {}) 108 | family = try(each.value.family, null) 109 | inference_accelerator = try(each.value.inference_accelerator, {}) 110 | ipc_mode = try(each.value.ipc_mode, null) 111 | memory = try(each.value.memory, 2048) 112 | network_mode = try(each.value.network_mode, "awsvpc") 113 | pid_mode = try(each.value.pid_mode, null) 114 | proxy_configuration = try(each.value.proxy_configuration, {}) 115 | requires_compatibilities = try(each.value.requires_compatibilities, ["FARGATE"]) 116 | runtime_platform = try(each.value.runtime_platform, { 117 | operating_system_family = "LINUX" 118 | cpu_architecture = "X86_64" 119 | }) 120 | skip_destroy = try(each.value.skip_destroy, null) 121 | volume = try(each.value.volume, {}) 122 | task_tags = try(each.value.task_tags, {}) 123 | 124 | # Task execution IAM role 125 | create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, true) 126 | task_exec_iam_role_arn = lookup(each.value, "task_exec_iam_role_arn", null) 127 | task_exec_iam_role_name = try(each.value.task_exec_iam_role_name, null) 128 | task_exec_iam_role_use_name_prefix = try(each.value.task_exec_iam_role_use_name_prefix, true) 129 | task_exec_iam_role_path = try(each.value.task_exec_iam_role_path, null) 130 | task_exec_iam_role_description = try(each.value.task_exec_iam_role_description, null) 131 | task_exec_iam_role_permissions_boundary = try(each.value.task_exec_iam_role_permissions_boundary, null) 132 | task_exec_iam_role_tags = try(each.value.task_exec_iam_role_tags, {}) 133 | task_exec_iam_role_policies = try(each.value.task_exec_iam_role_policies, {}) 134 | task_exec_iam_role_max_session_duration = try(each.value.task_exec_iam_role_max_session_duration, null) 135 | 136 | # Task execution IAM role policy 137 | create_task_exec_policy = try(each.value.create_task_exec_policy, true) 138 | task_exec_ssm_param_arns = lookup(each.value, "task_exec_ssm_param_arns", ["arn:aws:ssm:*:*:parameter/*"]) 139 | task_exec_secret_arns = lookup(each.value, "task_exec_secret_arns", ["arn:aws:secretsmanager:*:*:secret:*"]) 140 | task_exec_iam_statements = lookup(each.value, "task_exec_iam_statements", {}) 141 | 142 | # Tasks - IAM role 143 | create_tasks_iam_role = try(each.value.create_tasks_iam_role, true) 144 | tasks_iam_role_arn = lookup(each.value, "tasks_iam_role_arn", null) 145 | tasks_iam_role_name = try(each.value.tasks_iam_role_name, null) 146 | tasks_iam_role_use_name_prefix = try(each.value.tasks_iam_role_use_name_prefix, true) 147 | tasks_iam_role_path = try(each.value.tasks_iam_role_path, null) 148 | tasks_iam_role_description = try(each.value.tasks_iam_role_description, null) 149 | tasks_iam_role_permissions_boundary = try(each.value.tasks_iam_role_permissions_boundary, null) 150 | tasks_iam_role_tags = try(each.value.tasks_iam_role_tags, {}) 151 | tasks_iam_role_policies = lookup(each.value, "tasks_iam_role_policies", {}) 152 | tasks_iam_role_statements = lookup(each.value, "tasks_iam_role_statements", {}) 153 | 154 | # Task set 155 | external_id = try(each.value.external_id, null) 156 | scale = try(each.value.scale, {}) 157 | force_delete = try(each.value.force_delete, null) 158 | wait_until_stable = try(each.value.wait_until_stable, null) 159 | wait_until_stable_timeout = try(each.value.wait_until_stable_timeout, null) 160 | 161 | # Autoscaling 162 | enable_autoscaling = try(each.value.enable_autoscaling, true) 163 | autoscaling_min_capacity = try(each.value.autoscaling_min_capacity, 1) 164 | autoscaling_max_capacity = try(each.value.autoscaling_max_capacity, 10) 165 | autoscaling_policies = try(each.value.autoscaling_policies, { 166 | cpu = { 167 | policy_type = "TargetTrackingScaling" 168 | 169 | target_tracking_scaling_policy_configuration = { 170 | predefined_metric_specification = { 171 | predefined_metric_type = "ECSServiceAverageCPUUtilization" 172 | } 173 | } 174 | } 175 | memory = { 176 | policy_type = "TargetTrackingScaling" 177 | 178 | target_tracking_scaling_policy_configuration = { 179 | predefined_metric_specification = { 180 | predefined_metric_type = "ECSServiceAverageMemoryUtilization" 181 | } 182 | } 183 | } 184 | }) 185 | autoscaling_scheduled_actions = try(each.value.autoscaling_scheduled_actions, {}) 186 | 187 | # Security Group 188 | create_security_group = try(each.value.create_security_group, true) 189 | security_group_name = try(each.value.security_group_name, null) 190 | security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, true) 191 | security_group_description = try(each.value.security_group_description, null) 192 | security_group_rules = lookup(each.value, "security_group_rules", {}) 193 | security_group_tags = try(each.value.security_group_tags, {}) 194 | 195 | tags = merge(var.tags, try(each.value.tags, {})) 196 | } 197 | -------------------------------------------------------------------------------- /modules/cluster/README.md: -------------------------------------------------------------------------------- 1 | # Amazon ECS Cluster Terraform Module 2 | 3 | Terraform module which creates Amazon ECS (Elastic Container Service) cluster resources on AWS. 4 | 5 | ## Available Features 6 | 7 | - ECS cluster 8 | - Fargate capacity providers 9 | - EC2 AutoScaling Group capacity providers 10 | - ECS Service w/ task definition, task set, and container definition support 11 | 12 | For more details see the [design doc](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/docs/README.md) 13 | 14 | ## Usage 15 | 16 | ### Fargate Capacity Providers 17 | 18 | ```hcl 19 | module "ecs_cluster" { 20 | source = "terraform-aws-modules/ecs/aws//modules/cluster" 21 | 22 | cluster_name = "ecs-fargate" 23 | 24 | cluster_configuration = { 25 | execute_command_configuration = { 26 | logging = "OVERRIDE" 27 | log_configuration = { 28 | cloud_watch_log_group_name = "/aws/ecs/aws-ec2" 29 | } 30 | } 31 | } 32 | 33 | fargate_capacity_providers = { 34 | FARGATE = { 35 | default_capacity_provider_strategy = { 36 | weight = 50 37 | } 38 | } 39 | FARGATE_SPOT = { 40 | default_capacity_provider_strategy = { 41 | weight = 50 42 | } 43 | } 44 | } 45 | 46 | tags = { 47 | Environment = "Development" 48 | Project = "EcsEc2" 49 | } 50 | } 51 | ``` 52 | 53 | ### EC2 Autoscaling Capacity Providers 54 | 55 | ```hcl 56 | module "ecs_cluster" { 57 | source = "terraform-aws-modules/ecs/aws//modules/cluster" 58 | 59 | cluster_name = "ecs-ec2" 60 | 61 | cluster_configuration = { 62 | execute_command_configuration = { 63 | logging = "OVERRIDE" 64 | log_configuration = { 65 | cloud_watch_log_group_name = "/aws/ecs/aws-ec2" 66 | } 67 | } 68 | } 69 | 70 | autoscaling_capacity_providers = { 71 | one = { 72 | auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-one-20220603194933774300000011" 73 | managed_termination_protection = "ENABLED" 74 | 75 | managed_scaling = { 76 | maximum_scaling_step_size = 5 77 | minimum_scaling_step_size = 1 78 | status = "ENABLED" 79 | target_capacity = 60 80 | } 81 | 82 | default_capacity_provider_strategy = { 83 | weight = 60 84 | base = 20 85 | } 86 | } 87 | two = { 88 | auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-two-20220603194933774300000022" 89 | managed_termination_protection = "ENABLED" 90 | 91 | managed_scaling = { 92 | maximum_scaling_step_size = 15 93 | minimum_scaling_step_size = 5 94 | status = "ENABLED" 95 | target_capacity = 90 96 | } 97 | 98 | default_capacity_provider_strategy = { 99 | weight = 40 100 | } 101 | } 102 | } 103 | 104 | tags = { 105 | Environment = "Development" 106 | Project = "EcsEc2" 107 | } 108 | } 109 | ``` 110 | 111 | ## Conditional Creation 112 | 113 | The following values are provided to toggle on/off creation of the associated resources as desired: 114 | 115 | ```hcl 116 | module "ecs_cluster" { 117 | source = "terraform-aws-modules/ecs/aws//modules/cluster" 118 | 119 | # Disable creation of cluster and all resources 120 | create = false 121 | 122 | # ... omitted 123 | } 124 | ``` 125 | 126 | ## Examples 127 | 128 | - [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete) 129 | - [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling) 130 | - [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate) 131 | 132 | 133 | ## Requirements 134 | 135 | | Name | Version | 136 | |------|---------| 137 | | [terraform](#requirement\_terraform) | >= 1.0 | 138 | | [aws](#requirement\_aws) | >= 4.66.1 | 139 | 140 | ## Providers 141 | 142 | | Name | Version | 143 | |------|---------| 144 | | [aws](#provider\_aws) | >= 4.66.1 | 145 | 146 | ## Modules 147 | 148 | No modules. 149 | 150 | ## Resources 151 | 152 | | Name | Type | 153 | |------|------| 154 | | [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 155 | | [aws_ecs_capacity_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_capacity_provider) | resource | 156 | | [aws_ecs_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource | 157 | | [aws_ecs_cluster_capacity_providers.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster_capacity_providers) | resource | 158 | | [aws_iam_policy.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 159 | | [aws_iam_role.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 160 | | [aws_iam_role_policy_attachment.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 161 | | [aws_iam_role_policy_attachment.task_exec_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 162 | | [aws_iam_policy_document.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 163 | | [aws_iam_policy_document.task_exec_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 164 | 165 | ## Inputs 166 | 167 | | Name | Description | Type | Default | Required | 168 | |------|-------------|------|---------|:--------:| 169 | | [autoscaling\_capacity\_providers](#input\_autoscaling\_capacity\_providers) | Map of autoscaling capacity provider definitions to create for the cluster | `any` | `{}` | no | 170 | | [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no | 171 | | [cloudwatch\_log\_group\_name](#input\_cloudwatch\_log\_group\_name) | Custom name of CloudWatch Log Group for ECS cluster | `string` | `null` | no | 172 | | [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events | `number` | `90` | no | 173 | | [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | A map of additional tags to add to the log group created | `map(string)` | `{}` | no | 174 | | [cluster\_configuration](#input\_cluster\_configuration) | The execute command configuration for the cluster | `any` | `{}` | no | 175 | | [cluster\_name](#input\_cluster\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no | 176 | | [cluster\_service\_connect\_defaults](#input\_cluster\_service\_connect\_defaults) | Configures a default Service Connect namespace | `map(string)` | `{}` | no | 177 | | [cluster\_settings](#input\_cluster\_settings) | List of configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster | `any` |
[
{
"name": "containerInsights",
"value": "enabled"
}
]
| no | 178 | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | 179 | | [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled | `bool` | `true` | no | 180 | | [create\_task\_exec\_iam\_role](#input\_create\_task\_exec\_iam\_role) | Determines whether the ECS task definition IAM role should be created | `bool` | `false` | no | 181 | | [create\_task\_exec\_policy](#input\_create\_task\_exec\_policy) | Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters | `bool` | `true` | no | 182 | | [default\_capacity\_provider\_use\_fargate](#input\_default\_capacity\_provider\_use\_fargate) | Determines whether to use Fargate or autoscaling for default capacity provider strategy | `bool` | `true` | no | 183 | | [fargate\_capacity\_providers](#input\_fargate\_capacity\_providers) | Map of Fargate capacity provider definitions to use for the cluster | `any` | `{}` | no | 184 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | 185 | | [task\_exec\_iam\_role\_description](#input\_task\_exec\_iam\_role\_description) | Description of the role | `string` | `null` | no | 186 | | [task\_exec\_iam\_role\_name](#input\_task\_exec\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no | 187 | | [task\_exec\_iam\_role\_path](#input\_task\_exec\_iam\_role\_path) | IAM role path | `string` | `null` | no | 188 | | [task\_exec\_iam\_role\_permissions\_boundary](#input\_task\_exec\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | 189 | | [task\_exec\_iam\_role\_policies](#input\_task\_exec\_iam\_role\_policies) | Map of IAM role policy ARNs to attach to the IAM role | `map(string)` | `{}` | no | 190 | | [task\_exec\_iam\_role\_tags](#input\_task\_exec\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no | 191 | | [task\_exec\_iam\_role\_use\_name\_prefix](#input\_task\_exec\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix | `bool` | `true` | no | 192 | | [task\_exec\_iam\_statements](#input\_task\_exec\_iam\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no | 193 | | [task\_exec\_secret\_arns](#input\_task\_exec\_secret\_arns) | List of SecretsManager secret ARNs the task execution role will be permitted to get/read | `list(string)` |
[
"arn:aws:secretsmanager:*:*:secret:*"
]
| no | 194 | | [task\_exec\_ssm\_param\_arns](#input\_task\_exec\_ssm\_param\_arns) | List of SSM parameter ARNs the task execution role will be permitted to get/read | `list(string)` |
[
"arn:aws:ssm:*:*:parameter/*"
]
| no | 195 | 196 | ## Outputs 197 | 198 | | Name | Description | 199 | |------|-------------| 200 | | [arn](#output\_arn) | ARN that identifies the cluster | 201 | | [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes | 202 | | [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | ARN of CloudWatch log group created | 203 | | [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | Name of CloudWatch log group created | 204 | | [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes | 205 | | [id](#output\_id) | ID that identifies the cluster | 206 | | [name](#output\_name) | Name that identifies the cluster | 207 | | [task\_exec\_iam\_role\_arn](#output\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN | 208 | | [task\_exec\_iam\_role\_name](#output\_task\_exec\_iam\_role\_name) | Task execution IAM role name | 209 | | [task\_exec\_iam\_role\_unique\_id](#output\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role | 210 | 211 | 212 | ## License 213 | 214 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE). 215 | -------------------------------------------------------------------------------- /modules/cluster/main.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Cluster 3 | ################################################################################ 4 | 5 | locals { 6 | execute_command_configuration = { 7 | logging = "OVERRIDE" 8 | log_configuration = { 9 | cloud_watch_log_group_name = try(aws_cloudwatch_log_group.this[0].name, null) 10 | } 11 | } 12 | } 13 | 14 | resource "aws_ecs_cluster" "this" { 15 | count = var.create ? 1 : 0 16 | 17 | name = var.cluster_name 18 | 19 | dynamic "configuration" { 20 | for_each = var.create_cloudwatch_log_group ? [var.cluster_configuration] : [] 21 | 22 | content { 23 | dynamic "execute_command_configuration" { 24 | for_each = try([merge(local.execute_command_configuration, configuration.value.execute_command_configuration)], [{}]) 25 | 26 | content { 27 | kms_key_id = try(execute_command_configuration.value.kms_key_id, null) 28 | logging = try(execute_command_configuration.value.logging, "DEFAULT") 29 | 30 | dynamic "log_configuration" { 31 | for_each = try([execute_command_configuration.value.log_configuration], []) 32 | 33 | content { 34 | cloud_watch_encryption_enabled = try(log_configuration.value.cloud_watch_encryption_enabled, null) 35 | cloud_watch_log_group_name = try(log_configuration.value.cloud_watch_log_group_name, null) 36 | s3_bucket_name = try(log_configuration.value.s3_bucket_name, null) 37 | s3_bucket_encryption_enabled = try(log_configuration.value.s3_bucket_encryption_enabled, null) 38 | s3_key_prefix = try(log_configuration.value.s3_key_prefix, null) 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | dynamic "configuration" { 47 | for_each = !var.create_cloudwatch_log_group && length(var.cluster_configuration) > 0 ? [var.cluster_configuration] : [] 48 | 49 | content { 50 | dynamic "execute_command_configuration" { 51 | for_each = try([configuration.value.execute_command_configuration], [{}]) 52 | 53 | content { 54 | kms_key_id = try(execute_command_configuration.value.kms_key_id, null) 55 | logging = try(execute_command_configuration.value.logging, "DEFAULT") 56 | 57 | dynamic "log_configuration" { 58 | for_each = try([execute_command_configuration.value.log_configuration], []) 59 | 60 | content { 61 | cloud_watch_encryption_enabled = try(log_configuration.value.cloud_watch_encryption_enabled, null) 62 | cloud_watch_log_group_name = try(log_configuration.value.cloud_watch_log_group_name, null) 63 | s3_bucket_name = try(log_configuration.value.s3_bucket_name, null) 64 | s3_bucket_encryption_enabled = try(log_configuration.value.s3_bucket_encryption_enabled, null) 65 | s3_key_prefix = try(log_configuration.value.s3_key_prefix, null) 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | 73 | dynamic "service_connect_defaults" { 74 | for_each = length(var.cluster_service_connect_defaults) > 0 ? [var.cluster_service_connect_defaults] : [] 75 | 76 | content { 77 | namespace = service_connect_defaults.value.namespace 78 | } 79 | } 80 | 81 | dynamic "setting" { 82 | for_each = flatten([var.cluster_settings]) 83 | 84 | content { 85 | name = setting.value.name 86 | value = setting.value.value 87 | } 88 | } 89 | 90 | tags = var.tags 91 | } 92 | 93 | ################################################################################ 94 | # CloudWatch Log Group 95 | ################################################################################ 96 | resource "aws_cloudwatch_log_group" "this" { 97 | count = var.create && var.create_cloudwatch_log_group ? 1 : 0 98 | 99 | name = try(coalesce(var.cloudwatch_log_group_name, "/aws/ecs/${var.cluster_name}"), "") 100 | retention_in_days = var.cloudwatch_log_group_retention_in_days 101 | kms_key_id = var.cloudwatch_log_group_kms_key_id 102 | 103 | tags = merge(var.tags, var.cloudwatch_log_group_tags) 104 | } 105 | 106 | ################################################################################ 107 | # Cluster Capacity Providers 108 | ################################################################################ 109 | 110 | locals { 111 | default_capacity_providers = merge( 112 | { for k, v in var.fargate_capacity_providers : k => v if var.default_capacity_provider_use_fargate }, 113 | { for k, v in var.autoscaling_capacity_providers : k => v if !var.default_capacity_provider_use_fargate } 114 | ) 115 | } 116 | 117 | resource "aws_ecs_cluster_capacity_providers" "this" { 118 | count = var.create && length(merge(var.fargate_capacity_providers, var.autoscaling_capacity_providers)) > 0 ? 1 : 0 119 | 120 | cluster_name = aws_ecs_cluster.this[0].name 121 | capacity_providers = distinct(concat( 122 | [for k, v in var.fargate_capacity_providers : try(v.name, k)], 123 | [for k, v in var.autoscaling_capacity_providers : try(v.name, k)] 124 | )) 125 | 126 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-capacity-providers.html#capacity-providers-considerations 127 | dynamic "default_capacity_provider_strategy" { 128 | for_each = local.default_capacity_providers 129 | iterator = strategy 130 | 131 | content { 132 | capacity_provider = try(strategy.value.name, strategy.key) 133 | base = try(strategy.value.default_capacity_provider_strategy.base, null) 134 | weight = try(strategy.value.default_capacity_provider_strategy.weight, null) 135 | } 136 | } 137 | 138 | depends_on = [ 139 | aws_ecs_capacity_provider.this 140 | ] 141 | } 142 | 143 | ################################################################################ 144 | # Capacity Provider - Autoscaling Group(s) 145 | ################################################################################ 146 | 147 | resource "aws_ecs_capacity_provider" "this" { 148 | for_each = { for k, v in var.autoscaling_capacity_providers : k => v if var.create } 149 | 150 | name = try(each.value.name, each.key) 151 | 152 | auto_scaling_group_provider { 153 | auto_scaling_group_arn = each.value.auto_scaling_group_arn 154 | # When you use managed termination protection, you must also use managed scaling otherwise managed termination protection won't work 155 | managed_termination_protection = length(try([each.value.managed_scaling], [])) == 0 ? "DISABLED" : try(each.value.managed_termination_protection, null) 156 | 157 | dynamic "managed_scaling" { 158 | for_each = try([each.value.managed_scaling], []) 159 | 160 | content { 161 | instance_warmup_period = try(managed_scaling.value.instance_warmup_period, null) 162 | maximum_scaling_step_size = try(managed_scaling.value.maximum_scaling_step_size, null) 163 | minimum_scaling_step_size = try(managed_scaling.value.minimum_scaling_step_size, null) 164 | status = try(managed_scaling.value.status, null) 165 | target_capacity = try(managed_scaling.value.target_capacity, null) 166 | } 167 | } 168 | } 169 | 170 | tags = merge(var.tags, try(each.value.tags, {})) 171 | } 172 | 173 | ################################################################################ 174 | # Task Execution - IAM Role 175 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html 176 | ################################################################################ 177 | 178 | locals { 179 | task_exec_iam_role_name = try(coalesce(var.task_exec_iam_role_name, var.cluster_name), "") 180 | 181 | create_task_exec_iam_role = var.create && var.create_task_exec_iam_role 182 | create_task_exec_policy = local.create_task_exec_iam_role && var.create_task_exec_policy 183 | } 184 | 185 | data "aws_iam_policy_document" "task_exec_assume" { 186 | count = local.create_task_exec_iam_role ? 1 : 0 187 | 188 | statement { 189 | sid = "ECSTaskExecutionAssumeRole" 190 | actions = ["sts:AssumeRole"] 191 | 192 | principals { 193 | type = "Service" 194 | identifiers = ["ecs-tasks.amazonaws.com"] 195 | } 196 | } 197 | } 198 | 199 | resource "aws_iam_role" "task_exec" { 200 | count = local.create_task_exec_iam_role ? 1 : 0 201 | 202 | name = var.task_exec_iam_role_use_name_prefix ? null : local.task_exec_iam_role_name 203 | name_prefix = var.task_exec_iam_role_use_name_prefix ? "${local.task_exec_iam_role_name}-" : null 204 | path = var.task_exec_iam_role_path 205 | description = coalesce(var.task_exec_iam_role_description, "Task execution role for ${var.cluster_name}") 206 | 207 | assume_role_policy = data.aws_iam_policy_document.task_exec_assume[0].json 208 | permissions_boundary = var.task_exec_iam_role_permissions_boundary 209 | force_detach_policies = true 210 | 211 | tags = merge(var.tags, var.task_exec_iam_role_tags) 212 | } 213 | 214 | resource "aws_iam_role_policy_attachment" "task_exec_additional" { 215 | for_each = { for k, v in var.task_exec_iam_role_policies : k => v if local.create_task_exec_iam_role } 216 | 217 | role = aws_iam_role.task_exec[0].name 218 | policy_arn = each.value 219 | } 220 | 221 | data "aws_iam_policy_document" "task_exec" { 222 | count = local.create_task_exec_policy ? 1 : 0 223 | 224 | # Pulled from AmazonECSTaskExecutionRolePolicy 225 | statement { 226 | sid = "Logs" 227 | actions = [ 228 | "logs:CreateLogStream", 229 | "logs:PutLogEvents", 230 | ] 231 | resources = ["*"] 232 | } 233 | 234 | # Pulled from AmazonECSTaskExecutionRolePolicy 235 | statement { 236 | sid = "ECR" 237 | actions = [ 238 | "ecr:GetAuthorizationToken", 239 | "ecr:BatchCheckLayerAvailability", 240 | "ecr:GetDownloadUrlForLayer", 241 | "ecr:BatchGetImage", 242 | ] 243 | resources = ["*"] 244 | } 245 | 246 | dynamic "statement" { 247 | for_each = length(var.task_exec_ssm_param_arns) > 0 ? [1] : [] 248 | 249 | content { 250 | sid = "GetSSMParams" 251 | actions = ["ssm:GetParameters"] 252 | resources = var.task_exec_ssm_param_arns 253 | } 254 | } 255 | 256 | dynamic "statement" { 257 | for_each = length(var.task_exec_secret_arns) > 0 ? [1] : [] 258 | 259 | content { 260 | sid = "GetSecrets" 261 | actions = ["secretsmanager:GetSecretValue"] 262 | resources = var.task_exec_secret_arns 263 | } 264 | } 265 | 266 | dynamic "statement" { 267 | for_each = var.task_exec_iam_statements 268 | 269 | content { 270 | sid = try(statement.value.sid, null) 271 | actions = try(statement.value.actions, null) 272 | not_actions = try(statement.value.not_actions, null) 273 | effect = try(statement.value.effect, null) 274 | resources = try(statement.value.resources, null) 275 | not_resources = try(statement.value.not_resources, null) 276 | 277 | dynamic "principals" { 278 | for_each = try(statement.value.principals, []) 279 | 280 | content { 281 | type = principals.value.type 282 | identifiers = principals.value.identifiers 283 | } 284 | } 285 | 286 | dynamic "not_principals" { 287 | for_each = try(statement.value.not_principals, []) 288 | 289 | content { 290 | type = not_principals.value.type 291 | identifiers = not_principals.value.identifiers 292 | } 293 | } 294 | 295 | dynamic "condition" { 296 | for_each = try(statement.value.conditions, []) 297 | 298 | content { 299 | test = condition.value.test 300 | values = condition.value.values 301 | variable = condition.value.variable 302 | } 303 | } 304 | } 305 | } 306 | } 307 | 308 | resource "aws_iam_policy" "task_exec" { 309 | count = local.create_task_exec_policy ? 1 : 0 310 | 311 | name = var.task_exec_iam_role_use_name_prefix ? null : local.task_exec_iam_role_name 312 | name_prefix = var.task_exec_iam_role_use_name_prefix ? "${local.task_exec_iam_role_name}-" : null 313 | description = coalesce(var.task_exec_iam_role_description, "Task execution role IAM policy") 314 | policy = data.aws_iam_policy_document.task_exec[0].json 315 | 316 | tags = merge(var.tags, var.task_exec_iam_role_tags) 317 | } 318 | 319 | resource "aws_iam_role_policy_attachment" "task_exec" { 320 | count = local.create_task_exec_policy ? 1 : 0 321 | 322 | role = aws_iam_role.task_exec[0].name 323 | policy_arn = aws_iam_policy.task_exec[0].arn 324 | } 325 | -------------------------------------------------------------------------------- /modules/cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Cluster 3 | ################################################################################ 4 | 5 | output "arn" { 6 | description = "ARN that identifies the cluster" 7 | value = try(aws_ecs_cluster.this[0].arn, null) 8 | } 9 | 10 | output "id" { 11 | description = "ID that identifies the cluster" 12 | value = try(aws_ecs_cluster.this[0].id, null) 13 | } 14 | 15 | output "name" { 16 | description = "Name that identifies the cluster" 17 | value = try(aws_ecs_cluster.this[0].name, null) 18 | } 19 | 20 | ################################################################################ 21 | # CloudWatch Log Group 22 | ################################################################################ 23 | 24 | output "cloudwatch_log_group_name" { 25 | description = "Name of CloudWatch log group created" 26 | value = try(aws_cloudwatch_log_group.this[0].name, null) 27 | } 28 | 29 | output "cloudwatch_log_group_arn" { 30 | description = "ARN of CloudWatch log group created" 31 | value = try(aws_cloudwatch_log_group.this[0].arn, null) 32 | } 33 | 34 | ################################################################################ 35 | # Cluster Capacity Providers 36 | ################################################################################ 37 | 38 | output "cluster_capacity_providers" { 39 | description = "Map of cluster capacity providers attributes" 40 | value = { for k, v in aws_ecs_cluster_capacity_providers.this : v.id => v } 41 | } 42 | 43 | ################################################################################ 44 | # Capacity Provider - Autoscaling Group(s) 45 | ################################################################################ 46 | 47 | output "autoscaling_capacity_providers" { 48 | description = "Map of autoscaling capacity providers created and their attributes" 49 | value = aws_ecs_capacity_provider.this 50 | } 51 | 52 | ################################################################################ 53 | # Task Execution - IAM Role 54 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html 55 | ################################################################################ 56 | 57 | output "task_exec_iam_role_name" { 58 | description = "Task execution IAM role name" 59 | value = try(aws_iam_role.task_exec[0].name, null) 60 | } 61 | 62 | output "task_exec_iam_role_arn" { 63 | description = "Task execution IAM role ARN" 64 | value = try(aws_iam_role.task_exec[0].arn, null) 65 | } 66 | 67 | output "task_exec_iam_role_unique_id" { 68 | description = "Stable and unique string identifying the task execution IAM role" 69 | value = try(aws_iam_role.task_exec[0].unique_id, null) 70 | } 71 | -------------------------------------------------------------------------------- /modules/cluster/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Determines whether resources will be created (affects all resources)" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "tags" { 8 | description = "A map of tags to add to all resources" 9 | type = map(string) 10 | default = {} 11 | } 12 | 13 | ################################################################################ 14 | # Cluster 15 | ################################################################################ 16 | 17 | variable "cluster_name" { 18 | description = "Name of the cluster (up to 255 letters, numbers, hyphens, and underscores)" 19 | type = string 20 | default = "" 21 | } 22 | 23 | variable "cluster_configuration" { 24 | description = "The execute command configuration for the cluster" 25 | type = any 26 | default = {} 27 | } 28 | 29 | variable "cluster_settings" { 30 | description = "List of configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster" 31 | type = any 32 | default = [ 33 | { 34 | name = "containerInsights" 35 | value = "enabled" 36 | } 37 | ] 38 | } 39 | 40 | variable "cluster_service_connect_defaults" { 41 | description = "Configures a default Service Connect namespace" 42 | type = map(string) 43 | default = {} 44 | } 45 | 46 | ################################################################################ 47 | # CloudWatch Log Group 48 | ################################################################################ 49 | 50 | variable "create_cloudwatch_log_group" { 51 | description = "Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled" 52 | type = bool 53 | default = true 54 | } 55 | 56 | variable "cloudwatch_log_group_name" { 57 | description = "Custom name of CloudWatch Log Group for ECS cluster" 58 | type = string 59 | default = null 60 | } 61 | 62 | variable "cloudwatch_log_group_retention_in_days" { 63 | description = "Number of days to retain log events" 64 | type = number 65 | default = 90 66 | } 67 | 68 | variable "cloudwatch_log_group_kms_key_id" { 69 | description = "If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)" 70 | type = string 71 | default = null 72 | } 73 | 74 | variable "cloudwatch_log_group_tags" { 75 | description = "A map of additional tags to add to the log group created" 76 | type = map(string) 77 | default = {} 78 | } 79 | 80 | ################################################################################ 81 | # Capacity Providers 82 | ################################################################################ 83 | 84 | variable "default_capacity_provider_use_fargate" { 85 | description = "Determines whether to use Fargate or autoscaling for default capacity provider strategy" 86 | type = bool 87 | default = true 88 | } 89 | 90 | variable "fargate_capacity_providers" { 91 | description = "Map of Fargate capacity provider definitions to use for the cluster" 92 | type = any 93 | default = {} 94 | } 95 | 96 | variable "autoscaling_capacity_providers" { 97 | description = "Map of autoscaling capacity provider definitions to create for the cluster" 98 | type = any 99 | default = {} 100 | } 101 | 102 | ################################################################################ 103 | # Task Execution - IAM Role 104 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html 105 | ################################################################################ 106 | 107 | variable "create_task_exec_iam_role" { 108 | description = "Determines whether the ECS task definition IAM role should be created" 109 | type = bool 110 | default = false 111 | } 112 | 113 | variable "task_exec_iam_role_name" { 114 | description = "Name to use on IAM role created" 115 | type = string 116 | default = null 117 | } 118 | 119 | variable "task_exec_iam_role_use_name_prefix" { 120 | description = "Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix" 121 | type = bool 122 | default = true 123 | } 124 | 125 | variable "task_exec_iam_role_path" { 126 | description = "IAM role path" 127 | type = string 128 | default = null 129 | } 130 | 131 | variable "task_exec_iam_role_description" { 132 | description = "Description of the role" 133 | type = string 134 | default = null 135 | } 136 | 137 | variable "task_exec_iam_role_permissions_boundary" { 138 | description = "ARN of the policy that is used to set the permissions boundary for the IAM role" 139 | type = string 140 | default = null 141 | } 142 | 143 | variable "task_exec_iam_role_tags" { 144 | description = "A map of additional tags to add to the IAM role created" 145 | type = map(string) 146 | default = {} 147 | } 148 | 149 | variable "task_exec_iam_role_policies" { 150 | description = "Map of IAM role policy ARNs to attach to the IAM role" 151 | type = map(string) 152 | default = {} 153 | } 154 | 155 | variable "create_task_exec_policy" { 156 | description = "Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters" 157 | type = bool 158 | default = true 159 | } 160 | 161 | variable "task_exec_ssm_param_arns" { 162 | description = "List of SSM parameter ARNs the task execution role will be permitted to get/read" 163 | type = list(string) 164 | default = ["arn:aws:ssm:*:*:parameter/*"] 165 | } 166 | 167 | variable "task_exec_secret_arns" { 168 | description = "List of SecretsManager secret ARNs the task execution role will be permitted to get/read" 169 | type = list(string) 170 | default = ["arn:aws:secretsmanager:*:*:secret:*"] 171 | } 172 | 173 | variable "task_exec_iam_statements" { 174 | description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" 175 | type = any 176 | default = {} 177 | } 178 | -------------------------------------------------------------------------------- /modules/cluster/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/container-definition/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" {} 2 | 3 | locals { 4 | is_not_windows = contains(["LINUX"], var.operating_system_family) 5 | 6 | log_group_name = try(coalesce(var.cloudwatch_log_group_name, "/aws/ecs/${var.service}/${var.name}"), "") 7 | 8 | log_configuration = merge( 9 | { for k, v in { 10 | logDriver = "awslogs", 11 | options = { 12 | awslogs-region = data.aws_region.current.name, 13 | awslogs-group = try(aws_cloudwatch_log_group.this[0].name, ""), 14 | awslogs-stream-prefix = "ecs" 15 | }, 16 | } : k => v if var.enable_cloudwatch_logging }, 17 | var.log_configuration 18 | ) 19 | 20 | linux_parameters = var.enable_execute_command ? merge({ "initProcessEnabled" : true }, var.linux_parameters) : merge({ "initProcessEnabled" : false }, var.linux_parameters) 21 | 22 | health_check = length(var.health_check) > 0 ? merge({ 23 | interval = 30, 24 | retries = 3, 25 | timeout = 5 26 | }, var.health_check) : null 27 | 28 | definition = { 29 | command = length(var.command) > 0 ? var.command : null 30 | cpu = var.cpu 31 | dependsOn = length(var.dependencies) > 0 ? var.dependencies : null # depends_on is a reserved word 32 | disableNetworking = local.is_not_windows ? var.disable_networking : null 33 | dnsSearchDomains = local.is_not_windows && length(var.dns_search_domains) > 0 ? var.dns_search_domains : null 34 | dnsServers = local.is_not_windows && length(var.dns_servers) > 0 ? var.dns_servers : null 35 | dockerLabels = length(var.docker_labels) > 0 ? var.docker_labels : null 36 | dockerSecurityOptions = length(var.docker_security_options) > 0 ? var.docker_security_options : null 37 | entrypoint = length(var.entrypoint) > 0 ? var.entrypoint : null 38 | environment = var.environment 39 | environmentFiles = length(var.environment_files) > 0 ? var.environment_files : null 40 | essential = var.essential 41 | extraHosts = local.is_not_windows && length(var.extra_hosts) > 0 ? var.extra_hosts : null 42 | firelensConfiguration = length(var.firelens_configuration) > 0 ? var.firelens_configuration : null 43 | healthCheck = local.health_check 44 | hostname = var.hostname 45 | image = var.image 46 | interactive = var.interactive 47 | links = local.is_not_windows && length(var.links) > 0 ? var.links : null 48 | linuxParameters = local.is_not_windows && length(local.linux_parameters) > 0 ? local.linux_parameters : null 49 | logConfiguration = length(local.log_configuration) > 0 ? local.log_configuration : null 50 | memory = var.memory 51 | memoryReservation = var.memory_reservation 52 | mountPoints = var.mount_points 53 | name = var.name 54 | portMappings = var.port_mappings 55 | privileged = local.is_not_windows ? var.privileged : null 56 | pseudoTerminal = var.pseudo_terminal 57 | readonlyRootFilesystem = local.is_not_windows ? var.readonly_root_filesystem : null 58 | repositoryCredentials = length(var.repository_credentials) > 0 ? var.repository_credentials : null 59 | resourceRequirements = length(var.resource_requirements) > 0 ? var.resource_requirements : null 60 | secrets = length(var.secrets) > 0 ? var.secrets : null 61 | startTimeout = var.start_timeout 62 | stopTimeout = var.stop_timeout 63 | systemControls = length(var.system_controls) > 0 ? var.system_controls : [] 64 | ulimits = local.is_not_windows && length(var.ulimits) > 0 ? var.ulimits : null 65 | user = local.is_not_windows ? var.user : null 66 | volumesFrom = var.volumes_from 67 | workingDirectory = var.working_directory 68 | } 69 | 70 | # Strip out all null values, ECS API will provide defaults in place of null/empty values 71 | container_definition = { for k, v in local.definition : k => v if v != null } 72 | } 73 | 74 | resource "aws_cloudwatch_log_group" "this" { 75 | count = var.create_cloudwatch_log_group && var.enable_cloudwatch_logging ? 1 : 0 76 | 77 | name = var.cloudwatch_log_group_use_name_prefix ? null : local.log_group_name 78 | name_prefix = var.cloudwatch_log_group_use_name_prefix ? "${local.log_group_name}-" : null 79 | retention_in_days = var.cloudwatch_log_group_retention_in_days 80 | kms_key_id = var.cloudwatch_log_group_kms_key_id 81 | 82 | tags = var.tags 83 | } 84 | -------------------------------------------------------------------------------- /modules/container-definition/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Container Definition 3 | ################################################################################ 4 | 5 | output "container_definition" { 6 | description = "Container definition" 7 | value = local.container_definition 8 | } 9 | 10 | ################################################################################ 11 | # CloudWatch Log Group 12 | ################################################################################ 13 | 14 | output "cloudwatch_log_group_name" { 15 | description = "Name of CloudWatch log group created" 16 | value = try(aws_cloudwatch_log_group.this[0].name, null) 17 | } 18 | 19 | output "cloudwatch_log_group_arn" { 20 | description = "ARN of CloudWatch log group created" 21 | value = try(aws_cloudwatch_log_group.this[0].arn, null) 22 | } 23 | -------------------------------------------------------------------------------- /modules/container-definition/variables.tf: -------------------------------------------------------------------------------- 1 | variable "operating_system_family" { 2 | description = "The OS family for task" 3 | type = string 4 | default = "LINUX" 5 | } 6 | 7 | ################################################################################ 8 | # Container Definition 9 | ################################################################################ 10 | 11 | variable "command" { 12 | description = "The command that's passed to the container" 13 | type = list(string) 14 | default = [] 15 | } 16 | 17 | variable "cpu" { 18 | description = "The number of cpu units to reserve for the container. This is optional for tasks using Fargate launch type and the total amount of `cpu` of all containers in a task will need to be lower than the task-level cpu value" 19 | type = number 20 | default = null 21 | } 22 | 23 | variable "dependencies" { 24 | description = "The dependencies defined for container startup and shutdown. A container can contain multiple dependencies. When a dependency is defined for container startup, for container shutdown it is reversed. The condition can be one of START, COMPLETE, SUCCESS or HEALTHY" 25 | type = list(object({ 26 | condition = string 27 | containerName = string 28 | })) 29 | default = [] 30 | } 31 | 32 | variable "disable_networking" { 33 | description = "When this parameter is true, networking is disabled within the container" 34 | type = bool 35 | default = null 36 | } 37 | 38 | variable "dns_search_domains" { 39 | description = "Container DNS search domains. A list of DNS search domains that are presented to the container" 40 | type = list(string) 41 | default = [] 42 | } 43 | 44 | variable "dns_servers" { 45 | description = "Container DNS servers. This is a list of strings specifying the IP addresses of the DNS servers" 46 | type = list(string) 47 | default = [] 48 | } 49 | 50 | variable "docker_labels" { 51 | description = "A key/value map of labels to add to the container" 52 | type = map(string) 53 | default = {} 54 | } 55 | 56 | variable "docker_security_options" { 57 | description = "A list of strings to provide custom labels for SELinux and AppArmor multi-level security systems. This field isn't valid for containers in tasks using the Fargate launch type" 58 | type = list(string) 59 | default = [] 60 | } 61 | 62 | variable "enable_execute_command" { 63 | description = "Specifies whether to enable Amazon ECS Exec for the tasks within the service" 64 | type = bool 65 | default = false 66 | } 67 | 68 | variable "entrypoint" { 69 | description = "The entry point that is passed to the container" 70 | type = list(string) 71 | default = [] 72 | } 73 | 74 | variable "environment" { 75 | description = "The environment variables to pass to the container" 76 | type = list(object({ 77 | name = string 78 | value = string 79 | })) 80 | default = [] 81 | } 82 | 83 | variable "environment_files" { 84 | description = "A list of files containing the environment variables to pass to a container" 85 | type = list(object({ 86 | value = string 87 | type = string 88 | })) 89 | default = [] 90 | } 91 | 92 | variable "essential" { 93 | description = "If the `essential` parameter of a container is marked as `true`, and that container fails or stops for any reason, all other containers that are part of the task are stopped" 94 | type = bool 95 | default = null 96 | } 97 | 98 | variable "extra_hosts" { 99 | description = "A list of hostnames and IP address mappings to append to the `/etc/hosts` file on the container" 100 | type = list(object({ 101 | hostname = string 102 | ipAddress = string 103 | })) 104 | default = [] 105 | } 106 | 107 | variable "firelens_configuration" { 108 | description = "The FireLens configuration for the container. This is used to specify and configure a log router for container logs. For more information, see [Custom Log Routing](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html) in the Amazon Elastic Container Service Developer Guide" 109 | type = any 110 | default = {} 111 | } 112 | 113 | variable "health_check" { 114 | description = "The container health check command and associated configuration parameters for the container. See [HealthCheck](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_HealthCheck.html)" 115 | type = any 116 | default = {} 117 | } 118 | 119 | variable "hostname" { 120 | description = "The hostname to use for your container" 121 | type = string 122 | default = null 123 | } 124 | 125 | variable "image" { 126 | description = "The image used to start a container. This string is passed directly to the Docker daemon. By default, images in the Docker Hub registry are available. Other repositories are specified with either `repository-url/image:tag` or `repository-url/image@digest`" 127 | type = string 128 | default = null 129 | } 130 | 131 | variable "interactive" { 132 | description = "When this parameter is `true`, you can deploy containerized applications that require `stdin` or a `tty` to be allocated" 133 | type = bool 134 | default = false 135 | } 136 | 137 | variable "links" { 138 | description = "The links parameter allows containers to communicate with each other without the need for port mappings. This parameter is only supported if the network mode of a task definition is `bridge`" 139 | type = list(string) 140 | default = [] 141 | } 142 | 143 | variable "linux_parameters" { 144 | description = "Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. For more information see [KernelCapabilities](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_KernelCapabilities.html)" 145 | type = any 146 | default = {} 147 | } 148 | 149 | variable "log_configuration" { 150 | description = "The log configuration for the container. For more information see [LogConfiguration](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html)" 151 | type = any 152 | default = {} 153 | } 154 | 155 | variable "memory" { 156 | description = "The amount (in MiB) of memory to present to the container. If your container attempts to exceed the memory specified here, the container is killed. The total amount of memory reserved for all containers within a task must be lower than the task `memory` value, if one is specified" 157 | type = number 158 | default = null 159 | } 160 | 161 | variable "memory_reservation" { 162 | description = "The soft limit (in MiB) of memory to reserve for the container. When system memory is under heavy contention, Docker attempts to keep the container memory to this soft limit. However, your container can consume more memory when it needs to, up to either the hard limit specified with the `memory` parameter (if applicable), or all of the available memory on the container instance" 163 | type = number 164 | default = null 165 | } 166 | 167 | variable "mount_points" { 168 | description = "The mount points for data volumes in your container" 169 | type = list(any) 170 | default = [] 171 | } 172 | 173 | variable "name" { 174 | description = "The name of a container. If you're linking multiple containers together in a task definition, the name of one container can be entered in the links of another container to connect the containers. Up to 255 letters (uppercase and lowercase), numbers, underscores, and hyphens are allowed" 175 | type = string 176 | default = null 177 | } 178 | 179 | variable "port_mappings" { 180 | description = "The list of port mappings for the container. Port mappings allow containers to access ports on the host container instance to send or receive traffic. For task definitions that use the awsvpc network mode, only specify the containerPort. The hostPort can be left blank or it must be the same value as the containerPort" 181 | type = list(any) 182 | default = [] 183 | } 184 | 185 | variable "privileged" { 186 | description = "When this parameter is true, the container is given elevated privileges on the host container instance (similar to the root user)" 187 | type = bool 188 | default = false 189 | } 190 | 191 | variable "pseudo_terminal" { 192 | description = "When this parameter is true, a `TTY` is allocated" 193 | type = bool 194 | default = false 195 | } 196 | 197 | variable "readonly_root_filesystem" { 198 | description = "When this parameter is true, the container is given read-only access to its root file system" 199 | type = bool 200 | default = true 201 | } 202 | 203 | variable "repository_credentials" { 204 | description = "Container repository credentials; required when using a private repo. This map currently supports a single key; \"credentialsParameter\", which should be the ARN of a Secrets Manager's secret holding the credentials" 205 | type = map(string) 206 | default = {} 207 | } 208 | 209 | variable "resource_requirements" { 210 | description = "The type and amount of a resource to assign to a container. The only supported resource is a GPU" 211 | type = list(object({ 212 | type = string 213 | value = string 214 | })) 215 | default = [] 216 | } 217 | 218 | variable "secrets" { 219 | description = "The secrets to pass to the container. For more information, see [Specifying Sensitive Data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the Amazon Elastic Container Service Developer Guide" 220 | type = list(object({ 221 | name = string 222 | valueFrom = string 223 | })) 224 | default = [] 225 | } 226 | 227 | variable "start_timeout" { 228 | description = "Time duration (in seconds) to wait before giving up on resolving dependencies for a container" 229 | type = number 230 | default = 30 231 | } 232 | 233 | variable "stop_timeout" { 234 | description = "Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own" 235 | type = number 236 | default = 120 237 | } 238 | 239 | variable "system_controls" { 240 | description = "A list of namespaced kernel parameters to set in the container" 241 | type = list(map(string)) 242 | default = [] 243 | } 244 | 245 | variable "ulimits" { 246 | description = "A list of ulimits to set in the container. If a ulimit value is specified in a task definition, it overrides the default values set by Docker" 247 | type = list(object({ 248 | hardLimit = number 249 | name = string 250 | softLimit = number 251 | })) 252 | default = [] 253 | } 254 | 255 | variable "user" { 256 | description = "The user to run as inside the container. Can be any of these formats: user, user:group, uid, uid:gid, user:gid, uid:group. The default (null) will use the container's configured `USER` directive or root if not set" 257 | type = string 258 | default = null 259 | } 260 | 261 | variable "volumes_from" { 262 | description = "Data volumes to mount from another container" 263 | type = list(any) 264 | default = [] 265 | } 266 | 267 | variable "working_directory" { 268 | description = "The working directory to run commands inside the container" 269 | type = string 270 | default = null 271 | } 272 | 273 | ################################################################################ 274 | # CloudWatch Log Group 275 | ################################################################################ 276 | 277 | variable "service" { 278 | description = "The name of the service that the container definition is associated with" 279 | type = string 280 | default = "" 281 | } 282 | 283 | variable "enable_cloudwatch_logging" { 284 | description = "Determines whether CloudWatch logging is configured for this container definition. Set to `false` to use other logging drivers" 285 | type = bool 286 | default = true 287 | } 288 | 289 | variable "create_cloudwatch_log_group" { 290 | description = "Determines whether a log group is created by this module. If not, AWS will automatically create one if logging is enabled" 291 | type = bool 292 | default = true 293 | } 294 | 295 | variable "cloudwatch_log_group_name" { 296 | description = "Custom name of CloudWatch log group for a service associated with the container definition" 297 | type = string 298 | default = null 299 | } 300 | 301 | variable "cloudwatch_log_group_use_name_prefix" { 302 | description = "Determines whether the log group name should be used as a prefix" 303 | type = bool 304 | default = false 305 | } 306 | 307 | variable "cloudwatch_log_group_retention_in_days" { 308 | description = "Number of days to retain log events. Default is 30 days" 309 | type = number 310 | default = 30 311 | } 312 | 313 | variable "cloudwatch_log_group_kms_key_id" { 314 | description = "If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)" 315 | type = string 316 | default = null 317 | } 318 | 319 | variable "tags" { 320 | description = "A map of tags to add to all resources" 321 | type = map(string) 322 | default = {} 323 | } 324 | -------------------------------------------------------------------------------- /modules/container-definition/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/service/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Service 3 | ################################################################################ 4 | 5 | output "id" { 6 | description = "ARN that identifies the service" 7 | value = try(aws_ecs_service.this[0].id, aws_ecs_service.ignore_task_definition[0].id, null) 8 | } 9 | 10 | output "name" { 11 | description = "Name of the service" 12 | value = try(aws_ecs_service.this[0].name, aws_ecs_service.ignore_task_definition[0].name, null) 13 | } 14 | 15 | ################################################################################ 16 | # IAM Role 17 | ################################################################################ 18 | 19 | output "iam_role_name" { 20 | description = "Service IAM role name" 21 | value = try(aws_iam_role.service[0].name, null) 22 | } 23 | 24 | output "iam_role_arn" { 25 | description = "Service IAM role ARN" 26 | value = try(aws_iam_role.service[0].arn, var.iam_role_arn) 27 | } 28 | 29 | output "iam_role_unique_id" { 30 | description = "Stable and unique string identifying the service IAM role" 31 | value = try(aws_iam_role.service[0].unique_id, null) 32 | } 33 | 34 | ################################################################################ 35 | # Container Definition 36 | ################################################################################ 37 | 38 | output "container_definitions" { 39 | description = "Container definitions" 40 | value = module.container_definition 41 | } 42 | 43 | ################################################################################ 44 | # Task Definition 45 | ################################################################################ 46 | 47 | output "task_definition_arn" { 48 | description = "Full ARN of the Task Definition (including both `family` and `revision`)" 49 | value = try(aws_ecs_task_definition.this[0].arn, var.task_definition_arn) 50 | } 51 | 52 | output "task_definition_revision" { 53 | description = "Revision of the task in a particular family" 54 | value = try(aws_ecs_task_definition.this[0].revision, null) 55 | } 56 | 57 | output "task_definition_family" { 58 | description = "The unique name of the task definition" 59 | value = try(aws_ecs_task_definition.this[0].family, null) 60 | } 61 | 62 | output "task_definition_family_revision" { 63 | description = "The family and revision (family:revision) of the task definition" 64 | value = "${try(aws_ecs_task_definition.this[0].family, "")}:${local.max_task_def_revision}" 65 | } 66 | 67 | ################################################################################ 68 | # Task Execution - IAM Role 69 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html 70 | ################################################################################ 71 | 72 | output "task_exec_iam_role_name" { 73 | description = "Task execution IAM role name" 74 | value = try(aws_iam_role.task_exec[0].name, null) 75 | } 76 | 77 | output "task_exec_iam_role_arn" { 78 | description = "Task execution IAM role ARN" 79 | value = try(aws_iam_role.task_exec[0].arn, var.task_exec_iam_role_arn) 80 | } 81 | 82 | output "task_exec_iam_role_unique_id" { 83 | description = "Stable and unique string identifying the task execution IAM role" 84 | value = try(aws_iam_role.task_exec[0].unique_id, null) 85 | } 86 | 87 | ################################################################################ 88 | # Tasks - IAM role 89 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html 90 | ################################################################################ 91 | 92 | output "tasks_iam_role_name" { 93 | description = "Tasks IAM role name" 94 | value = try(aws_iam_role.tasks[0].name, null) 95 | } 96 | 97 | output "tasks_iam_role_arn" { 98 | description = "Tasks IAM role ARN" 99 | value = try(aws_iam_role.tasks[0].arn, var.tasks_iam_role_arn) 100 | } 101 | 102 | output "tasks_iam_role_unique_id" { 103 | description = "Stable and unique string identifying the tasks IAM role" 104 | value = try(aws_iam_role.tasks[0].unique_id, null) 105 | } 106 | 107 | ################################################################################ 108 | # Task Set 109 | ################################################################################ 110 | 111 | output "task_set_id" { 112 | description = "The ID of the task set" 113 | value = try(aws_ecs_task_set.this[0].task_set_id, aws_ecs_task_set.ignore_task_definition[0].task_set_id, null) 114 | } 115 | 116 | output "task_set_arn" { 117 | description = "The Amazon Resource Name (ARN) that identifies the task set" 118 | value = try(aws_ecs_task_set.this[0].arn, aws_ecs_task_set.ignore_task_definition[0].arn, null) 119 | } 120 | 121 | output "task_set_stability_status" { 122 | description = "The stability status. This indicates whether the task set has reached a steady state" 123 | value = try(aws_ecs_task_set.this[0].stability_status, aws_ecs_task_set.ignore_task_definition[0].stability_status, null) 124 | } 125 | 126 | output "task_set_status" { 127 | description = "The status of the task set" 128 | value = try(aws_ecs_task_set.this[0].status, aws_ecs_task_set.ignore_task_definition[0].status, null) 129 | } 130 | 131 | ################################################################################ 132 | # Autoscaling 133 | ################################################################################ 134 | 135 | output "autoscaling_policies" { 136 | description = "Map of autoscaling policies and their attributes" 137 | value = aws_appautoscaling_policy.this 138 | } 139 | 140 | output "autoscaling_scheduled_actions" { 141 | description = "Map of autoscaling scheduled actions and their attributes" 142 | value = aws_appautoscaling_scheduled_action.this 143 | } 144 | 145 | ################################################################################ 146 | # Security Group 147 | ################################################################################ 148 | 149 | output "security_group_arn" { 150 | description = "Amazon Resource Name (ARN) of the security group" 151 | value = try(aws_security_group.this[0].arn, null) 152 | } 153 | 154 | output "security_group_id" { 155 | description = "ID of the security group" 156 | value = try(aws_security_group.this[0].id, null) 157 | } 158 | -------------------------------------------------------------------------------- /modules/service/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Cluster 3 | ################################################################################ 4 | 5 | output "cluster_arn" { 6 | description = "ARN that identifies the cluster" 7 | value = module.cluster.arn 8 | } 9 | 10 | output "cluster_id" { 11 | description = "ID that identifies the cluster" 12 | value = module.cluster.id 13 | } 14 | 15 | output "cluster_name" { 16 | description = "Name that identifies the cluster" 17 | value = module.cluster.name 18 | } 19 | 20 | output "cloudwatch_log_group_name" { 21 | description = "Name of CloudWatch log group created" 22 | value = module.cluster.cloudwatch_log_group_name 23 | } 24 | 25 | output "cloudwatch_log_group_arn" { 26 | description = "ARN of CloudWatch log group created" 27 | value = module.cluster.cloudwatch_log_group_arn 28 | } 29 | 30 | output "cluster_capacity_providers" { 31 | description = "Map of cluster capacity providers attributes" 32 | value = module.cluster.cluster_capacity_providers 33 | } 34 | 35 | output "autoscaling_capacity_providers" { 36 | description = "Map of autoscaling capacity providers created and their attributes" 37 | value = module.cluster.autoscaling_capacity_providers 38 | } 39 | 40 | output "task_exec_iam_role_name" { 41 | description = "Task execution IAM role name" 42 | value = module.cluster.task_exec_iam_role_name 43 | } 44 | 45 | output "task_exec_iam_role_arn" { 46 | description = "Task execution IAM role ARN" 47 | value = module.cluster.task_exec_iam_role_arn 48 | } 49 | 50 | output "task_exec_iam_role_unique_id" { 51 | description = "Stable and unique string identifying the task execution IAM role" 52 | value = module.cluster.task_exec_iam_role_unique_id 53 | } 54 | 55 | ################################################################################ 56 | # Service(s) 57 | ################################################################################ 58 | 59 | output "services" { 60 | description = "Map of services created and their attributes" 61 | value = module.service 62 | } 63 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Determines whether resources will be created (affects all resources)" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "tags" { 8 | description = "A map of tags to add to all resources" 9 | type = map(string) 10 | default = {} 11 | } 12 | 13 | ################################################################################ 14 | # Cluster 15 | ################################################################################ 16 | 17 | variable "cluster_name" { 18 | description = "Name of the cluster (up to 255 letters, numbers, hyphens, and underscores)" 19 | type = string 20 | default = "" 21 | } 22 | 23 | variable "cluster_configuration" { 24 | description = "The execute command configuration for the cluster" 25 | type = any 26 | default = {} 27 | } 28 | 29 | variable "cluster_settings" { 30 | description = "List of configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster" 31 | type = any 32 | default = [ 33 | { 34 | name = "containerInsights" 35 | value = "enabled" 36 | } 37 | ] 38 | } 39 | 40 | variable "cluster_service_connect_defaults" { 41 | description = "Configures a default Service Connect namespace" 42 | type = map(string) 43 | default = {} 44 | } 45 | 46 | variable "cluster_tags" { 47 | description = "A map of additional tags to add to the cluster" 48 | type = map(string) 49 | default = {} 50 | } 51 | 52 | ################################################################################ 53 | # CloudWatch Log Group 54 | ################################################################################ 55 | 56 | variable "create_cloudwatch_log_group" { 57 | description = "Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled" 58 | type = bool 59 | default = true 60 | } 61 | 62 | variable "cloudwatch_log_group_name" { 63 | description = "Custom name of CloudWatch Log Group for ECS cluster" 64 | type = string 65 | default = null 66 | } 67 | 68 | variable "cloudwatch_log_group_retention_in_days" { 69 | description = "Number of days to retain log events" 70 | type = number 71 | default = 90 72 | } 73 | 74 | variable "cloudwatch_log_group_kms_key_id" { 75 | description = "If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)" 76 | type = string 77 | default = null 78 | } 79 | 80 | variable "cloudwatch_log_group_tags" { 81 | description = "A map of additional tags to add to the log group created" 82 | type = map(string) 83 | default = {} 84 | } 85 | 86 | ################################################################################ 87 | # Capacity Providers 88 | ################################################################################ 89 | 90 | variable "default_capacity_provider_use_fargate" { 91 | description = "Determines whether to use Fargate or autoscaling for default capacity provider strategy" 92 | type = bool 93 | default = true 94 | } 95 | 96 | variable "fargate_capacity_providers" { 97 | description = "Map of Fargate capacity provider definitions to use for the cluster" 98 | type = any 99 | default = {} 100 | } 101 | 102 | variable "autoscaling_capacity_providers" { 103 | description = "Map of autoscaling capacity provider definitions to create for the cluster" 104 | type = any 105 | default = {} 106 | } 107 | 108 | ################################################################################ 109 | # Task Execution - IAM Role 110 | # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html 111 | ################################################################################ 112 | 113 | variable "create_task_exec_iam_role" { 114 | description = "Determines whether the ECS task definition IAM role should be created" 115 | type = bool 116 | default = false 117 | } 118 | 119 | variable "task_exec_iam_role_name" { 120 | description = "Name to use on IAM role created" 121 | type = string 122 | default = null 123 | } 124 | 125 | variable "task_exec_iam_role_use_name_prefix" { 126 | description = "Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix" 127 | type = bool 128 | default = true 129 | } 130 | 131 | variable "task_exec_iam_role_path" { 132 | description = "IAM role path" 133 | type = string 134 | default = null 135 | } 136 | 137 | variable "task_exec_iam_role_description" { 138 | description = "Description of the role" 139 | type = string 140 | default = null 141 | } 142 | 143 | variable "task_exec_iam_role_permissions_boundary" { 144 | description = "ARN of the policy that is used to set the permissions boundary for the IAM role" 145 | type = string 146 | default = null 147 | } 148 | 149 | variable "task_exec_iam_role_tags" { 150 | description = "A map of additional tags to add to the IAM role created" 151 | type = map(string) 152 | default = {} 153 | } 154 | 155 | variable "task_exec_iam_role_policies" { 156 | description = "Map of IAM role policy ARNs to attach to the IAM role" 157 | type = map(string) 158 | default = {} 159 | } 160 | 161 | variable "create_task_exec_policy" { 162 | description = "Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters" 163 | type = bool 164 | default = true 165 | } 166 | 167 | variable "task_exec_ssm_param_arns" { 168 | description = "List of SSM parameter ARNs the task execution role will be permitted to get/read" 169 | type = list(string) 170 | default = ["arn:aws:ssm:*:*:parameter/*"] 171 | } 172 | 173 | variable "task_exec_secret_arns" { 174 | description = "List of SecretsManager secret ARNs the task execution role will be permitted to get/read" 175 | type = list(string) 176 | default = ["arn:aws:secretsmanager:*:*:secret:*"] 177 | } 178 | 179 | variable "task_exec_iam_statements" { 180 | description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" 181 | type = any 182 | default = {} 183 | } 184 | 185 | ################################################################################ 186 | # Service(s) 187 | ################################################################################ 188 | 189 | variable "services" { 190 | description = "Map of service definitions to create" 191 | type = any 192 | default = {} 193 | } 194 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for the root module 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/ecs/aws//wrappers" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/ecs/aws//wrappers" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/cluster/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/cluster` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/cluster" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/cluster?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/ecs/aws//wrappers/cluster" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/cluster/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/cluster" 3 | 4 | for_each = var.items 5 | 6 | autoscaling_capacity_providers = try(each.value.autoscaling_capacity_providers, var.defaults.autoscaling_capacity_providers, {}) 7 | cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null) 8 | cloudwatch_log_group_name = try(each.value.cloudwatch_log_group_name, var.defaults.cloudwatch_log_group_name, null) 9 | cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 90) 10 | cloudwatch_log_group_tags = try(each.value.cloudwatch_log_group_tags, var.defaults.cloudwatch_log_group_tags, {}) 11 | cluster_configuration = try(each.value.cluster_configuration, var.defaults.cluster_configuration, {}) 12 | cluster_name = try(each.value.cluster_name, var.defaults.cluster_name, "") 13 | cluster_service_connect_defaults = try(each.value.cluster_service_connect_defaults, var.defaults.cluster_service_connect_defaults, {}) 14 | cluster_settings = try(each.value.cluster_settings, var.defaults.cluster_settings, [ 15 | { 16 | name = "containerInsights" 17 | value = "enabled" 18 | } 19 | ]) 20 | create = try(each.value.create, var.defaults.create, true) 21 | create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true) 22 | create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, false) 23 | create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true) 24 | default_capacity_provider_use_fargate = try(each.value.default_capacity_provider_use_fargate, var.defaults.default_capacity_provider_use_fargate, true) 25 | fargate_capacity_providers = try(each.value.fargate_capacity_providers, var.defaults.fargate_capacity_providers, {}) 26 | tags = try(each.value.tags, var.defaults.tags, {}) 27 | task_exec_iam_role_description = try(each.value.task_exec_iam_role_description, var.defaults.task_exec_iam_role_description, null) 28 | task_exec_iam_role_name = try(each.value.task_exec_iam_role_name, var.defaults.task_exec_iam_role_name, null) 29 | task_exec_iam_role_path = try(each.value.task_exec_iam_role_path, var.defaults.task_exec_iam_role_path, null) 30 | task_exec_iam_role_permissions_boundary = try(each.value.task_exec_iam_role_permissions_boundary, var.defaults.task_exec_iam_role_permissions_boundary, null) 31 | task_exec_iam_role_policies = try(each.value.task_exec_iam_role_policies, var.defaults.task_exec_iam_role_policies, {}) 32 | task_exec_iam_role_tags = try(each.value.task_exec_iam_role_tags, var.defaults.task_exec_iam_role_tags, {}) 33 | task_exec_iam_role_use_name_prefix = try(each.value.task_exec_iam_role_use_name_prefix, var.defaults.task_exec_iam_role_use_name_prefix, true) 34 | task_exec_iam_statements = try(each.value.task_exec_iam_statements, var.defaults.task_exec_iam_statements, {}) 35 | task_exec_secret_arns = try(each.value.task_exec_secret_arns, var.defaults.task_exec_secret_arns, ["arn:aws:secretsmanager:*:*:secret:*"]) 36 | task_exec_ssm_param_arns = try(each.value.task_exec_ssm_param_arns, var.defaults.task_exec_ssm_param_arns, ["arn:aws:ssm:*:*:parameter/*"]) 37 | } 38 | -------------------------------------------------------------------------------- /wrappers/cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/cluster/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/cluster/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/container-definition/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/container-definition` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/container-definition" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/container-definition?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/ecs/aws//wrappers/container-definition" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/container-definition/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/container-definition" 3 | 4 | for_each = var.items 5 | 6 | cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null) 7 | cloudwatch_log_group_name = try(each.value.cloudwatch_log_group_name, var.defaults.cloudwatch_log_group_name, null) 8 | cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 30) 9 | cloudwatch_log_group_use_name_prefix = try(each.value.cloudwatch_log_group_use_name_prefix, var.defaults.cloudwatch_log_group_use_name_prefix, false) 10 | command = try(each.value.command, var.defaults.command, []) 11 | cpu = try(each.value.cpu, var.defaults.cpu, null) 12 | create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true) 13 | dependencies = try(each.value.dependencies, var.defaults.dependencies, []) 14 | disable_networking = try(each.value.disable_networking, var.defaults.disable_networking, null) 15 | dns_search_domains = try(each.value.dns_search_domains, var.defaults.dns_search_domains, []) 16 | dns_servers = try(each.value.dns_servers, var.defaults.dns_servers, []) 17 | docker_labels = try(each.value.docker_labels, var.defaults.docker_labels, {}) 18 | docker_security_options = try(each.value.docker_security_options, var.defaults.docker_security_options, []) 19 | enable_cloudwatch_logging = try(each.value.enable_cloudwatch_logging, var.defaults.enable_cloudwatch_logging, true) 20 | enable_execute_command = try(each.value.enable_execute_command, var.defaults.enable_execute_command, false) 21 | entrypoint = try(each.value.entrypoint, var.defaults.entrypoint, []) 22 | environment = try(each.value.environment, var.defaults.environment, []) 23 | environment_files = try(each.value.environment_files, var.defaults.environment_files, []) 24 | essential = try(each.value.essential, var.defaults.essential, null) 25 | extra_hosts = try(each.value.extra_hosts, var.defaults.extra_hosts, []) 26 | firelens_configuration = try(each.value.firelens_configuration, var.defaults.firelens_configuration, {}) 27 | health_check = try(each.value.health_check, var.defaults.health_check, {}) 28 | hostname = try(each.value.hostname, var.defaults.hostname, null) 29 | image = try(each.value.image, var.defaults.image, null) 30 | interactive = try(each.value.interactive, var.defaults.interactive, false) 31 | links = try(each.value.links, var.defaults.links, []) 32 | linux_parameters = try(each.value.linux_parameters, var.defaults.linux_parameters, {}) 33 | log_configuration = try(each.value.log_configuration, var.defaults.log_configuration, {}) 34 | memory = try(each.value.memory, var.defaults.memory, null) 35 | memory_reservation = try(each.value.memory_reservation, var.defaults.memory_reservation, null) 36 | mount_points = try(each.value.mount_points, var.defaults.mount_points, []) 37 | name = try(each.value.name, var.defaults.name, null) 38 | operating_system_family = try(each.value.operating_system_family, var.defaults.operating_system_family, "LINUX") 39 | port_mappings = try(each.value.port_mappings, var.defaults.port_mappings, []) 40 | privileged = try(each.value.privileged, var.defaults.privileged, false) 41 | pseudo_terminal = try(each.value.pseudo_terminal, var.defaults.pseudo_terminal, false) 42 | readonly_root_filesystem = try(each.value.readonly_root_filesystem, var.defaults.readonly_root_filesystem, true) 43 | repository_credentials = try(each.value.repository_credentials, var.defaults.repository_credentials, {}) 44 | resource_requirements = try(each.value.resource_requirements, var.defaults.resource_requirements, []) 45 | secrets = try(each.value.secrets, var.defaults.secrets, []) 46 | service = try(each.value.service, var.defaults.service, "") 47 | start_timeout = try(each.value.start_timeout, var.defaults.start_timeout, 30) 48 | stop_timeout = try(each.value.stop_timeout, var.defaults.stop_timeout, 120) 49 | system_controls = try(each.value.system_controls, var.defaults.system_controls, []) 50 | tags = try(each.value.tags, var.defaults.tags, {}) 51 | ulimits = try(each.value.ulimits, var.defaults.ulimits, []) 52 | user = try(each.value.user, var.defaults.user, null) 53 | volumes_from = try(each.value.volumes_from, var.defaults.volumes_from, []) 54 | working_directory = try(each.value.working_directory, var.defaults.working_directory, null) 55 | } 56 | -------------------------------------------------------------------------------- /wrappers/container-definition/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/container-definition/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/container-definition/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../" 3 | 4 | for_each = var.items 5 | 6 | autoscaling_capacity_providers = try(each.value.autoscaling_capacity_providers, var.defaults.autoscaling_capacity_providers, {}) 7 | cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null) 8 | cloudwatch_log_group_name = try(each.value.cloudwatch_log_group_name, var.defaults.cloudwatch_log_group_name, null) 9 | cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 90) 10 | cloudwatch_log_group_tags = try(each.value.cloudwatch_log_group_tags, var.defaults.cloudwatch_log_group_tags, {}) 11 | cluster_configuration = try(each.value.cluster_configuration, var.defaults.cluster_configuration, {}) 12 | cluster_name = try(each.value.cluster_name, var.defaults.cluster_name, "") 13 | cluster_service_connect_defaults = try(each.value.cluster_service_connect_defaults, var.defaults.cluster_service_connect_defaults, {}) 14 | cluster_settings = try(each.value.cluster_settings, var.defaults.cluster_settings, [ 15 | { 16 | name = "containerInsights" 17 | value = "enabled" 18 | } 19 | ]) 20 | cluster_tags = try(each.value.cluster_tags, var.defaults.cluster_tags, {}) 21 | create = try(each.value.create, var.defaults.create, true) 22 | create_cloudwatch_log_group = try(each.value.create_cloudwatch_log_group, var.defaults.create_cloudwatch_log_group, true) 23 | create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, false) 24 | create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true) 25 | default_capacity_provider_use_fargate = try(each.value.default_capacity_provider_use_fargate, var.defaults.default_capacity_provider_use_fargate, true) 26 | fargate_capacity_providers = try(each.value.fargate_capacity_providers, var.defaults.fargate_capacity_providers, {}) 27 | services = try(each.value.services, var.defaults.services, {}) 28 | tags = try(each.value.tags, var.defaults.tags, {}) 29 | task_exec_iam_role_description = try(each.value.task_exec_iam_role_description, var.defaults.task_exec_iam_role_description, null) 30 | task_exec_iam_role_name = try(each.value.task_exec_iam_role_name, var.defaults.task_exec_iam_role_name, null) 31 | task_exec_iam_role_path = try(each.value.task_exec_iam_role_path, var.defaults.task_exec_iam_role_path, null) 32 | task_exec_iam_role_permissions_boundary = try(each.value.task_exec_iam_role_permissions_boundary, var.defaults.task_exec_iam_role_permissions_boundary, null) 33 | task_exec_iam_role_policies = try(each.value.task_exec_iam_role_policies, var.defaults.task_exec_iam_role_policies, {}) 34 | task_exec_iam_role_tags = try(each.value.task_exec_iam_role_tags, var.defaults.task_exec_iam_role_tags, {}) 35 | task_exec_iam_role_use_name_prefix = try(each.value.task_exec_iam_role_use_name_prefix, var.defaults.task_exec_iam_role_use_name_prefix, true) 36 | task_exec_iam_statements = try(each.value.task_exec_iam_statements, var.defaults.task_exec_iam_statements, {}) 37 | task_exec_secret_arns = try(each.value.task_exec_secret_arns, var.defaults.task_exec_secret_arns, ["arn:aws:secretsmanager:*:*:secret:*"]) 38 | task_exec_ssm_param_arns = try(each.value.task_exec_ssm_param_arns, var.defaults.task_exec_ssm_param_arns, ["arn:aws:ssm:*:*:parameter/*"]) 39 | } 40 | -------------------------------------------------------------------------------- /wrappers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/service/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/service` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/ecs/aws//wrappers/service" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ecs.git//wrappers/service?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/ecs/aws//wrappers/service" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/service/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/service" 3 | 4 | for_each = var.items 5 | 6 | alarms = try(each.value.alarms, var.defaults.alarms, {}) 7 | assign_public_ip = try(each.value.assign_public_ip, var.defaults.assign_public_ip, false) 8 | autoscaling_max_capacity = try(each.value.autoscaling_max_capacity, var.defaults.autoscaling_max_capacity, 10) 9 | autoscaling_min_capacity = try(each.value.autoscaling_min_capacity, var.defaults.autoscaling_min_capacity, 1) 10 | autoscaling_policies = try(each.value.autoscaling_policies, var.defaults.autoscaling_policies, { 11 | cpu = { 12 | policy_type = "TargetTrackingScaling" 13 | 14 | target_tracking_scaling_policy_configuration = { 15 | predefined_metric_specification = { 16 | predefined_metric_type = "ECSServiceAverageCPUUtilization" 17 | } 18 | } 19 | } 20 | memory = { 21 | policy_type = "TargetTrackingScaling" 22 | 23 | target_tracking_scaling_policy_configuration = { 24 | predefined_metric_specification = { 25 | predefined_metric_type = "ECSServiceAverageMemoryUtilization" 26 | } 27 | } 28 | } 29 | }) 30 | autoscaling_scheduled_actions = try(each.value.autoscaling_scheduled_actions, var.defaults.autoscaling_scheduled_actions, {}) 31 | capacity_provider_strategy = try(each.value.capacity_provider_strategy, var.defaults.capacity_provider_strategy, {}) 32 | cluster_arn = try(each.value.cluster_arn, var.defaults.cluster_arn, "") 33 | container_definition_defaults = try(each.value.container_definition_defaults, var.defaults.container_definition_defaults, {}) 34 | container_definitions = try(each.value.container_definitions, var.defaults.container_definitions, {}) 35 | cpu = try(each.value.cpu, var.defaults.cpu, 1024) 36 | create = try(each.value.create, var.defaults.create, true) 37 | create_iam_role = try(each.value.create_iam_role, var.defaults.create_iam_role, true) 38 | create_security_group = try(each.value.create_security_group, var.defaults.create_security_group, true) 39 | create_service = try(each.value.create_service, var.defaults.create_service, true) 40 | create_task_definition = try(each.value.create_task_definition, var.defaults.create_task_definition, true) 41 | create_task_exec_iam_role = try(each.value.create_task_exec_iam_role, var.defaults.create_task_exec_iam_role, true) 42 | create_task_exec_policy = try(each.value.create_task_exec_policy, var.defaults.create_task_exec_policy, true) 43 | create_tasks_iam_role = try(each.value.create_tasks_iam_role, var.defaults.create_tasks_iam_role, true) 44 | deployment_circuit_breaker = try(each.value.deployment_circuit_breaker, var.defaults.deployment_circuit_breaker, {}) 45 | deployment_controller = try(each.value.deployment_controller, var.defaults.deployment_controller, {}) 46 | deployment_maximum_percent = try(each.value.deployment_maximum_percent, var.defaults.deployment_maximum_percent, 200) 47 | deployment_minimum_healthy_percent = try(each.value.deployment_minimum_healthy_percent, var.defaults.deployment_minimum_healthy_percent, 66) 48 | desired_count = try(each.value.desired_count, var.defaults.desired_count, 1) 49 | enable_autoscaling = try(each.value.enable_autoscaling, var.defaults.enable_autoscaling, true) 50 | enable_ecs_managed_tags = try(each.value.enable_ecs_managed_tags, var.defaults.enable_ecs_managed_tags, true) 51 | enable_execute_command = try(each.value.enable_execute_command, var.defaults.enable_execute_command, false) 52 | ephemeral_storage = try(each.value.ephemeral_storage, var.defaults.ephemeral_storage, {}) 53 | external_id = try(each.value.external_id, var.defaults.external_id, null) 54 | family = try(each.value.family, var.defaults.family, null) 55 | force_delete = try(each.value.force_delete, var.defaults.force_delete, null) 56 | force_new_deployment = try(each.value.force_new_deployment, var.defaults.force_new_deployment, true) 57 | health_check_grace_period_seconds = try(each.value.health_check_grace_period_seconds, var.defaults.health_check_grace_period_seconds, null) 58 | iam_role_arn = try(each.value.iam_role_arn, var.defaults.iam_role_arn, null) 59 | iam_role_description = try(each.value.iam_role_description, var.defaults.iam_role_description, null) 60 | iam_role_name = try(each.value.iam_role_name, var.defaults.iam_role_name, null) 61 | iam_role_path = try(each.value.iam_role_path, var.defaults.iam_role_path, null) 62 | iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.defaults.iam_role_permissions_boundary, null) 63 | iam_role_statements = try(each.value.iam_role_statements, var.defaults.iam_role_statements, {}) 64 | iam_role_tags = try(each.value.iam_role_tags, var.defaults.iam_role_tags, {}) 65 | iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.defaults.iam_role_use_name_prefix, true) 66 | ignore_task_definition_changes = try(each.value.ignore_task_definition_changes, var.defaults.ignore_task_definition_changes, false) 67 | inference_accelerator = try(each.value.inference_accelerator, var.defaults.inference_accelerator, {}) 68 | ipc_mode = try(each.value.ipc_mode, var.defaults.ipc_mode, null) 69 | launch_type = try(each.value.launch_type, var.defaults.launch_type, "FARGATE") 70 | load_balancer = try(each.value.load_balancer, var.defaults.load_balancer, {}) 71 | memory = try(each.value.memory, var.defaults.memory, 2048) 72 | name = try(each.value.name, var.defaults.name, null) 73 | network_mode = try(each.value.network_mode, var.defaults.network_mode, "awsvpc") 74 | ordered_placement_strategy = try(each.value.ordered_placement_strategy, var.defaults.ordered_placement_strategy, {}) 75 | pid_mode = try(each.value.pid_mode, var.defaults.pid_mode, null) 76 | placement_constraints = try(each.value.placement_constraints, var.defaults.placement_constraints, {}) 77 | platform_version = try(each.value.platform_version, var.defaults.platform_version, null) 78 | propagate_tags = try(each.value.propagate_tags, var.defaults.propagate_tags, null) 79 | proxy_configuration = try(each.value.proxy_configuration, var.defaults.proxy_configuration, {}) 80 | requires_compatibilities = try(each.value.requires_compatibilities, var.defaults.requires_compatibilities, ["FARGATE"]) 81 | runtime_platform = try(each.value.runtime_platform, var.defaults.runtime_platform, { 82 | operating_system_family = "LINUX" 83 | cpu_architecture = "X86_64" 84 | }) 85 | scale = try(each.value.scale, var.defaults.scale, {}) 86 | scheduling_strategy = try(each.value.scheduling_strategy, var.defaults.scheduling_strategy, null) 87 | security_group_description = try(each.value.security_group_description, var.defaults.security_group_description, null) 88 | security_group_ids = try(each.value.security_group_ids, var.defaults.security_group_ids, []) 89 | security_group_name = try(each.value.security_group_name, var.defaults.security_group_name, null) 90 | security_group_rules = try(each.value.security_group_rules, var.defaults.security_group_rules, {}) 91 | security_group_tags = try(each.value.security_group_tags, var.defaults.security_group_tags, {}) 92 | security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, var.defaults.security_group_use_name_prefix, true) 93 | service_connect_configuration = try(each.value.service_connect_configuration, var.defaults.service_connect_configuration, {}) 94 | service_registries = try(each.value.service_registries, var.defaults.service_registries, {}) 95 | service_tags = try(each.value.service_tags, var.defaults.service_tags, {}) 96 | skip_destroy = try(each.value.skip_destroy, var.defaults.skip_destroy, null) 97 | subnet_ids = try(each.value.subnet_ids, var.defaults.subnet_ids, []) 98 | tags = try(each.value.tags, var.defaults.tags, {}) 99 | task_definition_arn = try(each.value.task_definition_arn, var.defaults.task_definition_arn, null) 100 | task_definition_placement_constraints = try(each.value.task_definition_placement_constraints, var.defaults.task_definition_placement_constraints, {}) 101 | task_exec_iam_policy_path = try(each.value.task_exec_iam_policy_path, var.defaults.task_exec_iam_policy_path, null) 102 | task_exec_iam_role_arn = try(each.value.task_exec_iam_role_arn, var.defaults.task_exec_iam_role_arn, null) 103 | task_exec_iam_role_description = try(each.value.task_exec_iam_role_description, var.defaults.task_exec_iam_role_description, null) 104 | task_exec_iam_role_max_session_duration = try(each.value.task_exec_iam_role_max_session_duration, var.defaults.task_exec_iam_role_max_session_duration, null) 105 | task_exec_iam_role_name = try(each.value.task_exec_iam_role_name, var.defaults.task_exec_iam_role_name, null) 106 | task_exec_iam_role_path = try(each.value.task_exec_iam_role_path, var.defaults.task_exec_iam_role_path, null) 107 | task_exec_iam_role_permissions_boundary = try(each.value.task_exec_iam_role_permissions_boundary, var.defaults.task_exec_iam_role_permissions_boundary, null) 108 | task_exec_iam_role_policies = try(each.value.task_exec_iam_role_policies, var.defaults.task_exec_iam_role_policies, {}) 109 | task_exec_iam_role_tags = try(each.value.task_exec_iam_role_tags, var.defaults.task_exec_iam_role_tags, {}) 110 | task_exec_iam_role_use_name_prefix = try(each.value.task_exec_iam_role_use_name_prefix, var.defaults.task_exec_iam_role_use_name_prefix, true) 111 | task_exec_iam_statements = try(each.value.task_exec_iam_statements, var.defaults.task_exec_iam_statements, {}) 112 | task_exec_secret_arns = try(each.value.task_exec_secret_arns, var.defaults.task_exec_secret_arns, ["arn:aws:secretsmanager:*:*:secret:*"]) 113 | task_exec_ssm_param_arns = try(each.value.task_exec_ssm_param_arns, var.defaults.task_exec_ssm_param_arns, ["arn:aws:ssm:*:*:parameter/*"]) 114 | task_tags = try(each.value.task_tags, var.defaults.task_tags, {}) 115 | tasks_iam_role_arn = try(each.value.tasks_iam_role_arn, var.defaults.tasks_iam_role_arn, null) 116 | tasks_iam_role_description = try(each.value.tasks_iam_role_description, var.defaults.tasks_iam_role_description, null) 117 | tasks_iam_role_name = try(each.value.tasks_iam_role_name, var.defaults.tasks_iam_role_name, null) 118 | tasks_iam_role_path = try(each.value.tasks_iam_role_path, var.defaults.tasks_iam_role_path, null) 119 | tasks_iam_role_permissions_boundary = try(each.value.tasks_iam_role_permissions_boundary, var.defaults.tasks_iam_role_permissions_boundary, null) 120 | tasks_iam_role_policies = try(each.value.tasks_iam_role_policies, var.defaults.tasks_iam_role_policies, {}) 121 | tasks_iam_role_statements = try(each.value.tasks_iam_role_statements, var.defaults.tasks_iam_role_statements, {}) 122 | tasks_iam_role_tags = try(each.value.tasks_iam_role_tags, var.defaults.tasks_iam_role_tags, {}) 123 | tasks_iam_role_use_name_prefix = try(each.value.tasks_iam_role_use_name_prefix, var.defaults.tasks_iam_role_use_name_prefix, true) 124 | timeouts = try(each.value.timeouts, var.defaults.timeouts, {}) 125 | triggers = try(each.value.triggers, var.defaults.triggers, {}) 126 | volume = try(each.value.volume, var.defaults.volume, {}) 127 | wait_for_steady_state = try(each.value.wait_for_steady_state, var.defaults.wait_for_steady_state, null) 128 | wait_until_stable = try(each.value.wait_until_stable, var.defaults.wait_until_stable, null) 129 | wait_until_stable_timeout = try(each.value.wait_until_stable_timeout, var.defaults.wait_until_stable_timeout, null) 130 | } 131 | -------------------------------------------------------------------------------- /wrappers/service/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/service/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/service/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.66.1" 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------