├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── bug_report.yml
│ ├── config.yml
│ ├── feature_request.md
│ ├── feature_request.yml
│ └── question.md
├── PULL_REQUEST_TEMPLATE.md
├── auto-release.yml
├── mergify.yml
├── renovate.json
└── workflows
│ ├── feature-branch-chatops.yml
│ ├── feature-branch.yml
│ ├── release-branch.yml
│ ├── release-published.yml
│ └── scheduled.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── README.yaml
├── context.tf
├── docs
├── example.png
├── github-repo-scopes.png
├── logo.png
├── targets.md
└── terraform.md
├── examples
├── complete
│ ├── context.tf
│ ├── fixtures.us-west-2.tfvars
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── with_cognito_authentication
│ ├── context.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── with_google_oidc_authentication
│ ├── context.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
└── without_authentication
│ ├── context.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── main.tf
├── outputs.tf
├── test
├── .gitignore
├── Makefile
├── Makefile.alpine
└── src
│ ├── .gitignore
│ ├── Makefile
│ ├── examples_complete_test.go
│ ├── go.mod
│ └── go.sum
├── variables.tf
└── versions.tf
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Use this file to define individuals or teams that are responsible for code in a repository.
2 | # Read more:
3 | #
4 | # Order is important: the last matching pattern has the highest precedence
5 |
6 | # These owners will be the default owners for everything
7 | * @cloudposse/engineering @cloudposse/contributors
8 |
9 | # Cloud Posse must review any changes to Makefiles
10 | **/Makefile @cloudposse/engineering
11 | **/Makefile.* @cloudposse/engineering
12 |
13 | # Cloud Posse must review any changes to GitHub actions
14 | .github/* @cloudposse/engineering
15 |
16 | # Cloud Posse must review any changes to standard context definition,
17 | # but some changes can be rubber-stamped.
18 | **/*.tf @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers
19 | README.yaml @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers
20 | README.md @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers
21 | docs/*.md @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers
22 |
23 | # Cloud Posse Admins must review all changes to CODEOWNERS or the mergify configuration
24 | .github/mergify.yml @cloudposse/admins
25 | .github/CODEOWNERS @cloudposse/admins
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: 'bug'
6 | assignees: ''
7 |
8 | ---
9 |
10 | Found a bug? Maybe our [Slack Community](https://slack.cloudposse.com) can help.
11 |
12 | [](https://slack.cloudposse.com)
13 |
14 | ## Describe the Bug
15 | A clear and concise description of what the bug is.
16 |
17 | ## Expected Behavior
18 | A clear and concise description of what you expected to happen.
19 |
20 | ## Steps to Reproduce
21 | Steps to reproduce the behavior:
22 | 1. Go to '...'
23 | 2. Run '....'
24 | 3. Enter '....'
25 | 4. See error
26 |
27 | ## Screenshots
28 | If applicable, add screenshots or logs to help explain your problem.
29 |
30 | ## Environment (please complete the following information):
31 |
32 | Anything that will help us triage the bug will help. Here are some ideas:
33 | - OS: [e.g. Linux, OSX, WSL, etc]
34 | - Version [e.g. 10.15]
35 |
36 | ## Additional Context
37 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | description: Create a report to help us improve
4 | labels: ["bug"]
5 | assignees: [""]
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Found a bug?
11 |
12 | Please checkout our [Slack Community](https://slack.cloudposse.com)
13 | or visit our [Slack Archive](https://archive.sweetops.com/).
14 |
15 | [](https://slack.cloudposse.com)
16 |
17 | - type: textarea
18 | id: concise-description
19 | attributes:
20 | label: Describe the Bug
21 | description: A clear and concise description of what the bug is.
22 | placeholder: What is the bug about?
23 | validations:
24 | required: true
25 |
26 | - type: textarea
27 | id: expected
28 | attributes:
29 | label: Expected Behavior
30 | description: A clear and concise description of what you expected.
31 | placeholder: What happened?
32 | validations:
33 | required: true
34 |
35 | - type: textarea
36 | id: reproduction-steps
37 | attributes:
38 | label: Steps to Reproduce
39 | description: Steps to reproduce the behavior.
40 | placeholder: How do we reproduce it?
41 | validations:
42 | required: true
43 |
44 | - type: textarea
45 | id: screenshots
46 | attributes:
47 | label: Screenshots
48 | description: If applicable, add screenshots or logs to help explain.
49 | validations:
50 | required: false
51 |
52 | - type: textarea
53 | id: environment
54 | attributes:
55 | label: Environment
56 | description: Anything that will help us triage the bug.
57 | placeholder: |
58 | - OS: [e.g. Linux, OSX, WSL, etc]
59 | - Version [e.g. 10.15]
60 | - Module version
61 | - Terraform version
62 | validations:
63 | required: false
64 |
65 | - type: textarea
66 | id: additional
67 | attributes:
68 | label: Additional Context
69 | description: |
70 | Add any other context about the problem here.
71 | validations:
72 | required: false
73 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
3 | contact_links:
4 |
5 | - name: Community Slack Team
6 | url: https://cloudposse.com/slack/
7 | about: |-
8 | Please ask and answer questions here.
9 |
10 | - name: Office Hours
11 | url: https://cloudposse.com/office-hours/
12 | about: |-
13 | Join us every Wednesday for FREE Office Hours (lunch & learn).
14 |
15 | - name: DevOps Accelerator Program
16 | url: https://cloudposse.com/accelerate/
17 | about: |-
18 | Own your infrastructure in record time. We build it. You drive it.
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: 'feature request'
6 | assignees: ''
7 |
8 | ---
9 |
10 | Have a question? Please checkout our [Slack Community](https://slack.cloudposse.com) or visit our [Slack Archive](https://archive.sweetops.com/).
11 |
12 | [](https://slack.cloudposse.com)
13 |
14 | ## Describe the Feature
15 |
16 | A clear and concise description of what the bug is.
17 |
18 | ## Expected Behavior
19 |
20 | A clear and concise description of what you expected to happen.
21 |
22 | ## Use Case
23 |
24 | Is your feature request related to a problem/challenge you are trying to solve? Please provide some additional context of why this feature or capability will be valuable.
25 |
26 | ## Describe Ideal Solution
27 |
28 | A clear and concise description of what you want to happen. If you don't know, that's okay.
29 |
30 | ## Alternatives Considered
31 |
32 | Explain what alternative solutions or features you've considered.
33 |
34 | ## Additional Context
35 |
36 | Add any other context or screenshots about the feature request here.
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | description: Suggest an idea for this project
4 | labels: ["feature request"]
5 | assignees: [""]
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Have a question?
11 |
12 | Please checkout our [Slack Community](https://slack.cloudposse.com)
13 | or visit our [Slack Archive](https://archive.sweetops.com/).
14 |
15 | [](https://slack.cloudposse.com)
16 |
17 | - type: textarea
18 | id: concise-description
19 | attributes:
20 | label: Describe the Feature
21 | description: A clear and concise description of what the feature is.
22 | placeholder: What is the feature about?
23 | validations:
24 | required: true
25 |
26 | - type: textarea
27 | id: expected
28 | attributes:
29 | label: Expected Behavior
30 | description: A clear and concise description of what you expected.
31 | placeholder: What happened?
32 | validations:
33 | required: true
34 |
35 | - type: textarea
36 | id: use-case
37 | attributes:
38 | label: Use Case
39 | description: |
40 | Is your feature request related to a problem/challenge you are trying
41 | to solve?
42 |
43 | Please provide some additional context of why this feature or
44 | capability will be valuable.
45 | validations:
46 | required: true
47 |
48 | - type: textarea
49 | id: ideal-solution
50 | attributes:
51 | label: Describe Ideal Solution
52 | description: A clear and concise description of what you want to happen.
53 | validations:
54 | required: true
55 |
56 | - type: textarea
57 | id: alternatives-considered
58 | attributes:
59 | label: Alternatives Considered
60 | description: Explain alternative solutions or features considered.
61 | validations:
62 | required: false
63 |
64 | - type: textarea
65 | id: additional
66 | attributes:
67 | label: Additional Context
68 | description: |
69 | Add any other context about the problem here.
70 | validations:
71 | required: false
72 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudposse-archives/terraform-aws-ecs-atlantis/c4e95fed76876ab4e7eac391cd5cfed980bfc3de/.github/ISSUE_TEMPLATE/question.md
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## what
2 |
3 |
7 |
8 | ## why
9 |
10 |
15 |
16 | ## references
17 |
18 |
22 |
--------------------------------------------------------------------------------
/.github/auto-release.yml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION'
2 | tag-template: '$RESOLVED_VERSION'
3 | version-template: '$MAJOR.$MINOR.$PATCH'
4 | version-resolver:
5 | major:
6 | labels:
7 | - 'major'
8 | minor:
9 | labels:
10 | - 'minor'
11 | - 'enhancement'
12 | patch:
13 | labels:
14 | - 'auto-update'
15 | - 'patch'
16 | - 'fix'
17 | - 'bugfix'
18 | - 'bug'
19 | - 'hotfix'
20 | default: 'minor'
21 | filter-by-commitish: true
22 |
23 | categories:
24 | - title: '🚀 Enhancements'
25 | labels:
26 | - 'enhancement'
27 | - 'patch'
28 | - title: '🐛 Bug Fixes'
29 | labels:
30 | - 'fix'
31 | - 'bugfix'
32 | - 'bug'
33 | - 'hotfix'
34 | - title: '🤖 Automatic Updates'
35 | labels:
36 | - 'auto-update'
37 |
38 | change-template: |
39 |
40 | $TITLE @$AUTHOR (#$NUMBER)
41 |
42 | $BODY
43 |
44 |
45 | template: |
46 | $CHANGES
47 |
48 | replacers:
49 | # Remove irrelevant information from Renovate bot
50 | - search: '/(?<=---\s)\s*^#.*(Renovate configuration|Configuration)(?:.|\n)*?This PR has been generated .*/gm'
51 | replace: ''
52 | # Remove Renovate bot banner image
53 | - search: '/\[!\[[^\]]*Renovate\][^\]]*\](\([^)]*\))?\s*\n+/gm'
54 | replace: ''
55 |
--------------------------------------------------------------------------------
/.github/mergify.yml:
--------------------------------------------------------------------------------
1 | # https://docs.mergify.io/conditions.html
2 | # https://docs.mergify.io/actions.html
3 | pull_request_rules:
4 | - name: "approve automated PRs that have passed checks"
5 | conditions:
6 | - "author~=^(cloudpossebot|renovate\\[bot\\])$"
7 | - "-closed"
8 | - "head~=^(auto-update|renovate)/.*"
9 | - "check-success=test/bats"
10 | - "check-success=test/readme"
11 | - "check-success=test/terratest"
12 | - "check-success=validate-codeowners"
13 | - or:
14 | - "base=master"
15 | - "base=main"
16 | - "base~=^release/v\\d{1,2}$"
17 |
18 | actions:
19 | review:
20 | type: "APPROVE"
21 | bot_account: "cloudposse-mergebot"
22 | message: "We've automatically approved this PR because the checks from the automated Pull Request have passed."
23 |
24 | - name: "merge automated PRs when approved and tests pass"
25 | conditions:
26 | - "author~=^(cloudpossebot|renovate\\[bot\\])$"
27 | - "-closed"
28 | - "head~=^(auto-update|renovate)/.*"
29 | - "check-success=test/bats"
30 | - "check-success=test/readme"
31 | - "check-success=test/terratest"
32 | - "check-success=validate-codeowners"
33 | - "#approved-reviews-by>=1"
34 | - "#changes-requested-reviews-by=0"
35 | - "#commented-reviews-by=0"
36 | - or:
37 | - "base=master"
38 | - "base=main"
39 | - "base~=^release/v\\d{1,2}$"
40 |
41 | actions:
42 | merge:
43 | method: "squash"
44 |
45 | - name: "delete the head branch after merge"
46 | conditions:
47 | - "merged"
48 | actions:
49 | delete_head_branch: {}
50 |
51 | - name: "ask to resolve conflict"
52 | conditions:
53 | - "conflict"
54 | - "-closed"
55 | actions:
56 | comment:
57 | message: "This pull request is now in conflict. Could you fix it @{{author}}? 🙏"
58 |
59 | - name: "remove outdated reviews"
60 | conditions:
61 | - or:
62 | - "base=master"
63 | - "base=main"
64 | - "base~=^release/v\\d{1,2}$"
65 | actions:
66 | dismiss_reviews:
67 | changes_requested: true
68 | approved: true
69 | message: "This Pull Request has been updated, so we're dismissing all reviews."
70 |
71 | - name: "close Pull Requests without files changed"
72 | conditions:
73 | - "#files=0"
74 | actions:
75 | close:
76 | message: "This pull request has been automatically closed by Mergify because there are no longer any changes."
77 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base",
4 | ":preserveSemverRanges"
5 | ],
6 | "baseBranches": ["main", "master", "/^release\\/v\\d{1,2}$/"],
7 | "labels": ["auto-update"],
8 | "dependencyDashboardAutoclose": true,
9 | "enabledManagers": ["terraform"],
10 | "terraform": {
11 | "ignorePaths": ["**/context.tf", "examples/**"]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/feature-branch-chatops.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: feature-branch-chatops
3 | on:
4 | issue_comment:
5 | types: [created]
6 |
7 | permissions:
8 | pull-requests: write
9 | id-token: write
10 | contents: write
11 |
12 | jobs:
13 | terraform-module:
14 | uses: cloudposse/github-actions-workflows-terraform-module/.github/workflows/feature-branch-chatops.yml@main
15 | secrets:
16 | github_access_token: ${{ secrets.REPO_ACCESS_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/feature-branch.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: feature-branch
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | - release/**
8 | types: [opened, synchronize, reopened, labeled, unlabeled]
9 |
10 | permissions:
11 | pull-requests: write
12 | id-token: write
13 | contents: write
14 |
15 | jobs:
16 | terraform-module:
17 | uses: cloudposse/github-actions-workflows-terraform-module/.github/workflows/feature-branch.yml@main
18 | secrets:
19 | github_access_token: ${{ secrets.REPO_ACCESS_TOKEN }}
20 |
--------------------------------------------------------------------------------
/.github/workflows/release-branch.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: release-branch
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - release/**
8 | paths-ignore:
9 | - '.github/**'
10 | - 'docs/**'
11 | - 'examples/**'
12 | - 'test/**'
13 |
14 | permissions:
15 | contents: write
16 | id-token: write
17 |
18 | jobs:
19 | terraform-module:
20 | uses: cloudposse/github-actions-workflows-terraform-module/.github/workflows/release-branch.yml@main
21 | secrets:
22 | github_access_token: ${{ secrets.REPO_ACCESS_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.github/workflows/release-published.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: release-published
3 | on:
4 | release:
5 | types:
6 | - published
7 |
8 | permissions:
9 | contents: write
10 | id-token: write
11 |
12 | jobs:
13 | terraform-module:
14 | uses: cloudposse/github-actions-workflows-terraform-module/.github/workflows/release.yml@main
15 |
--------------------------------------------------------------------------------
/.github/workflows/scheduled.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled
3 | on:
4 | workflow_dispatch: { } # Allows manually trigger this workflow
5 | schedule:
6 | - cron: "0 3 * * *"
7 |
8 | permissions:
9 | pull-requests: write
10 | id-token: write
11 | contents: write
12 |
13 | jobs:
14 | scheduled:
15 | uses: cloudposse/github-actions-workflows-terraform-module/.github/workflows/scheduled.yml@main
16 | secrets:
17 | github_access_token: ${{ secrets.REPO_ACCESS_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 | .terraform.tfstate.lock.info
5 | **/.terraform.lock.hcl
6 | **/test.log
7 |
8 | # Module directory
9 | .terraform/
10 | .idea
11 | *.iml
12 |
13 | # Build Harness
14 | .build-harness
15 | build-harness/
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2018-2020 Cloud Posse, LLC
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SHELL := /bin/bash
2 |
3 | # List of targets the `readme` target should call before generating the readme
4 | export README_DEPS ?= docs/targets.md docs/terraform.md
5 |
6 | -include $(shell curl -sSL -o .build-harness "https://cloudposse.tools/build-harness"; echo .build-harness)
7 |
8 | ## Lint terraform code
9 | lint:
10 | $(SELF) terraform/install terraform/get-modules terraform/get-plugins terraform/lint terraform/validate
--------------------------------------------------------------------------------
/README.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | #
3 | # This is the canonical configuration for the `README.md`
4 | # Run `make readme` to rebuild the `README.md`
5 | #
6 |
7 | # Name of this project
8 | name: terraform-aws-ecs-atlantis
9 |
10 | # Logo for this project
11 | logo: docs/logo.png
12 |
13 | # License of this project
14 | license: "APACHE2"
15 |
16 | # Canonical GitHub repo
17 | github_repo: cloudposse/terraform-aws-ecs-atlantis
18 |
19 | # Screenshots
20 | screenshots:
21 | - name: "demo"
22 | description: "Example of a Pull Request comment from running `terraform plan` using `atlantis`"
23 | url: "docs/example.png"
24 |
25 | # Badges to display
26 | badges:
27 | - name: "Latest Release"
28 | image: "https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-atlantis.svg"
29 | url: "https://github.com/cloudposse/terraform-aws-ecs-atlantis/releases/latest"
30 | - name: "Slack Community"
31 | image: "https://slack.cloudposse.com/badge.svg"
32 | url: "https://slack.cloudposse.com"
33 |
34 | related:
35 | - name: "terraform-aws-ecs-web-app"
36 | description: "Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more"
37 | url: "https://github.com/cloudposse/terraform-aws-ecs-web-app"
38 | - name: "terraform-aws-alb"
39 | description: "Terraform module to provision a standard ALB for HTTP/HTTP traffic"
40 | url: "https://github.com/cloudposse/terraform-aws-alb"
41 | - name: "terraform-aws-alb-ingress"
42 | description: "Terraform module to provision an HTTP style ingress rule based on hostname and path for an ALB"
43 | url: "https://github.com/cloudposse/terraform-aws-alb-ingress"
44 | - name: "terraform-aws-codebuild"
45 | description: "Terraform Module to easily leverage AWS CodeBuild for Continuous Integration"
46 | url: "https://github.com/cloudposse/terraform-aws-codebuild"
47 | - name: "terraform-aws-ecr"
48 | description: "Terraform Module to manage Docker Container Registries on AWS ECR"
49 | url: "https://github.com/cloudposse/terraform-aws-ecr"
50 | - name: "terraform-aws-ecs-alb-service-task"
51 | description: "Terraform module which implements an ECS service which exposes a web service via ALB."
52 | url: "https://github.com/cloudposse/terraform-aws-ecs-alb-service-task"
53 | - name: "terraform-aws-ecs-codepipeline"
54 | description: "Terraform Module for CI/CD with AWS Code Pipeline and Code Build for ECS"
55 | url: "https://github.com/cloudposse/terraform-aws-ecs-codepipeline"
56 | - name: "terraform-aws-ecs-container-definition"
57 | description: "Terraform module to generate well-formed JSON documents that are passed to the aws_ecs_task_definition Terraform resource"
58 | url: "https://github.com/cloudposse/terraform-aws-ecs-container-definition"
59 | - name: "terraform-aws-lb-s3-bucket"
60 | description: "Terraform module to provision an S3 bucket with built in IAM policy to allow AWS Load Balancers to ship access logs."
61 | url: "https://github.com/cloudposse/terraform-aws-lb-s3-bucket"
62 |
63 |
64 | # Short description of this project
65 | description: |-
66 |
67 | A Terraform module for deploying [Atlantis](https://runatlantis.io) to an AWS ECS cluster.
68 |
69 | introduction: |-
70 |
71 | Atlantis enables GitOps workflows so that teams can collaborate on operations using Pull Requests.
72 |
73 | Under the hood, it's a small self-hosted daemon (`#golang`) that listens for Pull Request webhook events from GitHub.
74 |
75 | With Atlantis, engineers can run `terraform plan` and `terraform apply` using "chat ops" type comments on the Pull Request.
76 |
77 | ### Features
78 |
79 | This module provisions the following resources:
80 |
81 | - ECS Atlantis web application, which includes:
82 | - ECR Docker registry
83 | - ALB target group, listener rule and alarms
84 | - ECS container definition (using a default backend)
85 | - ECS task definition and IAM role
86 | - ECS service and IAM role
87 | - ECS task autoscaling
88 | - ECS SNS based alarms
89 | - ECS Codepipeline to build our Atlantis image on GitHub release
90 | - ECS Codedeploy to deploy our ECS Atlantis web app
91 | - SSH key pair for Atlantis to pull private Github repositories, which are written to SSM for reading with [chamber](https://github.com/segmentio/chamber)
92 | - Route53 alias for Atlantis
93 | - GitHub webhook to trigger Atlantis for a given repository
94 |
95 | What this module does not provision:
96 |
97 | - ECS Cluster (BYOC)
98 | - ALB
99 | - ACM certificate
100 | - VPC
101 | - Subnets
102 |
103 | ## Caveats
104 |
105 | - This project assumes that the repo being deployed defines a `Dockerfile` which runs `atlantis`. It might not work with the official version of atlantis. We use [`geodesic`](https://github.com/cloudposse/geodesic) as our docker base image.
106 | - This project defines parameters which are not available in the *official version* of `atlantis`. Our [fork](https://github.com/cloudposse/atlantis) implements the ability to restrict `plan` and `apply` to GitHub teams.
107 |
108 |
109 | ### GitHub Repo Scopes
110 |
111 | This module accepts two GitHub OAuth tokens:
112 |
113 | 1. `github_oauth_token` with permissions to pull private repos. Used by CodePipeline to clone repos before the build, and by the atlantis server to clone repos and comment on Pull Requests.
114 |
115 | The token needs the following OAuth scopes:
116 |
117 | - `repo`
118 | * `repo:status`
119 | * `repo_deployment`
120 | * `public_repo`
121 | * `repo:invite`
122 |
123 | 2. `github_webhooks_token` with permissions to create GitHub webhooks.
124 | Only used by [Terraform GitHub Provider](https://www.terraform.io/docs/providers/github/index.html) when provisioning the module.
125 |
126 | The token needs the following OAuth scopes:
127 |
128 | - `admin:repo_hook`
129 | * `write:repo_hook`
130 | * `read:repo_hook`
131 |
132 | We suggest the following steps when creating the tokens and provisioning the module:
133 |
134 | 1. Create a GitHub bot user
135 | 2. Create the two Personal Access Tokens and add them to the bot
136 | 3. In `github.com///settings/collaboration`, create a Team for the bot and add the bot user to it
137 | 4. Give `Admin` permissions to the Team (select it from the dropdown). We need it temporalily to provision GitHub webhooks on the repo
138 | 5. Provision the module with Terraform.
139 | [Terraform GitHub Provider](https://www.terraform.io/docs/providers/github/index.html) will use the `github_webhooks_token` to create webhooks on the repo
140 | 6. Go to `github.com///settings/hooks` and make sure that two webhooks have been created: one for the CodePipeline with `Releases` events,
141 | the other is for the `atlantis` server with `Issue comments`, `Pull request reviews`, `Pull requests`, `Pull request review comments` and `Pushes` events
142 | 7. **IMPORTANT:** Remove the `Admin` permissions and add `Read` permissions for the bot Team.
143 | The CodePipeline and `atlantis` server will use the `github_oauth_token` to clone repos, which does not require escalated privileges
144 |
145 | **IMPORTANT:** Do not commit the tokens to source control (_e.g._ via `terraform.tvfars`).
146 |
147 | **NOTE:** If the two tokens are not provided (left empty), they will be looked up from SSM Parameter Store.
148 | You can write `atlantis atlantis_gh` and `github_webhooks_token` to SSM Parameter Store before provisioning the module.
149 | For example, by using [chamber](https://github.com/segmentio/chamber):
150 |
151 | ```sh
152 | chamber write atlantis atlantis_gh_token "....."
153 | chamber write atlantis github_webhooks_token "....."
154 | ```
155 |
156 | # How to use this project
157 | usage: |-
158 |
159 | For a complete example, see [examples/complete](examples/complete).
160 |
161 | For automated tests of the complete example using [bats](https://github.com/bats-core/bats-core) and [Terratest](https://github.com/gruntwork-io/terratest) (which tests and deploys the example on AWS), see [test](test).
162 |
163 | Other examples:
164 |
165 | - [without authentication](examples/without_authentication) - example without authentication
166 | - [with Google OIDC authentication](examples/with_google_oidc_authentication) - example with Google OIDC authentication
167 | - [with Cognito authentication](examples/with_cognito_authentication) - example with Cognito authentication
168 |
169 |
170 | **NOTE:**
171 |
172 | If no `github_oauth_token` is set, the module attempts to look one up from SSM.
173 |
174 | If no `github_webhooks_token` is set, [Terraform GitHub Provider](https://www.terraform.io/docs/providers/github/index.html) attempts to look one up in the `GITHUB_TOKEN` environment variable.
175 |
176 | ```hcl
177 | provider "aws" {
178 | region = var.region
179 | }
180 |
181 | module "label" {
182 | source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.16.0"
183 | namespace = var.namespace
184 | name = var.name
185 | stage = var.stage
186 | delimiter = var.delimiter
187 | attributes = var.attributes
188 | tags = var.tags
189 | }
190 |
191 | module "vpc" {
192 | source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.8.1"
193 | namespace = var.namespace
194 | stage = var.stage
195 | name = var.name
196 | delimiter = var.delimiter
197 | attributes = var.attributes
198 | cidr_block = var.vpc_cidr_block
199 | tags = var.tags
200 | }
201 |
202 | module "subnets" {
203 | source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.16.1"
204 | availability_zones = var.availability_zones
205 | namespace = var.namespace
206 | stage = var.stage
207 | name = var.name
208 | attributes = var.attributes
209 | delimiter = var.delimiter
210 | vpc_id = module.vpc.vpc_id
211 | igw_id = module.vpc.igw_id
212 | cidr_block = module.vpc.vpc_cidr_block
213 | nat_gateway_enabled = true
214 | nat_instance_enabled = false
215 | tags = var.tags
216 | }
217 |
218 | module "alb" {
219 | source = "git::https://github.com/cloudposse/terraform-aws-alb.git?ref=tags/0.7.0"
220 | namespace = var.namespace
221 | stage = var.stage
222 | name = var.name
223 | attributes = var.attributes
224 | delimiter = var.delimiter
225 | vpc_id = module.vpc.vpc_id
226 | security_group_ids = [module.vpc.vpc_default_security_group_id]
227 | subnet_ids = module.subnets.public_subnet_ids
228 | internal = false
229 | http_enabled = true
230 | access_logs_enabled = false
231 | alb_access_logs_s3_bucket_force_destroy = true
232 | access_logs_region = var.region
233 | cross_zone_load_balancing_enabled = true
234 | http2_enabled = true
235 | deletion_protection_enabled = false
236 | tags = var.tags
237 | }
238 |
239 | resource "aws_ecs_cluster" "default" {
240 | name = module.label.id
241 | tags = module.label.tags
242 | }
243 |
244 | resource "aws_sns_topic" "sns_topic" {
245 | name = module.label.id
246 | display_name = "Test terraform-aws-ecs-atlantis"
247 | tags = module.label.tags
248 | }
249 |
250 | module "kms_key" {
251 | source = "git::https://github.com/cloudposse/terraform-aws-kms-key.git?ref=tags/0.3.0"
252 | enabled = var.enabled
253 | namespace = var.namespace
254 | stage = var.stage
255 | name = var.name
256 | attributes = var.attributes
257 | delimiter = var.delimiter
258 | tags = var.tags
259 | description = "Test terraform-aws-ecs-atlantis KMS key"
260 | deletion_window_in_days = 7
261 | enable_key_rotation = false
262 | }
263 |
264 | module "atlantis" {
265 | source = "cloudposse/ecs-atlantis/aws"
266 | # Cloud Posse recommends pinning every module to a specific version
267 | # version = "x.x.x"
268 | enabled = var.enabled
269 | namespace = var.namespace
270 | stage = var.stage
271 | name = var.name
272 | attributes = var.attributes
273 | delimiter = var.delimiter
274 | tags = var.tags
275 |
276 | region = var.region
277 | vpc_id = module.vpc.vpc_id
278 | policy_arn = var.policy_arn
279 | ssh_private_key_name = var.ssh_private_key_name
280 | ssh_public_key_name = var.ssh_public_key_name
281 | kms_key_id = module.kms_key.key_id
282 |
283 | atlantis_gh_user = var.atlantis_gh_user
284 | atlantis_gh_team_whitelist = var.atlantis_gh_team_whitelist
285 | atlantis_gh_webhook_secret = var.atlantis_gh_webhook_secret
286 | atlantis_log_level = var.atlantis_log_level
287 | atlantis_repo_config = var.atlantis_repo_config
288 | atlantis_repo_whitelist = var.atlantis_repo_whitelist
289 | atlantis_port = var.atlantis_port
290 | atlantis_webhook_format = var.atlantis_webhook_format
291 | atlantis_url_format = var.atlantis_url_format
292 |
293 | default_backend_image = var.default_backend_image
294 | healthcheck_path = var.healthcheck_path
295 | short_name = var.short_name
296 | hostname = var.hostname
297 | parent_zone_id = var.parent_zone_id
298 |
299 | // Container
300 | container_cpu = var.container_cpu
301 | container_memory = var.container_memory
302 |
303 | // Authentication
304 | authentication_type = var.authentication_type
305 | alb_ingress_listener_unauthenticated_priority = var.alb_ingress_listener_unauthenticated_priority
306 | alb_ingress_listener_authenticated_priority = var.alb_ingress_listener_authenticated_priority
307 | alb_ingress_unauthenticated_hosts = var.alb_ingress_unauthenticated_hosts
308 | alb_ingress_authenticated_hosts = var.alb_ingress_authenticated_hosts
309 | alb_ingress_unauthenticated_paths = var.alb_ingress_unauthenticated_paths
310 | alb_ingress_authenticated_paths = var.alb_ingress_authenticated_paths
311 | authentication_cognito_user_pool_arn = var.authentication_cognito_user_pool_arn
312 | authentication_cognito_user_pool_client_id = var.authentication_cognito_user_pool_client_id
313 | authentication_cognito_user_pool_domain = var.authentication_cognito_user_pool_domain
314 | authentication_oidc_client_id = var.authentication_oidc_client_id
315 | authentication_oidc_client_secret = var.authentication_oidc_client_secret
316 | authentication_oidc_issuer = var.authentication_oidc_issuer
317 | authentication_oidc_authorization_endpoint = var.authentication_oidc_authorization_endpoint
318 | authentication_oidc_token_endpoint = var.authentication_oidc_token_endpoint
319 | authentication_oidc_user_info_endpoint = var.authentication_oidc_user_info_endpoint
320 |
321 | // ECS
322 | private_subnet_ids = module.subnets.private_subnet_ids
323 | ecs_cluster_arn = aws_ecs_cluster.default.arn
324 | ecs_cluster_name = aws_ecs_cluster.default.name
325 | security_group_ids = var.security_group_ids
326 | desired_count = var.desired_count
327 | launch_type = var.launch_type
328 |
329 | // ALB
330 | alb_zone_id = module.alb.alb_zone_id
331 | alb_arn_suffix = module.alb.alb_arn_suffix
332 | alb_dns_name = module.alb.alb_dns_name
333 | alb_security_group = module.alb.security_group_id
334 | alb_ingress_unauthenticated_listener_arns = [module.alb.http_listener_arn]
335 | alb_ingress_unauthenticated_listener_arns_count = 1
336 |
337 | // CodePipeline
338 | codepipeline_enabled = var.codepipeline_enabled
339 | github_oauth_token = var.github_oauth_token
340 | github_webhooks_token = var.github_webhooks_token
341 | repo_owner = var.repo_owner
342 | repo_name = var.repo_name
343 | branch = var.branch
344 | build_timeout = var.build_timeout
345 | webhook_enabled = var.webhook_enabled
346 | webhook_secret_length = var.webhook_secret_length
347 | webhook_events = var.webhook_events
348 | codepipeline_s3_bucket_force_destroy = var.codepipeline_s3_bucket_force_destroy
349 |
350 | // Autoscaling
351 | autoscaling_enabled = var.autoscaling_enabled
352 | autoscaling_min_capacity = var.autoscaling_min_capacity
353 | autoscaling_max_capacity = var.autoscaling_max_capacity
354 |
355 | // Alarms
356 | alb_target_group_alarms_enabled = var.alb_target_group_alarms_enabled
357 | ecs_alarms_enabled = var.ecs_alarms_enabled
358 | alb_target_group_alarms_alarm_actions = [aws_sns_topic.sns_topic.arn]
359 | alb_target_group_alarms_ok_actions = [aws_sns_topic.sns_topic.arn]
360 | alb_target_group_alarms_insufficient_data_actions = [aws_sns_topic.sns_topic.arn]
361 | ecs_alarms_cpu_utilization_high_alarm_actions = [aws_sns_topic.sns_topic.arn]
362 | ecs_alarms_cpu_utilization_high_ok_actions = [aws_sns_topic.sns_topic.arn]
363 | ecs_alarms_cpu_utilization_low_alarm_actions = [aws_sns_topic.sns_topic.arn]
364 | ecs_alarms_cpu_utilization_low_ok_actions = [aws_sns_topic.sns_topic.arn]
365 | ecs_alarms_memory_utilization_high_alarm_actions = [aws_sns_topic.sns_topic.arn]
366 | ecs_alarms_memory_utilization_high_ok_actions = [aws_sns_topic.sns_topic.arn]
367 | ecs_alarms_memory_utilization_low_alarm_actions = [aws_sns_topic.sns_topic.arn]
368 | ecs_alarms_memory_utilization_low_ok_actions = [aws_sns_topic.sns_topic.arn]
369 | }
370 | ```
371 |
372 | # Example usage
373 | #examples: |-
374 | # Example goes here...
375 |
376 | # How to get started quickly
377 | #quickstart: |-
378 | # Here's how to get started...
379 |
380 | # Other files to include in this README from the project folder
381 | include:
382 | - "docs/targets.md"
383 | - "docs/terraform.md"
384 |
385 | references:
386 | - name: "atlantis"
387 | description: "Official home of the Atlantis project"
388 | url: "https://runatlantis.io"
389 |
390 | # Contributors to this project
391 | contributors:
392 | - name: "Josh Myers"
393 | github: "joshmyers"
394 | - name: "Erik Osterman"
395 | github: "osterman"
396 | - name: "Andriy Knysh"
397 | github: "aknysh"
398 | - name: "Igor Rodionov"
399 | github: "goruha"
400 |
--------------------------------------------------------------------------------
/context.tf:
--------------------------------------------------------------------------------
1 | #
2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label
3 | # All other instances of this file should be a copy of that one
4 | #
5 | #
6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf
7 | # and then place it in your Terraform module to automatically get
8 | # Cloud Posse's standard configuration inputs suitable for passing
9 | # to Cloud Posse modules.
10 | #
11 | # curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf
12 | #
13 | # Modules should access the whole context as `module.this.context`
14 | # to get the input variables with nulls for defaults,
15 | # for example `context = module.this.context`,
16 | # and access individual variables as `module.this.`,
17 | # with final values filled in.
18 | #
19 | # For example, when using defaults, `module.this.context.delimiter`
20 | # will be null, and `module.this.delimiter` will be `-` (hyphen).
21 | #
22 |
23 | module "this" {
24 | source = "cloudposse/label/null"
25 | version = "0.25.0" # requires Terraform >= 0.13.0
26 |
27 | enabled = var.enabled
28 | namespace = var.namespace
29 | tenant = var.tenant
30 | environment = var.environment
31 | stage = var.stage
32 | name = var.name
33 | delimiter = var.delimiter
34 | attributes = var.attributes
35 | tags = var.tags
36 | additional_tag_map = var.additional_tag_map
37 | label_order = var.label_order
38 | regex_replace_chars = var.regex_replace_chars
39 | id_length_limit = var.id_length_limit
40 | label_key_case = var.label_key_case
41 | label_value_case = var.label_value_case
42 | descriptor_formats = var.descriptor_formats
43 | labels_as_tags = var.labels_as_tags
44 |
45 | context = var.context
46 | }
47 |
48 | # Copy contents of cloudposse/terraform-null-label/variables.tf here
49 |
50 | variable "context" {
51 | type = any
52 | default = {
53 | enabled = true
54 | namespace = null
55 | tenant = null
56 | environment = null
57 | stage = null
58 | name = null
59 | delimiter = null
60 | attributes = []
61 | tags = {}
62 | additional_tag_map = {}
63 | regex_replace_chars = null
64 | label_order = []
65 | id_length_limit = null
66 | label_key_case = null
67 | label_value_case = null
68 | descriptor_formats = {}
69 | # Note: we have to use [] instead of null for unset lists due to
70 | # https://github.com/hashicorp/terraform/issues/28137
71 | # which was not fixed until Terraform 1.0.0,
72 | # but we want the default to be all the labels in `label_order`
73 | # and we want users to be able to prevent all tag generation
74 | # by setting `labels_as_tags` to `[]`, so we need
75 | # a different sentinel to indicate "default"
76 | labels_as_tags = ["unset"]
77 | }
78 | description = <<-EOT
79 | Single object for setting entire context at once.
80 | See description of individual variables for details.
81 | Leave string and numeric variables as `null` to use default value.
82 | Individual variable settings (non-null) override settings in context object,
83 | except for attributes, tags, and additional_tag_map, which are merged.
84 | EOT
85 |
86 | validation {
87 | condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"])
88 | error_message = "Allowed values: `lower`, `title`, `upper`."
89 | }
90 |
91 | validation {
92 | condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"])
93 | error_message = "Allowed values: `lower`, `title`, `upper`, `none`."
94 | }
95 | }
96 |
97 | variable "enabled" {
98 | type = bool
99 | default = null
100 | description = "Set to false to prevent the module from creating any resources"
101 | }
102 |
103 | variable "namespace" {
104 | type = string
105 | default = null
106 | description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique"
107 | }
108 |
109 | variable "tenant" {
110 | type = string
111 | default = null
112 | description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for"
113 | }
114 |
115 | variable "environment" {
116 | type = string
117 | default = null
118 | description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'"
119 | }
120 |
121 | variable "stage" {
122 | type = string
123 | default = null
124 | description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'"
125 | }
126 |
127 | variable "name" {
128 | type = string
129 | default = null
130 | description = <<-EOT
131 | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
132 | This is the only ID element not also included as a `tag`.
133 | The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input.
134 | EOT
135 | }
136 |
137 | variable "delimiter" {
138 | type = string
139 | default = null
140 | description = <<-EOT
141 | Delimiter to be used between ID elements.
142 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
143 | EOT
144 | }
145 |
146 | variable "attributes" {
147 | type = list(string)
148 | default = []
149 | description = <<-EOT
150 | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
151 | in the order they appear in the list. New attributes are appended to the
152 | end of the list. The elements of the list are joined by the `delimiter`
153 | and treated as a single ID element.
154 | EOT
155 | }
156 |
157 | variable "labels_as_tags" {
158 | type = set(string)
159 | default = ["default"]
160 | description = <<-EOT
161 | Set of labels (ID elements) to include as tags in the `tags` output.
162 | Default is to include all labels.
163 | Tags with empty values will not be included in the `tags` output.
164 | Set to `[]` to suppress all generated tags.
165 | **Notes:**
166 | The value of the `name` tag, if included, will be the `id`, not the `name`.
167 | Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
168 | changed in later chained modules. Attempts to change it will be silently ignored.
169 | EOT
170 | }
171 |
172 | variable "tags" {
173 | type = map(string)
174 | default = {}
175 | description = <<-EOT
176 | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
177 | Neither the tag keys nor the tag values will be modified by this module.
178 | EOT
179 | }
180 |
181 | variable "additional_tag_map" {
182 | type = map(string)
183 | default = {}
184 | description = <<-EOT
185 | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
186 | This is for some rare cases where resources want additional configuration of tags
187 | and therefore take a list of maps with tag key, value, and additional configuration.
188 | EOT
189 | }
190 |
191 | variable "label_order" {
192 | type = list(string)
193 | default = null
194 | description = <<-EOT
195 | The order in which the labels (ID elements) appear in the `id`.
196 | Defaults to ["namespace", "environment", "stage", "name", "attributes"].
197 | You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present.
198 | EOT
199 | }
200 |
201 | variable "regex_replace_chars" {
202 | type = string
203 | default = null
204 | description = <<-EOT
205 | Terraform regular expression (regex) string.
206 | Characters matching the regex will be removed from the ID elements.
207 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits.
208 | EOT
209 | }
210 |
211 | variable "id_length_limit" {
212 | type = number
213 | default = null
214 | description = <<-EOT
215 | Limit `id` to this many characters (minimum 6).
216 | Set to `0` for unlimited length.
217 | Set to `null` for keep the existing setting, which defaults to `0`.
218 | Does not affect `id_full`.
219 | EOT
220 | validation {
221 | condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0
222 | error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length."
223 | }
224 | }
225 |
226 | variable "label_key_case" {
227 | type = string
228 | default = null
229 | description = <<-EOT
230 | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
231 | Does not affect keys of tags passed in via the `tags` input.
232 | Possible values: `lower`, `title`, `upper`.
233 | Default value: `title`.
234 | EOT
235 |
236 | validation {
237 | condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case)
238 | error_message = "Allowed values: `lower`, `title`, `upper`."
239 | }
240 | }
241 |
242 | variable "label_value_case" {
243 | type = string
244 | default = null
245 | description = <<-EOT
246 | Controls the letter case of ID elements (labels) as included in `id`,
247 | set as tag values, and output by this module individually.
248 | Does not affect values of tags passed in via the `tags` input.
249 | Possible values: `lower`, `title`, `upper` and `none` (no transformation).
250 | Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
251 | Default value: `lower`.
252 | EOT
253 |
254 | validation {
255 | condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case)
256 | error_message = "Allowed values: `lower`, `title`, `upper`, `none`."
257 | }
258 | }
259 |
260 | variable "descriptor_formats" {
261 | type = any
262 | default = {}
263 | description = <<-EOT
264 | Describe additional descriptors to be output in the `descriptors` output map.
265 | Map of maps. Keys are names of descriptors. Values are maps of the form
266 | `{
267 | format = string
268 | labels = list(string)
269 | }`
270 | (Type is `any` so the map values can later be enhanced to provide additional options.)
271 | `format` is a Terraform format string to be passed to the `format()` function.
272 | `labels` is a list of labels, in order, to pass to `format()` function.
273 | Label values will be normalized before being passed to `format()` so they will be
274 | identical to how they appear in `id`.
275 | Default is `{}` (`descriptors` output will be empty).
276 | EOT
277 | }
278 |
279 | #### End of copy of cloudposse/terraform-null-label/variables.tf
280 |
--------------------------------------------------------------------------------
/docs/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudposse-archives/terraform-aws-ecs-atlantis/c4e95fed76876ab4e7eac391cd5cfed980bfc3de/docs/example.png
--------------------------------------------------------------------------------
/docs/github-repo-scopes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudposse-archives/terraform-aws-ecs-atlantis/c4e95fed76876ab4e7eac391cd5cfed980bfc3de/docs/github-repo-scopes.png
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudposse-archives/terraform-aws-ecs-atlantis/c4e95fed76876ab4e7eac391cd5cfed980bfc3de/docs/logo.png
--------------------------------------------------------------------------------
/docs/targets.md:
--------------------------------------------------------------------------------
1 |
2 | ## Makefile Targets
3 | ```text
4 | Available targets:
5 |
6 | help Help screen
7 | help/all Display help for all targets
8 | help/short This help short screen
9 | lint Lint terraform code
10 |
11 | ```
12 |
13 |
--------------------------------------------------------------------------------
/examples/complete/context.tf:
--------------------------------------------------------------------------------
1 | #
2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label
3 | # All other instances of this file should be a copy of that one
4 | #
5 | #
6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf
7 | # and then place it in your Terraform module to automatically get
8 | # Cloud Posse's standard configuration inputs suitable for passing
9 | # to Cloud Posse modules.
10 | #
11 | # curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf
12 | #
13 | # Modules should access the whole context as `module.this.context`
14 | # to get the input variables with nulls for defaults,
15 | # for example `context = module.this.context`,
16 | # and access individual variables as `module.this.`,
17 | # with final values filled in.
18 | #
19 | # For example, when using defaults, `module.this.context.delimiter`
20 | # will be null, and `module.this.delimiter` will be `-` (hyphen).
21 | #
22 |
23 | module "this" {
24 | source = "cloudposse/label/null"
25 | version = "0.25.0" # requires Terraform >= 0.13.0
26 |
27 | enabled = var.enabled
28 | namespace = var.namespace
29 | tenant = var.tenant
30 | environment = var.environment
31 | stage = var.stage
32 | name = var.name
33 | delimiter = var.delimiter
34 | attributes = var.attributes
35 | tags = var.tags
36 | additional_tag_map = var.additional_tag_map
37 | label_order = var.label_order
38 | regex_replace_chars = var.regex_replace_chars
39 | id_length_limit = var.id_length_limit
40 | label_key_case = var.label_key_case
41 | label_value_case = var.label_value_case
42 | descriptor_formats = var.descriptor_formats
43 | labels_as_tags = var.labels_as_tags
44 |
45 | context = var.context
46 | }
47 |
48 | # Copy contents of cloudposse/terraform-null-label/variables.tf here
49 |
50 | variable "context" {
51 | type = any
52 | default = {
53 | enabled = true
54 | namespace = null
55 | tenant = null
56 | environment = null
57 | stage = null
58 | name = null
59 | delimiter = null
60 | attributes = []
61 | tags = {}
62 | additional_tag_map = {}
63 | regex_replace_chars = null
64 | label_order = []
65 | id_length_limit = null
66 | label_key_case = null
67 | label_value_case = null
68 | descriptor_formats = {}
69 | # Note: we have to use [] instead of null for unset lists due to
70 | # https://github.com/hashicorp/terraform/issues/28137
71 | # which was not fixed until Terraform 1.0.0,
72 | # but we want the default to be all the labels in `label_order`
73 | # and we want users to be able to prevent all tag generation
74 | # by setting `labels_as_tags` to `[]`, so we need
75 | # a different sentinel to indicate "default"
76 | labels_as_tags = ["unset"]
77 | }
78 | description = <<-EOT
79 | Single object for setting entire context at once.
80 | See description of individual variables for details.
81 | Leave string and numeric variables as `null` to use default value.
82 | Individual variable settings (non-null) override settings in context object,
83 | except for attributes, tags, and additional_tag_map, which are merged.
84 | EOT
85 |
86 | validation {
87 | condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"])
88 | error_message = "Allowed values: `lower`, `title`, `upper`."
89 | }
90 |
91 | validation {
92 | condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"])
93 | error_message = "Allowed values: `lower`, `title`, `upper`, `none`."
94 | }
95 | }
96 |
97 | variable "enabled" {
98 | type = bool
99 | default = null
100 | description = "Set to false to prevent the module from creating any resources"
101 | }
102 |
103 | variable "namespace" {
104 | type = string
105 | default = null
106 | description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique"
107 | }
108 |
109 | variable "tenant" {
110 | type = string
111 | default = null
112 | description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for"
113 | }
114 |
115 | variable "environment" {
116 | type = string
117 | default = null
118 | description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'"
119 | }
120 |
121 | variable "stage" {
122 | type = string
123 | default = null
124 | description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'"
125 | }
126 |
127 | variable "name" {
128 | type = string
129 | default = null
130 | description = <<-EOT
131 | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
132 | This is the only ID element not also included as a `tag`.
133 | The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input.
134 | EOT
135 | }
136 |
137 | variable "delimiter" {
138 | type = string
139 | default = null
140 | description = <<-EOT
141 | Delimiter to be used between ID elements.
142 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
143 | EOT
144 | }
145 |
146 | variable "attributes" {
147 | type = list(string)
148 | default = []
149 | description = <<-EOT
150 | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
151 | in the order they appear in the list. New attributes are appended to the
152 | end of the list. The elements of the list are joined by the `delimiter`
153 | and treated as a single ID element.
154 | EOT
155 | }
156 |
157 | variable "labels_as_tags" {
158 | type = set(string)
159 | default = ["default"]
160 | description = <<-EOT
161 | Set of labels (ID elements) to include as tags in the `tags` output.
162 | Default is to include all labels.
163 | Tags with empty values will not be included in the `tags` output.
164 | Set to `[]` to suppress all generated tags.
165 | **Notes:**
166 | The value of the `name` tag, if included, will be the `id`, not the `name`.
167 | Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
168 | changed in later chained modules. Attempts to change it will be silently ignored.
169 | EOT
170 | }
171 |
172 | variable "tags" {
173 | type = map(string)
174 | default = {}
175 | description = <<-EOT
176 | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
177 | Neither the tag keys nor the tag values will be modified by this module.
178 | EOT
179 | }
180 |
181 | variable "additional_tag_map" {
182 | type = map(string)
183 | default = {}
184 | description = <<-EOT
185 | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
186 | This is for some rare cases where resources want additional configuration of tags
187 | and therefore take a list of maps with tag key, value, and additional configuration.
188 | EOT
189 | }
190 |
191 | variable "label_order" {
192 | type = list(string)
193 | default = null
194 | description = <<-EOT
195 | The order in which the labels (ID elements) appear in the `id`.
196 | Defaults to ["namespace", "environment", "stage", "name", "attributes"].
197 | You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present.
198 | EOT
199 | }
200 |
201 | variable "regex_replace_chars" {
202 | type = string
203 | default = null
204 | description = <<-EOT
205 | Terraform regular expression (regex) string.
206 | Characters matching the regex will be removed from the ID elements.
207 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits.
208 | EOT
209 | }
210 |
211 | variable "id_length_limit" {
212 | type = number
213 | default = null
214 | description = <<-EOT
215 | Limit `id` to this many characters (minimum 6).
216 | Set to `0` for unlimited length.
217 | Set to `null` for keep the existing setting, which defaults to `0`.
218 | Does not affect `id_full`.
219 | EOT
220 | validation {
221 | condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0
222 | error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length."
223 | }
224 | }
225 |
226 | variable "label_key_case" {
227 | type = string
228 | default = null
229 | description = <<-EOT
230 | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
231 | Does not affect keys of tags passed in via the `tags` input.
232 | Possible values: `lower`, `title`, `upper`.
233 | Default value: `title`.
234 | EOT
235 |
236 | validation {
237 | condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case)
238 | error_message = "Allowed values: `lower`, `title`, `upper`."
239 | }
240 | }
241 |
242 | variable "label_value_case" {
243 | type = string
244 | default = null
245 | description = <<-EOT
246 | Controls the letter case of ID elements (labels) as included in `id`,
247 | set as tag values, and output by this module individually.
248 | Does not affect values of tags passed in via the `tags` input.
249 | Possible values: `lower`, `title`, `upper` and `none` (no transformation).
250 | Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
251 | Default value: `lower`.
252 | EOT
253 |
254 | validation {
255 | condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case)
256 | error_message = "Allowed values: `lower`, `title`, `upper`, `none`."
257 | }
258 | }
259 |
260 | variable "descriptor_formats" {
261 | type = any
262 | default = {}
263 | description = <<-EOT
264 | Describe additional descriptors to be output in the `descriptors` output map.
265 | Map of maps. Keys are names of descriptors. Values are maps of the form
266 | `{
267 | format = string
268 | labels = list(string)
269 | }`
270 | (Type is `any` so the map values can later be enhanced to provide additional options.)
271 | `format` is a Terraform format string to be passed to the `format()` function.
272 | `labels` is a list of labels, in order, to pass to `format()` function.
273 | Label values will be normalized before being passed to `format()` so they will be
274 | identical to how they appear in `id`.
275 | Default is `{}` (`descriptors` output will be empty).
276 | EOT
277 | }
278 |
279 | #### End of copy of cloudposse/terraform-null-label/variables.tf
280 |
--------------------------------------------------------------------------------
/examples/complete/fixtures.us-west-2.tfvars:
--------------------------------------------------------------------------------
1 | enabled = true
2 |
3 | region = "us-west-2"
4 |
5 | availability_zones = ["us-west-2a", "us-west-2b"]
6 |
7 | namespace = "eg"
8 |
9 | stage = "test"
10 |
11 | name = "ecs-atlantis"
12 |
13 | vpc_cidr_block = "172.16.0.0/16"
14 |
15 | container_cpu = 256
16 |
17 | container_memory = 512
18 |
19 | desired_count = 1
20 |
21 | launch_type = "FARGATE"
22 |
23 | authentication_type = ""
24 |
25 | alb_ingress_listener_unauthenticated_priority = 1000
26 |
27 | alb_ingress_unauthenticated_paths = ["/"]
28 |
29 | autoscaling_enabled = true
30 |
31 | autoscaling_min_capacity = 1
32 |
33 | autoscaling_max_capacity = 2
34 |
35 | webhook_enabled = false
36 |
37 | github_oauth_token = "test"
38 |
39 | atlantis_gh_user = "test"
40 |
41 | atlantis_gh_team_whitelist = "dev:plan,ops:*"
42 |
43 | atlantis_repo_whitelist = ["cloudposse/terraform-aws-ecs-atlantis"]
44 |
45 | codepipeline_enabled = true
46 |
47 | codepipeline_s3_bucket_force_destroy = true
48 |
49 | build_timeout = 20
50 |
51 | repo_name = "atlantis"
52 |
53 | repo_owner = "cloudposse"
54 |
55 | branch = "master"
56 |
57 | alb_target_group_alarms_enabled = true
58 |
59 | ecs_alarms_enabled = true
60 |
61 | parent_zone_id = "Z3SO0TKDDQ0RGG"
62 |
63 | short_name = "ecs-atlantis-test"
64 |
65 | default_backend_image = "cloudposse/default-backend:0.1.2"
66 |
67 | chamber_service = "ecs-atlantis-test"
68 |
69 | kms_key_id = "alias/cpco-testing-chamber"
70 |
--------------------------------------------------------------------------------
/examples/complete/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
4 |
5 | module "vpc" {
6 | source = "cloudposse/vpc/aws"
7 | version = "0.18.1"
8 | cidr_block = var.vpc_cidr_block
9 |
10 | context = module.this.context
11 | }
12 |
13 | module "subnets" {
14 | source = "cloudposse/dynamic-subnets/aws"
15 | version = "0.33.0"
16 | availability_zones = var.availability_zones
17 | vpc_id = module.vpc.vpc_id
18 | igw_id = module.vpc.igw_id
19 | cidr_block = module.vpc.vpc_cidr_block
20 | nat_gateway_enabled = true
21 | nat_instance_enabled = false
22 |
23 | context = module.this.context
24 | }
25 |
26 | module "alb" {
27 | source = "cloudposse/alb/aws"
28 | version = "0.27.0"
29 | vpc_id = module.vpc.vpc_id
30 | security_group_ids = [module.vpc.vpc_default_security_group_id]
31 | subnet_ids = module.subnets.public_subnet_ids
32 | internal = false
33 | http_enabled = true
34 | alb_access_logs_s3_bucket_force_destroy = true
35 | access_logs_enabled = true
36 | cross_zone_load_balancing_enabled = true
37 | http2_enabled = true
38 | deletion_protection_enabled = false
39 |
40 | context = module.this.context
41 | }
42 |
43 | resource "aws_ecs_cluster" "default" {
44 | name = module.this.id
45 | tags = module.this.tags
46 | setting {
47 | name = "containerInsights"
48 | value = "enabled"
49 | }
50 | }
51 |
52 | resource "aws_sns_topic" "sns_topic" {
53 | #bridgecrew:skip=BC_AWS_GENERAL_15:Skipping `Encrypt SNS Topic Data` in example/test modules
54 | name = module.this.id
55 | display_name = "Test terraform-aws-ecs-atlantis"
56 | tags = module.this.tags
57 | }
58 |
59 | module "kms_key" {
60 | source = "cloudposse/kms-key/aws"
61 | version = "0.9.0"
62 | enabled = var.kms_key_id == "" ? false : true
63 | description = "Test terraform-aws-ecs-atlantis KMS key"
64 | deletion_window_in_days = 7
65 | enable_key_rotation = false
66 |
67 | context = module.this.context
68 | }
69 |
70 | module "atlantis_dns_name" {
71 | source = "cloudposse/label/null"
72 | version = "0.22.1"
73 |
74 | name = var.short_name
75 | attributes = module.this.attributes
76 |
77 | # Omission of context = module.this.context is intentional
78 | }
79 |
80 | module "atlantis" {
81 | source = "../.."
82 |
83 | region = var.region
84 | vpc_id = module.vpc.vpc_id
85 | policy_arn = var.policy_arn
86 | ssh_private_key_name = var.ssh_private_key_name
87 | ssh_public_key_name = var.ssh_public_key_name
88 | kms_key_id = var.kms_key_id == "" ? module.kms_key.key_id : var.kms_key_id
89 | chamber_service = var.chamber_service
90 |
91 | atlantis_gh_user = var.atlantis_gh_user
92 | atlantis_gh_team_whitelist = var.atlantis_gh_team_whitelist
93 | atlantis_gh_webhook_secret = var.atlantis_gh_webhook_secret
94 | atlantis_log_level = var.atlantis_log_level
95 | atlantis_repo_config = var.atlantis_repo_config
96 | atlantis_repo_whitelist = var.atlantis_repo_whitelist
97 | atlantis_port = var.atlantis_port
98 | atlantis_webhook_format = var.atlantis_webhook_format
99 | atlantis_url_format = var.atlantis_url_format
100 |
101 | default_backend_image = var.default_backend_image
102 | healthcheck_path = var.healthcheck_path
103 | short_name = module.atlantis_dns_name.id
104 | hostname = var.hostname
105 | parent_zone_id = var.parent_zone_id
106 |
107 | // Container
108 | container_cpu = var.container_cpu
109 | container_memory = var.container_memory
110 |
111 | // Authentication
112 | authentication_type = var.authentication_type
113 | alb_ingress_listener_unauthenticated_priority = var.alb_ingress_listener_unauthenticated_priority
114 | alb_ingress_listener_authenticated_priority = var.alb_ingress_listener_authenticated_priority
115 | alb_ingress_unauthenticated_hosts = var.alb_ingress_unauthenticated_hosts
116 | alb_ingress_authenticated_hosts = var.alb_ingress_authenticated_hosts
117 | alb_ingress_unauthenticated_paths = var.alb_ingress_unauthenticated_paths
118 | alb_ingress_authenticated_paths = var.alb_ingress_authenticated_paths
119 | authentication_cognito_user_pool_arn = var.authentication_cognito_user_pool_arn
120 | authentication_cognito_user_pool_client_id = var.authentication_cognito_user_pool_client_id
121 | authentication_cognito_user_pool_domain = var.authentication_cognito_user_pool_domain
122 | authentication_oidc_client_id = var.authentication_oidc_client_id
123 | authentication_oidc_client_secret = var.authentication_oidc_client_secret
124 | authentication_oidc_issuer = var.authentication_oidc_issuer
125 | authentication_oidc_authorization_endpoint = var.authentication_oidc_authorization_endpoint
126 | authentication_oidc_token_endpoint = var.authentication_oidc_token_endpoint
127 | authentication_oidc_user_info_endpoint = var.authentication_oidc_user_info_endpoint
128 |
129 | // ECS
130 | private_subnet_ids = module.subnets.private_subnet_ids
131 | ecs_cluster_arn = aws_ecs_cluster.default.arn
132 | ecs_cluster_name = aws_ecs_cluster.default.name
133 | security_group_ids = var.security_group_ids
134 | desired_count = var.desired_count
135 | launch_type = var.launch_type
136 |
137 | // ALB
138 | alb_zone_id = module.alb.alb_zone_id
139 | alb_arn_suffix = module.alb.alb_arn_suffix
140 | alb_dns_name = module.alb.alb_dns_name
141 | alb_security_group = module.alb.security_group_id
142 | alb_ingress_unauthenticated_listener_arns = [module.alb.http_listener_arn]
143 | alb_ingress_unauthenticated_listener_arns_count = 1
144 |
145 | // CodePipeline
146 | codepipeline_enabled = var.codepipeline_enabled
147 | github_oauth_token = var.github_oauth_token
148 | github_webhooks_token = var.github_webhooks_token
149 | repo_owner = var.repo_owner
150 | repo_name = var.repo_name
151 | branch = var.branch
152 | build_timeout = var.build_timeout
153 | webhook_enabled = var.webhook_enabled
154 | webhook_secret_length = var.webhook_secret_length
155 | webhook_events = var.webhook_events
156 | codepipeline_s3_bucket_force_destroy = var.codepipeline_s3_bucket_force_destroy
157 |
158 | // Autoscaling
159 | autoscaling_enabled = var.autoscaling_enabled
160 | autoscaling_min_capacity = var.autoscaling_min_capacity
161 | autoscaling_max_capacity = var.autoscaling_max_capacity
162 |
163 | // Alarms
164 | alb_target_group_alarms_enabled = var.alb_target_group_alarms_enabled
165 | ecs_alarms_enabled = var.ecs_alarms_enabled
166 | alb_target_group_alarms_alarm_actions = [aws_sns_topic.sns_topic.arn]
167 | alb_target_group_alarms_ok_actions = [aws_sns_topic.sns_topic.arn]
168 | alb_target_group_alarms_insufficient_data_actions = [aws_sns_topic.sns_topic.arn]
169 | ecs_alarms_cpu_utilization_high_alarm_actions = [aws_sns_topic.sns_topic.arn]
170 | ecs_alarms_cpu_utilization_high_ok_actions = [aws_sns_topic.sns_topic.arn]
171 | ecs_alarms_cpu_utilization_low_alarm_actions = [aws_sns_topic.sns_topic.arn]
172 | ecs_alarms_cpu_utilization_low_ok_actions = [aws_sns_topic.sns_topic.arn]
173 | ecs_alarms_memory_utilization_high_alarm_actions = [aws_sns_topic.sns_topic.arn]
174 | ecs_alarms_memory_utilization_high_ok_actions = [aws_sns_topic.sns_topic.arn]
175 | ecs_alarms_memory_utilization_low_alarm_actions = [aws_sns_topic.sns_topic.arn]
176 | ecs_alarms_memory_utilization_low_ok_actions = [aws_sns_topic.sns_topic.arn]
177 |
178 | context = module.this.context
179 | }
180 |
--------------------------------------------------------------------------------
/examples/complete/outputs.tf:
--------------------------------------------------------------------------------
1 | output "public_subnet_cidrs" {
2 | value = module.subnets.public_subnet_cidrs
3 | description = "Public subnet CIDRs"
4 | }
5 |
6 | output "private_subnet_cidrs" {
7 | value = module.subnets.private_subnet_cidrs
8 | description = "Private subnet CIDRs"
9 | }
10 |
11 | output "vpc_cidr" {
12 | value = module.vpc.vpc_cidr_block
13 | description = "VPC ID"
14 | }
15 |
16 | output "atlantis_ssh_public_key" {
17 | description = "Atlantis SSH Public Key"
18 | value = module.atlantis.atlantis_ssh_public_key
19 | }
20 |
21 | output "atlantis_url" {
22 | description = "The URL endpoint for the atlantis server"
23 | value = module.atlantis.atlantis_url
24 | }
25 |
26 | output "atlantis_webhook_url" {
27 | description = "atlantis webhook URL"
28 | value = module.atlantis.atlantis_webhook_url
29 | }
30 |
31 | output "alb_name" {
32 | description = "The ARN suffix of the ALB"
33 | value = module.alb.alb_name
34 | }
35 |
36 | output "alb_arn" {
37 | description = "The ARN of the ALB"
38 | value = module.alb.alb_arn
39 | }
40 |
41 | output "alb_arn_suffix" {
42 | description = "The ARN suffix of the ALB"
43 | value = module.alb.alb_arn_suffix
44 | }
45 |
46 | output "alb_dns_name" {
47 | description = "DNS name of ALB"
48 | value = module.alb.alb_dns_name
49 | }
50 |
51 | output "alb_zone_id" {
52 | description = "The ID of the zone which ALB is provisioned"
53 | value = module.alb.alb_zone_id
54 | }
55 |
56 | output "alb_security_group_id" {
57 | description = "The security group ID of the ALB"
58 | value = module.alb.security_group_id
59 | }
60 |
61 | output "alb_default_target_group_arn" {
62 | description = "The default target group ARN"
63 | value = module.alb.default_target_group_arn
64 | }
65 |
66 | output "alb_http_listener_arn" {
67 | description = "The ARN of the HTTP listener"
68 | value = module.alb.http_listener_arn
69 | }
70 |
71 | output "alb_listener_arns" {
72 | description = "A list of all the listener ARNs"
73 | value = module.alb.listener_arns
74 | }
75 |
76 | output "alb_access_logs_bucket_id" {
77 | description = "The S3 bucket ID for access logs"
78 | value = module.alb.access_logs_bucket_id
79 | }
80 |
81 | output "ecr_registry_id" {
82 | value = module.atlantis.ecr_registry_id
83 | description = "Registry ID"
84 | }
85 |
86 | output "ecr_repository_url" {
87 | value = module.atlantis.ecr_repository_url
88 | description = "Repository URL"
89 | }
90 |
91 | output "ecr_repository_name" {
92 | value = module.atlantis.ecr_repository_name
93 | description = "Repository name"
94 | }
95 |
96 | output "alb_ingress_target_group_name" {
97 | description = "ALB Target Group name"
98 | value = module.atlantis.alb_ingress_target_group_name
99 | }
100 |
101 | output "alb_ingress_target_group_arn" {
102 | description = "ALB Target Group ARN"
103 | value = module.atlantis.alb_ingress_target_group_arn
104 | }
105 |
106 | output "alb_ingress_target_group_arn_suffix" {
107 | description = "ALB Target Group ARN suffix"
108 | value = module.atlantis.alb_ingress_target_group_arn_suffix
109 | }
110 |
111 | output "container_definition_json" {
112 | description = "JSON encoded list of container definitions for use with other terraform resources such as aws_ecs_task_definition"
113 | value = module.atlantis.container_definition_json
114 | }
115 |
116 | output "container_definition_json_map" {
117 | description = "JSON encoded container definitions for use with other terraform resources such as aws_ecs_task_definition"
118 | value = module.atlantis.container_definition_json_map
119 | }
120 |
121 | output "ecs_exec_role_policy_id" {
122 | description = "The ECS service role policy ID, in the form of `role_name:role_policy_name`"
123 | value = module.atlantis.ecs_exec_role_policy_id
124 | }
125 |
126 | output "ecs_exec_role_policy_name" {
127 | description = "ECS service role name"
128 | value = module.atlantis.ecs_exec_role_policy_name
129 | }
130 |
131 | output "ecs_service_name" {
132 | description = "ECS Service name"
133 | value = module.atlantis.ecs_service_name
134 | }
135 |
136 | output "ecs_service_role_arn" {
137 | description = "ECS Service role ARN"
138 | value = module.atlantis.ecs_service_role_arn
139 | }
140 |
141 | output "ecs_task_exec_role_name" {
142 | description = "ECS Task role name"
143 | value = module.atlantis.ecs_task_exec_role_name
144 | }
145 |
146 | output "ecs_task_exec_role_arn" {
147 | description = "ECS Task exec role ARN"
148 | value = module.atlantis.ecs_task_exec_role_arn
149 | }
150 |
151 | output "ecs_task_role_name" {
152 | description = "ECS Task role name"
153 | value = module.atlantis.ecs_task_role_name
154 | }
155 |
156 | output "ecs_task_role_arn" {
157 | description = "ECS Task role ARN"
158 | value = module.atlantis.ecs_task_role_arn
159 | }
160 |
161 | output "ecs_task_role_id" {
162 | description = "ECS Task role id"
163 | value = module.atlantis.ecs_task_role_id
164 | }
165 |
166 | output "ecs_service_security_group_id" {
167 | description = "Security Group ID of the ECS task"
168 | value = module.atlantis.ecs_service_security_group_id
169 | }
170 |
171 | output "ecs_task_definition_family" {
172 | description = "ECS task definition family"
173 | value = module.atlantis.ecs_task_definition_family
174 | }
175 |
176 | output "ecs_task_definition_revision" {
177 | description = "ECS task definition revision"
178 | value = module.atlantis.ecs_task_definition_revision
179 | }
180 |
181 | output "codebuild_project_name" {
182 | description = "CodeBuild project name"
183 | value = module.atlantis.codebuild_project_name
184 | }
185 |
186 | output "codebuild_project_id" {
187 | description = "CodeBuild project ID"
188 | value = module.atlantis.codebuild_project_id
189 | }
190 |
191 | output "codebuild_role_id" {
192 | description = "CodeBuild IAM Role ID"
193 | value = module.atlantis.codebuild_role_id
194 | }
195 |
196 | output "codebuild_role_arn" {
197 | description = "CodeBuild IAM Role ARN"
198 | value = module.atlantis.codebuild_role_arn
199 | }
200 |
201 | output "codebuild_cache_bucket_name" {
202 | description = "CodeBuild cache S3 bucket name"
203 | value = module.atlantis.codebuild_cache_bucket_name
204 | }
205 |
206 | output "codebuild_cache_bucket_arn" {
207 | description = "CodeBuild cache S3 bucket ARN"
208 | value = module.atlantis.codebuild_cache_bucket_arn
209 | }
210 |
211 | output "codebuild_badge_url" {
212 | description = "The URL of the build badge when badge_enabled is enabled"
213 | value = module.atlantis.codebuild_badge_url
214 | }
215 |
216 | output "codepipeline_id" {
217 | description = "CodePipeline ID"
218 | value = module.atlantis.codepipeline_id
219 | }
220 |
221 | output "codepipeline_arn" {
222 | description = "CodePipeline ARN"
223 | value = module.atlantis.codepipeline_arn
224 | }
225 |
226 | output "codepipeline_webhook_id" {
227 | description = "The CodePipeline webhook's ID"
228 | value = module.atlantis.codepipeline_webhook_id
229 | }
230 |
231 | output "codepipeline_webhook_url" {
232 | description = "The CodePipeline webhook's URL. POST events to this endpoint to trigger the target"
233 | value = module.atlantis.codepipeline_webhook_url
234 | sensitive = true
235 | }
236 |
237 | output "ecs_cloudwatch_autoscaling_scale_up_policy_arn" {
238 | description = "ARN of the scale up policy"
239 | value = module.atlantis.ecs_cloudwatch_autoscaling_scale_up_policy_arn
240 | }
241 |
242 | output "ecs_cloudwatch_autoscaling_scale_down_policy_arn" {
243 | description = "ARN of the scale down policy"
244 | value = module.atlantis.ecs_cloudwatch_autoscaling_scale_down_policy_arn
245 | }
246 |
247 | output "ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_id" {
248 | value = module.atlantis.ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_id
249 | description = "ECS CPU utilization high CloudWatch metric alarm ID"
250 | }
251 |
252 | output "ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_arn" {
253 | value = module.atlantis.ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_arn
254 | description = "ECS CPU utilization high CloudWatch metric alarm ARN"
255 | }
256 |
257 | output "ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_id" {
258 | value = module.atlantis.ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_id
259 | description = "ECS CPU utilization low CloudWatch metric alarm ID"
260 | }
261 |
262 | output "ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_arn" {
263 | value = module.atlantis.ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_arn
264 | description = "ECS CPU utilization low CloudWatch metric alarm ARN"
265 | }
266 |
267 | output "ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_id" {
268 | value = module.atlantis.ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_id
269 | description = "ECS Memory utilization high CloudWatch metric alarm ID"
270 | }
271 |
272 | output "ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_arn" {
273 | value = module.atlantis.ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_arn
274 | description = "ECS Memory utilization high CloudWatch metric alarm ARN"
275 | }
276 |
277 | output "ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_id" {
278 | value = module.atlantis.ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_id
279 | description = "ECS Memory utilization low CloudWatch metric alarm ID"
280 | }
281 |
282 | output "ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_arn" {
283 | value = module.atlantis.ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_arn
284 | description = "ECS Memory utilization low CloudWatch metric alarm ARN"
285 | }
286 |
287 | output "httpcode_target_3xx_count_cloudwatch_metric_alarm_id" {
288 | value = module.atlantis.httpcode_target_3xx_count_cloudwatch_metric_alarm_id
289 | description = "ALB Target Group 3xx count CloudWatch metric alarm ID"
290 | }
291 |
292 | output "httpcode_target_3xx_count_cloudwatch_metric_alarm_arn" {
293 | value = module.atlantis.httpcode_target_3xx_count_cloudwatch_metric_alarm_arn
294 | description = "ALB Target Group 3xx count CloudWatch metric alarm ARN"
295 | }
296 |
297 | output "httpcode_target_4xx_count_cloudwatch_metric_alarm_id" {
298 | value = module.atlantis.httpcode_target_4xx_count_cloudwatch_metric_alarm_id
299 | description = "ALB Target Group 4xx count CloudWatch metric alarm ID"
300 | }
301 |
302 | output "httpcode_target_4xx_count_cloudwatch_metric_alarm_arn" {
303 | value = module.atlantis.httpcode_target_4xx_count_cloudwatch_metric_alarm_arn
304 | description = "ALB Target Group 4xx count CloudWatch metric alarm ARN"
305 | }
306 |
307 | output "httpcode_target_5xx_count_cloudwatch_metric_alarm_id" {
308 | value = module.atlantis.httpcode_target_5xx_count_cloudwatch_metric_alarm_id
309 | description = "ALB Target Group 5xx count CloudWatch metric alarm ID"
310 | }
311 |
312 | output "httpcode_target_5xx_count_cloudwatch_metric_alarm_arn" {
313 | value = module.atlantis.httpcode_target_5xx_count_cloudwatch_metric_alarm_arn
314 | description = "ALB Target Group 5xx count CloudWatch metric alarm ARN"
315 | }
316 |
317 | output "httpcode_elb_5xx_count_cloudwatch_metric_alarm_id" {
318 | value = module.atlantis.httpcode_elb_5xx_count_cloudwatch_metric_alarm_id
319 | description = "ALB 5xx count CloudWatch metric alarm ID"
320 | }
321 |
322 | output "httpcode_elb_5xx_count_cloudwatch_metric_alarm_arn" {
323 | value = module.atlantis.httpcode_elb_5xx_count_cloudwatch_metric_alarm_arn
324 | description = "ALB 5xx count CloudWatch metric alarm ARN"
325 | }
326 |
327 | output "target_response_time_average_cloudwatch_metric_alarm_id" {
328 | value = module.atlantis.target_response_time_average_cloudwatch_metric_alarm_id
329 | description = "ALB Target Group response time average CloudWatch metric alarm ID"
330 | }
331 |
332 | output "target_response_time_average_cloudwatch_metric_alarm_arn" {
333 | value = module.atlantis.target_response_time_average_cloudwatch_metric_alarm_arn
334 | description = "ALB Target Group response time average CloudWatch metric alarm ARN"
335 | }
336 |
--------------------------------------------------------------------------------
/examples/complete/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | type = string
3 | description = "AWS Region for S3 bucket"
4 | }
5 |
6 | variable "availability_zones" {
7 | type = list(string)
8 | description = "List of availability zones"
9 | }
10 |
11 | variable "vpc_cidr_block" {
12 | type = string
13 | description = "VPC CIDR block"
14 | }
15 |
16 | variable "launch_type" {
17 | type = string
18 | description = "The ECS launch type (valid options: FARGATE or EC2)"
19 | default = "FARGATE"
20 | }
21 |
22 | variable "default_backend_image" {
23 | type = string
24 | default = "cloudposse/default-backend:0.1.2"
25 | description = "ECS default (bootstrap) image"
26 | }
27 |
28 | variable "github_oauth_token" {
29 | type = string
30 | description = "GitHub OAuth token. If not provided the token is looked up from SSM"
31 | }
32 |
33 | variable "github_webhooks_token" {
34 | type = string
35 | description = "GitHub OAuth Token with permissions to create webhooks. If not provided the token is looked up from SSM"
36 | default = ""
37 | }
38 |
39 | variable "codepipeline_enabled" {
40 | type = bool
41 | description = "A boolean to enable/disable AWS Codepipeline and ECR"
42 | default = true
43 | }
44 |
45 | variable "codepipeline_s3_bucket_force_destroy" {
46 | type = bool
47 | description = "A boolean that indicates all objects should be deleted from the CodePipeline artifact store S3 bucket so that the bucket can be destroyed without error"
48 | default = false
49 | }
50 |
51 | variable "chamber_service" {
52 | type = string
53 | default = "atlantis"
54 | description = "SSM parameter service name for use with chamber. This is used in chamber_format where /$chamber_service/$parameter would be the default."
55 | }
56 |
57 | variable "autoscaling_enabled" {
58 | type = bool
59 | description = "A boolean to enable/disable Autoscaling policy for ECS Service"
60 | default = true
61 | }
62 |
63 | variable "build_timeout" {
64 | type = number
65 | default = 20
66 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed."
67 | }
68 |
69 | variable "branch" {
70 | type = string
71 | default = "master"
72 | description = "Atlantis branch of the GitHub repository, _e.g._ `master`"
73 | }
74 |
75 | variable "repo_name" {
76 | type = string
77 | description = "GitHub repository name of the atlantis to be built and deployed to ECS."
78 | }
79 |
80 | variable "repo_owner" {
81 | type = string
82 | description = "GitHub organization containing the Atlantis repository"
83 | }
84 |
85 | variable "atlantis_repo_config" {
86 | type = string
87 | description = "Path to atlantis server-side repo config file (https://www.runatlantis.io/docs/server-side-repo-config.html)"
88 | default = "atlantis-repo-config.yaml"
89 | }
90 |
91 | variable "atlantis_repo_whitelist" {
92 | type = list(string)
93 | description = "Whitelist of repositories Atlantis will accept webhooks from"
94 | default = []
95 | }
96 |
97 | variable "healthcheck_path" {
98 | type = string
99 | description = "Healthcheck path"
100 | default = "/healthz"
101 | }
102 |
103 | variable "desired_count" {
104 | type = number
105 | description = "Atlantis desired number of tasks"
106 | default = 1
107 | }
108 |
109 | variable "short_name" {
110 | type = string
111 | description = "Alantis short DNS name (e.g. `atlantis`)"
112 | default = "atlantis"
113 | }
114 |
115 | variable "hostname" {
116 | type = string
117 | description = "Atlantis URL"
118 | default = ""
119 | }
120 |
121 | variable "atlantis_gh_user" {
122 | type = string
123 | description = "Atlantis GitHub user"
124 | }
125 |
126 | variable "atlantis_gh_team_whitelist" {
127 | type = string
128 | description = "Atlantis GitHub team whitelist"
129 | default = ""
130 | }
131 |
132 | variable "atlantis_gh_webhook_secret" {
133 | type = string
134 | description = "Atlantis GitHub webhook secret"
135 | default = ""
136 | }
137 |
138 | variable "atlantis_log_level" {
139 | type = string
140 | description = "Atlantis log level"
141 | default = "info"
142 | }
143 |
144 | variable "atlantis_port" {
145 | type = number
146 | description = "Atlantis container port"
147 | default = 4141
148 | }
149 |
150 | variable "atlantis_webhook_format" {
151 | type = string
152 | default = "https://%s/events"
153 | description = "Template for the Atlantis webhook URL which is populated with the hostname"
154 | }
155 |
156 | variable "atlantis_url_format" {
157 | type = string
158 | default = "https://%s"
159 | description = "Template for the Atlantis URL which is populated with the hostname"
160 | }
161 |
162 | variable "autoscaling_min_capacity" {
163 | type = number
164 | description = "Atlantis minimum tasks to run"
165 | default = 1
166 | }
167 |
168 | variable "autoscaling_max_capacity" {
169 | type = number
170 | description = "Atlantis maximum tasks to run"
171 | default = 1
172 | }
173 |
174 | variable "container_cpu" {
175 | type = number
176 | description = "Atlantis CPUs per task"
177 | default = 256
178 | }
179 |
180 | variable "container_memory" {
181 | type = number
182 | description = "Atlantis memory per task"
183 | default = 512
184 | }
185 |
186 | variable "policy_arn" {
187 | type = string
188 | default = "arn:aws:iam::aws:policy/AdministratorAccess"
189 | description = "Permission to grant to atlantis server"
190 | }
191 |
192 | variable "webhook_enabled" {
193 | type = bool
194 | description = "Set to false to prevent the module from creating any webhook resources"
195 | default = false
196 | }
197 |
198 | variable "webhook_secret_length" {
199 | type = number
200 | default = 32
201 | description = "GitHub webhook secret length"
202 | }
203 |
204 | variable "webhook_events" {
205 | type = list(string)
206 | description = "A list of events which should trigger the webhook."
207 |
208 | default = [
209 | "issue_comment",
210 | "pull_request",
211 | "pull_request_review",
212 | "pull_request_review_comment",
213 | "push",
214 | ]
215 | }
216 |
217 | variable "ssh_private_key_name" {
218 | type = string
219 | default = "atlantis_ssh_private_key"
220 | description = "Atlantis SSH private key name"
221 | }
222 |
223 | variable "ssh_public_key_name" {
224 | type = string
225 | default = "atlantis_ssh_public_key"
226 | description = "Atlantis SSH public key name"
227 | }
228 |
229 | variable "security_group_ids" {
230 | type = list(string)
231 | default = []
232 | description = "Additional Security Group IDs to allow into ECS Service."
233 | }
234 |
235 | variable "parent_zone_id" {
236 | type = string
237 | description = "The zone ID where the DNS record for the `short_name` will be written"
238 | default = ""
239 | }
240 |
241 | variable "alb_ingress_listener_unauthenticated_priority" {
242 | type = number
243 | default = 50
244 | description = "The priority for the rules without authentication, between 1 and 50000 (1 being highest priority). Must be different from `alb_ingress_listener_authenticated_priority` since a listener can't have multiple rules with the same priority"
245 | }
246 |
247 | variable "alb_ingress_listener_authenticated_priority" {
248 | type = number
249 | default = 100
250 | description = "The priority for the rules with authentication, between 1 and 50000 (1 being highest priority). Must be different from `alb_ingress_listener_unauthenticated_priority` since a listener can't have multiple rules with the same priority"
251 | }
252 |
253 | variable "alb_ingress_unauthenticated_hosts" {
254 | type = list(string)
255 | default = []
256 | description = "Unauthenticated hosts to match in Hosts header (a maximum of 1 can be defined)"
257 | }
258 |
259 | variable "alb_ingress_authenticated_hosts" {
260 | type = list(string)
261 | default = []
262 | description = "Authenticated hosts to match in Hosts header (a maximum of 1 can be defined)"
263 | }
264 |
265 | variable "alb_ingress_unauthenticated_paths" {
266 | type = list(string)
267 | default = ["/events"]
268 | description = "Unauthenticated path pattern to match (a maximum of 1 can be defined)"
269 | }
270 |
271 | variable "alb_ingress_authenticated_paths" {
272 | type = list(string)
273 | default = ["/*"]
274 | description = "Authenticated path pattern to match (a maximum of 1 can be defined)"
275 | }
276 |
277 | variable "authentication_type" {
278 | type = string
279 | default = ""
280 | description = "Authentication type. Supported values are `COGNITO` and `OIDC`"
281 | }
282 |
283 | variable "authentication_cognito_user_pool_arn" {
284 | type = string
285 | description = "Cognito User Pool ARN"
286 | default = ""
287 | }
288 |
289 | variable "authentication_cognito_user_pool_client_id" {
290 | type = string
291 | description = "Cognito User Pool Client ID"
292 | default = ""
293 | }
294 |
295 | variable "authentication_cognito_user_pool_domain" {
296 | type = string
297 | description = "Cognito User Pool Domain. The User Pool Domain should be set to the domain prefix (`xxx`) instead of full domain (https://xxx.auth.us-west-2.amazoncognito.com)"
298 | default = ""
299 | }
300 |
301 | variable "authentication_oidc_client_id" {
302 | type = string
303 | description = "OIDC Client ID"
304 | default = ""
305 | }
306 |
307 | variable "authentication_oidc_client_secret" {
308 | type = string
309 | description = "OIDC Client Secret"
310 | default = ""
311 | }
312 |
313 | variable "authentication_oidc_issuer" {
314 | type = string
315 | description = "OIDC Issuer"
316 | default = ""
317 | }
318 |
319 | variable "authentication_oidc_authorization_endpoint" {
320 | type = string
321 | description = "OIDC Authorization Endpoint"
322 | default = ""
323 | }
324 |
325 | variable "authentication_oidc_token_endpoint" {
326 | type = string
327 | description = "OIDC Token Endpoint"
328 | default = ""
329 | }
330 |
331 | variable "authentication_oidc_user_info_endpoint" {
332 | type = string
333 | description = "OIDC User Info Endpoint"
334 | default = ""
335 | }
336 |
337 | variable "alb_target_group_alarms_enabled" {
338 | type = bool
339 | description = "A boolean to enable/disable CloudWatch Alarms for ALB Target metrics"
340 | default = true
341 | }
342 |
343 | variable "ecs_alarms_enabled" {
344 | type = bool
345 | description = "A boolean to enable/disable CloudWatch Alarms for ECS Service metrics"
346 | default = false
347 | }
348 |
349 | variable "kms_key_id" {
350 | type = string
351 | default = ""
352 | description = "KMS key ID used to encrypt SSM SecureString parameters"
353 | }
354 |
--------------------------------------------------------------------------------
/examples/complete/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12.26"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 2.0"
8 | }
9 | template = {
10 | source = "hashicorp/template"
11 | version = ">= 2.0"
12 | }
13 | null = {
14 | source = "hashicorp/null"
15 | version = ">= 2.0"
16 | }
17 | local = {
18 | source = "hashicorp/local"
19 | version = ">= 1.3"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/with_cognito_authentication/context.tf:
--------------------------------------------------------------------------------
1 | #
2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label
3 | # All other instances of this file should be a copy of that one
4 | #
5 | #
6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf
7 | # and then place it in your Terraform module to automatically get
8 | # Cloud Posse's standard configuration inputs suitable for passing
9 | # to Cloud Posse modules.
10 | #
11 | # Modules should access the whole context as `module.this.context`
12 | # to get the input variables with nulls for defaults,
13 | # for example `context = module.this.context`,
14 | # and access individual variables as `module.this.`,
15 | # with final values filled in.
16 | #
17 | # For example, when using defaults, `module.this.context.delimiter`
18 | # will be null, and `module.this.delimiter` will be `-` (hyphen).
19 | #
20 |
21 | module "this" {
22 | source = "cloudposse/label/null"
23 | version = "0.22.1" // requires Terraform >= 0.12.26
24 |
25 | enabled = var.enabled
26 | namespace = var.namespace
27 | environment = var.environment
28 | stage = var.stage
29 | name = var.name
30 | delimiter = var.delimiter
31 | attributes = var.attributes
32 | tags = var.tags
33 | additional_tag_map = var.additional_tag_map
34 | label_order = var.label_order
35 | regex_replace_chars = var.regex_replace_chars
36 | id_length_limit = var.id_length_limit
37 |
38 | context = var.context
39 | }
40 |
41 | # Copy contents of cloudposse/terraform-null-label/variables.tf here
42 |
43 | variable "context" {
44 | type = object({
45 | enabled = bool
46 | namespace = string
47 | environment = string
48 | stage = string
49 | name = string
50 | delimiter = string
51 | attributes = list(string)
52 | tags = map(string)
53 | additional_tag_map = map(string)
54 | regex_replace_chars = string
55 | label_order = list(string)
56 | id_length_limit = number
57 | })
58 | default = {
59 | enabled = true
60 | namespace = null
61 | environment = null
62 | stage = null
63 | name = null
64 | delimiter = null
65 | attributes = []
66 | tags = {}
67 | additional_tag_map = {}
68 | regex_replace_chars = null
69 | label_order = []
70 | id_length_limit = null
71 | }
72 | description = <<-EOT
73 | Single object for setting entire context at once.
74 | See description of individual variables for details.
75 | Leave string and numeric variables as `null` to use default value.
76 | Individual variable settings (non-null) override settings in context object,
77 | except for attributes, tags, and additional_tag_map, which are merged.
78 | EOT
79 | }
80 |
81 | variable "enabled" {
82 | type = bool
83 | default = null
84 | description = "Set to false to prevent the module from creating any resources"
85 | }
86 |
87 | variable "namespace" {
88 | type = string
89 | default = null
90 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'"
91 | }
92 |
93 | variable "environment" {
94 | type = string
95 | default = null
96 | description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'"
97 | }
98 |
99 | variable "stage" {
100 | type = string
101 | default = null
102 | description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'"
103 | }
104 |
105 | variable "name" {
106 | type = string
107 | default = null
108 | description = "Solution name, e.g. 'app' or 'jenkins'"
109 | }
110 |
111 | variable "delimiter" {
112 | type = string
113 | default = null
114 | description = <<-EOT
115 | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.
116 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
117 | EOT
118 | }
119 |
120 | variable "attributes" {
121 | type = list(string)
122 | default = []
123 | description = "Additional attributes (e.g. `1`)"
124 | }
125 |
126 | variable "tags" {
127 | type = map(string)
128 | default = {}
129 | description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`"
130 | }
131 |
132 | variable "additional_tag_map" {
133 | type = map(string)
134 | default = {}
135 | description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`."
136 | }
137 |
138 | variable "label_order" {
139 | type = list(string)
140 | default = null
141 | description = <<-EOT
142 | The naming order of the id output and Name tag.
143 | Defaults to ["namespace", "environment", "stage", "name", "attributes"].
144 | You can omit any of the 5 elements, but at least one must be present.
145 | EOT
146 | }
147 |
148 | variable "regex_replace_chars" {
149 | type = string
150 | default = null
151 | description = <<-EOT
152 | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.
153 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits.
154 | EOT
155 | }
156 |
157 | variable "id_length_limit" {
158 | type = number
159 | default = null
160 | description = <<-EOT
161 | Limit `id` to this many characters.
162 | Set to `0` for unlimited length.
163 | Set to `null` for default, which is `0`.
164 | Does not affect `id_full`.
165 | EOT
166 | }
167 |
168 | #### End of copy of cloudposse/terraform-null-label/variables.tf
169 |
--------------------------------------------------------------------------------
/examples/with_cognito_authentication/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
4 |
5 | data "aws_availability_zones" "available" {
6 | }
7 |
8 | locals {
9 | availability_zones = slice(data.aws_availability_zones.available.names, 0, 2)
10 | }
11 |
12 | module "vpc" {
13 | source = "cloudposse/vpc/aws"
14 | version = "0.18.1"
15 | cidr_block = "172.16.0.0/16"
16 |
17 | context = module.this.context
18 | }
19 |
20 | module "subnets" {
21 | source = "cloudposse/dynamic-subnets/aws"
22 | version = "0.33.0"
23 | availability_zones = local.availability_zones
24 | vpc_id = module.vpc.vpc_id
25 | igw_id = module.vpc.igw_id
26 | cidr_block = module.vpc.vpc_cidr_block
27 | nat_gateway_enabled = true
28 | nat_instance_enabled = false
29 |
30 | context = module.this.context
31 | }
32 |
33 | module "alb" {
34 | source = "cloudposse/alb/aws"
35 | version = "0.24.0"
36 | vpc_id = module.vpc.vpc_id
37 | security_group_ids = [module.vpc.vpc_default_security_group_id]
38 | subnet_ids = module.subnets.public_subnet_ids
39 | internal = false
40 | http_enabled = true
41 | alb_access_logs_s3_bucket_force_destroy = true
42 | access_logs_enabled = true
43 | cross_zone_load_balancing_enabled = true
44 | http2_enabled = true
45 | deletion_protection_enabled = false
46 |
47 | context = module.this.context
48 | }
49 |
50 | # ECS Cluster (needed even if using FARGATE launch type)
51 | resource "aws_ecs_cluster" "default" {
52 | name = module.this.id
53 | tags = module.this.tags
54 | setting {
55 | name = "containerInsights"
56 | value = "enabled"
57 | }
58 | }
59 |
60 | module "atlantis" {
61 | source = "../.."
62 | enabled = true
63 |
64 | atlantis_gh_team_whitelist = var.atlantis_gh_team_whitelist
65 | atlantis_gh_user = var.atlantis_gh_user
66 | atlantis_repo_whitelist = [var.atlantis_repo_whitelist]
67 |
68 | alb_arn_suffix = module.alb.alb_arn_suffix
69 | alb_dns_name = module.alb.alb_dns_name
70 | alb_name = module.alb.alb_name
71 | alb_zone_id = module.alb.alb_zone_id
72 | alb_security_group = module.alb.security_group_id
73 |
74 | container_cpu = var.atlantis_container_cpu
75 | container_memory = var.atlantis_container_memory
76 |
77 | branch = var.atlantis_branch
78 | parent_zone_id = var.parent_zone_id
79 | ecs_cluster_arn = aws_ecs_cluster.default.arn
80 | ecs_cluster_name = aws_ecs_cluster.default.name
81 | repo_name = var.atlantis_repo_name
82 | repo_owner = var.atlantis_repo_owner
83 | private_subnet_ids = [module.subnets.private_subnet_ids]
84 | security_group_ids = [module.vpc.vpc_default_security_group_id]
85 | vpc_id = module.vpc.vpc_id
86 |
87 | alb_ingress_authenticated_listener_arns = [module.alb.https_listener_arn]
88 | alb_ingress_authenticated_listener_arns_count = 1
89 |
90 | alb_ingress_unauthenticated_listener_arns = [module.alb.listener_arns]
91 | alb_ingress_unauthenticated_listener_arns_count = 2
92 |
93 | # Unauthenticated paths (with higher priority than the authenticated paths)
94 | alb_ingress_unauthenticated_paths = ["/events"]
95 | alb_ingress_listener_unauthenticated_priority = 50
96 |
97 | # Authenticated paths
98 | alb_ingress_authenticated_paths = ["/*"]
99 | alb_ingress_listener_authenticated_priority = 100
100 |
101 | authentication_type = "COGNITO"
102 | authentication_cognito_user_pool_arn = var.cognito_user_pool_arn
103 | authentication_cognito_user_pool_client_id = var.cognito_user_pool_client_id
104 | authentication_cognito_user_pool_domain = var.cognito_user_pool_domain
105 |
106 | context = module.this.context
107 | }
108 |
--------------------------------------------------------------------------------
/examples/with_cognito_authentication/outputs.tf:
--------------------------------------------------------------------------------
1 | output "atlantis_url" {
2 | value = module.atlantis.atlantis_url
3 | }
4 |
--------------------------------------------------------------------------------
/examples/with_cognito_authentication/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | type = string
3 | description = "AWS Region"
4 | default = "us-east-2"
5 | }
6 |
7 | variable "certificate_arn" {
8 | type = string
9 | description = "SSL certificate ARN for ALB HTTPS endpoints"
10 | }
11 |
12 | variable "cognito_user_pool_arn" {
13 | type = string
14 | description = "Cognito User Pool ARN"
15 | }
16 |
17 | variable "cognito_user_pool_client_id" {
18 | type = string
19 | description = "Cognito User Pool Client ID"
20 | }
21 |
22 | variable "cognito_user_pool_domain" {
23 | type = string
24 | description = "Cognito User Pool Domain. The User Pool Domain should be set to the domain prefix (`xxx`) instead of full domain (https://xxx.auth.us-west-2.amazoncognito.com)"
25 | }
26 |
27 | variable "atlantis_gh_team_whitelist" {
28 | type = string
29 | description = "Atlantis GitHub team whitelist"
30 | default = "engineering:plan,devops:*"
31 | }
32 |
33 | variable "atlantis_gh_user" {
34 | type = string
35 | description = "Atlantis GitHub user"
36 | default = "examplebot"
37 | }
38 |
39 | variable "atlantis_repo_whitelist" {
40 | type = list(string)
41 | description = "Whitelist of repositories Atlantis will accept webhooks from"
42 | default = ["github.com/example/*"]
43 | }
44 |
45 | variable "atlantis_repo_name" {
46 | type = string
47 | description = "GitHub repository name of the atlantis to be built and deployed to ECS"
48 | default = "atlantis"
49 | }
50 |
51 | variable "atlantis_repo_owner" {
52 | type = string
53 | description = "GitHub organization containing the Atlantis repository"
54 | default = "cloudposse"
55 | }
56 |
57 | variable "atlantis_branch" {
58 | type = string
59 | description = "Atlantis branch of the GitHub repository, _e.g._ `master`"
60 | default = "master"
61 | }
62 |
63 | variable "atlantis_container_cpu" {
64 | type = number
65 | description = "The vCPU setting to control cpu limits of container. (If FARGATE launch type is used below, this must be a supported vCPU size from the table here: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)"
66 | default = 256
67 | }
68 |
69 | variable "atlantis_container_memory" {
70 | type = number
71 | description = "The amount of RAM to allow container to use in MB. (If FARGATE launch type is used below, this must be a supported Memory size from the table here: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)"
72 | default = 512
73 | }
74 |
75 | variable "parent_zone_id" {
76 | type = string
77 | description = "The zone ID where the DNS record for the atlantis `short_name` will be written"
78 | }
79 |
--------------------------------------------------------------------------------
/examples/with_cognito_authentication/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12.26"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 2.0"
8 | }
9 | template = {
10 | source = "hashicorp/template"
11 | version = ">= 2.0"
12 | }
13 | null = {
14 | source = "hashicorp/null"
15 | version = ">= 2.0"
16 | }
17 | local = {
18 | source = "hashicorp/local"
19 | version = ">= 1.3"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/with_google_oidc_authentication/context.tf:
--------------------------------------------------------------------------------
1 | #
2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label
3 | # All other instances of this file should be a copy of that one
4 | #
5 | #
6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf
7 | # and then place it in your Terraform module to automatically get
8 | # Cloud Posse's standard configuration inputs suitable for passing
9 | # to Cloud Posse modules.
10 | #
11 | # Modules should access the whole context as `module.this.context`
12 | # to get the input variables with nulls for defaults,
13 | # for example `context = module.this.context`,
14 | # and access individual variables as `module.this.`,
15 | # with final values filled in.
16 | #
17 | # For example, when using defaults, `module.this.context.delimiter`
18 | # will be null, and `module.this.delimiter` will be `-` (hyphen).
19 | #
20 |
21 | module "this" {
22 | source = "cloudposse/label/null"
23 | version = "0.22.1" // requires Terraform >= 0.12.26
24 |
25 | enabled = var.enabled
26 | namespace = var.namespace
27 | environment = var.environment
28 | stage = var.stage
29 | name = var.name
30 | delimiter = var.delimiter
31 | attributes = var.attributes
32 | tags = var.tags
33 | additional_tag_map = var.additional_tag_map
34 | label_order = var.label_order
35 | regex_replace_chars = var.regex_replace_chars
36 | id_length_limit = var.id_length_limit
37 |
38 | context = var.context
39 | }
40 |
41 | # Copy contents of cloudposse/terraform-null-label/variables.tf here
42 |
43 | variable "context" {
44 | type = object({
45 | enabled = bool
46 | namespace = string
47 | environment = string
48 | stage = string
49 | name = string
50 | delimiter = string
51 | attributes = list(string)
52 | tags = map(string)
53 | additional_tag_map = map(string)
54 | regex_replace_chars = string
55 | label_order = list(string)
56 | id_length_limit = number
57 | })
58 | default = {
59 | enabled = true
60 | namespace = null
61 | environment = null
62 | stage = null
63 | name = null
64 | delimiter = null
65 | attributes = []
66 | tags = {}
67 | additional_tag_map = {}
68 | regex_replace_chars = null
69 | label_order = []
70 | id_length_limit = null
71 | }
72 | description = <<-EOT
73 | Single object for setting entire context at once.
74 | See description of individual variables for details.
75 | Leave string and numeric variables as `null` to use default value.
76 | Individual variable settings (non-null) override settings in context object,
77 | except for attributes, tags, and additional_tag_map, which are merged.
78 | EOT
79 | }
80 |
81 | variable "enabled" {
82 | type = bool
83 | default = null
84 | description = "Set to false to prevent the module from creating any resources"
85 | }
86 |
87 | variable "namespace" {
88 | type = string
89 | default = null
90 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'"
91 | }
92 |
93 | variable "environment" {
94 | type = string
95 | default = null
96 | description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'"
97 | }
98 |
99 | variable "stage" {
100 | type = string
101 | default = null
102 | description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'"
103 | }
104 |
105 | variable "name" {
106 | type = string
107 | default = null
108 | description = "Solution name, e.g. 'app' or 'jenkins'"
109 | }
110 |
111 | variable "delimiter" {
112 | type = string
113 | default = null
114 | description = <<-EOT
115 | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.
116 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
117 | EOT
118 | }
119 |
120 | variable "attributes" {
121 | type = list(string)
122 | default = []
123 | description = "Additional attributes (e.g. `1`)"
124 | }
125 |
126 | variable "tags" {
127 | type = map(string)
128 | default = {}
129 | description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`"
130 | }
131 |
132 | variable "additional_tag_map" {
133 | type = map(string)
134 | default = {}
135 | description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`."
136 | }
137 |
138 | variable "label_order" {
139 | type = list(string)
140 | default = null
141 | description = <<-EOT
142 | The naming order of the id output and Name tag.
143 | Defaults to ["namespace", "environment", "stage", "name", "attributes"].
144 | You can omit any of the 5 elements, but at least one must be present.
145 | EOT
146 | }
147 |
148 | variable "regex_replace_chars" {
149 | type = string
150 | default = null
151 | description = <<-EOT
152 | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.
153 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits.
154 | EOT
155 | }
156 |
157 | variable "id_length_limit" {
158 | type = number
159 | default = null
160 | description = <<-EOT
161 | Limit `id` to this many characters.
162 | Set to `0` for unlimited length.
163 | Set to `null` for default, which is `0`.
164 | Does not affect `id_full`.
165 | EOT
166 | }
167 |
168 | #### End of copy of cloudposse/terraform-null-label/variables.tf
169 |
--------------------------------------------------------------------------------
/examples/with_google_oidc_authentication/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
4 |
5 | data "aws_availability_zones" "available" {}
6 |
7 | locals {
8 | availability_zones = slice(data.aws_availability_zones.available.names, 0, 2)
9 | }
10 |
11 | module "vpc" {
12 | source = "cloudposse/vpc/aws"
13 | version = "0.18.1"
14 | cidr_block = "172.16.0.0/16"
15 |
16 | context = module.this.context
17 | }
18 |
19 | module "subnets" {
20 | source = "cloudposse/dynamic-subnets/aws"
21 | version = "0.33.0"
22 | availability_zones = local.availability_zones
23 | vpc_id = module.vpc.vpc_id
24 | igw_id = module.vpc.igw_id
25 | cidr_block = module.vpc.vpc_cidr_block
26 | nat_gateway_enabled = true
27 | nat_instance_enabled = false
28 |
29 | context = module.this.context
30 | }
31 |
32 | module "alb" {
33 | source = "cloudposse/alb/aws"
34 | version = "0.24.0"
35 | vpc_id = module.vpc.vpc_id
36 | security_group_ids = [module.vpc.vpc_default_security_group_id]
37 | subnet_ids = module.subnets.public_subnet_ids
38 | internal = false
39 | http_enabled = true
40 | alb_access_logs_s3_bucket_force_destroy = true
41 | access_logs_enabled = true
42 | cross_zone_load_balancing_enabled = true
43 | http2_enabled = true
44 | deletion_protection_enabled = false
45 |
46 | context = module.this.context
47 | }
48 |
49 | # ECS Cluster (needed even if using FARGATE launch type)
50 | resource "aws_ecs_cluster" "default" {
51 | name = module.this.id
52 | tags = module.this.tags
53 | setting {
54 | name = "containerInsights"
55 | value = "enabled"
56 | }
57 | }
58 |
59 | module "atlantis" {
60 | source = "../.."
61 | enabled = true
62 |
63 | atlantis_gh_team_whitelist = var.atlantis_gh_team_whitelist
64 | atlantis_gh_user = var.atlantis_gh_user
65 | atlantis_repo_whitelist = var.atlantis_repo_whitelist
66 |
67 | alb_arn_suffix = module.alb.alb_arn_suffix
68 | alb_dns_name = module.alb.alb_dns_name
69 | alb_name = module.alb.alb_name
70 | alb_zone_id = module.alb.alb_zone_id
71 | alb_security_group = module.alb.security_group_id
72 |
73 | container_cpu = var.atlantis_container_cpu
74 | container_memory = var.atlantis_container_memory
75 |
76 | branch = var.atlantis_branch
77 | parent_zone_id = var.parent_zone_id
78 | ecs_cluster_arn = aws_ecs_cluster.default.arn
79 | ecs_cluster_name = aws_ecs_cluster.default.name
80 | repo_name = var.atlantis_repo_name
81 | repo_owner = var.atlantis_repo_owner
82 | private_subnet_ids = module.subnets.private_subnet_ids
83 | security_group_ids = [module.vpc.vpc_default_security_group_id]
84 | vpc_id = module.vpc.vpc_id
85 |
86 | alb_ingress_authenticated_listener_arns = [module.alb.https_listener_arn]
87 | alb_ingress_authenticated_listener_arns_count = 1
88 |
89 | alb_ingress_unauthenticated_listener_arns = module.alb.listener_arns
90 | alb_ingress_unauthenticated_listener_arns_count = 2
91 |
92 | # Unauthenticated paths (with higher priority than the authenticated paths)
93 | alb_ingress_unauthenticated_paths = ["/events"]
94 | alb_ingress_listener_unauthenticated_priority = 50
95 |
96 | # Authenticated paths
97 | alb_ingress_authenticated_paths = ["/*"]
98 | alb_ingress_listener_authenticated_priority = 100
99 |
100 | authentication_type = "OIDC"
101 | authentication_oidc_client_id = var.google_oidc_client_id
102 | authentication_oidc_client_secret = var.google_oidc_client_secret
103 | authentication_oidc_issuer = "https://accounts.google.com"
104 | authentication_oidc_authorization_endpoint = "https://accounts.google.com/o/oauth2/v2/auth"
105 | authentication_oidc_token_endpoint = "https://oauth2.googleapis.com/token"
106 | authentication_oidc_user_info_endpoint = "https://openidconnect.googleapis.com/v1/userinfo"
107 |
108 | context = module.this.context
109 | }
110 |
--------------------------------------------------------------------------------
/examples/with_google_oidc_authentication/outputs.tf:
--------------------------------------------------------------------------------
1 | output "atlantis_url" {
2 | value = module.atlantis.atlantis_url
3 | }
4 |
--------------------------------------------------------------------------------
/examples/with_google_oidc_authentication/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | type = string
3 | description = "AWS Region"
4 | default = "us-east-2"
5 | }
6 |
7 | variable "certificate_arn" {
8 | type = string
9 | description = "SSL certificate ARN for ALB HTTPS endpoints"
10 | }
11 |
12 | variable "google_oidc_client_id" {
13 | type = string
14 | description = "Google OIDC Client ID. Use this URL to create a Google OAuth 2.0 Client and obtain the Client ID and Client Secret: https://console.developers.google.com/apis/credentials"
15 | }
16 |
17 | variable "google_oidc_client_secret" {
18 | type = string
19 | description = "Google OIDC Client Secret. Use this URL to create a Google OAuth 2.0 Client and obtain the Client ID and Client Secret: https://console.developers.google.com/apis/credentials"
20 | }
21 |
22 | variable "atlantis_gh_team_whitelist" {
23 | type = string
24 | description = "Atlantis GitHub team whitelist"
25 | default = "engineering:plan,devops:*"
26 | }
27 |
28 | variable "atlantis_gh_user" {
29 | type = string
30 | description = "Atlantis GitHub user"
31 | default = "examplebot"
32 | }
33 |
34 | variable "atlantis_repo_whitelist" {
35 | type = list(string)
36 | description = "Whitelist of repositories Atlantis will accept webhooks from"
37 | default = ["github.com/example/*"]
38 | }
39 |
40 | variable "atlantis_repo_name" {
41 | type = string
42 | description = "GitHub repository name of the atlantis to be built and deployed to ECS"
43 | default = "atlantis"
44 | }
45 |
46 | variable "atlantis_repo_owner" {
47 | type = string
48 | description = "GitHub organization containing the Atlantis repository"
49 | default = "cloudposse"
50 | }
51 |
52 | variable "atlantis_branch" {
53 | type = string
54 | description = "Atlantis branch of the GitHub repository, _e.g._ `master`"
55 | default = "master"
56 | }
57 |
58 | variable "atlantis_container_cpu" {
59 | type = number
60 | description = "The vCPU setting to control cpu limits of container. (If FARGATE launch type is used below, this must be a supported vCPU size from the table here: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)"
61 | default = 256
62 | }
63 |
64 | variable "atlantis_container_memory" {
65 | type = number
66 | description = "The amount of RAM to allow container to use in MB. (If FARGATE launch type is used below, this must be a supported Memory size from the table here: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)"
67 | default = 512
68 | }
69 |
70 | variable "parent_zone_id" {
71 | type = string
72 | description = "The zone ID where the DNS record for the atlantis `short_name` will be written"
73 | }
74 |
--------------------------------------------------------------------------------
/examples/with_google_oidc_authentication/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12.26"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 2.0"
8 | }
9 | template = {
10 | source = "hashicorp/template"
11 | version = ">= 2.0"
12 | }
13 | null = {
14 | source = "hashicorp/null"
15 | version = ">= 2.0"
16 | }
17 | local = {
18 | source = "hashicorp/local"
19 | version = ">= 1.3"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/without_authentication/context.tf:
--------------------------------------------------------------------------------
1 | #
2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label
3 | # All other instances of this file should be a copy of that one
4 | #
5 | #
6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf
7 | # and then place it in your Terraform module to automatically get
8 | # Cloud Posse's standard configuration inputs suitable for passing
9 | # to Cloud Posse modules.
10 | #
11 | # Modules should access the whole context as `module.this.context`
12 | # to get the input variables with nulls for defaults,
13 | # for example `context = module.this.context`,
14 | # and access individual variables as `module.this.`,
15 | # with final values filled in.
16 | #
17 | # For example, when using defaults, `module.this.context.delimiter`
18 | # will be null, and `module.this.delimiter` will be `-` (hyphen).
19 | #
20 |
21 | module "this" {
22 | source = "cloudposse/label/null"
23 | version = "0.22.1" // requires Terraform >= 0.12.26
24 |
25 | enabled = var.enabled
26 | namespace = var.namespace
27 | environment = var.environment
28 | stage = var.stage
29 | name = var.name
30 | delimiter = var.delimiter
31 | attributes = var.attributes
32 | tags = var.tags
33 | additional_tag_map = var.additional_tag_map
34 | label_order = var.label_order
35 | regex_replace_chars = var.regex_replace_chars
36 | id_length_limit = var.id_length_limit
37 |
38 | context = var.context
39 | }
40 |
41 | # Copy contents of cloudposse/terraform-null-label/variables.tf here
42 |
43 | variable "context" {
44 | type = object({
45 | enabled = bool
46 | namespace = string
47 | environment = string
48 | stage = string
49 | name = string
50 | delimiter = string
51 | attributes = list(string)
52 | tags = map(string)
53 | additional_tag_map = map(string)
54 | regex_replace_chars = string
55 | label_order = list(string)
56 | id_length_limit = number
57 | })
58 | default = {
59 | enabled = true
60 | namespace = null
61 | environment = null
62 | stage = null
63 | name = null
64 | delimiter = null
65 | attributes = []
66 | tags = {}
67 | additional_tag_map = {}
68 | regex_replace_chars = null
69 | label_order = []
70 | id_length_limit = null
71 | }
72 | description = <<-EOT
73 | Single object for setting entire context at once.
74 | See description of individual variables for details.
75 | Leave string and numeric variables as `null` to use default value.
76 | Individual variable settings (non-null) override settings in context object,
77 | except for attributes, tags, and additional_tag_map, which are merged.
78 | EOT
79 | }
80 |
81 | variable "enabled" {
82 | type = bool
83 | default = null
84 | description = "Set to false to prevent the module from creating any resources"
85 | }
86 |
87 | variable "namespace" {
88 | type = string
89 | default = null
90 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'"
91 | }
92 |
93 | variable "environment" {
94 | type = string
95 | default = null
96 | description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'"
97 | }
98 |
99 | variable "stage" {
100 | type = string
101 | default = null
102 | description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'"
103 | }
104 |
105 | variable "name" {
106 | type = string
107 | default = null
108 | description = "Solution name, e.g. 'app' or 'jenkins'"
109 | }
110 |
111 | variable "delimiter" {
112 | type = string
113 | default = null
114 | description = <<-EOT
115 | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.
116 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
117 | EOT
118 | }
119 |
120 | variable "attributes" {
121 | type = list(string)
122 | default = []
123 | description = "Additional attributes (e.g. `1`)"
124 | }
125 |
126 | variable "tags" {
127 | type = map(string)
128 | default = {}
129 | description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`"
130 | }
131 |
132 | variable "additional_tag_map" {
133 | type = map(string)
134 | default = {}
135 | description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`."
136 | }
137 |
138 | variable "label_order" {
139 | type = list(string)
140 | default = null
141 | description = <<-EOT
142 | The naming order of the id output and Name tag.
143 | Defaults to ["namespace", "environment", "stage", "name", "attributes"].
144 | You can omit any of the 5 elements, but at least one must be present.
145 | EOT
146 | }
147 |
148 | variable "regex_replace_chars" {
149 | type = string
150 | default = null
151 | description = <<-EOT
152 | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.
153 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits.
154 | EOT
155 | }
156 |
157 | variable "id_length_limit" {
158 | type = number
159 | default = null
160 | description = <<-EOT
161 | Limit `id` to this many characters.
162 | Set to `0` for unlimited length.
163 | Set to `null` for default, which is `0`.
164 | Does not affect `id_full`.
165 | EOT
166 | }
167 |
168 | #### End of copy of cloudposse/terraform-null-label/variables.tf
169 |
--------------------------------------------------------------------------------
/examples/without_authentication/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
4 |
5 | data "aws_availability_zones" "available" {}
6 |
7 | locals {
8 | availability_zones = slice(data.aws_availability_zones.available.names, 0, 2)
9 | }
10 |
11 | module "vpc" {
12 | source = "cloudposse/vpc/aws"
13 | version = "0.18.1"
14 | cidr_block = "172.16.0.0/16"
15 |
16 | context = module.this.context
17 | }
18 |
19 | module "subnets" {
20 | source = "cloudposse/dynamic-subnets/aws"
21 | version = "0.33.0"
22 | availability_zones = local.availability_zones
23 | vpc_id = module.vpc.vpc_id
24 | igw_id = module.vpc.igw_id
25 | cidr_block = module.vpc.vpc_cidr_block
26 | nat_gateway_enabled = true
27 | nat_instance_enabled = false
28 |
29 | context = module.this.context
30 | }
31 |
32 | module "alb" {
33 | source = "cloudposse/alb/aws"
34 | version = "0.24.0"
35 | vpc_id = module.vpc.vpc_id
36 | security_group_ids = [module.vpc.vpc_default_security_group_id]
37 | subnet_ids = module.subnets.public_subnet_ids
38 | internal = false
39 | http_enabled = true
40 | alb_access_logs_s3_bucket_force_destroy = true
41 | access_logs_enabled = true
42 | cross_zone_load_balancing_enabled = true
43 | http2_enabled = true
44 | deletion_protection_enabled = false
45 |
46 | context = module.this.context
47 | }
48 |
49 | # ECS Cluster (needed even if using FARGATE launch type)
50 | resource "aws_ecs_cluster" "default" {
51 | name = module.this.id
52 | tags = module.this.tags
53 | setting {
54 | name = "containerInsights"
55 | value = "enabled"
56 | }
57 | }
58 |
59 | module "atlantis" {
60 | source = "../.."
61 | enabled = true
62 |
63 | atlantis_gh_team_whitelist = var.atlantis_gh_team_whitelist
64 | atlantis_gh_user = var.atlantis_gh_user
65 | atlantis_repo_whitelist = var.atlantis_repo_whitelist
66 |
67 | alb_arn_suffix = module.alb.alb_arn_suffix
68 | alb_dns_name = module.alb.alb_dns_name
69 | alb_name = module.alb.alb_name
70 | alb_zone_id = module.alb.alb_zone_id
71 | alb_security_group = module.alb.security_group_id
72 |
73 | container_cpu = var.atlantis_container_cpu
74 | container_memory = var.atlantis_container_memory
75 |
76 | branch = var.atlantis_branch
77 | parent_zone_id = var.parent_zone_id
78 | ecs_cluster_arn = aws_ecs_cluster.default.arn
79 | ecs_cluster_name = aws_ecs_cluster.default.name
80 | repo_name = var.atlantis_repo_name
81 | repo_owner = var.atlantis_repo_owner
82 | private_subnet_ids = module.subnets.private_subnet_ids
83 | security_group_ids = [module.vpc.vpc_default_security_group_id]
84 | vpc_id = module.vpc.vpc_id
85 |
86 | # Without authentication, both HTTP and HTTPS endpoints are supported
87 | alb_ingress_unauthenticated_listener_arns = [module.alb.listener_arns]
88 | alb_ingress_unauthenticated_listener_arns_count = 2
89 |
90 | # All paths are unauthenticated
91 | alb_ingress_unauthenticated_paths = ["/*"]
92 | alb_ingress_listener_unauthenticated_priority = 100
93 | alb_ingress_authenticated_paths = []
94 |
95 | context = module.this.context
96 | }
97 |
--------------------------------------------------------------------------------
/examples/without_authentication/outputs.tf:
--------------------------------------------------------------------------------
1 | output "atlantis_url" {
2 | value = module.atlantis.atlantis_url
3 | }
4 |
--------------------------------------------------------------------------------
/examples/without_authentication/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | type = string
3 | description = "AWS Region"
4 | default = "us-west-2"
5 | }
6 |
7 | variable "certificate_arn" {
8 | type = string
9 | description = "SSL certificate ARN for ALB HTTPS endpoints"
10 | }
11 |
12 | variable "atlantis_gh_team_whitelist" {
13 | type = string
14 | description = "Atlantis GitHub team whitelist"
15 | default = "engineering:plan,devops:*"
16 | }
17 |
18 | variable "atlantis_gh_user" {
19 | type = string
20 | description = "Atlantis GitHub user"
21 | default = "examplebot"
22 | }
23 |
24 | variable "atlantis_repo_whitelist" {
25 | type = list(string)
26 | description = "Whitelist of repositories Atlantis will accept webhooks from"
27 | default = ["github.com/example/*"]
28 | }
29 |
30 | variable "atlantis_repo_name" {
31 | type = string
32 | description = "GitHub repository name of the atlantis to be built and deployed to ECS"
33 | default = "atlantis"
34 | }
35 |
36 | variable "atlantis_repo_owner" {
37 | type = string
38 | description = "GitHub organization containing the Atlantis repository"
39 | default = "cloudposse"
40 | }
41 |
42 | variable "atlantis_branch" {
43 | type = string
44 | description = "Atlantis branch of the GitHub repository, _e.g._ `master`"
45 | default = "master"
46 | }
47 |
48 | variable "atlantis_container_cpu" {
49 | type = number
50 | description = "The vCPU setting to control cpu limits of container. (If FARGATE launch type is used below, this must be a supported vCPU size from the table here: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)"
51 | default = 256
52 | }
53 |
54 | variable "atlantis_container_memory" {
55 | type = number
56 | description = "The amount of RAM to allow container to use in MB. (If FARGATE launch type is used below, this must be a supported Memory size from the table here: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)"
57 | default = 512
58 | }
59 |
60 | variable "parent_zone_id" {
61 | type = string
62 | description = "The zone ID where the DNS record for the atlantis `short_name` will be written"
63 | }
64 |
--------------------------------------------------------------------------------
/examples/without_authentication/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.12.26"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 2.0"
8 | }
9 | template = {
10 | source = "hashicorp/template"
11 | version = ">= 2.0"
12 | }
13 | null = {
14 | source = "hashicorp/null"
15 | version = ">= 2.0"
16 | }
17 | local = {
18 | source = "hashicorp/local"
19 | version = ">= 1.3"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/main.tf:
--------------------------------------------------------------------------------
1 | # Data
2 | #--------------------------------------------------------------
3 | data "aws_ssm_parameter" "atlantis_gh_token" {
4 | count = local.enabled && var.github_oauth_token == "" ? 1 : 0
5 | name = local.github_oauth_token_ssm_name
6 | }
7 |
8 | data "aws_ssm_parameter" "github_webhooks_token" {
9 | count = local.enabled && var.github_webhooks_token == "" ? 1 : 0
10 | name = local.github_webhooks_token_ssm_name
11 | }
12 |
13 | data "aws_kms_key" "chamber_kms_key" {
14 | count = local.enabled ? 1 : 0
15 | key_id = local.kms_key_id
16 | }
17 |
18 | # Locals
19 | #--------------------------------------------------------------
20 | locals {
21 | enabled = module.this.enabled
22 | hostname = var.hostname != "" ? var.hostname : local.default_hostname
23 | atlantis_webhook_url = format(var.atlantis_webhook_format, local.hostname)
24 | atlantis_url = format(var.atlantis_url_format, local.hostname)
25 | atlantis_gh_webhook_secret = var.atlantis_gh_webhook_secret != "" ? var.atlantis_gh_webhook_secret : join("", random_string.atlantis_gh_webhook_secret.*.result)
26 | default_hostname = join("", aws_route53_record.default.*.fqdn)
27 | kms_key_id = var.kms_key_id != "" ? var.kms_key_id : format("alias/%s-%s-chamber", module.this.namespace, module.this.stage)
28 | }
29 |
30 | # GitHub tokens
31 | locals {
32 | github_oauth_token = var.github_oauth_token != "" ? var.github_oauth_token : join("", data.aws_ssm_parameter.atlantis_gh_token.*.value)
33 | github_oauth_token_ssm_name = var.github_oauth_token_ssm_name != "" ? var.github_oauth_token_ssm_name : format(var.chamber_format, var.chamber_service, "atlantis_gh_token")
34 |
35 | github_webhooks_token = var.github_webhooks_token != "" ? var.github_webhooks_token : join("", data.aws_ssm_parameter.github_webhooks_token.*.value)
36 | github_webhooks_token_ssm_name = var.github_webhooks_token_ssm_name != "" ? var.github_webhooks_token_ssm_name : format(var.chamber_format, var.chamber_service, "github_webhooks_token")
37 | }
38 |
39 | # Modules
40 | #--------------------------------------------------------------
41 | module "ssh_key_pair" {
42 | source = "cloudposse/ssm-tls-ssh-key-pair/aws"
43 | version = "0.10.0"
44 | ssh_private_key_name = var.ssh_private_key_name
45 | ssh_public_key_name = var.ssh_public_key_name
46 | ssm_path_prefix = var.chamber_service
47 | kms_key_id = local.kms_key_id
48 |
49 | context = module.this.context
50 | }
51 |
52 | module "github_webhooks" {
53 | source = "cloudposse/repository-webhooks/github"
54 | version = "0.12.0"
55 | enabled = local.enabled && var.webhook_enabled ? true : false
56 | github_organization = var.repo_owner
57 | github_repositories = [var.repo_name]
58 | github_token = local.github_webhooks_token
59 | webhook_secret = local.atlantis_gh_webhook_secret
60 | webhook_url = local.atlantis_webhook_url
61 | webhook_content_type = "json"
62 | events = var.webhook_events
63 |
64 | context = module.this.context
65 | }
66 |
67 | module "ecs_web_app" {
68 | source = "cloudposse/ecs-web-app/aws"
69 | version = "0.60.0"
70 |
71 | region = var.region
72 | vpc_id = var.vpc_id
73 | launch_type = var.launch_type
74 |
75 | container_environment = [
76 | {
77 | name = "ATLANTIS_ENABLED"
78 | value = local.enabled
79 | }
80 | ]
81 |
82 | container_image = var.default_backend_image
83 | container_cpu = var.container_cpu
84 | container_memory = var.container_memory
85 |
86 | container_repo_credentials = var.container_repo_credentials
87 |
88 | container_port = var.atlantis_port
89 |
90 | port_mappings = [
91 | {
92 | containerPort = var.atlantis_port
93 | hostPort = var.atlantis_port
94 | protocol = "tcp"
95 | }
96 | ]
97 |
98 | desired_count = var.desired_count
99 |
100 | autoscaling_enabled = var.autoscaling_enabled
101 | autoscaling_dimension = "cpu"
102 | autoscaling_min_capacity = var.autoscaling_min_capacity
103 | autoscaling_max_capacity = var.autoscaling_max_capacity
104 | autoscaling_scale_up_adjustment = 1
105 | autoscaling_scale_up_cooldown = 60
106 | autoscaling_scale_down_adjustment = -1
107 | autoscaling_scale_down_cooldown = 300
108 |
109 | aws_logs_region = var.region
110 | ecs_cluster_arn = var.ecs_cluster_arn
111 | ecs_cluster_name = var.ecs_cluster_name
112 | ecs_security_group_ids = var.security_group_ids
113 | ecs_private_subnet_ids = var.private_subnet_ids
114 |
115 | alb_ingress_healthcheck_path = var.healthcheck_path
116 |
117 | webhook_enabled = var.webhook_enabled
118 | github_webhook_events = ["release"]
119 | webhook_filter_json_path = "$.action"
120 | webhook_filter_match_equals = "published"
121 |
122 | github_oauth_token = local.github_oauth_token
123 | github_webhooks_token = local.github_webhooks_token
124 |
125 | repo_owner = var.repo_owner
126 | repo_name = var.repo_name
127 | branch = var.branch
128 | build_timeout = var.build_timeout
129 | badge_enabled = false
130 |
131 | codepipeline_enabled = var.codepipeline_enabled
132 | codepipeline_s3_bucket_force_destroy = var.codepipeline_s3_bucket_force_destroy
133 |
134 | ecs_alarms_enabled = var.ecs_alarms_enabled
135 | alb_target_group_alarms_enabled = var.alb_target_group_alarms_enabled
136 | alb_target_group_alarms_3xx_threshold = 25
137 | alb_target_group_alarms_4xx_threshold = 25
138 | alb_target_group_alarms_5xx_threshold = 25
139 | alb_target_group_alarms_response_time_threshold = 0.5
140 | alb_target_group_alarms_period = 300
141 | alb_target_group_alarms_evaluation_periods = 1
142 | alb_arn_suffix = var.alb_arn_suffix
143 | use_alb_security_group = var.use_alb_security_group
144 | alb_security_group = var.alb_security_group
145 |
146 | alb_target_group_alarms_alarm_actions = var.alb_target_group_alarms_alarm_actions
147 | alb_target_group_alarms_ok_actions = var.alb_target_group_alarms_ok_actions
148 | alb_target_group_alarms_insufficient_data_actions = var.alb_target_group_alarms_insufficient_data_actions
149 |
150 | alb_ingress_authenticated_paths = var.alb_ingress_authenticated_paths
151 | alb_ingress_unauthenticated_paths = var.alb_ingress_unauthenticated_paths
152 | alb_ingress_authenticated_hosts = var.alb_ingress_authenticated_hosts
153 | alb_ingress_unauthenticated_hosts = var.alb_ingress_unauthenticated_hosts
154 |
155 | alb_ingress_listener_authenticated_priority = var.alb_ingress_listener_authenticated_priority
156 | alb_ingress_listener_unauthenticated_priority = var.alb_ingress_listener_unauthenticated_priority
157 |
158 | alb_ingress_unauthenticated_listener_arns = var.alb_ingress_unauthenticated_listener_arns
159 | alb_ingress_unauthenticated_listener_arns_count = var.alb_ingress_unauthenticated_listener_arns_count
160 | alb_ingress_authenticated_listener_arns = var.alb_ingress_authenticated_listener_arns
161 | alb_ingress_authenticated_listener_arns_count = var.alb_ingress_authenticated_listener_arns_count
162 |
163 | authentication_type = var.authentication_type
164 | authentication_cognito_user_pool_arn = local.authentication_cognito_user_pool_arn
165 | authentication_cognito_user_pool_client_id = local.authentication_cognito_user_pool_client_id
166 | authentication_cognito_user_pool_domain = local.authentication_cognito_user_pool_domain
167 | authentication_oidc_client_id = local.authentication_oidc_client_id
168 | authentication_oidc_client_secret = local.authentication_oidc_client_secret
169 | authentication_oidc_issuer = var.authentication_oidc_issuer
170 | authentication_oidc_authorization_endpoint = var.authentication_oidc_authorization_endpoint
171 | authentication_oidc_token_endpoint = var.authentication_oidc_token_endpoint
172 | authentication_oidc_user_info_endpoint = var.authentication_oidc_user_info_endpoint
173 |
174 | context = module.this.context
175 | }
176 |
177 | # Resources
178 | #--------------------------------------------------------------
179 |
180 | resource "aws_route53_record" "default" {
181 | count = local.enabled ? 1 : 0
182 | zone_id = var.parent_zone_id
183 | name = var.short_name
184 | type = "A"
185 |
186 | alias {
187 | name = var.alb_dns_name
188 | zone_id = var.alb_zone_id
189 | evaluate_target_health = false
190 | }
191 | }
192 |
193 | resource "random_string" "atlantis_gh_webhook_secret" {
194 | count = local.enabled ? 1 : 0
195 | length = var.webhook_secret_length
196 | special = true
197 | }
198 |
199 | resource "aws_ssm_parameter" "atlantis_port" {
200 | count = local.enabled ? 1 : 0
201 | description = "Atlantis server port"
202 | name = format(var.chamber_format, var.chamber_service, "atlantis_port")
203 | overwrite = var.overwrite_ssm_parameter
204 | type = "String"
205 | value = var.atlantis_port
206 | tags = module.this.tags
207 | }
208 |
209 | resource "aws_ssm_parameter" "atlantis_atlantis_url" {
210 | count = local.enabled ? 1 : 0
211 | description = "Atlantis URL"
212 | name = format(var.chamber_format, var.chamber_service, "atlantis_atlantis_url")
213 | overwrite = var.overwrite_ssm_parameter
214 | type = "String"
215 | value = local.atlantis_url
216 | tags = module.this.tags
217 | }
218 |
219 | resource "aws_ssm_parameter" "atlantis_gh_user" {
220 | count = local.enabled ? 1 : 0
221 | description = "Atlantis GitHub user"
222 | name = format(var.chamber_format, var.chamber_service, "atlantis_gh_user")
223 | overwrite = var.overwrite_ssm_parameter
224 | type = "String"
225 | value = var.atlantis_gh_user
226 | tags = module.this.tags
227 | }
228 |
229 | resource "aws_ssm_parameter" "atlantis_gh_team_whitelist" {
230 | count = local.enabled ? 1 : 0
231 | description = "Atlantis GitHub team whitelist"
232 | name = format(var.chamber_format, var.chamber_service, "atlantis_gh_team_whitelist")
233 | overwrite = var.overwrite_ssm_parameter
234 | type = "String"
235 | value = var.atlantis_gh_team_whitelist
236 | tags = module.this.tags
237 | }
238 |
239 | resource "aws_ssm_parameter" "atlantis_gh_webhook_secret" {
240 | count = local.enabled ? 1 : 0
241 | description = "Atlantis GitHub webhook secret"
242 | key_id = join("", data.aws_kms_key.chamber_kms_key.*.id)
243 | name = format(var.chamber_format, var.chamber_service, "atlantis_gh_webhook_secret")
244 | overwrite = var.overwrite_ssm_parameter
245 | type = "SecureString"
246 | value = local.atlantis_gh_webhook_secret
247 | tags = module.this.tags
248 | }
249 |
250 | resource "aws_ssm_parameter" "atlantis_iam_role_arn" {
251 | count = local.enabled ? 1 : 0
252 | description = "Atlantis IAM role ARN"
253 | name = format(var.chamber_format, var.chamber_service, "atlantis_iam_role_arn")
254 | overwrite = var.overwrite_ssm_parameter
255 | type = "String"
256 | value = module.ecs_web_app.ecs_task_role_arn
257 | tags = module.this.tags
258 | }
259 |
260 | resource "aws_ssm_parameter" "atlantis_log_level" {
261 | count = local.enabled ? 1 : 0
262 | description = "Atlantis log level"
263 | name = format(var.chamber_format, var.chamber_service, "atlantis_log_level")
264 | overwrite = var.overwrite_ssm_parameter
265 | type = "String"
266 | value = var.atlantis_log_level
267 | tags = module.this.tags
268 | }
269 |
270 | resource "aws_ssm_parameter" "atlantis_repo_config" {
271 | count = local.enabled ? 1 : 0
272 | description = "Path to atlantis config file"
273 | name = format(var.chamber_format, var.chamber_service, "atlantis_repo_config")
274 | overwrite = var.overwrite_ssm_parameter
275 | type = "String"
276 | value = var.atlantis_repo_config
277 | tags = module.this.tags
278 | }
279 |
280 | resource "aws_ssm_parameter" "atlantis_repo_whitelist" {
281 | count = local.enabled ? 1 : 0
282 | description = "Whitelist of repositories Atlantis will accept webhooks from"
283 | name = format(var.chamber_format, var.chamber_service, "atlantis_repo_whitelist")
284 | overwrite = var.overwrite_ssm_parameter
285 | type = "String"
286 | value = join(",", var.atlantis_repo_whitelist)
287 | tags = module.this.tags
288 | }
289 |
290 | resource "aws_ssm_parameter" "atlantis_wake_word" {
291 | count = local.enabled ? 1 : 0
292 | description = "Wake world for Atlantis"
293 | name = format(var.chamber_format, var.chamber_service, "atlantis_wake_word")
294 | overwrite = var.overwrite_ssm_parameter
295 | type = "String"
296 | value = var.atlantis_wake_word
297 | tags = module.this.tags
298 | }
299 |
300 | resource "aws_ssm_parameter" "atlantis_gh_token" {
301 | count = local.enabled && var.github_oauth_token != "" ? 1 : 0
302 | description = "Atlantis GitHub OAuth token"
303 | key_id = join("", data.aws_kms_key.chamber_kms_key.*.id)
304 | name = local.github_oauth_token_ssm_name
305 | overwrite = var.overwrite_ssm_parameter
306 | type = "SecureString"
307 | value = local.github_oauth_token
308 | tags = module.this.tags
309 | }
310 |
311 | resource "aws_ssm_parameter" "github_webhooks_token" {
312 | count = local.enabled && var.github_webhooks_token != "" ? 1 : 0
313 | description = "GitHub OAuth token with permission to create webhooks"
314 | key_id = join("", data.aws_kms_key.chamber_kms_key.*.id)
315 | name = local.github_webhooks_token_ssm_name
316 | overwrite = var.overwrite_ssm_parameter
317 | type = "SecureString"
318 | value = local.github_webhooks_token
319 | tags = module.this.tags
320 | }
321 |
322 | resource "aws_security_group_rule" "egress_http" {
323 | count = local.enabled ? 1 : 0
324 | cidr_blocks = ["0.0.0.0/0"]
325 | from_port = 80
326 | protocol = "tcp"
327 | security_group_id = module.ecs_web_app.ecs_service_security_group_id
328 | to_port = 80
329 | type = "egress"
330 | }
331 |
332 | resource "aws_security_group_rule" "egress_https" {
333 | count = local.enabled ? 1 : 0
334 | cidr_blocks = ["0.0.0.0/0"]
335 | from_port = 443
336 | protocol = "tcp"
337 | security_group_id = module.ecs_web_app.ecs_service_security_group_id
338 | to_port = 443
339 | type = "egress"
340 | }
341 |
342 | resource "aws_security_group_rule" "egress_udp_dns" {
343 | count = local.enabled ? 1 : 0
344 | cidr_blocks = ["0.0.0.0/0"]
345 | from_port = 53
346 | protocol = "udp"
347 | security_group_id = module.ecs_web_app.ecs_service_security_group_id
348 | to_port = 53
349 | type = "egress"
350 | }
351 |
352 | resource "aws_security_group_rule" "egress_tcp_dns" {
353 | count = local.enabled ? 1 : 0
354 | cidr_blocks = ["0.0.0.0/0"]
355 | from_port = 53
356 | protocol = "tcp"
357 | security_group_id = module.ecs_web_app.ecs_service_security_group_id
358 | to_port = 53
359 | type = "egress"
360 | }
361 |
362 | resource "aws_iam_role_policy_attachment" "default" {
363 | count = local.enabled ? 1 : 0
364 | role = module.ecs_web_app.ecs_task_role_name
365 | policy_arn = var.policy_arn
366 |
367 | lifecycle {
368 | create_before_destroy = true
369 | }
370 | }
371 |
372 | locals {
373 | authentication_cognito_user_pool_arn = var.authentication_cognito_user_pool_arn != "" ? var.authentication_cognito_user_pool_arn : join("", data.aws_ssm_parameter.atlantis_cognito_user_pool_arn.*.value)
374 |
375 | authentication_cognito_user_pool_arn_ssm_name = var.authentication_cognito_user_pool_arn_ssm_name != "" ? var.authentication_cognito_user_pool_arn_ssm_name : format(
376 | var.chamber_format,
377 | var.chamber_service,
378 | "atlantis_cognito_user_pool_arn"
379 | )
380 |
381 | authentication_cognito_user_pool_client_id = var.authentication_cognito_user_pool_client_id != "" ? var.authentication_cognito_user_pool_client_id : join("", data.aws_ssm_parameter.atlantis_cognito_user_pool_client_id.*.value)
382 |
383 | authentication_cognito_user_pool_client_id_ssm_name = var.authentication_cognito_user_pool_client_id_ssm_name != "" ? var.authentication_cognito_user_pool_client_id_ssm_name : format(
384 | var.chamber_format,
385 | var.chamber_service,
386 | "atlantis_cognito_user_pool_client_id"
387 | )
388 |
389 | authentication_cognito_user_pool_domain = var.authentication_cognito_user_pool_domain != "" ? var.authentication_cognito_user_pool_domain : join("", data.aws_ssm_parameter.atlantis_cognito_user_pool_domain.*.value)
390 |
391 | authentication_cognito_user_pool_domain_ssm_name = var.authentication_cognito_user_pool_domain_ssm_name != "" ? var.authentication_cognito_user_pool_domain_ssm_name : format(
392 | var.chamber_format,
393 | var.chamber_service,
394 | "atlantis_cognito_user_pool_domain"
395 | )
396 |
397 | authentication_oidc_client_id = var.authentication_oidc_client_id != "" ? var.authentication_oidc_client_id : join("", data.aws_ssm_parameter.atlantis_oidc_client_id.*.value)
398 |
399 | authentication_oidc_client_id_ssm_name = var.authentication_oidc_client_id_ssm_name != "" ? var.authentication_oidc_client_id_ssm_name : format(
400 | var.chamber_format,
401 | var.chamber_service,
402 | "atlantis_oidc_client_id"
403 | )
404 |
405 | authentication_oidc_client_secret = var.authentication_oidc_client_secret != "" ? var.authentication_oidc_client_secret : join("", data.aws_ssm_parameter.atlantis_oidc_client_secret.*.value)
406 |
407 | authentication_oidc_client_secret_ssm_name = var.authentication_oidc_client_secret_ssm_name != "" ? var.authentication_oidc_client_secret_ssm_name : format(
408 | var.chamber_format,
409 | var.chamber_service,
410 | "atlantis_oidc_client_secret"
411 | )
412 | }
413 |
414 | data "aws_ssm_parameter" "atlantis_cognito_user_pool_arn" {
415 | count = local.enabled && var.authentication_type == "COGNITO" && var.authentication_cognito_user_pool_arn == "" ? 1 : 0
416 | name = local.authentication_cognito_user_pool_arn_ssm_name
417 | }
418 |
419 | data "aws_ssm_parameter" "atlantis_cognito_user_pool_client_id" {
420 | count = local.enabled && var.authentication_type == "COGNITO" && var.authentication_cognito_user_pool_client_id == "" ? 1 : 0
421 | name = local.authentication_cognito_user_pool_client_id_ssm_name
422 | }
423 |
424 | data "aws_ssm_parameter" "atlantis_cognito_user_pool_domain" {
425 | count = local.enabled && var.authentication_type == "COGNITO" && var.authentication_cognito_user_pool_domain == "" ? 1 : 0
426 | name = local.authentication_cognito_user_pool_domain_ssm_name
427 | }
428 |
429 | data "aws_ssm_parameter" "atlantis_oidc_client_id" {
430 | count = local.enabled && var.authentication_type == "OIDC" && var.authentication_oidc_client_id == "" ? 1 : 0
431 | name = local.authentication_oidc_client_id_ssm_name
432 | }
433 |
434 | data "aws_ssm_parameter" "atlantis_oidc_client_secret" {
435 | count = local.enabled && var.authentication_type == "OIDC" && var.authentication_oidc_client_secret == "" ? 1 : 0
436 | name = local.authentication_oidc_client_secret_ssm_name
437 | }
438 |
439 | resource "aws_ssm_parameter" "atlantis_cognito_user_pool_arn" {
440 | count = local.enabled && var.authentication_type == "COGNITO" && var.authentication_cognito_user_pool_arn != "" ? 1 : 0
441 | overwrite = var.overwrite_ssm_parameter
442 | type = "SecureString"
443 | description = "Atlantis Cognito User Pool ARN"
444 | key_id = local.kms_key_id
445 | name = local.authentication_cognito_user_pool_arn_ssm_name
446 | value = local.authentication_cognito_user_pool_arn
447 | tags = module.this.tags
448 | }
449 |
450 | resource "aws_ssm_parameter" "atlantis_cognito_user_pool_client_id" {
451 | count = local.enabled && var.authentication_type == "COGNITO" && var.authentication_cognito_user_pool_client_id != "" ? 1 : 0
452 | overwrite = var.overwrite_ssm_parameter
453 | type = "SecureString"
454 | description = "Atlantis Cognito User Pool Client ID"
455 | key_id = local.kms_key_id
456 | name = local.authentication_cognito_user_pool_client_id_ssm_name
457 | value = local.authentication_cognito_user_pool_client_id
458 | tags = module.this.tags
459 | }
460 |
461 | resource "aws_ssm_parameter" "atlantis_cognito_user_pool_domain" {
462 | count = local.enabled && var.authentication_type == "COGNITO" && var.authentication_cognito_user_pool_domain != "" ? 1 : 0
463 | overwrite = var.overwrite_ssm_parameter
464 | type = "SecureString"
465 | description = "Atlantis Cognito User Pool Domain"
466 | key_id = local.kms_key_id
467 | name = local.authentication_cognito_user_pool_domain_ssm_name
468 | value = local.authentication_cognito_user_pool_domain
469 | tags = module.this.tags
470 | }
471 |
472 | resource "aws_ssm_parameter" "atlantis_oidc_client_id" {
473 | count = local.enabled && var.authentication_type == "OIDC" && var.authentication_oidc_client_id != "" ? 1 : 0
474 | overwrite = var.overwrite_ssm_parameter
475 | type = "SecureString"
476 | description = "Atlantis OIDC Client ID"
477 | key_id = local.kms_key_id
478 | name = local.authentication_oidc_client_id_ssm_name
479 | value = local.authentication_oidc_client_id
480 | tags = module.this.tags
481 | }
482 |
483 | resource "aws_ssm_parameter" "atlantis_oidc_client_secret" {
484 | count = local.enabled && var.authentication_type == "OIDC" && var.authentication_oidc_client_secret != "" ? 1 : 0
485 | overwrite = var.overwrite_ssm_parameter
486 | type = "SecureString"
487 | description = "Atlantis OIDC Client Secret"
488 | key_id = local.kms_key_id
489 | name = local.authentication_oidc_client_secret_ssm_name
490 | value = local.authentication_oidc_client_secret
491 | tags = module.this.tags
492 | }
493 |
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 | output "atlantis_ssh_public_key" {
2 | description = "Atlantis SSH Public Key"
3 | value = module.ssh_key_pair.public_key
4 | }
5 |
6 | output "atlantis_url" {
7 | description = "The URL endpoint for the atlantis server"
8 | value = local.atlantis_url
9 | }
10 |
11 | output "atlantis_webhook_url" {
12 | description = "atlantis webhook URL"
13 | value = local.atlantis_webhook_url
14 | }
15 |
16 | output "ecr_registry_id" {
17 | value = module.ecs_web_app.ecr_registry_id
18 | description = "Registry ID"
19 | }
20 |
21 | output "ecr_repository_url" {
22 | value = module.ecs_web_app.ecr_repository_url
23 | description = "Repository URL"
24 | }
25 |
26 | output "ecr_repository_name" {
27 | value = module.ecs_web_app.ecr_repository_name
28 | description = "Repository name"
29 | }
30 |
31 | output "alb_ingress_target_group_name" {
32 | description = "ALB Target Group name"
33 | value = module.ecs_web_app.alb_ingress_target_group_name
34 | }
35 |
36 | output "alb_ingress_target_group_arn" {
37 | description = "ALB Target Group ARN"
38 | value = module.ecs_web_app.alb_ingress_target_group_arn
39 | }
40 |
41 | output "alb_ingress_target_group_arn_suffix" {
42 | description = "ALB Target Group ARN suffix"
43 | value = module.ecs_web_app.alb_ingress_target_group_arn_suffix
44 | }
45 |
46 | output "container_definition_json" {
47 | description = "JSON encoded list of container definitions for use with other terraform resources such as aws_ecs_task_definition"
48 | value = module.ecs_web_app.container_definition_json
49 | sensitive = true
50 | }
51 |
52 | output "container_definition_json_map" {
53 | description = "JSON encoded container definitions for use with other terraform resources such as aws_ecs_task_definition"
54 | value = module.ecs_web_app.container_definition_json_map
55 | sensitive = true
56 | }
57 |
58 | output "ecs_exec_role_policy_id" {
59 | description = "The ECS service role policy ID, in the form of `role_name:role_policy_name`"
60 | value = module.ecs_web_app.ecs_exec_role_policy_id
61 | }
62 |
63 | output "ecs_exec_role_policy_name" {
64 | description = "ECS service role name"
65 | value = module.ecs_web_app.ecs_exec_role_policy_name
66 | }
67 |
68 | output "ecs_service_name" {
69 | description = "ECS Service name"
70 | value = module.ecs_web_app.ecs_service_name
71 | }
72 |
73 | output "ecs_service_role_arn" {
74 | description = "ECS Service role ARN"
75 | value = module.ecs_web_app.ecs_service_role_arn
76 | }
77 |
78 | output "ecs_task_exec_role_name" {
79 | description = "ECS Task role name"
80 | value = module.ecs_web_app.ecs_task_exec_role_name
81 | }
82 |
83 | output "ecs_task_exec_role_arn" {
84 | description = "ECS Task exec role ARN"
85 | value = module.ecs_web_app.ecs_task_exec_role_arn
86 | }
87 |
88 | output "ecs_task_role_name" {
89 | description = "ECS Task role name"
90 | value = module.ecs_web_app.ecs_task_role_name
91 | }
92 |
93 | output "ecs_task_role_arn" {
94 | description = "ECS Task role ARN"
95 | value = module.ecs_web_app.ecs_task_role_arn
96 | }
97 |
98 | output "ecs_task_role_id" {
99 | description = "ECS Task role id"
100 | value = module.ecs_web_app.ecs_task_role_id
101 | }
102 |
103 | output "ecs_service_security_group_id" {
104 | description = "Security Group ID of the ECS task"
105 | value = module.ecs_web_app.ecs_service_security_group_id
106 | }
107 |
108 | output "ecs_task_definition_family" {
109 | description = "ECS task definition family"
110 | value = module.ecs_web_app.ecs_task_definition_family
111 | }
112 |
113 | output "ecs_task_definition_revision" {
114 | description = "ECS task definition revision"
115 | value = module.ecs_web_app.ecs_task_definition_revision
116 | }
117 |
118 | output "codebuild_project_name" {
119 | description = "CodeBuild project name"
120 | value = module.ecs_web_app.codebuild_project_name
121 | }
122 |
123 | output "codebuild_project_id" {
124 | description = "CodeBuild project ID"
125 | value = module.ecs_web_app.codebuild_project_id
126 | }
127 |
128 | output "codebuild_role_id" {
129 | description = "CodeBuild IAM Role ID"
130 | value = module.ecs_web_app.codebuild_role_id
131 | }
132 |
133 | output "codebuild_role_arn" {
134 | description = "CodeBuild IAM Role ARN"
135 | value = module.ecs_web_app.codebuild_role_arn
136 | }
137 |
138 | output "codebuild_cache_bucket_name" {
139 | description = "CodeBuild cache S3 bucket name"
140 | value = module.ecs_web_app.codebuild_cache_bucket_name
141 | }
142 |
143 | output "codebuild_cache_bucket_arn" {
144 | description = "CodeBuild cache S3 bucket ARN"
145 | value = module.ecs_web_app.codebuild_cache_bucket_arn
146 | }
147 |
148 | output "codebuild_badge_url" {
149 | description = "The URL of the build badge when badge_enabled is enabled"
150 | value = module.ecs_web_app.codebuild_badge_url
151 | }
152 |
153 | output "codepipeline_id" {
154 | description = "CodePipeline ID"
155 | value = module.ecs_web_app.codepipeline_id
156 | }
157 |
158 | output "codepipeline_arn" {
159 | description = "CodePipeline ARN"
160 | value = module.ecs_web_app.codepipeline_arn
161 | }
162 |
163 | output "codepipeline_webhook_id" {
164 | description = "The CodePipeline webhook's ID"
165 | value = module.ecs_web_app.codepipeline_webhook_id
166 | }
167 |
168 | output "codepipeline_webhook_url" {
169 | description = "The CodePipeline webhook's URL. POST events to this endpoint to trigger the target"
170 | value = module.ecs_web_app.codepipeline_webhook_url
171 | sensitive = true
172 | }
173 |
174 | output "ecs_cloudwatch_autoscaling_scale_up_policy_arn" {
175 | description = "ARN of the scale up policy"
176 | value = module.ecs_web_app.ecs_cloudwatch_autoscaling_scale_up_policy_arn
177 | }
178 |
179 | output "ecs_cloudwatch_autoscaling_scale_down_policy_arn" {
180 | description = "ARN of the scale down policy"
181 | value = module.ecs_web_app.ecs_cloudwatch_autoscaling_scale_down_policy_arn
182 | }
183 |
184 | output "ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_id" {
185 | value = module.ecs_web_app.ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_id
186 | description = "ECS CPU utilization high CloudWatch metric alarm ID"
187 | }
188 |
189 | output "ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_arn" {
190 | value = module.ecs_web_app.ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_arn
191 | description = "ECS CPU utilization high CloudWatch metric alarm ARN"
192 | }
193 |
194 | output "ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_id" {
195 | value = module.ecs_web_app.ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_id
196 | description = "ECS CPU utilization low CloudWatch metric alarm ID"
197 | }
198 |
199 | output "ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_arn" {
200 | value = module.ecs_web_app.ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_arn
201 | description = "ECS CPU utilization low CloudWatch metric alarm ARN"
202 | }
203 |
204 | output "ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_id" {
205 | value = module.ecs_web_app.ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_id
206 | description = "ECS Memory utilization high CloudWatch metric alarm ID"
207 | }
208 |
209 | output "ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_arn" {
210 | value = module.ecs_web_app.ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_arn
211 | description = "ECS Memory utilization high CloudWatch metric alarm ARN"
212 | }
213 |
214 | output "ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_id" {
215 | value = module.ecs_web_app.ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_id
216 | description = "ECS Memory utilization low CloudWatch metric alarm ID"
217 | }
218 |
219 | output "ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_arn" {
220 | value = module.ecs_web_app.ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_arn
221 | description = "ECS Memory utilization low CloudWatch metric alarm ARN"
222 | }
223 |
224 | output "httpcode_target_3xx_count_cloudwatch_metric_alarm_id" {
225 | value = module.ecs_web_app.httpcode_target_3xx_count_cloudwatch_metric_alarm_id
226 | description = "ALB Target Group 3xx count CloudWatch metric alarm ID"
227 | }
228 |
229 | output "httpcode_target_3xx_count_cloudwatch_metric_alarm_arn" {
230 | value = module.ecs_web_app.httpcode_target_3xx_count_cloudwatch_metric_alarm_arn
231 | description = "ALB Target Group 3xx count CloudWatch metric alarm ARN"
232 | }
233 |
234 | output "httpcode_target_4xx_count_cloudwatch_metric_alarm_id" {
235 | value = module.ecs_web_app.httpcode_target_4xx_count_cloudwatch_metric_alarm_id
236 | description = "ALB Target Group 4xx count CloudWatch metric alarm ID"
237 | }
238 |
239 | output "httpcode_target_4xx_count_cloudwatch_metric_alarm_arn" {
240 | value = module.ecs_web_app.httpcode_target_4xx_count_cloudwatch_metric_alarm_arn
241 | description = "ALB Target Group 4xx count CloudWatch metric alarm ARN"
242 | }
243 |
244 | output "httpcode_target_5xx_count_cloudwatch_metric_alarm_id" {
245 | value = module.ecs_web_app.httpcode_target_5xx_count_cloudwatch_metric_alarm_id
246 | description = "ALB Target Group 5xx count CloudWatch metric alarm ID"
247 | }
248 |
249 | output "httpcode_target_5xx_count_cloudwatch_metric_alarm_arn" {
250 | value = module.ecs_web_app.httpcode_target_5xx_count_cloudwatch_metric_alarm_arn
251 | description = "ALB Target Group 5xx count CloudWatch metric alarm ARN"
252 | }
253 |
254 | output "httpcode_elb_5xx_count_cloudwatch_metric_alarm_id" {
255 | value = module.ecs_web_app.httpcode_elb_5xx_count_cloudwatch_metric_alarm_id
256 | description = "ALB 5xx count CloudWatch metric alarm ID"
257 | }
258 |
259 | output "httpcode_elb_5xx_count_cloudwatch_metric_alarm_arn" {
260 | value = module.ecs_web_app.httpcode_elb_5xx_count_cloudwatch_metric_alarm_arn
261 | description = "ALB 5xx count CloudWatch metric alarm ARN"
262 | }
263 |
264 | output "target_response_time_average_cloudwatch_metric_alarm_id" {
265 | value = module.ecs_web_app.target_response_time_average_cloudwatch_metric_alarm_id
266 | description = "ALB Target Group response time average CloudWatch metric alarm ID"
267 | }
268 |
269 | output "target_response_time_average_cloudwatch_metric_alarm_arn" {
270 | value = module.ecs_web_app.target_response_time_average_cloudwatch_metric_alarm_arn
271 | description = "ALB Target Group response time average CloudWatch metric alarm ARN"
272 | }
273 |
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | .test-harness
2 |
--------------------------------------------------------------------------------
/test/Makefile:
--------------------------------------------------------------------------------
1 | TEST_HARNESS ?= https://github.com/cloudposse/test-harness.git
2 | TEST_HARNESS_BRANCH ?= master
3 | TEST_HARNESS_PATH = $(realpath .test-harness)
4 | BATS_ARGS ?= --tap
5 | BATS_LOG ?= test.log
6 |
7 | # Define a macro to run the tests
8 | define RUN_TESTS
9 | @echo "Running tests in $(1)"
10 | @cd $(1) && bats $(BATS_ARGS) $(addsuffix .bats,$(addprefix $(TEST_HARNESS_PATH)/test/terraform/,$(TESTS)))
11 | endef
12 |
13 | default: all
14 |
15 | -include Makefile.*
16 |
17 | ## Provision the test-harnesss
18 | .test-harness:
19 | [ -d $@ ] || git clone --depth=1 -b $(TEST_HARNESS_BRANCH) $(TEST_HARNESS) $@
20 |
21 | ## Initialize the tests
22 | init: .test-harness
23 |
24 | ## Install all dependencies (OS specific)
25 | deps::
26 | @exit 0
27 |
28 | ## Clean up the test harness
29 | clean:
30 | [ "$(TEST_HARNESS_PATH)" == "/" ] || rm -rf $(TEST_HARNESS_PATH)
31 |
32 | ## Run all tests
33 | all: module examples/complete
34 |
35 | ## Run basic sanity checks against the module itself
36 | module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions
37 | module: deps
38 | $(call RUN_TESTS, ../)
39 |
40 | ## Run tests against example
41 | examples/complete: export TESTS ?= installed lint get-modules get-plugins validate
42 | examples/complete: deps
43 | $(call RUN_TESTS, ../$@)
44 |
--------------------------------------------------------------------------------
/test/Makefile.alpine:
--------------------------------------------------------------------------------
1 | ifneq (,$(wildcard /sbin/apk))
2 | ## Install all dependencies for alpine
3 | deps:: init
4 | @apk add --update terraform-docs@cloudposse json2hcl@cloudposse
5 | endif
6 |
--------------------------------------------------------------------------------
/test/src/.gitignore:
--------------------------------------------------------------------------------
1 | .gopath
2 | vendor/
3 |
--------------------------------------------------------------------------------
/test/src/Makefile:
--------------------------------------------------------------------------------
1 | export TF_CLI_ARGS_init ?= -get-plugins=true
2 | export TERRAFORM_VERSION ?= $(shell curl -s https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r -M '.current_version' | cut -d. -f1-2)
3 |
4 | .DEFAULT_GOAL : all
5 |
6 | .PHONY: all
7 | ## Default target
8 | all: test
9 |
10 | .PHONY : init
11 | ## Initialize tests
12 | init:
13 | @exit 0
14 |
15 | .PHONY : test
16 | ## Run tests
17 | test: init
18 | go mod download
19 | go test -v -timeout 60m -run TestExamplesComplete
20 |
21 | ## Run tests in docker container
22 | docker/test:
23 | docker run --name terratest --rm -it -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e GITHUB_TOKEN \
24 | -e PATH="/usr/local/terraform/$(TERRAFORM_VERSION)/bin:/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
25 | -v $(CURDIR)/../../:/module/ cloudposse/test-harness:latest -C /module/test/src test
26 |
27 | .PHONY : clean
28 | ## Clean up files
29 | clean:
30 | rm -rf ../../examples/complete/*.tfstate*
--------------------------------------------------------------------------------
/test/src/examples_complete_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "testing"
7 |
8 | "github.com/gruntwork-io/terratest/modules/terraform"
9 | "github.com/stretchr/testify/assert"
10 | "math/rand"
11 | "strconv"
12 | "time"
13 | )
14 |
15 | // Test the Terraform module in examples/complete using Terratest.
16 | func TestExamplesComplete(t *testing.T) {
17 | t.Parallel()
18 |
19 | targets := []string{"module.label", "module.vpc", "module.subnets", "module.alb"}
20 |
21 | rand.Seed(time.Now().UnixNano())
22 |
23 | attributes := []string{strconv.Itoa(rand.Intn(1000))}
24 |
25 | // We need to create the ALB first because terraform does not wwait for it to be in the ready state before creating ECS target group
26 | terraformOptions := &terraform.Options{
27 | // The path to where our Terraform code is located
28 | TerraformDir: "../../examples/complete",
29 | Upgrade: true,
30 | // Variables to pass to our Terraform code using -var-file options
31 | VarFiles: []string{"fixtures.us-west-2.tfvars"},
32 | Vars: map[string]interface{}{
33 | "attributes": attributes,
34 | },
35 | Targets: targets,
36 | }
37 |
38 | // At the end of the test, run `terraform destroy` to clean up any resources that were created
39 | defer func() {
40 | if r := recover(); r != nil {
41 | terraformOptions.Targets = targets
42 | terraform.Destroy(t, terraformOptions)
43 | terraformOptions.Targets = nil
44 | terraform.Destroy(t, terraformOptions)
45 | assert.Fail(t, fmt.Sprintf("Panicked: %v", r))
46 | } else {
47 | terraformOptions.Targets = nil
48 | terraform.Destroy(t, terraformOptions)
49 | }
50 | }()
51 |
52 | // This will run `terraform init` and `terraform apply` and fail the test if there are any errors
53 | terraform.InitAndApply(t, terraformOptions)
54 |
55 | terraformOptions.Targets = nil
56 |
57 | // This will run `terraform init` and `terraform apply` and fail the test if there are any errors
58 | terraform.Apply(t, terraformOptions)
59 |
60 | // Run `terraform output` to get the value of an output variable
61 | vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr")
62 | // Verify we're getting back the outputs we expect
63 | assert.Equal(t, "172.16.0.0/16", vpcCidr)
64 |
65 | // Run `terraform output` to get the value of an output variable
66 | privateSubnetCidrs := terraform.OutputList(t, terraformOptions, "private_subnet_cidrs")
67 | // Verify we're getting back the outputs we expect
68 | assert.Equal(t, []string{"172.16.0.0/19", "172.16.32.0/19"}, privateSubnetCidrs)
69 |
70 | // Run `terraform output` to get the value of an output variable
71 | publicSubnetCidrs := terraform.OutputList(t, terraformOptions, "public_subnet_cidrs")
72 | // Verify we're getting back the outputs we expect
73 | assert.Equal(t, []string{"172.16.128.0/19", "172.16.160.0/19"}, publicSubnetCidrs)
74 |
75 | // Run `terraform output` to get the value of an output variable
76 | albName := terraform.Output(t, terraformOptions, "alb_name")
77 | // Verify we're getting back the outputs we expect
78 | expectedAlbName := "eg-test-ecs-atlantis-" + attributes[0]
79 | assert.Equal(t, expectedAlbName, albName)
80 |
81 | // Run `terraform output` to get the value of an output variable
82 | albHttpListenerArn := terraform.Output(t, terraformOptions, "alb_http_listener_arn")
83 | // Verify we're getting back the outputs we expect
84 | assert.Contains(t, albHttpListenerArn, "listener/app/eg-test-ecs-atlantis")
85 |
86 | // Run `terraform output` to get the value of an output variable
87 | albIngressTargetGroupName := terraform.Output(t, terraformOptions, "alb_ingress_target_group_name")
88 | // Verify we're getting back the outputs we expect
89 | expectedAlbIngressTargetGroupName := "eg-test-ecs-atlantis-" + attributes[0]
90 | assert.Equal(t, expectedAlbIngressTargetGroupName, albIngressTargetGroupName)
91 |
92 | // Run `terraform output` to get the value of an output variable
93 | albAccessLogsBucketId := terraform.Output(t, terraformOptions, "alb_access_logs_bucket_id")
94 | // Verify we're getting back the outputs we expect
95 | expectedAlbAccessLogsBucketId := "eg-test-ecs-atlantis-" + attributes[0] + "-alb-access-logs"
96 | assert.Equal(t, expectedAlbAccessLogsBucketId, albAccessLogsBucketId)
97 |
98 | // Run `terraform output` to get the value of an output variable
99 | containerDefinitionJsonMap := terraform.OutputRequired(t, terraformOptions, "container_definition_json_map")
100 | // Verify we're getting back the outputs we expect
101 | var jsonObject map[string]interface{}
102 | err := json.Unmarshal([]byte(containerDefinitionJsonMap), &jsonObject)
103 | assert.NoError(t, err)
104 | expectedContainerDefinitionName := "eg-test-ecs-atlantis-" + attributes[0]
105 | assert.Equal(t, expectedContainerDefinitionName, jsonObject["name"])
106 | assert.Equal(t, "cloudposse/default-backend:0.1.2", jsonObject["image"])
107 | assert.Equal(t, 512, int((jsonObject["memory"]).(float64)))
108 | assert.Equal(t, 128, int((jsonObject["memoryReservation"]).(float64)))
109 | assert.Equal(t, 256, int((jsonObject["cpu"]).(float64)))
110 | assert.Equal(t, true, jsonObject["essential"])
111 | assert.Equal(t, false, jsonObject["readonlyRootFilesystem"])
112 |
113 | // Run `terraform output` to get the value of an output variable
114 | codebuildCacheBucketName := terraform.Output(t, terraformOptions, "codebuild_cache_bucket_name")
115 | // Verify we're getting back the outputs we expect
116 | expectedCodebuildCacheBucketName := "eg-test-ecs-atlantis-" + attributes[0] + "-build"
117 | assert.Contains(t, codebuildCacheBucketName, expectedCodebuildCacheBucketName)
118 |
119 | // Run `terraform output` to get the value of an output variable
120 | codebuildProjectName := terraform.Output(t, terraformOptions, "codebuild_project_name")
121 | // Verify we're getting back the outputs we expect
122 | expectedCodebuildProjectName := "eg-test-ecs-atlantis-" + attributes[0] + "-build"
123 | assert.Equal(t, expectedCodebuildProjectName, codebuildProjectName)
124 |
125 | // Run `terraform output` to get the value of an output variable
126 | codebuildRoleId := terraform.Output(t, terraformOptions, "codebuild_role_id")
127 | // Verify we're getting back the outputs we expect
128 | expectedCodebuildRoleId := "eg-test-ecs-atlantis-" + attributes[0] + "-build"
129 | assert.Equal(t, expectedCodebuildRoleId, codebuildRoleId)
130 |
131 | // Run `terraform output` to get the value of an output variable
132 | codepipelineId := terraform.Output(t, terraformOptions, "codepipeline_id")
133 | // Verify we're getting back the outputs we expect
134 | expectedCodepipelineId := "eg-test-ecs-atlantis-" + attributes[0] + "-codepipeline"
135 | assert.Equal(t, expectedCodepipelineId, codepipelineId)
136 |
137 | // Run `terraform output` to get the value of an output variable
138 | ecrRepositoryName := terraform.Output(t, terraformOptions, "ecr_repository_name")
139 | // Verify we're getting back the outputs we expect
140 | expectedEcrRepositoryName := "eg-test-ecs-atlantis-" + attributes[0] + "-ecr"
141 | assert.Equal(t, expectedEcrRepositoryName, ecrRepositoryName)
142 |
143 | // Run `terraform output` to get the value of an output variable
144 | ecsTaskRoleName := terraform.Output(t, terraformOptions, "ecs_task_role_name")
145 | // Verify we're getting back the outputs we expect
146 | expectedEcsTaskRoleName := "eg-test-ecs-atlantis-" + attributes[0] + "-task"
147 | assert.Equal(t, expectedEcsTaskRoleName, ecsTaskRoleName)
148 |
149 | // Run `terraform output` to get the value of an output variable
150 | ecsTaskExecRoleName := terraform.Output(t, terraformOptions, "ecs_task_exec_role_name")
151 | // Verify we're getting back the outputs we expect
152 | expectedEcsTaskExecRoleName := "eg-test-ecs-atlantis-" + attributes[0] + "-exec"
153 | assert.Equal(t, expectedEcsTaskExecRoleName, ecsTaskExecRoleName)
154 |
155 | // Run `terraform output` to get the value of an output variable
156 | ecsServiceName := terraform.Output(t, terraformOptions, "ecs_service_name")
157 | // Verify we're getting back the outputs we expect
158 | expectedEcsServiceName := "eg-test-ecs-atlantis-" + attributes[0]
159 | assert.Equal(t, expectedEcsServiceName, ecsServiceName)
160 |
161 | // Run `terraform output` to get the value of an output variable
162 | ecsExecRolePolicyName := terraform.Output(t, terraformOptions, "ecs_exec_role_policy_name")
163 | // Verify we're getting back the outputs we expect
164 | expectedEcsExecRolePolicyName := "eg-test-ecs-atlantis-" + attributes[0] + "-exec"
165 | assert.Equal(t, expectedEcsExecRolePolicyName, ecsExecRolePolicyName)
166 |
167 | // Run `terraform output` to get the value of an output variable
168 | ecsCloudwatchAutoscalingScaleDownPolicyArn := terraform.Output(t, terraformOptions, "ecs_cloudwatch_autoscaling_scale_down_policy_arn")
169 | // Verify we're getting back the outputs we expect
170 | expectedEcsCloudwatchAutoscalingScaleDownPolicyArn := "eg-test-ecs-atlantis-" + attributes[0] + ":policyName/down"
171 | assert.Contains(t, ecsCloudwatchAutoscalingScaleDownPolicyArn, expectedEcsCloudwatchAutoscalingScaleDownPolicyArn)
172 |
173 | // Run `terraform output` to get the value of an output variable
174 | ecsCloudwatchAutoscalingScaleUpPolicyArn := terraform.Output(t, terraformOptions, "ecs_cloudwatch_autoscaling_scale_up_policy_arn")
175 | // Verify we're getting back the outputs we expect
176 | expectedEcsCloudwatchAutoscalingScaleUpPolicyArn := "eg-test-ecs-atlantis-" + attributes[0] + ":policyName/up"
177 | assert.Contains(t, ecsCloudwatchAutoscalingScaleUpPolicyArn, expectedEcsCloudwatchAutoscalingScaleUpPolicyArn)
178 |
179 | // Run `terraform output` to get the value of an output variable
180 | ecsAlarmsCpuUtilizationHighCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "ecs_alarms_cpu_utilization_high_cloudwatch_metric_alarm_id")
181 | // Verify we're getting back the outputs we expect
182 | expectedEcsAlarmsCpuUtilizationHighCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-cpu-utilization-high"
183 | assert.Equal(t, expectedEcsAlarmsCpuUtilizationHighCloudwatchMetricAlarmId, ecsAlarmsCpuUtilizationHighCloudwatchMetricAlarmId)
184 |
185 | // Run `terraform output` to get the value of an output variable
186 | ecsAlarmsCpuUtilizationLowCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "ecs_alarms_cpu_utilization_low_cloudwatch_metric_alarm_id")
187 | // Verify we're getting back the outputs we expect
188 | expectedEcsAlarmsCpuUtilizationLowCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-cpu-utilization-low"
189 | assert.Equal(t, expectedEcsAlarmsCpuUtilizationLowCloudwatchMetricAlarmId, ecsAlarmsCpuUtilizationLowCloudwatchMetricAlarmId)
190 |
191 | // Run `terraform output` to get the value of an output variable
192 | ecsAlarmsMemoryUtilizationHighCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "ecs_alarms_memory_utilization_high_cloudwatch_metric_alarm_id")
193 | // Verify we're getting back the outputs we expect
194 | expectedEcsAlarmsMemoryUtilizationHighCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-memory-utilization-high"
195 | assert.Equal(t, expectedEcsAlarmsMemoryUtilizationHighCloudwatchMetricAlarmId, ecsAlarmsMemoryUtilizationHighCloudwatchMetricAlarmId)
196 |
197 | // Run `terraform output` to get the value of an output variable
198 | ecsAlarmsMemoryUtilizationLowCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "ecs_alarms_memory_utilization_low_cloudwatch_metric_alarm_id")
199 | // Verify we're getting back the outputs we expect
200 | expectedEcsAlarmsMemoryUtilizationLowCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-memory-utilization-low"
201 | assert.Equal(t, expectedEcsAlarmsMemoryUtilizationLowCloudwatchMetricAlarmId, ecsAlarmsMemoryUtilizationLowCloudwatchMetricAlarmId)
202 |
203 | // Run `terraform output` to get the value of an output variable
204 | httpcodeElb5xxCountCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "httpcode_elb_5xx_count_cloudwatch_metric_alarm_id")
205 | // Verify we're getting back the outputs we expect
206 | expectedHttpcodeElb5xxCountCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-elb-5xx-count-high"
207 | assert.Equal(t, expectedHttpcodeElb5xxCountCloudwatchMetricAlarmId, httpcodeElb5xxCountCloudwatchMetricAlarmId)
208 |
209 | // Run `terraform output` to get the value of an output variable
210 | httpcodeTarget3xxCountCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "httpcode_target_3xx_count_cloudwatch_metric_alarm_id")
211 | // Verify we're getting back the outputs we expect
212 | expectedHttpcodeTarget3xxCountCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-3xx-count-high"
213 | assert.Equal(t, expectedHttpcodeTarget3xxCountCloudwatchMetricAlarmId, httpcodeTarget3xxCountCloudwatchMetricAlarmId)
214 |
215 | // Run `terraform output` to get the value of an output variable
216 | httpcodeTarget4xxCountCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "httpcode_target_4xx_count_cloudwatch_metric_alarm_id")
217 | // Verify we're getting back the outputs we expect
218 | expectedHttpcodeTarget4xxCountCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-4xx-count-high"
219 | assert.Equal(t, expectedHttpcodeTarget4xxCountCloudwatchMetricAlarmId, httpcodeTarget4xxCountCloudwatchMetricAlarmId)
220 |
221 | // Run `terraform output` to get the value of an output variable
222 | httpcodeTarget5xxCountCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "httpcode_target_5xx_count_cloudwatch_metric_alarm_id")
223 | // Verify we're getting back the outputs we expect
224 | expectedHttpcodeTarget5xxCountCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-5xx-count-high"
225 | assert.Equal(t, expectedHttpcodeTarget5xxCountCloudwatchMetricAlarmId, httpcodeTarget5xxCountCloudwatchMetricAlarmId)
226 |
227 | // Run `terraform output` to get the value of an output variable
228 | targetResponseTimeAverageCloudwatchMetricAlarmId := terraform.Output(t, terraformOptions, "target_response_time_average_cloudwatch_metric_alarm_id")
229 | // Verify we're getting back the outputs we
230 | expectedTargetResponseTimeAverageCloudwatchMetricAlarmId := "eg-test-ecs-atlantis-" + attributes[0] + "-target-response-high"
231 | assert.Equal(t, expectedTargetResponseTimeAverageCloudwatchMetricAlarmId, targetResponseTimeAverageCloudwatchMetricAlarmId)
232 |
233 | // Run `terraform output` to get the value of an output variable
234 | atlantisUrl := terraform.Output(t, terraformOptions, "atlantis_url")
235 | // Verify we're getting back the outputs we expect
236 | assert.Equal(t, "https://ecs-atlantis-test-" + attributes[0] +".testing.cloudposse.co", atlantisUrl)
237 |
238 | // Run `terraform output` to get the value of an output variable
239 | atlantisWebhookUrl := terraform.Output(t, terraformOptions, "atlantis_webhook_url")
240 | // Verify we're getting back the outputs we expect
241 | assert.Equal(t, "https://ecs-atlantis-test-" + attributes[0] +".testing.cloudposse.co/events", atlantisWebhookUrl)
242 | }
243 |
--------------------------------------------------------------------------------
/test/src/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/cloudposse/terraform-aws-ecs-atlantis
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gruntwork-io/terratest v0.31.4
7 | github.com/stretchr/testify v1.6.1
8 | )
9 |
--------------------------------------------------------------------------------
/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | type = string
3 | description = "AWS Region for S3 bucket"
4 | }
5 |
6 | variable "launch_type" {
7 | type = string
8 | description = "The ECS launch type (valid options: FARGATE or EC2)"
9 | default = "FARGATE"
10 | }
11 |
12 | variable "default_backend_image" {
13 | type = string
14 | default = "cloudposse/default-backend:0.1.2"
15 | description = "ECS default (bootstrap) image"
16 | }
17 |
18 | variable "github_oauth_token" {
19 | type = string
20 | description = "GitHub OAuth token. If not provided the token is looked up from SSM"
21 | default = ""
22 | }
23 |
24 | variable "github_webhooks_token" {
25 | type = string
26 | description = "GitHub OAuth Token with permissions to create webhooks. If not provided the token is looked up from SSM"
27 | default = ""
28 | }
29 |
30 | variable "github_oauth_token_ssm_name" {
31 | type = string
32 | description = "SSM param name to lookup `github_oauth_token` if not provided"
33 | default = ""
34 | }
35 |
36 | variable "github_webhooks_token_ssm_name" {
37 | type = string
38 | description = "SSM param name to lookup `github_webhooks_token` if not provided"
39 | default = ""
40 | }
41 |
42 | variable "codepipeline_s3_bucket_force_destroy" {
43 | type = bool
44 | description = "A boolean that indicates all objects should be deleted from the CodePipeline artifact store S3 bucket so that the bucket can be destroyed without error"
45 | default = false
46 | }
47 |
48 | variable "codepipeline_enabled" {
49 | type = bool
50 | description = "A boolean to enable/disable AWS Codepipeline and ECR"
51 | default = false
52 | }
53 |
54 | variable "build_timeout" {
55 | type = number
56 | default = 10
57 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed."
58 | }
59 |
60 | variable "branch" {
61 | type = string
62 | default = "master"
63 | description = "Atlantis branch of the GitHub repository, _e.g._ `master`"
64 | }
65 |
66 | variable "repo_name" {
67 | type = string
68 | description = "GitHub repository name of the atlantis to be built and deployed to ECS."
69 | }
70 |
71 | variable "repo_owner" {
72 | type = string
73 | description = "GitHub organization containing the Atlantis repository"
74 | }
75 |
76 | variable "atlantis_repo_config" {
77 | type = string
78 | description = "Path to atlantis server-side repo config file (https://www.runatlantis.io/docs/server-side-repo-config.html)"
79 | default = "atlantis-repo-config.yaml"
80 | }
81 |
82 | variable "atlantis_repo_whitelist" {
83 | type = list(string)
84 | description = "Whitelist of repositories Atlantis will accept webhooks from"
85 | default = []
86 | }
87 |
88 | variable "autoscaling_enabled" {
89 | type = bool
90 | description = "A boolean to enable/disable Autoscaling policy for ECS Service"
91 | default = false
92 | }
93 |
94 | variable "healthcheck_path" {
95 | type = string
96 | description = "Healthcheck path"
97 | default = "/healthz"
98 | }
99 |
100 | variable "chamber_format" {
101 | type = string
102 | default = "/%s/%s"
103 | description = "Format to store parameters in SSM, for consumption with chamber"
104 | }
105 |
106 | variable "chamber_service" {
107 | type = string
108 | default = "atlantis"
109 | description = "SSM parameter service name for use with chamber. This is used in chamber_format where /$chamber_service/$parameter would be the default."
110 | }
111 |
112 | variable "desired_count" {
113 | type = number
114 | description = "Atlantis desired number of tasks"
115 | default = 1
116 | }
117 |
118 | variable "short_name" {
119 | type = string
120 | description = "Alantis short DNS name (e.g. `atlantis`)"
121 | default = "atlantis"
122 | }
123 |
124 | variable "hostname" {
125 | type = string
126 | description = "Atlantis URL"
127 | default = ""
128 | }
129 |
130 | variable "atlantis_gh_user" {
131 | type = string
132 | description = "Atlantis GitHub user"
133 | }
134 |
135 | variable "atlantis_gh_team_whitelist" {
136 | type = string
137 | description = "Atlantis GitHub team whitelist"
138 | default = ""
139 | }
140 |
141 | variable "atlantis_gh_webhook_secret" {
142 | type = string
143 | description = "Atlantis GitHub webhook secret"
144 | default = ""
145 | }
146 |
147 | variable "atlantis_log_level" {
148 | type = string
149 | description = "Atlantis log level"
150 | default = "info"
151 | }
152 |
153 | variable "atlantis_port" {
154 | type = number
155 | description = "Atlantis container port"
156 | default = 4141
157 | }
158 |
159 | variable "atlantis_wake_word" {
160 | type = string
161 | description = "Wake world for atlantis"
162 | default = "atlantis"
163 | }
164 |
165 | variable "atlantis_webhook_format" {
166 | type = string
167 | default = "https://%s/events"
168 | description = "Template for the Atlantis webhook URL which is populated with the hostname"
169 | }
170 |
171 | variable "atlantis_url_format" {
172 | type = string
173 | default = "https://%s"
174 | description = "Template for the Atlantis URL which is populated with the hostname"
175 | }
176 |
177 | variable "autoscaling_min_capacity" {
178 | type = number
179 | description = "Atlantis minimum tasks to run"
180 | default = 1
181 | }
182 |
183 | variable "autoscaling_max_capacity" {
184 | type = number
185 | description = "Atlantis maximum tasks to run"
186 | default = 1
187 | }
188 |
189 | variable "container_repo_credentials" {
190 | type = map(string)
191 | default = null
192 | 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"
193 | }
194 |
195 | variable "container_cpu" {
196 | type = number
197 | description = "Atlantis CPUs per task"
198 | default = 256
199 | }
200 |
201 | variable "container_memory" {
202 | type = number
203 | description = "Atlantis memory per task"
204 | default = 512
205 | }
206 |
207 | variable "policy_arn" {
208 | type = string
209 | default = "arn:aws:iam::aws:policy/AdministratorAccess"
210 | description = "Permission to grant to atlantis server"
211 | }
212 |
213 | variable "kms_key_id" {
214 | type = string
215 | default = ""
216 | description = "KMS key ID used to encrypt SSM SecureString parameters"
217 | }
218 |
219 | variable "webhook_enabled" {
220 | type = bool
221 | description = "Set to false to prevent the module from creating any webhook resources"
222 | default = true
223 | }
224 |
225 | variable "webhook_secret_length" {
226 | type = number
227 | default = 32
228 | description = "GitHub webhook secret length"
229 | }
230 |
231 | variable "webhook_events" {
232 | type = list(string)
233 | description = "A list of events which should trigger the webhook."
234 |
235 | default = [
236 | "issue_comment",
237 | "pull_request",
238 | "pull_request_review",
239 | "pull_request_review_comment",
240 | "push",
241 | ]
242 | }
243 |
244 | variable "ssh_private_key_name" {
245 | type = string
246 | default = "atlantis_ssh_private_key"
247 | description = "Atlantis SSH private key name"
248 | }
249 |
250 | variable "ssh_public_key_name" {
251 | type = string
252 | default = "atlantis_ssh_public_key"
253 | description = "Atlantis SSH public key name"
254 | }
255 |
256 | variable "vpc_id" {
257 | type = string
258 | description = "VPC ID for the ECS Cluster"
259 | }
260 |
261 | variable "alb_arn_suffix" {
262 | type = string
263 | description = "The ARN suffix of the ALB"
264 | }
265 |
266 | variable "use_alb_security_group" {
267 | type = bool
268 | description = "A flag to enable/disable adding the ingress rule to the ALB security group"
269 | default = true
270 | }
271 |
272 | variable "alb_security_group" {
273 | type = string
274 | description = "Security group of the ALB"
275 | }
276 |
277 | variable "alb_target_group_alarms_alarm_actions" {
278 | type = list(string)
279 | description = "A list of ARNs (i.e. SNS Topic ARN) to execute when ALB Target Group alarms transition into an ALARM state from any other state."
280 | default = []
281 | }
282 |
283 | variable "alb_target_group_alarms_ok_actions" {
284 | type = list(string)
285 | description = "A list of ARNs (i.e. SNS Topic ARN) to execute when ALB Target Group alarms transition into an OK state from any other state."
286 | default = []
287 | }
288 |
289 | variable "alb_target_group_alarms_insufficient_data_actions" {
290 | type = list(string)
291 | description = "A list of ARNs (i.e. SNS Topic ARN) to execute when ALB Target Group alarms transition into an INSUFFICIENT_DATA state from any other state."
292 | default = []
293 | }
294 |
295 | variable "ecs_alarms_cpu_utilization_high_alarm_actions" {
296 | type = list(string)
297 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on CPU Utilization High Alarm action"
298 | default = []
299 | }
300 |
301 | variable "ecs_alarms_cpu_utilization_high_ok_actions" {
302 | type = list(string)
303 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on CPU Utilization High OK action"
304 | default = []
305 | }
306 |
307 | variable "ecs_alarms_cpu_utilization_low_alarm_actions" {
308 | type = list(string)
309 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on CPU Utilization Low Alarm action"
310 | default = []
311 | }
312 |
313 | variable "ecs_alarms_cpu_utilization_low_ok_actions" {
314 | type = list(string)
315 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on CPU Utilization Low OK action"
316 | default = []
317 | }
318 |
319 | variable "ecs_alarms_memory_utilization_high_alarm_actions" {
320 | type = list(string)
321 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on Memory Utilization High Alarm action"
322 | default = []
323 | }
324 |
325 | variable "ecs_alarms_memory_utilization_high_ok_actions" {
326 | type = list(string)
327 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on Memory Utilization High OK action"
328 | default = []
329 | }
330 |
331 | variable "ecs_alarms_memory_utilization_low_alarm_actions" {
332 | type = list(string)
333 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on Memory Utilization Low Alarm action"
334 | default = []
335 | }
336 |
337 | variable "ecs_alarms_memory_utilization_low_ok_actions" {
338 | type = list(string)
339 | description = "A list of ARNs (i.e. SNS Topic ARN) to notify on Memory Utilization Low OK action"
340 | default = []
341 | }
342 |
343 | variable "alb_dns_name" {
344 | type = string
345 | description = "DNS name of ALB"
346 | }
347 |
348 | variable "alb_zone_id" {
349 | type = string
350 | description = "The ID of the zone in which ALB is provisioned"
351 | }
352 |
353 | variable "ecs_cluster_name" {
354 | type = string
355 | description = "Name of the ECS cluster to deploy Atlantis"
356 | }
357 |
358 | variable "ecs_cluster_arn" {
359 | type = string
360 | description = "ARN of the ECS cluster to deploy Atlantis"
361 | }
362 |
363 | variable "security_group_ids" {
364 | type = list(string)
365 | default = []
366 | description = "Additional Security Group IDs to allow into ECS Service."
367 | }
368 |
369 | variable "private_subnet_ids" {
370 | type = list(string)
371 | default = []
372 | description = "The private subnet IDs"
373 | }
374 |
375 | variable "parent_zone_id" {
376 | type = string
377 | description = "The zone ID where the DNS record for the `short_name` will be written"
378 | default = ""
379 | }
380 |
381 | variable "overwrite_ssm_parameter" {
382 | type = bool
383 | default = true
384 | description = "Whether to overwrite an existing SSM parameter"
385 | }
386 |
387 | variable "alb_ingress_listener_unauthenticated_priority" {
388 | type = number
389 | default = 50
390 | description = "The priority for the rules without authentication, between 1 and 50000 (1 being highest priority). Must be different from `alb_ingress_listener_authenticated_priority` since a listener can't have multiple rules with the same priority"
391 | }
392 |
393 | variable "alb_ingress_listener_authenticated_priority" {
394 | type = number
395 | default = 100
396 | description = "The priority for the rules with authentication, between 1 and 50000 (1 being highest priority). Must be different from `alb_ingress_listener_unauthenticated_priority` since a listener can't have multiple rules with the same priority"
397 | }
398 |
399 | variable "alb_ingress_unauthenticated_hosts" {
400 | type = list(string)
401 | default = []
402 | description = "Unauthenticated hosts to match in Hosts header (a maximum of 1 can be defined)"
403 | }
404 |
405 | variable "alb_ingress_authenticated_hosts" {
406 | type = list(string)
407 | default = []
408 | description = "Authenticated hosts to match in Hosts header (a maximum of 1 can be defined)"
409 | }
410 |
411 | variable "alb_ingress_unauthenticated_paths" {
412 | type = list(string)
413 | default = ["/events"]
414 | description = "Unauthenticated path pattern to match (a maximum of 1 can be defined)"
415 | }
416 |
417 | variable "alb_ingress_authenticated_paths" {
418 | type = list(string)
419 | default = ["/*"]
420 | description = "Authenticated path pattern to match (a maximum of 1 can be defined)"
421 | }
422 |
423 | variable "alb_ingress_unauthenticated_listener_arns" {
424 | type = list(string)
425 | default = []
426 | description = "A list of unauthenticated ALB listener ARNs to attach ALB listener rules to"
427 | }
428 |
429 | variable "alb_ingress_unauthenticated_listener_arns_count" {
430 | type = number
431 | default = 0
432 | description = "The number of unauthenticated ARNs in `alb_ingress_unauthenticated_listener_arns`. This is necessary to work around a limitation in Terraform where counts cannot be computed"
433 | }
434 |
435 | variable "alb_ingress_authenticated_listener_arns" {
436 | type = list(string)
437 | default = []
438 | description = "A list of authenticated ALB listener ARNs to attach ALB listener rules to"
439 | }
440 |
441 | variable "alb_ingress_authenticated_listener_arns_count" {
442 | type = number
443 | default = 0
444 | description = "The number of authenticated ARNs in `alb_ingress_authenticated_listener_arns`. This is necessary to work around a limitation in Terraform where counts cannot be computed"
445 | }
446 |
447 | variable "authentication_type" {
448 | type = string
449 | default = ""
450 | description = "Authentication type. Supported values are `COGNITO` and `OIDC`"
451 | }
452 |
453 | variable "authentication_cognito_user_pool_arn" {
454 | type = string
455 | description = "Cognito User Pool ARN"
456 | default = ""
457 | }
458 |
459 | variable "authentication_cognito_user_pool_client_id" {
460 | type = string
461 | description = "Cognito User Pool Client ID"
462 | default = ""
463 | }
464 |
465 | variable "authentication_cognito_user_pool_domain" {
466 | type = string
467 | description = "Cognito User Pool Domain. The User Pool Domain should be set to the domain prefix (`xxx`) instead of full domain (https://xxx.auth.us-west-2.amazoncognito.com)"
468 | default = ""
469 | }
470 |
471 | variable "authentication_oidc_client_id" {
472 | type = string
473 | description = "OIDC Client ID"
474 | default = ""
475 | }
476 |
477 | variable "authentication_oidc_client_secret" {
478 | type = string
479 | description = "OIDC Client Secret"
480 | default = ""
481 | }
482 |
483 | variable "authentication_oidc_issuer" {
484 | type = string
485 | description = "OIDC Issuer"
486 | default = ""
487 | }
488 |
489 | variable "authentication_oidc_authorization_endpoint" {
490 | type = string
491 | description = "OIDC Authorization Endpoint"
492 | default = ""
493 | }
494 |
495 | variable "authentication_oidc_token_endpoint" {
496 | type = string
497 | description = "OIDC Token Endpoint"
498 | default = ""
499 | }
500 |
501 | variable "authentication_oidc_user_info_endpoint" {
502 | type = string
503 | description = "OIDC User Info Endpoint"
504 | default = ""
505 | }
506 |
507 | variable "authentication_cognito_user_pool_arn_ssm_name" {
508 | type = string
509 | description = "SSM param name to lookup `authentication_cognito_user_pool_arn` if not provided"
510 | default = ""
511 | }
512 |
513 | variable "authentication_cognito_user_pool_client_id_ssm_name" {
514 | type = string
515 | description = "SSM param name to lookup `authentication_cognito_user_pool_client_id` if not provided"
516 | default = ""
517 | }
518 |
519 | variable "authentication_cognito_user_pool_domain_ssm_name" {
520 | type = string
521 | description = "SSM param name to lookup `authentication_cognito_user_pool_domain` if not provided"
522 | default = ""
523 | }
524 |
525 | variable "authentication_oidc_client_id_ssm_name" {
526 | type = string
527 | description = "SSM param name to lookup `authentication_oidc_client_id` if not provided"
528 | default = ""
529 | }
530 |
531 | variable "authentication_oidc_client_secret_ssm_name" {
532 | type = string
533 | description = "SSM param name to lookup `authentication_oidc_client_secret` if not provided"
534 | default = ""
535 | }
536 |
537 | variable "alb_target_group_alarms_enabled" {
538 | type = bool
539 | description = "A boolean to enable/disable CloudWatch Alarms for ALB Target metrics"
540 | default = false
541 | }
542 |
543 | variable "ecs_alarms_enabled" {
544 | type = bool
545 | description = "A boolean to enable/disable CloudWatch Alarms for ECS Service metrics"
546 | default = false
547 | }
548 |
--------------------------------------------------------------------------------
/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 0.13.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 2.0"
8 | }
9 | random = {
10 | source = "hashicorp/random"
11 | version = ">= 2.0"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------