diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d03bb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# .tfvars files +*.tfvars + +**/.idea +**/*.iml + +**/.build-harness +**/build-harness + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..241026e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +addons: + apt: + packages: + - git + - make + - curl + +install: + - make init + +script: + - make terraform/install + - make terraform/get-plugins + - make terraform/get-modules + - make terraform/lint + - make terraform/validate diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..97d8087 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +SHELL := /bin/bash + +# List of targets the `readme` target should call before generating the readme +export README_DEPS ?= docs/targets.md docs/terraform.md + +-include $(shell curl -sSL -o .build-harness "https://git.io/build-harness"; echo .build-harness) + +## Lint terraform code +lint: + $(SELF) terraform/install terraform/get-modules terraform/get-plugins terraform/lint terraform/validate diff --git a/README.md b/README.md index b31e2d3..880ed6e 100644 --- a/README.md +++ b/README.md @@ -1 +1,222 @@ + + +[![Cloud Posse](https://cloudposse.com/logo-300x69.svg)](https://cloudposse.com) + # terraform-aws-ecs-cloudwatch-autoscaling + + [![Build Status](https://travis-ci.org/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling.svg?branch=master)](https://travis-ci.org/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling.svg)](https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling/releases) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) + + +Terraform module for creating alarms for tracking important changes and occurrences from ECS Services. + + +--- + +This project is part of our comprehensive ["SweetOps"](https://docs.cloudposse.com) approach towards DevOps. + + +It's 100% Open Source and licensed under the [APACHE2](LICENSE). + + + + + + + + + + +## Usage + +```hcl +module "autoscaling" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling.git?ref=tags/0.1.0" + name = "default" + namespace = "cp" + stage = "staging" + service_name = "cp-staging-example-service" + cluster_name = "cp-staging-example-cluster" +} +``` + + + + +## Examples + +For a complete usage example, see [terraform-aws-ecs-web-app module](https://github.com/cloudposse/terraform-aws-ecs-web-app/blob/master/main.tf). + + + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| attributes | List of attributes to add to label. | list | `` | no | +| cluster_name | The name of the ECS cluster where service is to be autoscaled. | string | - | yes | +| delimiter | The delimiter to be used in labels. | string | `-` | no | +| enabled | Whether to create all resources | string | `true` | no | +| max_capacity | Maximum number of running instances of a Service. | string | `2` | no | +| min_capacity | Minimum number of running instances of a Service. | string | `1` | no | +| name | Name (unique identifier for app or service) | string | - | yes | +| namespace | Namespace (e.g. `cp` or `cloudposse`) | string | - | yes | +| scale_down_adjustment | Scaling adjustment to make during scale down event. | string | `-1` | no | +| scale_down_cooldown | Period (in seconds) to wait between scale down events. | string | `300` | no | +| scale_up_adjustment | Scaling adjustment to make during scale up event. | string | `1` | no | +| scale_up_cooldown | Period (in seconds) to wait between scale up events. | string | `60` | no | +| service_name | The name of the ECS Service to autoscale. | string | - | yes | +| stage | Stage (e.g. `prod`, `dev`, `staging`) | string | - | yes | +| tags | Map of key-value pairs to use for tags. | map | `` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| scale_down_policy_arn | ARN of the scale down policy. | +| scale_up_policy_arn | ARN of the scale up policy. | + + + + +## Related Projects + +Check out these related projects. + +- [terraform-aws-ecs-web-app](https://github.com/cloudposse/terraform-aws-ecs-web-app) - Terraform module that implements a web app on ECS and supporting AWS resources +- [terraform-aws-alb](https://github.com/cloudposse/terraform-aws-alb) - Terraform module to provision a standard ALB for HTTP/HTTP traffic +- [terraform-aws-alb-ingress](https://github.com/cloudposse/terraform-aws-alb-ingress) - Terraform module to provision an HTTP style ingress rule based on hostname and path for an ALB +- [terraform-aws-codebuild](https://github.com/cloudposse/terraform-aws-codebuild) - Terraform Module to easily leverage AWS CodeBuild for Continuous Integration +- [terraform-aws-ecr](https://github.com/cloudposse/terraform-aws-ecr) - Terraform Module to manage Docker Container Registries on AWS ECR +- [terraform-aws-ecs-alb-service-task](https://github.com/cloudposse/terraform-aws-ecs-alb-service-task) - Terraform module which implements an ECS service which exposes a web service via ALB. +- [terraform-aws-ecs-codepipeline](https://github.com/cloudposse/terraform-aws-ecs-codepipeline) - Terraform Module for CI/CD with AWS Code Pipeline and Code Build for ECS +- [terraform-aws-ecs-container-definition](https://github.com/cloudposse/terraform-aws-ecs-container-definition) - Terraform module to generate well-formed JSON documents that are passed to the aws_ecs_task_definition Terraform resource + + + +## Help + +**Got a question?** + +File a GitHub [issue](https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling/issues), send us an [email][email] or join our [Slack Community][slack]. + +## Commercial Support + +Work directly with our team of DevOps experts via email, slack, and video conferencing. + +We provide [*commercial support*][commercial_support] for all of our [Open Source][github] projects. As a *Dedicated Support* customer, you have access to our team of subject matter experts at a fraction of the cost of a full-time engineer. + +[![E-Mail](https://img.shields.io/badge/email-hello@cloudposse.com-blue.svg)](mailto:hello@cloudposse.com) + +- **Questions.** We'll use a Shared Slack channel between your team and ours. +- **Troubleshooting.** We'll help you triage why things aren't working. +- **Code Reviews.** We'll review your Pull Requests and provide constructive feedback. +- **Bug Fixes.** We'll rapidly work to fix any bugs in our projects. +- **Build New Terraform Modules.** We'll develop original modules to provision infrastructure. +- **Cloud Architecture.** We'll assist with your cloud strategy and design. +- **Implementation.** We'll provide hands-on support to implement our reference architectures. + + +## Community Forum + +Get access to our [Open Source Community Forum][slack] on Slack. It's **FREE** to join for everyone! Our "SweetOps" community is where you get to talk with others who share a similar vision for how to rollout and manage infrastructure. This is the best place to talk shop, ask questions, solicit feedback, and work together as a community to build *sweet* infrastructure. + +## Contributing + +### Bug Reports & Feature Requests + +Please use the [issue tracker](https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling/issues) to report any bugs or file feature requests. + +### Developing + +If you are interested in being a contributor and want to get involved in developing this project or [help out](https://github.com/orgs/cloudposse/projects/3) with our other projects, we would love to hear from you! Shoot us an [email](mailto:hello@cloudposse.com). + +In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow. + + 1. **Fork** the repo on GitHub + 2. **Clone** the project to your own machine + 3. **Commit** changes to your own branch + 4. **Push** your work back up to your fork + 5. Submit a **Pull Request** so that we can review your changes + +**NOTE:** Be sure to merge the latest changes from "upstream" before making a pull request! + + +## Copyright + +Copyright © 2017-2018 [Cloud Posse, LLC](https://cloudposse.com) + + + +## License + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +See [LICENSE](LICENSE) for full details. + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + + + + + + + + + +## Trademarks + +All other trademarks referenced herein are the property of their respective owners. + +## About + +This project is maintained and funded by [Cloud Posse, LLC][website]. Like it? Please let us know at + +[![Cloud Posse](https://cloudposse.com/logo-300x69.svg)](https://cloudposse.com) + +We're a [DevOps Professional Services][hire] company based in Los Angeles, CA. We love [Open Source Software](https://github.com/cloudposse/)! + +We offer paid support on all of our projects. + +Check out [our other projects][github], [apply for a job][jobs], or [hire us][hire] to help with your cloud strategy and implementation. + + [docs]: https://docs.cloudposse.com/ + [website]: https://cloudposse.com/ + [github]: https://github.com/cloudposse/ + [commercial_support]: https://github.com/orgs/cloudposse/projects + [jobs]: https://cloudposse.com/jobs/ + [hire]: https://cloudposse.com/contact/ + [slack]: https://slack.cloudposse.com/ + [linkedin]: https://www.linkedin.com/company/cloudposse + [twitter]: https://twitter.com/cloudposse/ + [email]: mailto:hello@cloudposse.com + + +### Contributors + +| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Jamie Nelson][Jamie-BitFlight_avatar]][Jamie-BitFlight_homepage]
[Jamie Nelson][Jamie-BitFlight_homepage] | [![Sarkis Varozian][sarkis_avatar]][sarkis_homepage]
[Sarkis Varozian][sarkis_homepage] | +|---|---|---| + + [osterman_homepage]: https://github.com/osterman + [osterman_avatar]: https://github.com/osterman.png?size=150 + [Jamie-BitFlight_homepage]: https://github.com/Jamie-BitFlight + [Jamie-BitFlight_avatar]: https://github.com/Jamie-BitFlight.png?size=150 + [sarkis_homepage]: https://github.com/sarkis + [sarkis_avatar]: https://github.com/sarkis.png?size=150 + + diff --git a/README.yaml b/README.yaml new file mode 100644 index 0000000..36830f4 --- /dev/null +++ b/README.yaml @@ -0,0 +1,101 @@ +--- +# +# This is the canonical configuration for the `README.md` +# Run `make readme` to rebuild the `README.md` +# + +# Name of this project +name: terraform-aws-ecs-cloudwatch-autoscaling + +# Logo for this project +#logo: docs/logo.png + +# License of this project +license: "APACHE2" + +# Canonical GitHub repo +github_repo: cloudposse/terraform-aws-ecs-cloudwatch-autoscaling + +# Badges to display +badges: + - name: "Build Status" + image: "https://travis-ci.org/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling.svg?branch=master" + url: "https://travis-ci.org/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling" + - name: "Latest Release" + image: "https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling.svg" + url: "https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling/releases" + - name: "Slack Community" + image: "https://slack.cloudposse.com/badge.svg" + url: "https://slack.cloudposse.com" + +related: + - name: "terraform-aws-ecs-web-app" + description: "Terraform module that implements a web app on ECS and supporting AWS resources" + url: "https://github.com/cloudposse/terraform-aws-ecs-web-app" + + - name: "terraform-aws-alb" + description: "Terraform module to provision a standard ALB for HTTP/HTTP traffic" + url: "https://github.com/cloudposse/terraform-aws-alb" + + - name: "terraform-aws-alb-ingress" + description: "Terraform module to provision an HTTP style ingress rule based on hostname and path for an ALB" + url: "https://github.com/cloudposse/terraform-aws-alb-ingress" + + - name: "terraform-aws-codebuild" + description: "Terraform Module to easily leverage AWS CodeBuild for Continuous Integration" + url: "https://github.com/cloudposse/terraform-aws-codebuild" + + - name: "terraform-aws-ecr" + description: "Terraform Module to manage Docker Container Registries on AWS ECR" + url: "https://github.com/cloudposse/terraform-aws-ecr" + + - name: "terraform-aws-ecs-alb-service-task" + description: "Terraform module which implements an ECS service which exposes a web service via ALB." + url: "https://github.com/cloudposse/terraform-aws-ecs-alb-service-task" + + - name: "terraform-aws-ecs-codepipeline" + description: "Terraform Module for CI/CD with AWS Code Pipeline and Code Build for ECS" + url: "https://github.com/cloudposse/terraform-aws-ecs-codepipeline" + + - name: "terraform-aws-ecs-container-definition" + description: "Terraform module to generate well-formed JSON documents that are passed to the aws_ecs_task_definition Terraform resource" + url: "https://github.com/cloudposse/terraform-aws-ecs-container-definition" + + +# Short description of this project +description: |- + Terraform module for creating alarms for tracking important changes and occurrences from ECS Services. + +# How to use this project +usage: |- + ```hcl + module "autoscaling" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling.git?ref=tags/0.1.0" + name = "default" + namespace = "cp" + stage = "staging" + service_name = "cp-staging-example-service" + cluster_name = "cp-staging-example-cluster" + } + ``` + +# Example usage +examples: |- + For a complete usage example, see [terraform-aws-ecs-web-app module](https://github.com/cloudposse/terraform-aws-ecs-web-app/blob/master/main.tf). + +# How to get started quickly +#quickstart: |- +# Here's how to get started... + +# Other files to include in this README from the project folder +include: + - "docs/terraform.md" + +# Contributors to this project +contributors: + - name: "Erik Osterman" + github: "osterman" + - name: "Jamie Nelson" + github: "Jamie-BitFlight" + - name: "Sarkis Varozian" + github: "sarkis" diff --git a/docs/targets.md b/docs/targets.md new file mode 100644 index 0000000..09c39cd --- /dev/null +++ b/docs/targets.md @@ -0,0 +1,9 @@ +## Makefile Targets +``` +Available targets: + + help This help screen + help/all Display help for all targets + lint Lint terraform code + +``` diff --git a/docs/terraform.md b/docs/terraform.md new file mode 100644 index 0000000..7cc5521 --- /dev/null +++ b/docs/terraform.md @@ -0,0 +1,28 @@ + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| attributes | List of attributes to add to label. | list | `` | no | +| cluster_name | The name of the ECS cluster where service is to be autoscaled. | string | - | yes | +| delimiter | The delimiter to be used in labels. | string | `-` | no | +| enabled | Whether to create all resources | string | `true` | no | +| max_capacity | Maximum number of running instances of a Service. | string | `2` | no | +| min_capacity | Minimum number of running instances of a Service. | string | `1` | no | +| name | Name (unique identifier for app or service) | string | - | yes | +| namespace | Namespace (e.g. `cp` or `cloudposse`) | string | - | yes | +| scale_down_adjustment | Scaling adjustment to make during scale down event. | string | `-1` | no | +| scale_down_cooldown | Period (in seconds) to wait between scale down events. | string | `300` | no | +| scale_up_adjustment | Scaling adjustment to make during scale up event. | string | `1` | no | +| scale_up_cooldown | Period (in seconds) to wait between scale up events. | string | `60` | no | +| service_name | The name of the ECS Service to autoscale. | string | - | yes | +| stage | Stage (e.g. `prod`, `dev`, `staging`) | string | - | yes | +| tags | Map of key-value pairs to use for tags. | map | `` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| scale_down_policy_arn | ARN of the scale down policy. | +| scale_up_policy_arn | ARN of the scale up policy. | + diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..b429c48 --- /dev/null +++ b/main.tf @@ -0,0 +1,66 @@ +module "scale_up_label" { + source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=tags/0.1.3" + name = "${var.name}" + namespace = "${var.namespace}" + stage = "${var.stage}" + attributes = "${compact(concat(var.attributes, list("up")))}" +} + +module "scale_down_label" { + source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=tags/0.1.3" + name = "${var.name}" + namespace = "${var.namespace}" + stage = "${var.stage}" + attributes = "${compact(concat(var.attributes, list("down")))}" +} + +locals { + enabled = "${var.enabled == "true" ? 1 : 0}" +} + +resource "aws_appautoscaling_target" "default" { + count = "${local.enabled}" + service_namespace = "ecs" + resource_id = "service/${var.cluster_name}/${var.service_name}" + scalable_dimension = "ecs:service:DesiredCount" + min_capacity = "${var.min_capacity}" + max_capacity = "${var.max_capacity}" +} + +resource "aws_appautoscaling_policy" "up" { + count = "${local.enabled}" + name = "${module.scale_up_label.id}" + service_namespace = "ecs" + resource_id = "service/${var.cluster_name}/${var.service_name}" + scalable_dimension = "ecs:service:DesiredCount" + + step_scaling_policy_configuration { + adjustment_type = "ChangeInCapacity" + cooldown = "${var.scale_up_cooldown}" + metric_aggregation_type = "Average" + + step_adjustment { + metric_interval_lower_bound = 0 + scaling_adjustment = "${var.scale_up_adjustment}" + } + } +} + +resource "aws_appautoscaling_policy" "down" { + count = "${local.enabled}" + name = "${module.scale_down_label.id}" + service_namespace = "ecs" + resource_id = "service/${var.cluster_name}/${var.service_name}" + scalable_dimension = "ecs:service:DesiredCount" + + step_scaling_policy_configuration { + adjustment_type = "ChangeInCapacity" + cooldown = "${var.scale_down_cooldown}" + metric_aggregation_type = "Average" + + step_adjustment { + metric_interval_upper_bound = 0 + scaling_adjustment = "${var.scale_down_adjustment}" + } + } +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..9f3f7ff --- /dev/null +++ b/outputs.tf @@ -0,0 +1,9 @@ +output "scale_up_policy_arn" { + description = "ARN of the scale up policy." + value = "${join("", aws_appautoscaling_policy.up.*.arn)}" +} + +output "scale_down_policy_arn" { + description = "ARN of the scale down policy." + value = "${join("", aws_appautoscaling_policy.down.*.arn)}" +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..d7e3671 --- /dev/null +++ b/variables.tf @@ -0,0 +1,84 @@ +variable "name" { + type = "string" + description = "Name (unique identifier for app or service)" +} + +variable "namespace" { + type = "string" + description = "Namespace (e.g. `cp` or `cloudposse`)" +} + +variable "delimiter" { + type = "string" + description = "The delimiter to be used in labels." + default = "-" +} + +variable "stage" { + type = "string" + description = "Stage (e.g. `prod`, `dev`, `staging`)" +} + +variable "attributes" { + type = "list" + description = "List of attributes to add to label." + default = [] +} + +variable "tags" { + type = "map" + description = "Map of key-value pairs to use for tags." + default = {} +} + +variable "enabled" { + type = "string" + description = "Whether to create all resources" + default = "true" +} + +variable "min_capacity" { + type = "string" + description = "Minimum number of running instances of a Service." + default = "1" +} + +variable "max_capacity" { + type = "string" + description = "Maximum number of running instances of a Service." + default = "2" +} + +variable "cluster_name" { + type = "string" + description = "The name of the ECS cluster where service is to be autoscaled." +} + +variable "service_name" { + type = "string" + description = "The name of the ECS Service to autoscale." +} + +variable "scale_up_adjustment" { + type = "string" + description = "Scaling adjustment to make during scale up event." + default = "1" +} + +variable "scale_up_cooldown" { + type = "string" + description = "Period (in seconds) to wait between scale up events." + default = "60" +} + +variable "scale_down_adjustment" { + type = "string" + description = "Scaling adjustment to make during scale down event." + default = "-1" +} + +variable "scale_down_cooldown" { + type = "string" + description = "Period (in seconds) to wait between scale down events." + default = "300" +}