├── .circleci └── config.yml ├── .env.docker ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Rakefile ├── bin ├── .covalence │ └── launcher └── covalence ├── cluster ├── iam.tf ├── main.tf ├── outputs.tf ├── templates │ └── user_data.hcl └── variables.tf ├── consul ├── iam.tf ├── main.tf ├── outputs.tf ├── templates │ ├── agent.hcl │ ├── registrator.hcl │ └── server.hcl └── variables.tf ├── covalence.yaml ├── data ├── environments.yaml ├── globals.yaml └── stacks │ ├── common.yaml │ ├── consul-agent.yaml │ ├── defaults.yaml │ ├── networking.yaml │ ├── overrides.yaml │ ├── service-discovery.yaml │ └── service-registration.yaml └── examples ├── basic ├── main.tf └── variables.tf ├── complete ├── main.tf ├── user_data.hcl └── variables.tf └── prereqs ├── main.tf ├── outputs.tf └── variables.tf /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | working_directory: ~/repo 6 | 7 | docker: 8 | - image: unifio/ci:3.0.622-ruby-2.5.3 9 | 10 | environment: 11 | AWS_REGION: 'us-east-2' 12 | CI_REPORTS: 'reports/infrastructure' 13 | TF_PLUGIN_CACHE_DIR: "/root/.terraform.d/plugin-cache" 14 | 15 | steps: 16 | - checkout 17 | 18 | - run: 19 | name: Verify 20 | command: bundle exec rake ci 21 | 22 | - run: 23 | name: Test defaults 24 | command: bundle exec rake basic:defaults:apply 25 | 26 | - run: 27 | name: Test overrides 28 | command: bundle exec rake complete:overrides:apply 29 | 30 | - run: 31 | name: Test service discovery 32 | command: | 33 | bundle exec rake complete:service-discovery:apply || true 34 | # Two applies required due to LC change 35 | bundle exec rake complete:service-discovery:apply 36 | bundle exec rake complete:service-registration:apply 37 | bundle exec rake complete:consul-agent:apply 38 | 39 | - run: 40 | name: Clean up 41 | when: always 42 | command: | 43 | bundle exec rake basic:destroy 44 | bundle exec rake complete:consul-agent:destroy 45 | 46 | - store_test_results: 47 | path: reports 48 | -------------------------------------------------------------------------------- /.env.docker: -------------------------------------------------------------------------------- 1 | AWS_REGION=us-east-2 2 | COVALENCE_LOG=info 3 | COVALENCE_TEST_ENVS=basic,complete 4 | CHECKPOINT_DISABLE=1 5 | TF_PLUGIN_CACHE_DIR=${HOME}/.terraform.d/plugin-cache 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tfstate* 2 | .terraform/ 3 | .env 4 | spec/reports/** 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.renderWhitespace": "all", 3 | "files.associations": { 4 | "*.tpl": "hcl", 5 | "*.tf": "terraform" 6 | }, 7 | "files.insertFinalNewline": true, 8 | "files.trimTrailingWhitespace": true, 9 | "terraform.formatOnSave": true 10 | } 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | #### IMPROVEMENTS / NEW FEATURES: 4 | * Add support for application auto scaling 5 | 6 | ## 0.3.3 (March 10, 2019) 7 | 8 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 9 | ##### [terraform-provider-aws v2.0](https://github.com/terraform-providers/terraform-provider-aws/blob/master/CHANGELOG.md) Updates 10 | * The [terraform-provider-aws](https://github.com/terraform-providers/terraform-provider-aws/issues/7697) has deprecated the use of the `current` filter in regions. The `current` filter was removed for [issue#7](https://github.com/unifio/terraform-aws-ecs/issues/8). 11 | * The [terraform-provider-aws](https://github.com/terraform-providers/terraform-provider-aws/issues/5576) now requires an `owners` argument. This was added in place of the owner-alias for [issue#8](https://github.com/unifio/terraform-aws-ecs/issues/8) 12 | * Updated [terraform-aws-asg](https://github.com/unifio/terraform-aws-asg/releases/tag/v0.3.7) to v0.3.7 13 | 14 | ## 0.3.2 (March 9, 2018) 15 | 16 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 17 | * The `consul_gossip_cidrs` parameter has been removed. 18 | 19 | #### IMPROVEMENTS / NEW FEATURES: 20 | * Module now properly supports the service discovery use case of a Consul server count of 0. 21 | * Consul communications are now bound by security group and not CIDR 22 | 23 | ## 0.3.1 (June 29, 2017) 24 | 25 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 26 | * Cluster module no longer accepts a pre-provisioned ALB when service discovery is enabled. 27 | * Consul ALB listener rule no longer provisioned by this module. 28 | 29 | #### IMPROVEMENTS / NEW FEATURES: 30 | * Upgraded `terraform-aws-asg` module to v0.3.1. 31 | * Cluster module no longer requires a pre-provisioned ALB when service discovery is enabled. The module now returns a security group ID and target group ARN for association with an ALB at a later time if desired. 32 | * The cluster module will now accept a list of additional target group ARNs. 33 | * Consul ECS service role updated to AWS best practices for IAM policy. 34 | 35 | ## 0.3.0 (April 26, 2017) 36 | 37 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 38 | * Versions of Terraform prior to v0.9.0 no longer supported. 39 | * The following input variables have been changed: 40 | * `cluster_name (string, required)` -> `cluster_label (string, required)` 41 | 42 | #### IMPROVEMENTS / NEW FEATURES: 43 | * Support for dpeloyment of Consul service discovery & configuration. 44 | * Support for deployment of Registrator for service registration with Consul. 45 | 46 | ## 0.2.0 (April 9, 2017) 47 | 48 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 49 | * Versions of Terraform prior to v0.8.0 no longer supported. 50 | * The `common` module has been removed. Similar functionality has been moved into the `cluster` module. Existing resources will be recreated in an update. 51 | * The following input variables have been changed: 52 | * `agent_role_name (string, required)` -> (Removed. Use the `agent_role_id` output to add additional policies.) 53 | * `ami (string, required)` -> `ami_override (string, optional)` 54 | * `domain (string, optional)` -> Removed 55 | * `ecs_config (string, optional)` -> (Removed. Use `user_data_override` to specify custom configuraton.) 56 | * `hc_grace_period (string, default: 420)` -> `hc_grace_period (string, optional)` 57 | * `instance_type (string, default: t2.small)` -> `instance_type (string, required)` 58 | * `key_name (string, required)` -> `key_name (string, optional)` 59 | * `max_size (string, default: 3)` -> `max_size (string, required)` 60 | * `min_size (string, default: 3)` -> `min_size (string, required)` 61 | * `subnets (string, required)` -> `subnets (list, required)` 62 | 63 | #### IMPROVEMENTS / NEW FEATURES: 64 | * Module now provides a default ECS configuration to the cluster hosts in the abscense of user supplied `user_data`. 65 | * The following parameters are now configurable: 66 | * `associate_public_ip_address` 67 | * `default_cooldown` 68 | * `desired_capacity` 69 | * `ebs_optimized` 70 | * `ebs_vol_del_on_term` 71 | * `ebs_vol_device_name` 72 | * `ebs_vol_encrypted` 73 | * `ebs_vol_iops` 74 | * `ebs_vol_size` 75 | * `ebs_vol_snapshot_id` 76 | * `ebs_vol_type` 77 | * `enable_monitoring` 78 | * `enabled_metrics` 79 | * `force_delete` 80 | * `iam_path` 81 | * `instance_based_naming_enabled` 82 | * `instance_name_prefix` 83 | * `instance_tags` 84 | * `logs_bucket_enabled` 85 | * `logs_bucket_name` 86 | * `placenment_group` 87 | * `placement_tenancy` 88 | * `protect_from_scale_in` 89 | * `root_vol_del_on_temr` 90 | * `root_vol_iops` 91 | * `root_vol_size` 92 | * `root_vol_type` 93 | * `security_groups` 94 | * `spot_price` 95 | * `suspended_processes` 96 | * `termination_policies` 97 | * `user_data_override` 98 | * `wait_for_capacity_timeout` 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform AWS ECS Stack # 2 | [![Circle CI](https://circleci.com/gh/unifio/terraform-aws-ecs/tree/master.svg?style=svg)](https://circleci.com/gh/unifio/terraform-aws-ecs/tree/master) 3 | 4 | Terraform module for the deployment of an AWS Elastic Container Service (ECS) cluster. 5 | 6 | ## Requirements ## 7 | 8 | - Terraform 0.11.0 or newer 9 | - AWS provider 10 | 11 | ## Cluster module ## 12 | 13 | The cluster module provisions an ECS cluster and auto scaling group of agent instances. 14 | 15 | ### Input Variables ### 16 | 17 | #### Resource tags 18 | Name | Type | Required | Description 19 | --- | --- | --- | --- 20 | `cluster_label` | string | yes | Short form identifier for this cluster. 21 | `stack_item_fullname` | string | yes | Long form descriptive name for this stack item. This value is used to create the 'application' resource tag for resources created by this stack item. 22 | `stack_item_label` | string | yes | Short form identifier for this stack. This value is used to create the 'Name' resource tag for resources created by this stack item, and also serves as a unique key for re-use. 23 | 24 | #### VPC parameters 25 | Name | Type | Required | Description 26 | --- | --- | --- | --- 27 | `subnets` | list | yes | A list of subnet IDs to launch resources in. 28 | `vpc_id` | string | yes | ID of the target VPC. 29 | 30 | #### Cluster launch configuration parameters 31 | Name | Type | Required | Description 32 | --- | --- | --- | --- 33 | `ami_override` | string | | Custom Amazon Machine Image (AMI) to associate with the launch configuration. 34 | `associate_public_ip_address` | string | | Flag for associating public IP addresses with instances managed by the auto scaling group. 35 | `ebs_optimized` | string | | Flag to enable EBS optimization. 36 | `ebs_vol_del_on_term` | string | Default: `true` | Whether the volume should be destroyed on instance termination. 37 | `ebs_vol_device_name` | string | | The name of the device to mount. 38 | `ebs_vol_encrypted` | string | | Whether the volume should be encrypted or not. Do not use this option if you are using `ebs_vol_snapshot_id` as the encrypted flag will be determined by the snapshot. 39 | `ebs_vol_iops` | string | Default: `2000` | The amount of provisioned IOPS. Only utilized with `ebs_vol_type` of `io1`. 40 | `ebs_vol_size` | string | | The size of the volume in gigabytes. 41 | `ebs_vol_snapshot_id` | string | | The Snapshot ID to mount. 42 | `ebs_vol_type` | string | Default: `gp2` | The type of volume. Valid values are `standard`, `gp2` and `io1`. 43 | `enable_monitoring` | string | | Flag to enable detailed monitoring. 44 | `iam_path` | string | Default: `/` | The path to the IAM resource. 45 | `instance_based_naming_enabled` | string | | Flag to enable instance-id based name tagging. Requires the AWS CLI to be installed on the instance. Currently only supports Linux based systems. 46 | `instance_name_prefix` | string | | String to prepend instance-id based name tags with. 47 | `instance_tags` | map | | Map of tags to add to instances. Requires the AWS CLI to be installed on the instance. Currently only supports Linux based systems. 48 | `instance_type` | string | yes | The EC2 instance type to associate with the launch configuration. 49 | `key_name` | string | | The SSH key pair to associate with the launch configuration. 50 | `logs_bucket_enabled` | string | Default: `false` | Flag for enabling access to the logs bucket from the instances. 51 | `logs_bucket_name` | string | | Name of the S3 bucket for logging. 52 | `placement_tenancy` | string | Default: `default` | The tenancy of the instance. Valid values are `default` or `dedicated`. 53 | `root_vol_del_on_term` | string | Default: `true` | Whether the volume should be destroyed on instance termination. 54 | `root_vol_iops` | string | Default: `2000` | The amount of provisioned IOPS. Only utilized with `root_vol_type` of `io1`. 55 | `root_vol_size` | string | | The size of the volume in gigabytes. 56 | `root_vol_type` | string | Default: `gp2` | The type of volume. Valid values are `standard`, `gp2` and `io1`. 57 | `security_groups` | list | Default: [] | A list of security group IDs to associate with the instances. 58 | `spot_price` | string | | The price to use for reserving spot instances. 59 | `user_data_override` | string | | Custom instance initialization data to associate with the launch configuration. 60 | 61 | #### Cluster auto scaling group parameters 62 | Name | Type | Required | Description 63 | --- | --- | --- | --- 64 | `default_cooldown` | string | | The amount of time, in seconds, after a scaling activity completes before another scaling activity can start. 65 | `desired_capacity` | string | | The number of Amazon EC2 instances that should be running in the group. 66 | `enabled_metrics` | string | Default: [] | A list of metrics to collect. The allowed values are `GroupMinSize`, `GroupMaxSize`, `GroupDesiredCapacity`, `GroupInServiceInstances`, `GroupPendingInstances`, `GroupStandbyInstances`, `GroupTerminatingInstances`, `GroupTotalInstances`. 67 | `force_delete` | string | Default: `false` | Flag to allow deletion of the auto scaling group without waiting for all instances in the pool to terminate. 68 | `hc_check_type` | string | | Type of health check performed by the auto scaling group. Valid values are `ELB` or `EC2`. 69 | `hc_grace_period` | string | | Time allowed after an instance comes into service before checking health. 70 | `max_size` | string | yes | The maximum number of instances allowed by the auto scaling group. 71 | `min_size` | string | yes | Minimum number of instance to be maintained by the auto scaling group. 72 | `placement_group` | string | | The name of the placement group into which you'll launch your instances, if any. 73 | `protect_from_scale_in` | string | | Allows setting instance protection. The autoscaling group will not select instances with this setting for terminination during scale in events. 74 | `suspended_processes` | list | Default: [] | A list of processes to suspend for the AutoScaling Group. The allowed values are `Launch`, `Terminate`, `HealthCheck`, `ReplaceUnhealthy`, `AZRebalance`, `AlarmNotification`, `ScheduledActions`, `AddToLoadBalancer`. Note that if you suspend either the `Launch` or `Terminate` process types, it can prevent your autoscaling group from functioning properly. 75 | `target_group_arns` | list | Default: [] | A list of 'aws_alb_target_group' ARNs, for use with Application Load Balancing. 76 | `termination_policies` | list | Default: [] | A list of policies to decide how the instances in the auto scale group should be terminated. The allowed values are `OldestInstance`, `NewestInstance`, `OldestLaunchConfiguration`, `ClosestToNextInstanceHour`, `Default`. 77 | `wait_for_capacity_timeout` | string | | A maximum duration that Terraform should wait for ASG managed instances to become healthy before timing out. 78 | 79 | #### Service discovery & configuration parameters 80 | Name | Type | Required | Description 81 | --- | --- | --- | --- 82 | `agent_config_override` | string | | Consul agent ECS task configuration JSON. 83 | `agent_task_arn_override` | string | | Consul agent ECS task ARN. 84 | `consul_dc` | string | Default: `dc1` | Consul datacenter of the specified cluster. 85 | `registrator_config_override` | string | | Registrator ECS task configuration JSON. 86 | `registrator_task_arn_override` | string | | Registrator ECS task ARN. 87 | `server_config_override` | string | | Consul server ECS task configuration JSON. 88 | `server_task_arn_override` | string | | Consul server ECS task ARN. 89 | `server_desired_count` | string | Default: `3` | The number of Consul server containers to run. 90 | `service_discovery_enabled` | string | Default: `false` | Flag for the deployment of Consul service discovery and configuration. 91 | `service_registration_enabled` | string | Default: `false` | Flag for the deployment of Registrator service registration. 92 | 93 | ### Usage ### 94 | 95 | ```js 96 | module "cluster" { 97 | source = "github.com/unifio/terraform-aws-ecs?ref=master//cluster" 98 | 99 | # Resource tags 100 | cluster_name = "xmpl-prod" 101 | stack_item_fullname = "Example Cluster" 102 | stack_item_label = "xmpl" 103 | 104 | # VPC parameters 105 | subnets = ["subnet-aaaaaaaa","subnet-bbbbbbbb","subnet-cccccccc"] 106 | vpc_id = "vpc-xxxxxxxx" 107 | 108 | # LC parameters 109 | iam_path = "/tf_managed/" 110 | instance_based_naming_enabled = "true" 111 | instance_type = "t2.small" 112 | 113 | # ASG parameters 114 | max_size = "3" 115 | min_size = "3" 116 | 117 | # Service discovery parameters 118 | service_discovery_enabled = true 119 | service_registration_enabled = true 120 | } 121 | ``` 122 | 123 | ### Outputs ### 124 | 125 | Name | Type | Description 126 | --- | --- | --- 127 | `agent_role_id` | string | ID of the ECS agent IAM role. 128 | `cluster_id` | string | ID of the ECS cluster. 129 | `cluster_name` | string | Name of the ECS cluster. 130 | `consul_sg_id` | string | ID of the security group associated with the agent instances for enabling Consul HTTP communication. 131 | `consul_target_group_arn` | string | ARN of the Consul server target group. 132 | `sg_id` | string | ID of the security group associated with the agent instances. 133 | 134 | ## Examples ## 135 | 136 | See the [examples](examples) directory for a complete set of example source files. 137 | 138 | ## License ## 139 | 140 | MPL 2. See [LICENSE](./LICENSE) for full details. 141 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'covalence/environment_tasks' 3 | require 'covalence/spec_tasks' 4 | -------------------------------------------------------------------------------- /bin/.covalence/launcher: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Run from the source tree root 3 | cd `dirname $0` 4 | cd ../.. 5 | ### Environment Variables 6 | # Variables are used for determining CI settings 7 | # Variable precedence is as follows with the last taking 8 | # the highest precedence 9 | # 10 | # 1. Default values 11 | # 2. Exported environment variables 12 | # 3. .env.covalence loaded values 13 | # 4. bin/covalence argument switches 14 | # 15 | # source local .env.covalence file if present 16 | LOCAL_ENVFILE=${LOCAL_ENVFILE:-".env.covalence"} 17 | 18 | # If set to true will no use old docker-wrapper behavior 19 | # omitting the `rake` command 20 | DOCKER_WRAPPER=${DOCKER_WRAPPER:-} 21 | # The docker environment variable file passed to the container 22 | # Can contain multiple envfiles separated by : env1:env2:env3 23 | LOAD_ENVFILE=${LOAD_ENVFILE:-".env.docker:.env.secrets"} 24 | # AWS Credentials path to mount (defaults to data/secure/.aws) 25 | AWS_CREDENTIAL_PATH=${AWS_CREDENTIAL_PATH:-"$HOME/.aws"} 26 | # The Container home directory 27 | DOCKER_HOMEDIR=${DOCKER_HOMEDIR:-"/root"} 28 | # The docker DNS can be updated to AWS DNS 29 | DOCKER_DNS=${DOCKER_DNS:-} 30 | # If you want docker to run as specific user 31 | CONTAINER_USER_ID=${CONTAINER_USER_ID:-} 32 | # Alternative Covalence Rakefile can be specified 33 | # will add `-f Rakefile`` 34 | COVALENCE_RAKEFILE=${COVALENCE_RAKEFILE:-} 35 | # Alternative Covalence configuration can be specified 36 | COVALENCE_CONFIG=${COVALENCE_CONFIG:-"covalence.yaml"} 37 | # Environments to be included in CI 38 | COVALENCE_TEST_ENVS=${COVALENCE_TEST_ENVS:-} 39 | # The Container image to use for the ci defaults to unifio/ci latest 40 | DOCKER_IMAGE_NAME=${DOCKER_IMAGE_NAME:-"unifio/ci"} 41 | # Enable debugging of script 42 | S_DEBUG=${S_DEBUG:-} 43 | # Dump verbose information about commands without executing them. 44 | DUMP_ENV=${DUMP_ENV:-} 45 | # Causes covalence rake -T 46 | LIST_RAKE_TASKS=${LIST_RAKE_TASKS:-} 47 | # Adds volume to docker container HOST_MNT:CONTAINER_MNT 48 | ADD_VOLUMES=${ADD_VOLUMES:-} 49 | # Can be used to change docker run behavior ie -it vs --rm 50 | DOCKER_RUN_TYPE=${DOCKER_RUN_TYPE:-"--rm"} 51 | # TEST_HOST_LOCAL is used for specifying a domain to overload 52 | # and point to the Docker host IP in the container /etc/host file 53 | # --add-host CONSUL_TEST_IP will be set automatically 54 | TEST_HOST_LOCAL=${TEST_HOST_LOCAL:-} 55 | CONSUL_TEST_IP=${CONSUL_TEST_IP:-} 56 | # Allows specifying --entrypoint= command 57 | ENTRYPOINT=${ENTRYPOINT:-} 58 | # Interactive Shell enabled 59 | INTSHELL=${INTSHELL:-} 60 | # Sets the Docker workspace to mount and set as working directory -w 61 | DOCKER_WORKSPACE=${DOCKER_WORKSPACE:-"/workspace"} 62 | # The project root directory to mount in docker workspace 63 | SRC_ROOT=${SRC_ROOT:-"$(pwd)"} 64 | # Atlas and AWS tokens if needed but defaults to .aws 65 | # For possible future use currently should be set in .env.docker 66 | #ATLAS_TOKEN=${AWS_SECRET_ACCESS_KEY:-} 67 | #AWS_ACCESS_KEY_ID=${AWS_SECRET_ACCESS_KEY:-} 68 | #AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-} 69 | 70 | # Create initial Docker Base Command 71 | DOCKER_BASE_COMMANDS[0]="docker run" 72 | ARGS=() 73 | # Check for debug statements 74 | if [[ $S_DEBUG ]]; then 75 | set -x 76 | fi 77 | 78 | # Checks if ARGS already contains the given value 79 | has_arg() { 80 | local element 81 | for element in "${@:2}"; do 82 | [ "${element}" == "${1}" ] && return 0 83 | done 84 | return 1 85 | } 86 | # Adds the given argument if not specified 87 | add_arg() { 88 | local arg="${1}" 89 | [ $# -ge 1 ] && local val="${2}" 90 | if ! has_arg "${arg}" "${DOCKER_BASE_COMMANDS[@]}"; then 91 | ARGS+=("${arg}") 92 | [ $# -ge 1 ] && ARGS+=("${val}") 93 | fi 94 | } 95 | # Adds the given argument duplicates ok. 96 | add_arg_simple() { 97 | local arg="${1}" 98 | [ $# -ge 1 ] && local val="${2}" 99 | ARGS+=("${arg}") 100 | [ $# -ge 1 ] && ARGS+=("${val}") 101 | } 102 | # get the docker host ip address. and add it to container /etc/host 103 | # for TEST_HOST_LOCAL URL provided. 104 | get_docker_host(){ 105 | if [[ $TEST_HOST_LOCAL ]]; then 106 | GET_DOCKER_HOST_IP=$(docker inspect --format '{{ .NetworkSettings.Gateway }}' $(docker ps -q | grep -m 1 "") 2>/dev/null) 107 | if [[ ${GET_DOCKER_HOST_IP} ]];then 108 | CONSUL_TEST_IP=${GET_DOCKER_HOST_IP} 109 | add_arg_simple "--add-host" "${TEST_HOST_LOCAL}:${CONSUL_TEST_IP}" 110 | fi 111 | fi 112 | } 113 | # add a volume host:docker mount. 114 | add_host_volume(){ 115 | local host_vol="${1%:*}" 116 | local dkr_vol="${1##*:}" 117 | add_arg "-v" "${host_vol}:${dkr_vol}" 118 | } 119 | 120 | # add envfiles for docker if they exist in working directory 121 | add_docker_envfiles(){ 122 | local envfiles="${1}" 123 | IFS=':' read -r -a arrenvs <<< "$envfiles" 124 | for i in "${arrenvs[@]}" 125 | do 126 | if [[ -r "${i}" ]];then 127 | add_arg "--env-file" "$(pwd)/${i}" 128 | fi 129 | done 130 | } 131 | usage () { 132 | echo "" 133 | echo "Usage : $0 [OPTIONS] [COMMANDS|task]" 134 | echo "Options:" 135 | echo " -l List available rake tasks " 136 | echo " -e FILE:FILE Envfiles for docker : separated " 137 | echo " -s FILE Local env file to source " 138 | echo " -d DNS Docker DNS " 139 | echo " -u USER Run Docker as user " 140 | echo " -O Use Wrapper without Covalence " 141 | echo " -T URL URL for Consul overload " 142 | echo " -R Leave intermediary containers " 143 | echo " -v VOL:MNT Add a volume mount to container " 144 | echo " -c AWS_DIR AWS credentials path " 145 | echo " -w DIR Host workspace to mount " 146 | echo " -E ENTRYPOINT Override entrypoint command " 147 | echo " -i DKR_IMG_NAME Docker container Image name " 148 | echo " -h View help. " 149 | echo " -r RAKEFILE Specify separate rakefile " 150 | echo " -D Turn on debug " 151 | echo " -H Environment dump " 152 | } 153 | # require at lest a task or -l to run 154 | if [ $# -lt 1 ]; then 155 | usage 156 | exit 1 157 | fi 158 | 159 | # Load local env file if provided/available 160 | # That way explicit options will overwrite 161 | # any env vars sourced in .env.covalence 162 | if [[ -r "${LOCAL_ENVFILE}" ]]; then 163 | . ./"${LOCAL_ENVFILE}" 164 | fi 165 | 166 | # Parse arguments and populate ENV vars respectively 167 | # See Environment Variable section or .env.covalence for 168 | # option details. 169 | while getopts ":le:s:d:OIT:Rv:c:w:E:i:hr:DH" opt; do 170 | case $opt in 171 | l) 172 | LIST_RAKE_TASKS=1 173 | ;; 174 | e) 175 | LOAD_ENVFILE="$OPTARG" 176 | ;; 177 | s) 178 | LOCAL_ENVFILE="$OPTARG" 179 | ;; 180 | d) 181 | DOCKER_DNS="$OPTARG" 182 | ;; 183 | u) 184 | CONTAINER_USER_ID="$OPTARG" 185 | ;; 186 | O) 187 | DOCKER_WRAPPER=1 188 | ;; 189 | T) 190 | TEST_HOST_LOCAL="$OPTARG" 191 | ;; 192 | R) 193 | DOCKER_RUN_TYPE="--it" 194 | ;; 195 | v) 196 | ADD_VOLUMES="$OPTARG" 197 | ;; 198 | c) 199 | AWS_CREDENTIAL_PATH="$OPTARG" 200 | ;; 201 | w) 202 | DOCKER_WORKSPACE="$OPTARG" 203 | ;; 204 | E) 205 | ENTRYPOINT="$OPTARG" 206 | ;; 207 | i) 208 | DOCKER_IMAGE_NAME="$OPTARG" 209 | ;; 210 | h) 211 | usage 212 | exit 0 213 | ;; 214 | D) 215 | S_DEBUG=1 216 | ;; 217 | H) 218 | DUMP_ENV=1 219 | ;; 220 | I) 221 | INTSHELL=1 222 | ;; 223 | r) 224 | COVALENCE_RAKEFILE="$OPTARG" 225 | ;; 226 | \?) 227 | set +x 228 | echo "Invalid option: -$OPTARG" >&2 229 | usage 230 | exit 1 231 | ;; 232 | :) 233 | set +x 234 | echo "Option -$OPTARG requires an argument." >&2 235 | usage 236 | exit 1 237 | ;; 238 | esac 239 | done 240 | 241 | # Get rid of processed options from Array 242 | shift "$((OPTIND-1))" 243 | USER_ARGS=("${@}") 244 | 245 | if [[ "${COVALENCE_CONFIG}" ]]; then 246 | add_arg_simple "-e" "COVALENCE_CONFIG=${COVALENCE_CONFIG}" 247 | fi 248 | 249 | if [[ "${COVALENCE_TEST_ENVS}" ]]; then 250 | add_arg_simple "-e" "COVALENCE_TEST_ENVS=${COVALENCE_TEST_ENVS}" 251 | fi 252 | 253 | # Add the --rm or --it argument to the docker command array. 254 | if [[ "${DOCKER_RUN_TYPE}" ]]; then 255 | DOCKER_BASE_COMMANDS[3]="${DOCKER_RUN_TYPE}" 256 | fi 257 | 258 | if [[ "${DOCKER_DNS}" ]]; then 259 | add_arg "--dns" "${DOCKER_DNS}" 260 | fi 261 | 262 | if [[ "${CONTAINER_USER_ID}" ]]; then 263 | DOCKER_HOMEDIR="" 264 | add_arg_simple "-e" "AWS_CONFIG_FILE=${DOCKER_HOMEDIR}/.aws/config" 265 | add_arg_simple "-e" "AWS_SHARED_CREDENTIALS_FILE=${DOCKER_HOMEDIR}/.aws/credentials" 266 | add_arg_simple "-e" "USER=user" 267 | add_arg_simple "-e" "LOCAL_USER_ID=${CONTAINER_USER_ID}" 268 | else 269 | add_arg_simple "-e" "USER=root" 270 | fi 271 | 272 | get_docker_host "$TEST_HOST_LOCAL" 273 | 274 | if [[ "$ADD_VOLUMES" ]];then 275 | add_host_volume "${ADD_VOLUMES}" 276 | fi 277 | 278 | if [[ -d "$AWS_CREDENTIAL_PATH" ]];then 279 | add_arg_simple "-v" "${AWS_CREDENTIAL_PATH}:${DOCKER_HOMEDIR}/.aws" 280 | fi 281 | 282 | if [[ -d "${SRC_ROOT}" ]];then 283 | add_arg_simple "-v" "${SRC_ROOT}:${DOCKER_WORKSPACE}" 284 | add_arg "-w" "${DOCKER_WORKSPACE}" 285 | fi 286 | 287 | if [[ "${LOAD_ENVFILE}" ]]; then 288 | add_docker_envfiles "${LOAD_ENVFILE}" 289 | fi 290 | 291 | if [[ "${ENTRYPOINT}" ]]; then 292 | ARGS+=("--entrypoint=${ENTRYPOINT}") 293 | fi 294 | if [[ $INTSHELL && ! $ENTRYPOINT ]]; then 295 | ARGS+=("--entrypoint=/bin/sh") 296 | fi 297 | # All options should be completed 298 | # Only image and task remain. 299 | 300 | if [[ $DOCKER_IMAGE_NAME ]];then 301 | if [[ $INTSHELL && $DOCKER_WRAPPER ]]; then 302 | ARGS+=("-it") 303 | fi 304 | ARGS+=("$DOCKER_IMAGE_NAME") 305 | fi 306 | 307 | #Check whether docker wrapper or covalence 308 | if [[ ! $DOCKER_WRAPPER ]]; then 309 | ARGS+=("bundle exec rake") 310 | fi 311 | 312 | if [[ -r "${COVALENCE_RAKEFILE}" && ! $DOCKER_WRAPPER ]];then 313 | add_arg "-f" "${COVALENCE_RAKEFILE}" 314 | fi 315 | 316 | if [[ $LIST_RAKE_TASKS && ! $DOCKER_WRAPPER ]];then 317 | ARGS+=("-T") 318 | fi 319 | # Merged Commands for execution 320 | DOCKER_BASE_COMMANDS=(${DOCKER_BASE_COMMANDS[@]} ${ARGS[@]} ${USER_ARGS[@]}) 321 | 322 | if [[ $DUMP_ENV ]]; then 323 | echo "DOCKER_BASE_COMMANDS that would have been executed without -H" 324 | echo "${DOCKER_BASE_COMMANDS[@]}" 325 | # echo "ARGS array" 326 | # echo "${ARGS[@]}" 327 | # echo "USER_ARGS array" 328 | # echo "${USER_ARGS[@]}" 329 | # echo "" 330 | else 331 | # Execute the commands 332 | # If we are listing, remove the rake as user won't pass that in. 333 | if [[ $LIST_RAKE_TASKS && ! $DOCKER_WRAPPER ]];then 334 | "${DOCKER_BASE_COMMANDS[@]}" | sed -e "s/^rake //" 335 | else 336 | "${DOCKER_BASE_COMMANDS[@]}" 337 | fi 338 | fi 339 | -------------------------------------------------------------------------------- /bin/covalence: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | COVALENCE_SCRIPT="https://s3.amazonaws.com/unifio-covalence/covalence?versionId=k_MRX2uIWItAsCR1YFrJWZOaDIB9FFAR" 4 | 5 | cd `dirname $0` 6 | 7 | if [[ ! -e ./.covalence/launcher ]]; then 8 | mkdir -p .covalence 9 | curl -o .covalence/launcher -s $COVALENCE_SCRIPT 10 | chmod 0755 .covalence/launcher 11 | fi 12 | 13 | bash .covalence/launcher "$@" 14 | -------------------------------------------------------------------------------- /cluster/iam.tf: -------------------------------------------------------------------------------- 1 | # Elastic Container Service (ECS) cluster 2 | 3 | ## Creates IAM role for agent instances 4 | data "aws_iam_policy_document" "agent_policy" { 5 | statement { 6 | actions = ["sts:AssumeRole"] 7 | effect = "Allow" 8 | 9 | principals { 10 | type = "Service" 11 | identifiers = ["ec2.amazonaws.com"] 12 | } 13 | } 14 | } 15 | 16 | resource "aws_iam_role" "agent_role" { 17 | assume_role_policy = "${data.aws_iam_policy_document.agent_policy.json}" 18 | name = "ecs-agent-${var.cluster_label}-${var.stack_item_label}-${data.aws_region.current.name}" 19 | path = "${var.iam_path}" 20 | } 21 | 22 | resource "aws_iam_instance_profile" "agent_profile" { 23 | name = "ecs-agent-${var.cluster_label}-${var.stack_item_label}-${data.aws_region.current.name}" 24 | path = "${var.iam_path}" 25 | role = "${aws_iam_role.agent_role.name}" 26 | } 27 | 28 | ### Creates monitoring policy 29 | data "aws_iam_policy_document" "monitoring_policy" { 30 | statement { 31 | actions = [ 32 | "cloudwatch:*", 33 | "logs:*", 34 | ] 35 | 36 | effect = "Allow" 37 | resources = ["*"] 38 | } 39 | } 40 | 41 | resource "aws_iam_role_policy" "monitoring_policy" { 42 | name = "monitoring" 43 | policy = "${data.aws_iam_policy_document.monitoring_policy.json}" 44 | role = "${aws_iam_role.agent_role.id}" 45 | } 46 | 47 | ### Creates resource tagging policy 48 | data "aws_iam_policy_document" "tagging_policy" { 49 | statement { 50 | actions = ["ec2:CreateTags"] 51 | effect = "Allow" 52 | resources = ["*"] 53 | } 54 | } 55 | 56 | resource "aws_iam_role_policy" "tagging_policy" { 57 | name = "tagging" 58 | policy = "${data.aws_iam_policy_document.tagging_policy.json}" 59 | role = "${aws_iam_role.agent_role.id}" 60 | } 61 | 62 | ### Creates Elastic Container Service (ECS) service policy 63 | data "aws_iam_policy_document" "ecs_policy" { 64 | statement { 65 | actions = [ 66 | "ecs:CreateCluster", 67 | "ecs:DeregisterContainerInstance", 68 | "ecs:DiscoverPollEndpoint", 69 | "ecs:Poll", 70 | "ecs:RegisterContainerInstance", 71 | "ecs:StartTelemetrySession", 72 | "ecs:Submit*", 73 | "ecs:StartTask", 74 | ] 75 | 76 | effect = "Allow" 77 | resources = ["*"] 78 | } 79 | } 80 | 81 | resource "aws_iam_role_policy" "ecs_policy" { 82 | name = "ecs" 83 | policy = "${data.aws_iam_policy_document.ecs_policy.json}" 84 | role = "${aws_iam_role.agent_role.id}" 85 | } 86 | 87 | ### Creates Simple Storage Service (S3) policy for logging buckets 88 | data "aws_iam_policy_document" "logging_policy" { 89 | count = "${var.logs_bucket_enabled == "true" ? "1" : "0"}" 90 | 91 | statement { 92 | actions = ["s3:ListBucket"] 93 | effect = "Allow" 94 | resources = ["arn:aws:s3:::${var.logs_bucket_name}"] 95 | } 96 | 97 | statement { 98 | actions = [ 99 | "s3:PutObject", 100 | "s3:GetObject", 101 | ] 102 | 103 | effect = "Allow" 104 | resources = ["arn:aws:s3:::${var.logs_bucket_name}/*"] 105 | } 106 | } 107 | 108 | resource "aws_iam_role_policy" "logging_policy" { 109 | count = "${var.logs_bucket_enabled == "true" ? "1" : "0"}" 110 | 111 | name = "logging" 112 | policy = "${data.aws_iam_policy_document.logging_policy.json}" 113 | role = "${aws_iam_role.agent_role.id}" 114 | } 115 | -------------------------------------------------------------------------------- /cluster/main.tf: -------------------------------------------------------------------------------- 1 | # Elastic Container Service (ECS) cluster 2 | 3 | ## Set Terraform version constraint 4 | terraform { 5 | required_version = "> 0.11.0" 6 | } 7 | 8 | data "aws_region" "current" {} 9 | 10 | ## Creates cloud-config data for agent cluster 11 | data "template_file" "user_data" { 12 | template = "${var.user_data_override != "" ? "" : file("${path.module}/templates/user_data.hcl")}" 13 | 14 | vars { 15 | cluster_label = "${var.cluster_label}" 16 | stack_item_label = "${var.stack_item_label}" 17 | } 18 | } 19 | 20 | ## Creates autoscaling cluster 21 | data "aws_ami" "ecs_ami" { 22 | most_recent = true 23 | owners = ["amazon"] 24 | 25 | filter { 26 | name = "architecture" 27 | values = ["x86_64"] 28 | } 29 | 30 | filter { 31 | name = "name" 32 | values = ["amzn-ami-*-amazon-ecs-optimized"] 33 | } 34 | 35 | filter { 36 | name = "root-device-type" 37 | values = ["ebs"] 38 | } 39 | 40 | filter { 41 | name = "virtualization-type" 42 | values = ["hvm"] 43 | } 44 | } 45 | 46 | module "cluster" { 47 | source = "github.com/unifio/terraform-aws-asg?ref=v0.3.7//group" 48 | 49 | # Resource tags 50 | stack_item_fullname = "${var.stack_item_fullname}" 51 | stack_item_label = "${var.cluster_label}-${var.stack_item_label}" 52 | 53 | # VPC parameters 54 | subnets = ["${var.subnets}"] 55 | vpc_id = "${var.vpc_id}" 56 | 57 | # LC parameters 58 | ami = "${coalesce(var.ami_override,data.aws_ami.ecs_ami.id)}" 59 | associate_public_ip_address = "${var.associate_public_ip_address}" 60 | ebs_optimized = "${var.ebs_optimized}" 61 | ebs_vol_del_on_term = "${var.ebs_vol_del_on_term}" 62 | ebs_vol_device_name = "${var.ebs_vol_device_name}" 63 | ebs_vol_encrypted = "${var.ebs_vol_encrypted}" 64 | ebs_vol_iops = "${var.ebs_vol_iops}" 65 | ebs_vol_size = "${var.ebs_vol_size}" 66 | ebs_vol_snapshot_id = "${var.ebs_vol_snapshot_id}" 67 | ebs_vol_type = "${var.ebs_vol_type}" 68 | enable_monitoring = "${var.enable_monitoring}" 69 | instance_based_naming_enabled = "${var.instance_based_naming_enabled}" 70 | instance_name_prefix = "${var.instance_name_prefix}" 71 | instance_profile = "${aws_iam_instance_profile.agent_profile.id}" 72 | instance_tags = "${var.instance_tags}" 73 | instance_type = "${var.instance_type}" 74 | key_name = "${var.key_name}" 75 | placement_tenancy = "${var.placement_tenancy}" 76 | root_vol_del_on_term = "${var.root_vol_del_on_term}" 77 | root_vol_iops = "${var.root_vol_iops}" 78 | root_vol_size = "${var.root_vol_size}" 79 | root_vol_type = "${var.root_vol_type}" 80 | security_groups = ["${distinct(concat(list(module.consul.sg_id), compact(var.security_groups)))}"] 81 | spot_price = "${var.spot_price}" 82 | user_data = "${coalesce(var.user_data_override,data.template_file.user_data.rendered)}" 83 | 84 | # ASG parameters 85 | default_cooldown = "${var.default_cooldown}" 86 | desired_capacity = "${var.desired_capacity}" 87 | enabled_metrics = ["${var.enabled_metrics}"] 88 | force_delete = "${var.force_delete}" 89 | hc_check_type = "${var.hc_check_type}" 90 | hc_grace_period = "${var.hc_grace_period}" 91 | max_size = "${var.max_size}" 92 | min_size = "${var.min_size}" 93 | placement_group = "${var.placement_group}" 94 | protect_from_scale_in = "${var.protect_from_scale_in}" 95 | suspended_processes = ["${var.suspended_processes}"] 96 | target_group_arns = ["${var.target_group_arns}"] 97 | termination_policies = ["${var.termination_policies}"] 98 | wait_for_capacity_timeout = "${var.wait_for_capacity_timeout}" 99 | } 100 | 101 | ## Updates security groups 102 | resource "aws_security_group_rule" "agent_egress" { 103 | cidr_blocks = ["0.0.0.0/0"] 104 | from_port = 0 105 | protocol = -1 106 | security_group_id = "${module.cluster.sg_id}" 107 | to_port = 0 108 | type = "egress" 109 | } 110 | 111 | ## Registers ECS cluster 112 | resource "aws_ecs_cluster" "cluster" { 113 | name = "${var.cluster_label}-${var.stack_item_label}" 114 | } 115 | 116 | ## Enables Consul service discovery 117 | module "consul" { 118 | source = "../consul" 119 | 120 | # Resource tags 121 | stack_item_fullname = "${var.stack_item_fullname}" 122 | stack_item_label = "${var.cluster_label}-${var.stack_item_label}" 123 | 124 | # ECS parameters 125 | cluster_id = "${aws_ecs_cluster.cluster.id}" 126 | cluster_name = "${aws_ecs_cluster.cluster.name}" 127 | cluster_sg_id = "${module.cluster.sg_id}" 128 | iam_path = "${var.iam_path}" 129 | vpc_id = "${var.vpc_id}" 130 | 131 | # Service discovery parameters 132 | ## TODO: Enable for auto scaling 133 | 134 | agent_config_override = "${var.agent_config_override}" 135 | agent_desired_count = "${((length(var.desired_capacity) > 0 ? var.desired_capacity : var.min_size) - var.server_desired_count) >= 0 ? (var.min_size - var.server_desired_count) : "0"}" 136 | agent_task_arn_override = "${var.agent_task_arn_override}" 137 | consul_dc = "${var.consul_dc}" 138 | consul_docker_image = "${var.consul_docker_image}" 139 | registrator_config_override = "${var.registrator_config_override}" 140 | registrator_desired_count = "${length(var.desired_capacity) > 0 ? var.desired_capacity : var.min_size}" 141 | registrator_docker_image = "${var.registrator_docker_image}" 142 | registrator_task_arn_override = "${var.registrator_task_arn_override}" 143 | server_config_override = "${var.server_config_override}" 144 | server_desired_count = "${var.server_desired_count}" 145 | server_task_arn_override = "${var.server_task_arn_override}" 146 | service_discovery_enabled = "${(var.min_size - var.server_desired_count) < 0 ? "false" : var.service_discovery_enabled}" 147 | service_registration_enabled = "${var.service_registration_enabled}" 148 | } 149 | -------------------------------------------------------------------------------- /cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | # Outputs 2 | 3 | output "agent_role_id" { 4 | value = "${aws_iam_role.agent_role.id}" 5 | } 6 | 7 | output "cluster_id" { 8 | value = "${aws_ecs_cluster.cluster.id}" 9 | } 10 | 11 | output "cluster_name" { 12 | value = "${aws_ecs_cluster.cluster.name}" 13 | } 14 | 15 | output "consul_sg_id" { 16 | value = "${module.consul.sg_id}" 17 | } 18 | 19 | output "consul_target_group_arn" { 20 | value = "${module.consul.target_group_arn}" 21 | } 22 | 23 | output "sg_id" { 24 | value = "${module.cluster.sg_id}" 25 | } 26 | -------------------------------------------------------------------------------- /cluster/templates/user_data.hcl: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | manage_etc_hosts: True 3 | bootcmd: 4 | - [ cloud-init-per, instance, docker_storage_setup, /usr/bin/docker-storage-setup ] 5 | - service docker restart 6 | 7 | write_files: 8 | - path: /etc/ecs/ecs.config 9 | permissions: '0644' 10 | content: | 11 | ECS_CLUSTER=${cluster_label}-${stack_item_label} 12 | 13 | runcmd: 14 | - restart ecs 15 | output : { all : '| tee -a /var/log/cloud-init-output.log' } 16 | -------------------------------------------------------------------------------- /cluster/variables.tf: -------------------------------------------------------------------------------- 1 | # Input variables 2 | 3 | ## Resource tags 4 | variable "cluster_label" { 5 | type = "string" 6 | description = "Short form identifier for this cluster." 7 | } 8 | 9 | variable "stack_item_fullname" { 10 | type = "string" 11 | description = "Long form descriptive name for this stack item. This value is used to create the 'application' resource tag for resources created by this stack item." 12 | } 13 | 14 | variable "stack_item_label" { 15 | type = "string" 16 | description = "Short form identifier for this stack. This value is used to create the 'Name' resource tag for resources created by this stack item, and also serves as a unique key for re-use." 17 | } 18 | 19 | ## VPC parameters 20 | variable "subnets" { 21 | type = "list" 22 | description = "A list of subnet IDs to launch resources in." 23 | } 24 | 25 | variable "vpc_id" { 26 | type = "string" 27 | description = "ID of the target VPC." 28 | } 29 | 30 | ## Cluster parameters 31 | 32 | ### LC parameters 33 | variable "ami_override" { 34 | type = "string" 35 | description = "Custom Amazon Machine Image (AMI) to associate with the launch configuration." 36 | default = "" 37 | } 38 | 39 | variable "associate_public_ip_address" { 40 | type = "string" 41 | description = "Flag for associating public IP addresses with instances managed by the auto scaling group." 42 | default = "" 43 | } 44 | 45 | variable "ebs_optimized" { 46 | type = "string" 47 | description = "Flag to enable EBS optimization." 48 | default = "false" 49 | } 50 | 51 | variable "ebs_vol_del_on_term" { 52 | type = "string" 53 | description = "Whether the volume should be destroyed on instance termination." 54 | default = "true" 55 | } 56 | 57 | variable "ebs_vol_device_name" { 58 | type = "string" 59 | description = "The name of the device to mount." 60 | default = "" 61 | } 62 | 63 | variable "ebs_vol_encrypted" { 64 | type = "string" 65 | description = "Whether the volume should be encrypted or not. Do not use this option if you are using 'ebs_vol_snapshot_id' as the encrypted flag will be determined by the snapshot." 66 | default = "" 67 | } 68 | 69 | /* 70 | http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html 71 | For the best per-I/O latency experience, we recommend that you provision an IOPS-to-GiB ratio greater than 2:1. For example, a 2,000 IOPS volume should be smaller than 1,000 GiB. 72 | */ 73 | variable "ebs_vol_iops" { 74 | type = "string" 75 | description = "The amount of provisioned IOPS. Only utilized with 'ebs_vol_type' of 'io1'." 76 | default = "2000" 77 | } 78 | 79 | variable "ebs_vol_size" { 80 | type = "string" 81 | description = "The size of the volume in gigabytes." 82 | default = "" 83 | } 84 | 85 | variable "ebs_vol_snapshot_id" { 86 | type = "string" 87 | description = "The Snapshot ID to mount." 88 | default = "" 89 | } 90 | 91 | variable "ebs_vol_type" { 92 | type = "string" 93 | description = "The type of volume. Valid values are 'standard', 'gp2' and 'io1'." 94 | default = "gp2" 95 | } 96 | 97 | variable "enable_monitoring" { 98 | type = "string" 99 | description = "Flag to enable detailed monitoring." 100 | default = "" 101 | } 102 | 103 | variable "iam_path" { 104 | type = "string" 105 | description = "The path to the IAM resource." 106 | default = "/" 107 | } 108 | 109 | variable "instance_based_naming_enabled" { 110 | type = "string" 111 | description = "Flag to enable instance-id based name tagging. Requires the AWS CLI to be installed on the instance. Currently only supports Linux based systems." 112 | default = "" 113 | } 114 | 115 | variable "instance_name_prefix" { 116 | type = "string" 117 | description = "String to prepend instance-id based name tags with." 118 | default = "" 119 | } 120 | 121 | variable "instance_tags" { 122 | type = "map" 123 | description = "Map of tags to add to instances. Requires the AWS CLI to be installed on the instance. Currently only supports Linux based systems." 124 | 125 | default = { 126 | "" = "" 127 | } 128 | } 129 | 130 | variable "instance_type" { 131 | type = "string" 132 | description = "The EC2 instance type to associate with the launch configuration." 133 | } 134 | 135 | variable "key_name" { 136 | type = "string" 137 | description = "The SSH key pair to associate with the launch configuration." 138 | default = "" 139 | } 140 | 141 | variable "logs_bucket_enabled" { 142 | type = "string" 143 | description = "Flag for enabling access to the logs bucket from the instances." 144 | default = "false" 145 | } 146 | 147 | variable "logs_bucket_name" { 148 | type = "string" 149 | description = "Name of the S3 bucket for logging." 150 | default = "" 151 | } 152 | 153 | variable "placement_tenancy" { 154 | type = "string" 155 | description = "The tenancy of the instance. Valid values are 'default' or 'dedicated'." 156 | default = "default" 157 | } 158 | 159 | variable "root_vol_del_on_term" { 160 | type = "string" 161 | description = "Whether the volume should be destroyed on instance termination." 162 | default = "true" 163 | } 164 | 165 | /* 166 | http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html 167 | For the best per-I/O latency experience, we recommend that you provision an IOPS-to-GiB ratio greater than 2:1. For example, a 2,000 IOPS volume should be smaller than 1,000 GiB. 168 | */ 169 | variable "root_vol_iops" { 170 | type = "string" 171 | description = "The amount of provisioned IOPS. Only utilized with 'root_vol_type' of 'io1'" 172 | default = "2000" 173 | } 174 | 175 | variable "root_vol_size" { 176 | type = "string" 177 | description = "The size of the volume in gigabytes." 178 | default = "" 179 | } 180 | 181 | variable "root_vol_type" { 182 | type = "string" 183 | description = "The type of volume. Valid values are 'standard', 'gp2' and 'io1'." 184 | default = "gp2" 185 | } 186 | 187 | variable "security_groups" { 188 | type = "list" 189 | description = "A list of security group IDs to associate with the instances." 190 | default = [] 191 | } 192 | 193 | variable "spot_price" { 194 | type = "string" 195 | description = "The price to use for reserving spot instances." 196 | default = "" 197 | } 198 | 199 | variable "user_data_override" { 200 | type = "string" 201 | description = "Custom instance initialization data to associate with the launch configuration." 202 | default = "" 203 | } 204 | 205 | ### ASG parameters 206 | variable "default_cooldown" { 207 | type = "string" 208 | description = "The amount of time, in seconds, after a scaling activity completes before another scaling activity can start." 209 | default = "" 210 | } 211 | 212 | variable "desired_capacity" { 213 | type = "string" 214 | description = "The number of Amazon EC2 instances that should be running in the group." 215 | default = "" 216 | } 217 | 218 | variable "enabled_metrics" { 219 | type = "list" 220 | description = "A list of metrics to collect. The allowed values are 'GroupMinSize', 'GroupMaxSize', 'GroupDesiredCapacity', 'GroupInServiceInstances', 'GroupPendingInstances', 'GroupStandbyInstances', 'GroupTerminatingInstances', 'GroupTotalInstances'." 221 | default = [] 222 | } 223 | 224 | variable "force_delete" { 225 | type = "string" 226 | description = "Flag to allow deletion of the auto scaling group without waiting for all instances in the pool to terminate." 227 | default = "false" 228 | } 229 | 230 | variable "hc_check_type" { 231 | type = "string" 232 | description = "Type of health check performed by the auto scaling group. Valid values are 'ELB' or 'EC2'." 233 | default = "" 234 | } 235 | 236 | variable "hc_grace_period" { 237 | type = "string" 238 | description = "Time allowed after an instance comes into service before checking health." 239 | default = "" 240 | } 241 | 242 | variable "max_size" { 243 | type = "string" 244 | description = "The maximum number of instances allowed by the auto scaling group." 245 | } 246 | 247 | variable "min_size" { 248 | type = "string" 249 | description = "The minimum number of instance to be maintained by the auto scaling group." 250 | } 251 | 252 | variable "placement_group" { 253 | type = "string" 254 | description = "The name of the placement group into which you'll launch your instances, if any." 255 | default = "" 256 | } 257 | 258 | variable "protect_from_scale_in" { 259 | type = "string" 260 | description = "Allows setting instance protection. The autoscaling group will not select instances with this setting for terminination during scale in events." 261 | default = "" 262 | } 263 | 264 | variable "suspended_processes" { 265 | type = "list" 266 | description = "A list of processes to suspend for the AutoScaling Group. The allowed values are 'Launch', 'Terminate', 'HealthCheck', 'ReplaceUnhealthy', 'AZRebalance', 'AlarmNotification', 'ScheduledActions', 'AddToLoadBalancer'. Note that if you suspend either the 'Launch' or 'Terminate' process types, it can prevent your autoscaling group from functioning properly." 267 | default = [] 268 | } 269 | 270 | variable "target_group_arns" { 271 | type = "list" 272 | description = "A list of 'aws_alb_target_group' ARNs, for use with Application Load Balancing" 273 | default = [] 274 | } 275 | 276 | variable "termination_policies" { 277 | type = "list" 278 | description = "A list of policies to decide how the instances in the auto scale group should be terminated. The allowed values are 'OldestInstance', 'NewestInstance', 'OldestLaunchConfiguration', 'ClosestToNextInstanceHour', 'Default'." 279 | default = [] 280 | } 281 | 282 | variable "wait_for_capacity_timeout" { 283 | type = "string" 284 | description = "A maximum duration that Terraform should wait for ASG managed instances to become healthy before timing out." 285 | default = "" 286 | } 287 | 288 | ## Service discovery parameters 289 | variable "agent_config_override" { 290 | type = "string" 291 | description = "Consul agent ECS task configuration JSON." 292 | default = "" 293 | } 294 | 295 | variable "agent_task_arn_override" { 296 | type = "string" 297 | description = "Consul agent ECS task ARN." 298 | default = "" 299 | } 300 | 301 | variable "consul_dc" { 302 | type = "string" 303 | description = "Consul datacenter of the specified cluster." 304 | default = "dc1" 305 | } 306 | 307 | variable "consul_docker_image" { 308 | type = "string" 309 | description = "Consul Docker image and tag" 310 | default = "consul:latest" 311 | } 312 | 313 | variable "registrator_config_override" { 314 | type = "string" 315 | description = "Registrator ECS task configuration JSON." 316 | default = "" 317 | } 318 | 319 | variable "registrator_docker_image" { 320 | type = "string" 321 | description = "Registrator Docker image and tag" 322 | default = "gliderlabs/registrator:v7" 323 | } 324 | 325 | variable "registrator_task_arn_override" { 326 | type = "string" 327 | description = "Registrator ECS task ARN." 328 | default = "" 329 | } 330 | 331 | variable "server_config_override" { 332 | type = "string" 333 | description = "Consul server ECS task configuration JSON." 334 | default = "" 335 | } 336 | 337 | variable "server_task_arn_override" { 338 | type = "string" 339 | description = "Consul server ECS task ARN." 340 | default = "" 341 | } 342 | 343 | variable "server_desired_count" { 344 | type = "string" 345 | description = "The number of Consul server containers to run." 346 | default = "3" 347 | } 348 | 349 | variable "service_discovery_enabled" { 350 | type = "string" 351 | description = "Flag for the deployment of Consul service discovery and configuration." 352 | default = "false" 353 | } 354 | 355 | variable "service_registration_enabled" { 356 | type = "string" 357 | description = "Flag for the deployment of Registrator service registration." 358 | default = "false" 359 | } 360 | -------------------------------------------------------------------------------- /consul/iam.tf: -------------------------------------------------------------------------------- 1 | # Consul service discovery & configuration 2 | 3 | ## Creates IAM role for Consul ECS services 4 | data "aws_iam_policy_document" "consul_policy" { 5 | count = "${local.service_discovery_check}" 6 | 7 | statement { 8 | actions = ["sts:AssumeRole"] 9 | effect = "Allow" 10 | 11 | principals { 12 | type = "Service" 13 | identifiers = ["ecs-tasks.amazonaws.com"] 14 | } 15 | } 16 | } 17 | 18 | resource "aws_iam_role" "consul_role" { 19 | count = "${local.service_discovery_check}" 20 | 21 | assume_role_policy = "${data.aws_iam_policy_document.consul_policy.json}" 22 | name = "consul-${var.stack_item_label}-${data.aws_region.current.name}" 23 | path = "${var.iam_path}" 24 | } 25 | 26 | data "aws_iam_policy_document" "consul_ec2_policy" { 27 | count = "${local.service_discovery_check}" 28 | 29 | statement { 30 | actions = [ 31 | "ec2:Describe*", 32 | ] 33 | 34 | effect = "Allow" 35 | resources = ["*"] 36 | } 37 | } 38 | 39 | resource "aws_iam_role_policy" "consul_ec2_policy" { 40 | count = "${local.service_discovery_check}" 41 | 42 | name = "ec2" 43 | policy = "${data.aws_iam_policy_document.consul_ec2_policy.json}" 44 | role = "${aws_iam_role.consul_role.id}" 45 | } 46 | 47 | ## Creates IAM role for the ECS service 48 | data "aws_iam_policy_document" "ecs_policy" { 49 | count = "${local.service_discovery_check * local.consul_server_check}" 50 | 51 | statement { 52 | actions = ["sts:AssumeRole"] 53 | effect = "Allow" 54 | 55 | principals { 56 | type = "Service" 57 | identifiers = ["ecs.amazonaws.com"] 58 | } 59 | } 60 | } 61 | 62 | resource "aws_iam_role" "ecs_role" { 63 | count = "${local.service_discovery_check * local.consul_server_check}" 64 | 65 | assume_role_policy = "${data.aws_iam_policy_document.ecs_policy.json}" 66 | name = "ecs-consul-${var.stack_item_label}-${data.aws_region.current.name}" 67 | path = "${var.iam_path}" 68 | } 69 | 70 | data "aws_iam_policy_document" "lb_policy" { 71 | statement { 72 | actions = [ 73 | "ec2:AuthorizeSecurityGroupIngress", 74 | "ec2:Describe*", 75 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 76 | "elasticloadbalancing:DeregisterTargets", 77 | "elasticloadbalancing:Describe*", 78 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 79 | "elasticloadbalancing:RegisterTargets", 80 | ] 81 | 82 | effect = "Allow" 83 | resources = ["*"] 84 | } 85 | } 86 | 87 | resource "aws_iam_role_policy" "lb_policy" { 88 | count = "${local.service_discovery_check * local.consul_server_check}" 89 | 90 | name = "lb" 91 | policy = "${data.aws_iam_policy_document.lb_policy.json}" 92 | role = "${aws_iam_role.ecs_role.id}" 93 | } 94 | 95 | data "aws_iam_policy_document" "ecs_ec2_policy" { 96 | statement { 97 | actions = [ 98 | "ec2:AuthorizeSecurityGroupIngress", 99 | "ec2:Describe*", 100 | ] 101 | 102 | effect = "Allow" 103 | resources = ["*"] 104 | } 105 | } 106 | 107 | resource "aws_iam_role_policy" "ecs_ec2_policy" { 108 | count = "${local.service_discovery_check * local.consul_server_check}" 109 | 110 | name = "ec2" 111 | policy = "${data.aws_iam_policy_document.ecs_ec2_policy.json}" 112 | role = "${aws_iam_role.ecs_role.id}" 113 | } 114 | -------------------------------------------------------------------------------- /consul/main.tf: -------------------------------------------------------------------------------- 1 | # Consul service discovery & configuration 2 | 3 | ## Set Terraform version constraint 4 | terraform { 5 | required_version = "> 0.11.0" 6 | } 7 | 8 | data "aws_region" "current" {} 9 | 10 | locals { 11 | service_discovery_check = "${var.service_discovery_enabled == "true" ? 1 : 0}" 12 | consul_server_check = "${var.server_desired_count > 0 ? 1 : 0}" 13 | consul_agent_check = "${var.agent_desired_count > 0 ? 1 : 0}" 14 | registrator_check = "${var.service_registration_enabled == "true" ? 1 : 0}" 15 | } 16 | 17 | ## Creates Consul communication security group 18 | 19 | resource "aws_security_group" "consul_sg" { 20 | count = "${local.service_discovery_check * local.consul_server_check}" 21 | 22 | description = "${var.stack_item_fullname} Consul security group" 23 | name_prefix = "consul-${var.stack_item_label}-" 24 | vpc_id = "${var.vpc_id}" 25 | 26 | tags { 27 | application = "${var.stack_item_fullname}" 28 | managed_by = "terraform" 29 | Name = "consul-${var.stack_item_label}" 30 | } 31 | 32 | lifecycle { 33 | create_before_destroy = true 34 | } 35 | } 36 | 37 | ### Traffic within the environment 38 | resource "aws_security_group_rule" "agent_consul_rpc" { 39 | count = "${local.service_discovery_check * local.consul_server_check}" 40 | 41 | from_port = 8300 42 | protocol = "tcp" 43 | security_group_id = "${aws_security_group.consul_sg.id}" 44 | self = true 45 | to_port = 8300 46 | type = "ingress" 47 | } 48 | 49 | resource "aws_security_group_rule" "agent_serf_lan_tcp" { 50 | count = "${local.service_discovery_check * local.consul_server_check}" 51 | 52 | from_port = 8301 53 | protocol = "tcp" 54 | security_group_id = "${aws_security_group.consul_sg.id}" 55 | self = true 56 | to_port = 8301 57 | type = "ingress" 58 | } 59 | 60 | resource "aws_security_group_rule" "agent_serf_lan_udp" { 61 | count = "${local.service_discovery_check * local.consul_server_check}" 62 | 63 | from_port = 8301 64 | protocol = "udp" 65 | security_group_id = "${aws_security_group.consul_sg.id}" 66 | self = true 67 | to_port = 8301 68 | type = "ingress" 69 | } 70 | 71 | resource "aws_security_group_rule" "agent_serf_wan_tcp" { 72 | count = "${local.service_discovery_check * local.consul_server_check}" 73 | 74 | from_port = 8302 75 | protocol = "tcp" 76 | security_group_id = "${aws_security_group.consul_sg.id}" 77 | self = true 78 | to_port = 8302 79 | type = "ingress" 80 | } 81 | 82 | resource "aws_security_group_rule" "agent_serf_wan_udp" { 83 | count = "${local.service_discovery_check * local.consul_server_check}" 84 | 85 | from_port = 8302 86 | protocol = "udp" 87 | security_group_id = "${aws_security_group.consul_sg.id}" 88 | self = true 89 | to_port = 8302 90 | type = "ingress" 91 | } 92 | 93 | resource "aws_security_group_rule" "agent_http" { 94 | count = "${local.service_discovery_check * local.consul_server_check}" 95 | 96 | from_port = 8500 97 | protocol = "tcp" 98 | security_group_id = "${aws_security_group.consul_sg.id}" 99 | self = true 100 | to_port = 8500 101 | type = "ingress" 102 | } 103 | 104 | ## Creates ALB target group 105 | resource "aws_alb_target_group" "consul_group" { 106 | count = "${local.service_discovery_check * local.consul_server_check}" 107 | 108 | name = "consul-${var.stack_item_label}" 109 | port = 8500 110 | protocol = "HTTP" 111 | vpc_id = "${var.vpc_id}" 112 | 113 | health_check { 114 | path = "/v1/agent/self" 115 | port = 8500 116 | protocol = "HTTP" 117 | } 118 | 119 | tags { 120 | application = "${var.stack_item_fullname}" 121 | Name = "consul-${var.stack_item_label}" 122 | managed_by = "terraform" 123 | } 124 | } 125 | 126 | ## Creates ECS tasks 127 | 128 | ### Consul server 129 | data "template_file" "server_config" { 130 | count = "${local.service_discovery_check * local.consul_server_check}" 131 | 132 | template = "${file("${path.module}/templates/server.hcl")}" 133 | 134 | vars { 135 | bootstrap_expect = "${var.server_desired_count}" 136 | consul_dc = "${var.consul_dc}" 137 | docker_image = "${var.consul_docker_image}" 138 | join = "${var.cluster_name}" 139 | } 140 | } 141 | 142 | resource "aws_ecs_task_definition" "server_task" { 143 | count = "${local.service_discovery_check * local.consul_server_check}" 144 | 145 | container_definitions = "${coalesce(var.server_config_override,data.template_file.server_config.rendered)}" 146 | family = "consul-server-${var.stack_item_label}" 147 | network_mode = "host" 148 | task_role_arn = "${aws_iam_role.consul_role.arn}" 149 | 150 | volume { 151 | host_path = "/etc/consul.d" 152 | name = "consul_config" 153 | } 154 | 155 | volume { 156 | host_path = "/var/lib/consul" 157 | name = "consul_data" 158 | } 159 | } 160 | 161 | ### Consul agent 162 | data "template_file" "agent_config" { 163 | count = "${local.service_discovery_check * local.consul_agent_check}" 164 | 165 | template = "${file("${path.module}/templates/agent.hcl")}" 166 | 167 | vars { 168 | consul_dc = "${var.consul_dc}" 169 | docker_image = "${var.consul_docker_image}" 170 | join = "${var.cluster_name}" 171 | } 172 | } 173 | 174 | resource "aws_ecs_task_definition" "agent_task" { 175 | count = "${local.service_discovery_check * local.consul_agent_check}" 176 | 177 | container_definitions = "${coalesce(var.agent_config_override,data.template_file.agent_config.rendered)}" 178 | family = "consul-agent-${var.stack_item_label}" 179 | network_mode = "host" 180 | task_role_arn = "${aws_iam_role.consul_role.arn}" 181 | 182 | volume { 183 | host_path = "/etc/consul.d" 184 | name = "consul_config" 185 | } 186 | 187 | volume { 188 | host_path = "/var/lib/consul" 189 | name = "consul_data" 190 | } 191 | } 192 | 193 | ### Registrator 194 | data "template_file" "registrator_config" { 195 | count = "${local.service_discovery_check * local.registrator_check}" 196 | 197 | template = "${file("${path.module}/templates/registrator.hcl")}" 198 | 199 | vars { 200 | docker_image = "${var.registrator_docker_image}" 201 | } 202 | } 203 | 204 | resource "aws_ecs_task_definition" "registrator_task" { 205 | count = "${local.service_discovery_check * local.registrator_check}" 206 | 207 | container_definitions = "${coalesce(var.registrator_config_override,data.template_file.registrator_config.rendered)}" 208 | family = "registrator-${var.stack_item_label}" 209 | network_mode = "host" 210 | 211 | volume { 212 | host_path = "/var/run/docker.sock" 213 | name = "docker_socket" 214 | } 215 | } 216 | 217 | ## Creates ECS services 218 | 219 | ### Consul server 220 | resource "aws_ecs_service" "consul_server" { 221 | count = "${local.service_discovery_check * local.consul_server_check}" 222 | depends_on = ["aws_iam_role.ecs_role"] 223 | 224 | cluster = "${var.cluster_id}" 225 | deployment_maximum_percent = "100" 226 | deployment_minimum_healthy_percent = "50" 227 | desired_count = "${var.server_desired_count}" 228 | iam_role = "${aws_iam_role.ecs_role.arn}" 229 | name = "consul-server" 230 | task_definition = "${coalesce(var.server_task_arn_override,aws_ecs_task_definition.server_task.arn)}" 231 | 232 | load_balancer { 233 | container_name = "consul-server" 234 | container_port = "8500" 235 | target_group_arn = "${aws_alb_target_group.consul_group.arn}" 236 | } 237 | 238 | placement_constraints { 239 | type = "distinctInstance" 240 | } 241 | } 242 | 243 | ### Consul agent 244 | resource "aws_ecs_service" "consul_agent" { 245 | count = "${local.service_discovery_check * local.consul_agent_check}" 246 | 247 | cluster = "${var.cluster_id}" 248 | deployment_maximum_percent = "100" 249 | deployment_minimum_healthy_percent = "50" 250 | desired_count = "${var.agent_desired_count}" 251 | name = "consul-agent" 252 | task_definition = "${coalesce(var.agent_task_arn_override,aws_ecs_task_definition.agent_task.arn)}" 253 | 254 | placement_constraints { 255 | type = "distinctInstance" 256 | } 257 | } 258 | 259 | ### Registrator 260 | resource "aws_ecs_service" "registrator" { 261 | count = "${local.service_discovery_check * local.registrator_check}" 262 | 263 | cluster = "${var.cluster_id}" 264 | deployment_maximum_percent = "100" 265 | deployment_minimum_healthy_percent = "50" 266 | desired_count = "${var.registrator_desired_count}" 267 | name = "registrator" 268 | task_definition = "${coalesce(var.registrator_task_arn_override,aws_ecs_task_definition.registrator_task.arn)}" 269 | 270 | placement_constraints { 271 | type = "distinctInstance" 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /consul/outputs.tf: -------------------------------------------------------------------------------- 1 | # Outputs 2 | 3 | output "sg_id" { 4 | value = "${join(",", compact(aws_security_group.consul_sg.*.id))}" 5 | } 6 | 7 | output "target_group_arn" { 8 | value = "${join(",", compact(aws_alb_target_group.consul_group.*.arn))}" 9 | } 10 | -------------------------------------------------------------------------------- /consul/templates/agent.hcl: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "consul-agent", 3 | "image": "${docker_image}", 4 | "memory": 128, 5 | "cpu": 64, 6 | "essential": true, 7 | "dockerLabels": { 8 | "service": "consul" 9 | }, 10 | "portMappings": [{ 11 | "hostPort": 8500, 12 | "containerPort": 8500, 13 | "protocol": "tcp" 14 | }], 15 | "mountPoints": [{ 16 | "containerPath": "/consul/config", 17 | "sourceVolume": "consul_config", 18 | "readOnly": false 19 | }, 20 | { 21 | "containerPath": "/consul/data", 22 | "sourceVolume": "consul_data", 23 | "readOnly": false 24 | }], 25 | "command": [ 26 | "agent", 27 | "-raft-protocol=3", 28 | "-dc=${consul_dc}", 29 | "-retry-join-ec2-tag-key=aws:autoscaling:groupName", 30 | "-retry-join-ec2-tag-value=${join}" 31 | ], 32 | "environment": [{ 33 | "name": "CONSUL_BIND_INTERFACE", 34 | "value": "eth0" 35 | }, 36 | { 37 | "name": "CONSUL_CLIENT_INTERFACE", 38 | "value": "eth0" 39 | }, 40 | { 41 | "name": "SERVICE_8500_IGNORE", 42 | "value": "true" 43 | }] 44 | }] 45 | -------------------------------------------------------------------------------- /consul/templates/registrator.hcl: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "registrator", 3 | "image": "${docker_image}", 4 | "memory": 16, 5 | "cpu": 10, 6 | "essential": true, 7 | "dockerLabels": { 8 | "service": "registrator" 9 | }, 10 | "mountPoints": [{ 11 | "containerPath": "/tmp/docker.sock", 12 | "sourceVolume": "docker_socket", 13 | "readOnly": true 14 | }], 15 | "command": [ 16 | "-resync", 17 | "60", 18 | "-retry-attempts", 19 | "10", 20 | "-retry-interval", 21 | "1000", 22 | "-ip", 23 | "127.0.0.1", 24 | "consul://localhost:8500" 25 | ] 26 | }] 27 | -------------------------------------------------------------------------------- /consul/templates/server.hcl: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "consul-server", 3 | "image": "${docker_image}", 4 | "memory": 256, 5 | "cpu": 128, 6 | "essential": true, 7 | "dockerLabels": { 8 | "service": "consul" 9 | }, 10 | "portMappings": [{ 11 | "hostPort": 8500, 12 | "containerPort": 8500, 13 | "protocol": "tcp" 14 | }], 15 | "mountPoints": [{ 16 | "containerPath": "/consul/config", 17 | "sourceVolume": "consul_config", 18 | "readOnly": false 19 | }, 20 | { 21 | "containerPath": "/consul/data", 22 | "sourceVolume": "consul_data", 23 | "readOnly": false 24 | }], 25 | "command": [ 26 | "agent", 27 | "-server", 28 | "-raft-protocol=3", 29 | "-bootstrap-expect=${bootstrap_expect}", 30 | "-ui", 31 | "-dc=${consul_dc}", 32 | "-retry-join-ec2-tag-key=aws:autoscaling:groupName", 33 | "-retry-join-ec2-tag-value=${join}" 34 | ], 35 | "environment": [{ 36 | "name": "CONSUL_BIND_INTERFACE", 37 | "value": "eth0" 38 | }, 39 | { 40 | "name": "CONSUL_CLIENT_INTERFACE", 41 | "value": "eth0" 42 | }, 43 | { 44 | "name": "SERVICE_8500_IGNORE", 45 | "value": "true" 46 | }] 47 | }] 48 | -------------------------------------------------------------------------------- /consul/variables.tf: -------------------------------------------------------------------------------- 1 | # Input Variables 2 | 3 | ## Resource tags 4 | variable "stack_item_fullname" { 5 | type = "string" 6 | } 7 | 8 | variable "stack_item_label" { 9 | type = "string" 10 | } 11 | 12 | ## ECS parameters 13 | variable "cluster_id" { 14 | type = "string" 15 | description = "ECS cluster ID." 16 | } 17 | 18 | variable "cluster_name" { 19 | type = "string" 20 | description = "ECS cluster name." 21 | } 22 | 23 | variable "cluster_sg_id" { 24 | type = "string" 25 | description = "ECS cluster security group ID." 26 | } 27 | 28 | variable "iam_path" { 29 | type = "string" 30 | } 31 | 32 | variable "vpc_id" { 33 | type = "string" 34 | description = "ID of the target VPC." 35 | } 36 | 37 | ## Service discovery parameters 38 | variable "agent_config_override" { 39 | type = "string" 40 | } 41 | 42 | variable "agent_desired_count" { 43 | type = "string" 44 | } 45 | 46 | variable "agent_task_arn_override" { 47 | type = "string" 48 | } 49 | 50 | variable "consul_dc" { 51 | type = "string" 52 | } 53 | 54 | variable "consul_docker_image" { 55 | type = "string" 56 | } 57 | 58 | variable "registrator_config_override" { 59 | type = "string" 60 | } 61 | 62 | variable "registrator_desired_count" { 63 | type = "string" 64 | } 65 | 66 | variable "registrator_docker_image" { 67 | type = "string" 68 | } 69 | 70 | variable "registrator_task_arn_override" { 71 | type = "string" 72 | } 73 | 74 | variable "server_config_override" { 75 | type = "string" 76 | } 77 | 78 | variable "server_desired_count" { 79 | type = "string" 80 | } 81 | 82 | variable "server_task_arn_override" { 83 | type = "string" 84 | } 85 | 86 | variable "service_discovery_enabled" { 87 | type = "string" 88 | } 89 | 90 | variable "service_registration_enabled" { 91 | type = "string" 92 | } 93 | -------------------------------------------------------------------------------- /covalence.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :backends: 3 | - yaml 4 | 5 | :logger: noop 6 | 7 | :merge_behavior: 'deeper' 8 | 9 | :hierarchy: 10 | - "stacks/common" 11 | - "stacks/%{stack}" 12 | - 'globals' 13 | - 'environments' 14 | 15 | :yaml: 16 | :datadir: data 17 | -------------------------------------------------------------------------------- /data/environments.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Environments 3 | 4 | environments: 5 | prereqs: 6 | - networking 7 | 8 | basic: 9 | - defaults 10 | 11 | complete: 12 | - overrides 13 | - service-discovery 14 | - service-registration 15 | - consul-agent 16 | -------------------------------------------------------------------------------- /data/globals.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Global variables 3 | 4 | ## Terraform 5 | tf_state_bucket: 'unifio-terraform-state' 6 | tf_state_region: 'us-east-2' 7 | -------------------------------------------------------------------------------- /data/stacks/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Common data 3 | 4 | ## Complete examples 5 | examples::complete::vars: 6 | ami_override: 'ami-abf2a9ce' # amzn-ami-2016.03.j-amazon-ecs-optimized 7 | cluster_label: 'exmpl' 8 | enable_monitoring: 'true' 9 | iam_path: '/terraform/' 10 | instance_based_naming_enabled: 'true' 11 | instance_type: 't2.nano' 12 | region: 'us-east-2' 13 | stack_item_label: 'cmpl' 14 | stack_item_fullname: 'Complete Examples' 15 | subnets: 16 | type: 's3.state' 17 | bucket: "%{hiera('tf_state_bucket')}" 18 | document: 'terraform-aws-ecs/prereqs/terraform.tfstate' 19 | key: 'dmz_subnet_ids' 20 | vpc_id: 21 | type: 's3.state' 22 | bucket: "%{hiera('tf_state_bucket')}" 23 | document: 'terraform-aws-ecs/prereqs/terraform.tfstate' 24 | key: 'vpc_id' 25 | -------------------------------------------------------------------------------- /data/stacks/consul-agent.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Complete Consul agent example 3 | 4 | ## Module 5 | consul-agent::module: 'examples/complete' 6 | 7 | ## State storage 8 | consul-agent::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-ecs/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | max_size: '4' 17 | min_size: '4' 18 | service_discovery_enabled: 'true' 19 | service_registration_enabled: 'true' 20 | -------------------------------------------------------------------------------- /data/stacks/defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Basic defaults example 3 | 4 | ## Module 5 | defaults::module: 'examples/basic' 6 | 7 | ## State storage 8 | defaults::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-ecs/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::basic::vars: 16 | cluster_label: 'exmpl' 17 | instance_type: 't2.nano' 18 | max_size: '3' 19 | min_size: '2' 20 | region: 'us-east-2' 21 | stack_item_fullname: 'Basic Examples' 22 | stack_item_label: 'bsc' 23 | subnets: 24 | type: 's3.state' 25 | bucket: "%{hiera('tf_state_bucket')}" 26 | document: 'terraform-aws-ecs/prereqs/terraform.tfstate' 27 | key: 'dmz_subnet_ids' 28 | vpc_id: 29 | type: 's3.state' 30 | bucket: "%{hiera('tf_state_bucket')}" 31 | document: 'terraform-aws-ecs/prereqs/terraform.tfstate' 32 | key: 'vpc_id' 33 | -------------------------------------------------------------------------------- /data/stacks/networking.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Prereqs VPC example 3 | 4 | ## Module 5 | networking::module: 'examples/prereqs' 6 | 7 | ## State storage 8 | networking::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-ecs/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::prereqs::vars: 16 | region: 'us-east-2' 17 | stack_item_fullname: 'ECS Examples' 18 | stack_item_label: 'ecs-exmpl' 19 | -------------------------------------------------------------------------------- /data/stacks/overrides.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Complete overrides example 3 | 4 | ## Module 5 | overrides::module: 'examples/complete' 6 | 7 | ## State storage 8 | overrides::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-ecs/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | ami_override: 'ami-abf2a9ce' # amzn-ami-2016.03.j-amazon-ecs-optimized 17 | cluster_label: 'exmpl' 18 | enable_monitoring: 'true' 19 | iam_path: '/terraform/' 20 | instance_based_naming_enabled: 'true' 21 | instance_type: 't2.small' 22 | max_size: '3' 23 | min_size: '2' 24 | region: 'us-east-2' 25 | stack_item_fullname: 'Complete Examples' 26 | stack_item_label: 'cmpl' 27 | subnets: 28 | type: 's3.state' 29 | bucket: "%{hiera('tf_state_bucket')}" 30 | document: 'terraform-aws-ecs/prereqs/terraform.tfstate' 31 | key: 'dmz_subnet_ids' 32 | vpc_id: 33 | type: 's3.state' 34 | bucket: "%{hiera('tf_state_bucket')}" 35 | document: 'terraform-aws-ecs/prereqs/terraform.tfstate' 36 | key: 'vpc_id' 37 | -------------------------------------------------------------------------------- /data/stacks/service-discovery.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Complete service discovery example 3 | 4 | ## Module 5 | service-discovery::module: 'examples/complete' 6 | 7 | ## State storage 8 | service-discovery::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-ecs/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | max_size: '3' 17 | min_size: '3' 18 | service_discovery_enabled: 'true' 19 | -------------------------------------------------------------------------------- /data/stacks/service-registration.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Complete service registration example 3 | 4 | ## Module 5 | service-registration::module: 'examples/complete' 6 | 7 | ## State storage 8 | service-registration::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-ecs/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | max_size: '3' 17 | min_size: '3' 18 | service_discovery_enabled: 'true' 19 | service_registration_enabled: 'true' 20 | -------------------------------------------------------------------------------- /examples/basic/main.tf: -------------------------------------------------------------------------------- 1 | # AWS Elastic Container Service (ECS) Stack 2 | 3 | ## Configures AWS provider 4 | provider "aws" { 5 | region = "${var.region}" 6 | } 7 | 8 | ## Configures ECS cluster 9 | module "cluster" { 10 | # Example GitHub source 11 | #source = "github.com/unifio/terraform-aws-ecs?ref=master//cluster" 12 | source = "../../cluster" 13 | 14 | # Resource tags 15 | cluster_label = "${var.cluster_label}" 16 | stack_item_fullname = "${var.stack_item_fullname}" 17 | stack_item_label = "${var.stack_item_label}" 18 | 19 | # Cluster parameters 20 | associate_public_ip_address = "true" 21 | instance_type = "${var.instance_type}" 22 | max_size = "${var.max_size}" 23 | min_size = "${var.min_size}" 24 | subnets = ["${var.subnets}"] 25 | vpc_id = "${var.vpc_id}" 26 | } 27 | -------------------------------------------------------------------------------- /examples/basic/variables.tf: -------------------------------------------------------------------------------- 1 | # Input variables 2 | 3 | ## Resource tags 4 | variable "cluster_label" { 5 | type = "string" 6 | } 7 | 8 | variable "stack_item_fullname" { 9 | type = "string" 10 | } 11 | 12 | variable "stack_item_label" { 13 | type = "string" 14 | } 15 | 16 | ## Cluster parameters 17 | variable "instance_type" { 18 | type = "string" 19 | } 20 | 21 | variable "max_size" { 22 | type = "string" 23 | } 24 | 25 | variable "min_size" { 26 | type = "string" 27 | } 28 | 29 | variable "region" { 30 | type = "string" 31 | } 32 | 33 | variable "subnets" { 34 | type = "list" 35 | } 36 | 37 | variable "vpc_id" { 38 | type = "string" 39 | } 40 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | # AWS Elastic Container Service (ECS) Stack 2 | 3 | ## Configures AWS provider 4 | provider "aws" { 5 | region = "${var.region}" 6 | } 7 | 8 | ## Creates logs bucket 9 | resource "random_id" "bucket" { 10 | byte_length = 8 11 | } 12 | 13 | resource "aws_s3_bucket" "logs" { 14 | bucket = "unifio-ecs-exmpl-${random_id.bucket.hex}" 15 | acl = "private" 16 | } 17 | 18 | ## Creates cloud-config 19 | data "template_file" "init" { 20 | template = "${file("${path.module}/user_data.hcl")}" 21 | 22 | vars { 23 | cluster_label = "${var.cluster_label}" 24 | stack_item_label = "${var.stack_item_label}" 25 | } 26 | } 27 | 28 | ## Configures ECS cluster 29 | module "cluster" { 30 | # Example GitHub source 31 | #source = "github.com/unifio/terraform-aws-ecs?ref=master//cluster" 32 | source = "../../cluster" 33 | 34 | # Resource tags 35 | cluster_label = "${var.cluster_label}" 36 | stack_item_fullname = "${var.stack_item_fullname}" 37 | stack_item_label = "${var.stack_item_label}" 38 | 39 | # Cluster parameters 40 | associate_public_ip_address = "true" 41 | ami_override = "${var.ami_override}" 42 | enable_monitoring = "${var.enable_monitoring}" 43 | iam_path = "${var.iam_path}" 44 | instance_based_naming_enabled = "${var.instance_based_naming_enabled}" 45 | 46 | instance_tags = { 47 | "env" = "example" 48 | } 49 | 50 | instance_type = "${var.instance_type}" 51 | logs_bucket_enabled = "true" 52 | logs_bucket_name = "${aws_s3_bucket.logs.id}" 53 | max_size = "${var.max_size}" 54 | min_size = "${var.min_size}" 55 | subnets = ["${var.subnets}"] 56 | user_data_override = "${data.template_file.init.rendered}" 57 | vpc_id = "${var.vpc_id}" 58 | 59 | # Service discovery parameters 60 | service_discovery_enabled = "${var.service_discovery_enabled}" 61 | service_registration_enabled = "${var.service_registration_enabled}" 62 | } 63 | 64 | # Configures ALB for internal dashboards 65 | 66 | ## Creates elastic load balancer security group 67 | resource "aws_security_group" "lb" { 68 | name_prefix = "${var.stack_item_label}-lb-" 69 | description = "${var.stack_item_fullname} load balancer security group" 70 | vpc_id = "${var.vpc_id}" 71 | 72 | tags { 73 | application = "${var.stack_item_fullname}" 74 | managed_by = "terraform" 75 | Name = "${var.stack_item_label}-lb" 76 | } 77 | } 78 | 79 | ### Creates ELB security group rules 80 | resource "aws_security_group_rule" "lb_egress" { 81 | cidr_blocks = ["0.0.0.0/0"] 82 | from_port = 0 83 | protocol = -1 84 | security_group_id = "${aws_security_group.lb.id}" 85 | to_port = 0 86 | type = "egress" 87 | } 88 | 89 | resource "aws_security_group_rule" "lb_http" { 90 | cidr_blocks = ["0.0.0.0/0"] 91 | from_port = 80 92 | protocol = "tcp" 93 | security_group_id = "${aws_security_group.lb.id}" 94 | to_port = 80 95 | type = "ingress" 96 | } 97 | 98 | resource "aws_alb" "lb" { 99 | name = "${var.cluster_label}-${var.stack_item_label}" 100 | security_groups = ["${aws_security_group.lb.id}", "${module.cluster.consul_sg_id}"] 101 | subnets = ["${var.subnets}"] 102 | 103 | tags { 104 | application = "${var.stack_item_fullname}" 105 | managed_by = "terraform" 106 | Name = "${var.stack_item_label}" 107 | } 108 | } 109 | 110 | resource "aws_alb_target_group" "default" { 111 | name = "default-${var.stack_item_label}" 112 | port = 80 113 | protocol = "HTTP" 114 | vpc_id = "${var.vpc_id}" 115 | 116 | health_check { 117 | port = 80 118 | protocol = "HTTP" 119 | } 120 | 121 | tags { 122 | application = "${var.stack_item_fullname}" 123 | Name = "default-${var.stack_item_label}" 124 | managed_by = "terraform" 125 | } 126 | } 127 | 128 | resource "aws_alb_listener" "admin" { 129 | load_balancer_arn = "${aws_alb.lb.arn}" 130 | port = 80 131 | protocol = "HTTP" 132 | 133 | default_action { 134 | target_group_arn = "${aws_alb_target_group.default.arn}" 135 | type = "forward" 136 | } 137 | } 138 | 139 | resource "aws_alb_listener_rule" "consul_rule" { 140 | count = "${var.service_discovery_enabled == "true" ? 1 : 0}" 141 | 142 | listener_arn = "${aws_alb_listener.admin.arn}" 143 | priority = 100 144 | 145 | action { 146 | type = "forward" 147 | target_group_arn = "${module.cluster.consul_target_group_arn}" 148 | } 149 | 150 | condition { 151 | field = "path-pattern" 152 | values = ["/*"] 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /examples/complete/user_data.hcl: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | manage_etc_hosts: True 3 | bootcmd: 4 | - [ cloud-init-per, instance, docker_storage_setup, /usr/bin/docker-storage-setup ] 5 | - service docker restart 6 | 7 | write_files: 8 | - path: /etc/ecs/ecs.config 9 | permissions: '0644' 10 | content: | 11 | ECS_CLUSTER=${cluster_label}-${stack_item_label} 12 | ECS_ENGINE_AUTH_TYPE=dockercfg 13 | 14 | runcmd: 15 | # Configure ECS 16 | - yum install -y aws-cli 17 | - restart ecs 18 | output : { all : '| tee -a /var/log/cloud-init-output.log' } 19 | -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- 1 | # Input variables 2 | 3 | ## Resource tags 4 | variable "cluster_label" { 5 | type = "string" 6 | } 7 | 8 | variable "stack_item_fullname" { 9 | type = "string" 10 | } 11 | 12 | variable "stack_item_label" { 13 | type = "string" 14 | } 15 | 16 | ## Cluster parameters 17 | variable "ami_override" { 18 | type = "string" 19 | } 20 | 21 | variable "enable_monitoring" { 22 | type = "string" 23 | } 24 | 25 | variable "iam_path" { 26 | type = "string" 27 | } 28 | 29 | variable "instance_based_naming_enabled" { 30 | type = "string" 31 | } 32 | 33 | variable "instance_type" { 34 | type = "string" 35 | } 36 | 37 | variable "max_size" { 38 | type = "string" 39 | } 40 | 41 | variable "min_size" { 42 | type = "string" 43 | } 44 | 45 | variable "region" { 46 | type = "string" 47 | } 48 | 49 | variable "subnets" { 50 | type = "list" 51 | } 52 | 53 | variable "vpc_id" { 54 | type = "string" 55 | } 56 | 57 | ## Service discovery parameters 58 | variable "service_discovery_enabled" { 59 | type = "string" 60 | default = "false" 61 | } 62 | 63 | variable "service_registration_enabled" { 64 | type = "string" 65 | default = "false" 66 | } 67 | -------------------------------------------------------------------------------- /examples/prereqs/main.tf: -------------------------------------------------------------------------------- 1 | # AWS Elastic Container Service (ECS) Stack Prerequisites 2 | 3 | ## Configures AWS provider 4 | provider "aws" { 5 | region = "${var.region}" 6 | } 7 | 8 | ## Configures base VPC 9 | module "vpc_base" { 10 | source = "github.com/unifio/terraform-aws-vpc?ref=v0.4.0//base" 11 | 12 | enable_dns = "true" 13 | stack_item_fullname = "${var.stack_item_fullname}" 14 | stack_item_label = "${var.stack_item_label}" 15 | vpc_cidr = "172.16.0.0/24" 16 | } 17 | 18 | ## Configures VPC availabilty zones 19 | module "vpc_az" { 20 | source = "github.com/unifio/terraform-aws-vpc?ref=v0.4.0//az" 21 | 22 | azs_provisioned = 2 23 | lans_per_az = 0 24 | rt_dmz_id = "${module.vpc_base.rt_dmz_id}" 25 | stack_item_fullname = "${var.stack_item_fullname}" 26 | stack_item_label = "${var.stack_item_label}" 27 | vpc_id = "${module.vpc_base.vpc_id}" 28 | } 29 | 30 | ## Configures routing 31 | resource "aws_route" "dmz-to-igw" { 32 | destination_cidr_block = "0.0.0.0/0" 33 | gateway_id = "${module.vpc_base.igw_id}" 34 | route_table_id = "${module.vpc_base.rt_dmz_id}" 35 | } 36 | -------------------------------------------------------------------------------- /examples/prereqs/outputs.tf: -------------------------------------------------------------------------------- 1 | # Output variables 2 | 3 | output "dmz_rt_id" { 4 | value = "${module.vpc_base.rt_dmz_id}" 5 | } 6 | 7 | output "dmz_subnet_ids" { 8 | value = "${module.vpc_az.dmz_ids}" 9 | } 10 | 11 | output "vpc_id" { 12 | value = "${module.vpc_base.vpc_id}" 13 | } 14 | -------------------------------------------------------------------------------- /examples/prereqs/variables.tf: -------------------------------------------------------------------------------- 1 | # Input variables 2 | 3 | variable "region" { 4 | type = "string" 5 | } 6 | 7 | variable "stack_item_fullname" { 8 | type = "string" 9 | } 10 | 11 | variable "stack_item_label" { 12 | type = "string" 13 | } 14 | --------------------------------------------------------------------------------