├── .gitignore ├── Rakefile ├── policy ├── outputs.tf ├── main.tf └── variables.tf ├── examples ├── complete │ ├── user_data.tpl │ ├── variables.tf │ └── main.tf └── basic │ ├── variables.tf │ └── main.tf ├── .env.docker ├── group ├── templates │ ├── tag.tpl │ └── name.tpl ├── lc │ ├── outputs.tf │ ├── variables.tf │ └── main.tf ├── outputs.tf ├── asg │ ├── outputs.tf │ ├── variables.tf │ └── main.tf ├── main.tf └── variables.tf ├── .vscode └── settings.json ├── data ├── environments.yaml ├── globals.yaml └── stacks │ ├── defaults.yaml │ ├── lc-ebs-new.yaml │ ├── lc-ebs-snap.yaml │ ├── lc-options.yaml │ ├── asg-elb.yaml │ ├── asg-elb-wait.yaml │ ├── common.yaml │ └── asg-options.yaml ├── covalence.yaml ├── .circleci └── config.yml ├── CHANGELOG.md ├── bin └── covalence ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.tfstate* 2 | .terraform/ 3 | spec/reports/* 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'covalence/environment_tasks' 3 | require 'covalence/spec_tasks' 4 | -------------------------------------------------------------------------------- /policy/outputs.tf: -------------------------------------------------------------------------------- 1 | # Outputs 2 | 3 | output "sns_arn" { 4 | value = "${aws_sns_topic.sns_asg.arn}" 5 | } 6 | -------------------------------------------------------------------------------- /examples/complete/user_data.tpl: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | runcmd: 3 | - yum install -y aws-cli 4 | output : { all : '| tee -a /var/log/cloud-init-output.log' } 5 | -------------------------------------------------------------------------------- /.env.docker: -------------------------------------------------------------------------------- 1 | AWS_REGION=us-east-2 2 | COVALENCE_PACKER_DIR=./ 3 | COVALENCE_TERRAFORM_DIR=./ 4 | COVALENCE_TEST_ENVS=basic 5 | CHECKPOINT_DISABLE=1 6 | GODEBUG=netdns=cgo 7 | USER=root 8 | -------------------------------------------------------------------------------- /group/templates/tag.tpl: -------------------------------------------------------------------------------- 1 | - "`which aws` ec2 create-tags --region ${region} --resources `curl -s http://169.254.169.254/latest/meta-data/instance-id` --tags Key=${key},Value=${value}" 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.renderWhitespace": "all", 3 | "files.associations": { 4 | "*.tpl": "hcl" 5 | }, 6 | "files.insertFinalNewline": true, 7 | "files.trimTrailingWhitespace": true 8 | } 9 | -------------------------------------------------------------------------------- /data/environments.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Environments 3 | 4 | environments: 5 | basic: 6 | - defaults 7 | - lc-options 8 | - asg-options 9 | 10 | complete: 11 | - lc-ebs-new 12 | - lc-ebs-snap 13 | - asg-elb 14 | - asg-elb-wait 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /group/lc/outputs.tf: -------------------------------------------------------------------------------- 1 | # Outputs 2 | 3 | output "lc_id" { 4 | value = "${coalesce(join(",",aws_launch_configuration.lc.*.id),join(",",aws_launch_configuration.lc_ebs.*.id))}" 5 | } 6 | 7 | output "sg_id" { 8 | value = "${aws_security_group.sg_asg.id}" 9 | } 10 | -------------------------------------------------------------------------------- /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 | 8 | ## VPC 9 | vpc_id: 'vpc-0f986c66' 10 | vpc_region: 'us-east-2' 11 | vpc_subnets: 'subnet-3315e85a,subnet-3bbaaf43,subnet-ec1326a6' 12 | -------------------------------------------------------------------------------- /group/outputs.tf: -------------------------------------------------------------------------------- 1 | # Outputs 2 | 3 | output "asg_id" { 4 | value = "${module.asg.asg_id}" 5 | } 6 | 7 | output "asg_name" { 8 | value = "${module.asg.asg_name}" 9 | } 10 | 11 | output "lc_id" { 12 | value = "${module.lc.lc_id}" 13 | } 14 | 15 | output "sg_id" { 16 | value = "${module.lc.sg_id}" 17 | } 18 | -------------------------------------------------------------------------------- /group/asg/outputs.tf: -------------------------------------------------------------------------------- 1 | # Outputs 2 | 3 | output "asg_id" { 4 | value = "${coalesce(join(",",aws_autoscaling_group.asg.*.id),join(",",aws_autoscaling_group.asg_elb.*.id))}" 5 | } 6 | 7 | output "asg_name" { 8 | value = "${coalesce(join(",",aws_autoscaling_group.asg.*.name),join(",",aws_autoscaling_group.asg_elb.*.name))}" 9 | } 10 | -------------------------------------------------------------------------------- /group/templates/name.tpl: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | runcmd: 3 | - "`which aws` ec2 create-tags --region=${region} --resources `curl -s http://169.254.169.254/latest/meta-data/instance-id` --tags Key=Name,Value=${name_prefix}-`curl -s http://169.254.169.254/latest/meta-data/instance-id | tr -d 'i-'`" 4 | output : { all : '| tee -a /var/log/cloud-init-output.log' } 5 | -------------------------------------------------------------------------------- /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-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::basic::vars: 16 | # ASG parameters 17 | max_size: '1' 18 | -------------------------------------------------------------------------------- /data/stacks/lc-ebs-new.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Advanced LC new EBS example 3 | 4 | ## Module 5 | lc-ebs-new::module: 'examples/complete' 6 | 7 | ## State storage 8 | lc-ebs-new::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | # LC parameters 17 | ebs_vol_device_name: '/dev/xvdb' 18 | ebs_vol_encrypted: 'true' 19 | ebs_vol_size: '1' 20 | root_vol_size: '10' 21 | 22 | # ASG parameters 23 | max_size: '1' 24 | 25 | # TODO: Add test for io1 ebs volume 26 | -------------------------------------------------------------------------------- /data/stacks/lc-ebs-snap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Advanced LC EBS snapshot example 3 | 4 | ## Module 5 | lc-ebs-snap::module: 'examples/complete' 6 | 7 | ## State storage 8 | lc-ebs-snap::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | # LC parameters 17 | ebs_vol_device_name: '/dev/xvdb' 18 | ## amzn-ami-minimal-hvm-2016.03.3.x86_64 19 | ebs_vol_snapshot_id: 'snap-08b95a31adefd036f' 20 | 21 | # ASG parameters 22 | max_size: '1' 23 | 24 | # TODO: Add test for io1 ebs volume 25 | -------------------------------------------------------------------------------- /data/stacks/lc-options.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Basic LC options example 3 | 4 | ## Module 5 | lc-options::module: 'examples/basic' 6 | 7 | ## State storage 8 | lc-options::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::basic::vars: 16 | # LC parameters 17 | associate_public_ip_address: 'true' 18 | enable_monitoring: 'true' 19 | key_name: 'unifio_ops_pub' 20 | security_groups: 'sg-d4f906bd' 21 | 22 | # ASG parameters 23 | max_size: '1' 24 | 25 | # TODO: Add test for spot price 26 | # TODO: Add test for io1 root volume 27 | -------------------------------------------------------------------------------- /data/stacks/asg-elb.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Advanced ASG ELB example 3 | 4 | ## Module 5 | asg-elb::module: 'examples/complete' 6 | 7 | ## State storage 8 | asg-elb::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | # Resource tags 17 | env_tag: 'example' 18 | team_tag: 'engineering' 19 | 20 | # LC parameters 21 | ebs_vol_device_name: '/dev/xvdb' 22 | ## amzn-ami-minimal-hvm-2016.03.3.x86_64 23 | ebs_vol_snapshot_id: 'snap-08b95a31adefd036f' 24 | instance_based_naming_enabled: 'true' 25 | instance_name_prefix: 'example' 26 | 27 | # ASG parameters 28 | max_size: '2' 29 | min_elb_capacity: '1' 30 | -------------------------------------------------------------------------------- /data/stacks/asg-elb-wait.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Advanced ASG ELB wait example 3 | 4 | ## Module 5 | asg-elb-wait::module: 'examples/complete' 6 | 7 | ## State storage 8 | asg-elb-wait::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::complete::vars: 16 | # Resource tags 17 | env_tag: 'example' 18 | team_tag: 'engineering' 19 | 20 | # LC parameters 21 | ebs_vol_device_name: '/dev/xvdb' 22 | ## amzn-ami-minimal-hvm-2016.03.3.x86_64 23 | ebs_vol_snapshot_id: 'snap-08b95a31adefd036f' 24 | instance_based_naming_enabled: 'true' 25 | instance_name_prefix: 'example' 26 | 27 | # ASG parameters 28 | desired_capacity: '2' 29 | max_size: '3' 30 | min_elb_capacity: '1' 31 | wait_for_elb_capacity: '2' 32 | -------------------------------------------------------------------------------- /data/stacks/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Common resources 3 | 4 | ## Basic example 5 | examples::basic::vars: 6 | # Amazon Linux AMI 2016.09.1.20170119 x86_64 HVM GP2 7 | ami: 'ami-c55673a0' 8 | instance_type: 't2.nano' 9 | min_size: '1' 10 | region: "%{hiera('vpc_region')}" 11 | stack_item_fullname: 'Basic Examples' 12 | stack_item_label: 'bsc' 13 | subnets: "%{hiera('vpc_subnets')}" 14 | vpc_id: "%{hiera('vpc_id')}" 15 | 16 | ## Advanced example 17 | examples::complete::vars: 18 | # Amazon Linux AMI 2016.09.1.20170119 x86_64 HVM GP2 19 | ami: 'ami-c55673a0' 20 | enable_monitoring: 'true' 21 | instance_type: 't2.nano' 22 | key_name: 'unifio_ops_pub' 23 | min_size: '1' 24 | region: "%{hiera('vpc_region')}" 25 | stack_item_fullname: 'Complete Examples' 26 | stack_item_label: 'cmpl' 27 | subnets: "%{hiera('vpc_subnets')}" 28 | vpc_id: "%{hiera('vpc_id')}" 29 | -------------------------------------------------------------------------------- /data/stacks/asg-options.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Basic ASG options example 3 | 4 | ## Module 5 | asg-options::module: 'examples/basic' 6 | 7 | ## State storage 8 | asg-options::state: 9 | - s3: 10 | bucket: "%{hiera('tf_state_bucket')}" 11 | name: "terraform-aws-asg/%{environment}" 12 | region: "%{hiera('tf_state_region')}" 13 | 14 | ## Input variables 15 | examples::basic::vars: 16 | # LC parameters 17 | associate_public_ip_address: 'true' 18 | enable_monitoring: 'true' 19 | key_name: 'unifio_ops_pub' 20 | security_groups: 'sg-d4f906bd' 21 | 22 | # ASG parameters 23 | default_cooldown: '400' 24 | desired_capacity: '2' 25 | enabled_metrics: 'GroupTotalInstances,GroupMinSize,GroupMaxSize' 26 | force_delete: 'true' 27 | hc_grace_period: '400' 28 | max_size: '3' 29 | protect_from_scale_in: 'true' 30 | suspended_processes: 'ScheduledActions,AddToLoadBalancer' 31 | termination_policies: 'OldestInstance' 32 | 33 | # TODO: Add test for placement group 34 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | common: &common 2 | working_directory: ~/repo 3 | 4 | docker: 5 | - image: unifio/ci:3.0.551-ruby-2.5.1 6 | 7 | environment: 8 | AWS_REGION: 'us-east-2' 9 | TF_PLUGIN_CACHE_DIR: '/root/.terraform.d/plugin-cache' 10 | 11 | version: 2 12 | 13 | jobs: 14 | build: 15 | <<: *common 16 | 17 | steps: 18 | - checkout 19 | 20 | - run: 21 | name: Verify 22 | environment: 23 | CI_REPORTS: 'reports' 24 | COVALENCE_TEST_ENVS: 'basic,complete' 25 | command: | 26 | mkdir reports 27 | bundle exec rake ci 28 | - store_test_results: 29 | path: reports 30 | 31 | - run: 32 | name: Basic 33 | command: | 34 | bundle exec rake basic:defaults:apply 35 | ## TODO: Currently need to run this twice to account for a race condition in LC updates 36 | bundle exec rake basic:lc-options:apply || true 37 | bundle exec rake basic:lc-options:apply 38 | bundle exec rake basic:asg-options:apply 39 | bundle exec rake basic:asg-options:destroy 40 | 41 | - run: 42 | name: Complete 43 | command: | 44 | bundle exec rake complete:lc-ebs-new:apply 45 | ## TODO: Currently need to run this twice to account for a race condition in LC updates 46 | bundle exec rake complete:lc-ebs-snap:apply || true 47 | bundle exec rake complete:lc-ebs-snap:apply 48 | bundle exec rake complete:lc-ebs-snap:destroy 49 | bundle exec rake complete:asg-elb:apply 50 | bundle exec rake complete:asg-elb-wait:apply 51 | bundle exec rake complete:asg-elb-wait:destroy 52 | 53 | experimental: 54 | notify: 55 | branches: 56 | only: 57 | - master 58 | -------------------------------------------------------------------------------- /policy/main.tf: -------------------------------------------------------------------------------- 1 | # Simple scaling auto scaling policy 2 | 3 | ## Set Terraform version constraint 4 | terraform { 5 | required_version = "> 0.8.0" 6 | } 7 | 8 | ## Creates simple scaling policy 9 | resource "aws_autoscaling_policy" "asg_policy_simple" { 10 | adjustment_type = "${var.adjustment_type}" 11 | autoscaling_group_name = "${var.asg_name}" 12 | cooldown = "${var.cooldown}" 13 | name = "${var.stack_item_label}" 14 | policy_type = "SimpleScaling" 15 | scaling_adjustment = "${var.scaling_adjustment}" 16 | } 17 | 18 | ## Creates Simple Notification Service (SNS) topic 19 | resource "aws_sns_topic" "sns_asg" { 20 | display_name = "${var.stack_item_fullname} ASG SNS topic" 21 | name = "${var.stack_item_label}-asg" 22 | } 23 | 24 | ## Configures autoscaling notifications 25 | resource "aws_autoscaling_notification" "asg_notify" { 26 | group_names = ["${var.asg_name}"] 27 | notifications = ["${var.notifications}"] 28 | topic_arn = "${aws_sns_topic.sns_asg.arn}" 29 | } 30 | 31 | ## Creates CloudWatch monitor 32 | resource "aws_cloudwatch_metric_alarm" "monitor_asg" { 33 | actions_enabled = true 34 | alarm_actions = ["${aws_autoscaling_policy.asg_policy_simple.arn}"] 35 | alarm_description = "${var.stack_item_fullname} ASG Monitor" 36 | alarm_name = "${var.stack_item_label}-asg" 37 | comparison_operator = "${var.comparison_operator}" 38 | 39 | dimensions = { 40 | "AutoScalingGroupName" = "${var.asg_name}" 41 | } 42 | 43 | evaluation_periods = "${var.evaluation_periods}" 44 | metric_name = "${var.metric_name}" 45 | namespace = "${var.name_space}" 46 | period = "${var.period}" 47 | statistic = "${lookup(var.valid_statistics, var.statistic)}" 48 | threshold = "${var.threshold}" 49 | treat_missing_data = "${lookup(var.valid_missing_data, var.treat_missing_data)}" 50 | } 51 | -------------------------------------------------------------------------------- /group/lc/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 | ## Allow override of resource naming 13 | variable "lc_sg_name_prefix_override" { 14 | type = "string" 15 | } 16 | 17 | ## VPC parameters 18 | variable "vpc_id" { 19 | type = "string" 20 | } 21 | 22 | ## LC parameters 23 | variable "associate_public_ip_address" { 24 | type = "string" 25 | } 26 | 27 | variable "ami" { 28 | type = "string" 29 | } 30 | 31 | variable "ebs_optimized" { 32 | type = "string" 33 | } 34 | 35 | variable "ebs_vol_del_on_term" { 36 | type = "string" 37 | } 38 | 39 | variable "ebs_vol_device_name" { 40 | type = "string" 41 | } 42 | 43 | variable "ebs_vol_encrypted" { 44 | type = "string" 45 | } 46 | 47 | variable "ebs_vol_snapshot_id" { 48 | type = "string" 49 | } 50 | 51 | variable "ebs_vol_iops" { 52 | type = "string" 53 | } 54 | 55 | variable "ebs_vol_size" { 56 | type = "string" 57 | } 58 | 59 | variable "ebs_vol_type" { 60 | type = "string" 61 | } 62 | 63 | variable "enable_monitoring" { 64 | type = "string" 65 | } 66 | 67 | variable "instance_profile" { 68 | type = "string" 69 | } 70 | 71 | variable "instance_type" { 72 | type = "string" 73 | } 74 | 75 | variable "key_name" { 76 | type = "string" 77 | } 78 | 79 | variable "placement_tenancy" { 80 | type = "string" 81 | } 82 | 83 | variable "root_vol_del_on_term" { 84 | type = "string" 85 | } 86 | 87 | variable "root_vol_iops" { 88 | type = "string" 89 | } 90 | 91 | variable "root_vol_size" { 92 | type = "string" 93 | } 94 | 95 | variable "root_vol_type" { 96 | type = "string" 97 | } 98 | 99 | variable "security_groups" { 100 | type = "list" 101 | } 102 | 103 | variable "spot_price" { 104 | type = "string" 105 | } 106 | 107 | variable "user_data" { 108 | type = "string" 109 | } 110 | -------------------------------------------------------------------------------- /group/asg/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 | variable "additional_asg_tags" { 13 | type = "list" 14 | default = [] 15 | } 16 | 17 | ## Allow override of resource naming 18 | variable "asg_name_override" { 19 | type = "string" 20 | } 21 | 22 | variable "propagate_name_at_launch" { 23 | type = "string" 24 | default = "true" 25 | } 26 | 27 | ## VPC parameters 28 | variable "subnets" { 29 | type = "list" 30 | } 31 | 32 | ## LC parameters 33 | variable "lc_id" { 34 | type = "string" 35 | } 36 | 37 | ## ASG parameters 38 | 39 | variable "default_cooldown" { 40 | type = "string" 41 | } 42 | 43 | variable "desired_capacity" { 44 | type = "string" 45 | } 46 | 47 | variable "enabled_metrics" { 48 | type = "list" 49 | } 50 | 51 | variable "force_delete" { 52 | type = "string" 53 | } 54 | 55 | variable "hc_check_type" { 56 | type = "string" 57 | } 58 | 59 | variable "hc_grace_period" { 60 | type = "string" 61 | } 62 | 63 | variable "max_size" { 64 | type = "string" 65 | } 66 | 67 | variable "metrics_granularity" { 68 | type = "string" 69 | } 70 | 71 | variable "min_size" { 72 | type = "string" 73 | } 74 | 75 | variable "placement_group" { 76 | type = "string" 77 | } 78 | 79 | variable "protect_from_scale_in" { 80 | type = "string" 81 | } 82 | 83 | variable "suspended_processes" { 84 | type = "list" 85 | } 86 | 87 | variable "termination_policies" { 88 | type = "list" 89 | } 90 | 91 | variable "wait_for_capacity_timeout" { 92 | type = "string" 93 | } 94 | 95 | ## ELB parameters 96 | variable "load_balancers" { 97 | type = "list" 98 | } 99 | 100 | variable "min_elb_capacity" { 101 | type = "string" 102 | } 103 | 104 | variable "target_group_arns" { 105 | type = "list" 106 | } 107 | 108 | variable "wait_for_elb_capacity" { 109 | type = "string" 110 | } 111 | -------------------------------------------------------------------------------- /examples/basic/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 | ## VPC parameters 13 | variable "subnets" { 14 | type = "string" 15 | } 16 | 17 | variable "region" { 18 | type = "string" 19 | } 20 | 21 | variable "vpc_id" { 22 | type = "string" 23 | } 24 | 25 | ## LC parameters 26 | variable "ami" { 27 | type = "string" 28 | } 29 | 30 | variable "associate_public_ip_address" { 31 | type = "string" 32 | default = "" 33 | } 34 | 35 | variable "enable_monitoring" { 36 | type = "string" 37 | default = "" 38 | } 39 | 40 | variable "instance_type" { 41 | type = "string" 42 | } 43 | 44 | variable "key_name" { 45 | type = "string" 46 | default = "" 47 | } 48 | 49 | variable "security_groups" { 50 | type = "string" 51 | default = "" 52 | } 53 | 54 | variable "spot_price" { 55 | type = "string" 56 | default = "" 57 | } 58 | 59 | ## ASG parameters 60 | variable "default_cooldown" { 61 | type = "string" 62 | default = "" 63 | } 64 | 65 | variable "desired_capacity" { 66 | type = "string" 67 | default = "" 68 | } 69 | 70 | variable "enabled_metrics" { 71 | type = "string" 72 | default = "" 73 | } 74 | 75 | variable "force_delete" { 76 | type = "string" 77 | default = "" 78 | } 79 | 80 | variable "hc_grace_period" { 81 | type = "string" 82 | default = "" 83 | } 84 | 85 | variable "max_size" { 86 | type = "string" 87 | } 88 | 89 | variable "min_size" { 90 | type = "string" 91 | } 92 | 93 | variable "protect_from_scale_in" { 94 | type = "string" 95 | default = "" 96 | } 97 | 98 | variable "suspended_processes" { 99 | type = "string" 100 | default = "" 101 | } 102 | 103 | variable "termination_policies" { 104 | type = "string" 105 | default = "" 106 | } 107 | 108 | variable "wait_for_capacity_timeout" { 109 | type = "string" 110 | default = "" 111 | } 112 | -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- 1 | # Input Variables 2 | 3 | ## Resource tags 4 | variable "stack_item_label" { 5 | type = "string" 6 | } 7 | 8 | variable "stack_item_fullname" { 9 | type = "string" 10 | } 11 | 12 | variable "env_tag" { 13 | type = "string" 14 | default = "" 15 | } 16 | 17 | variable "team_tag" { 18 | type = "string" 19 | default = "" 20 | } 21 | 22 | ## VPC parameters 23 | variable "vpc_id" { 24 | type = "string" 25 | } 26 | 27 | variable "region" { 28 | type = "string" 29 | } 30 | 31 | variable "subnets" { 32 | type = "string" 33 | } 34 | 35 | ## LC parameters 36 | variable "ami" { 37 | type = "string" 38 | } 39 | 40 | variable "ebs_vol_device_name" { 41 | type = "string" 42 | } 43 | 44 | variable "ebs_vol_encrypted" { 45 | type = "string" 46 | default = "" 47 | } 48 | 49 | variable "ebs_vol_size" { 50 | type = "string" 51 | default = "" 52 | } 53 | 54 | variable "ebs_vol_snapshot_id" { 55 | type = "string" 56 | default = "" 57 | } 58 | 59 | variable "enable_monitoring" { 60 | type = "string" 61 | default = "" 62 | } 63 | 64 | variable "instance_based_naming_enabled" { 65 | type = "string" 66 | default = "" 67 | } 68 | 69 | variable "instance_name_prefix" { 70 | type = "string" 71 | default = "" 72 | } 73 | 74 | variable "instance_type" { 75 | type = "string" 76 | } 77 | 78 | variable "key_name" { 79 | type = "string" 80 | default = "" 81 | } 82 | 83 | variable "root_vol_size" { 84 | type = "string" 85 | default = "" 86 | } 87 | 88 | ## ASG parameters 89 | variable "desired_capacity" { 90 | type = "string" 91 | default = "" 92 | } 93 | 94 | variable "max_size" { 95 | type = "string" 96 | } 97 | 98 | variable "min_elb_capacity" { 99 | type = "string" 100 | default = "" 101 | } 102 | 103 | variable "min_size" { 104 | type = "string" 105 | } 106 | 107 | variable "wait_for_elb_capacity" { 108 | type = "string" 109 | default = "" 110 | } 111 | 112 | ## ELB parameters 113 | variable "internal" { 114 | type = "string" 115 | default = "true" 116 | } 117 | -------------------------------------------------------------------------------- /examples/basic/main.tf: -------------------------------------------------------------------------------- 1 | # Basic ASG Example 2 | 3 | ## Configures providers 4 | provider "aws" { 5 | region = "${var.region}" 6 | } 7 | 8 | ## Adds security group rules 9 | resource "aws_security_group_rule" "sg_asg_egress" { 10 | cidr_blocks = ["0.0.0.0/0"] 11 | from_port = 0 12 | protocol = -1 13 | security_group_id = "${module.example.sg_id}" 14 | to_port = 0 15 | type = "egress" 16 | 17 | lifecycle { 18 | create_before_destroy = true 19 | } 20 | } 21 | 22 | resource "aws_security_group_rule" "sg_asg_ssh" { 23 | cidr_blocks = ["0.0.0.0/0"] 24 | from_port = 22 25 | protocol = "tcp" 26 | security_group_id = "${module.example.sg_id}" 27 | to_port = 22 28 | type = "ingress" 29 | 30 | lifecycle { 31 | create_before_destroy = true 32 | } 33 | } 34 | 35 | ## Provisions basic autoscaling group 36 | module "example" { 37 | # Example GitHub source 38 | #source = "github.com/unifio/terraform-aws-asg//group" 39 | source = "../../group" 40 | 41 | # Resource tags 42 | stack_item_fullname = "${var.stack_item_fullname}" 43 | stack_item_label = "${var.stack_item_label}" 44 | 45 | # VPC parameters 46 | subnets = ["${split(",",var.subnets)}"] 47 | vpc_id = "${var.vpc_id}" 48 | 49 | # LC parameters 50 | ami = "${var.ami}" 51 | associate_public_ip_address = "${var.associate_public_ip_address}" 52 | enable_monitoring = "${var.enable_monitoring}" 53 | instance_type = "${var.instance_type}" 54 | key_name = "${var.key_name}" 55 | security_groups = ["${split(",",var.security_groups)}"] 56 | spot_price = "${var.spot_price}" 57 | 58 | # ASG parameters 59 | default_cooldown = "${var.default_cooldown}" 60 | desired_capacity = "${var.desired_capacity}" 61 | enabled_metrics = ["${split(",",var.enabled_metrics)}"] 62 | force_delete = "${var.force_delete}" 63 | hc_grace_period = "${var.hc_grace_period}" 64 | max_size = "${var.max_size}" 65 | min_size = "${var.min_size}" 66 | protect_from_scale_in = "${var.protect_from_scale_in}" 67 | suspended_processes = ["${split(",",var.suspended_processes)}"] 68 | termination_policies = ["${split(",",var.termination_policies)}"] 69 | wait_for_capacity_timeout = "${var.wait_for_capacity_timeout}" 70 | } 71 | -------------------------------------------------------------------------------- /group/lc/main.tf: -------------------------------------------------------------------------------- 1 | # AWS Launch Configuration 2 | 3 | ## Creates security group 4 | resource "aws_security_group" "sg_asg" { 5 | description = "${var.stack_item_fullname} security group" 6 | name_prefix = "${length(var.lc_sg_name_prefix_override) > 0 ? format("%s-", var.lc_sg_name_prefix_override) : format("%s-asg-", var.stack_item_label)}" 7 | vpc_id = "${var.vpc_id}" 8 | 9 | tags { 10 | application = "${var.stack_item_fullname}" 11 | managed_by = "terraform" 12 | Name = "${length(var.lc_sg_name_prefix_override) > 0 ? var.lc_sg_name_prefix_override : format("%s-asg", var.stack_item_label)}" 13 | } 14 | 15 | lifecycle { 16 | create_before_destroy = true 17 | } 18 | } 19 | 20 | ## Creates launch configuration 21 | resource "aws_launch_configuration" "lc" { 22 | count = "${length(var.ebs_vol_device_name) > 0 ? 0 : 1}" 23 | 24 | associate_public_ip_address = "${var.associate_public_ip_address}" 25 | ebs_optimized = "${var.ebs_optimized}" 26 | enable_monitoring = "${var.enable_monitoring}" 27 | iam_instance_profile = "${var.instance_profile}" 28 | image_id = "${var.ami}" 29 | instance_type = "${var.instance_type}" 30 | key_name = "${var.key_name}" 31 | name_prefix = "${var.stack_item_label}-" 32 | placement_tenancy = "${var.placement_tenancy}" 33 | security_groups = ["${distinct(concat(list(aws_security_group.sg_asg.id), compact(var.security_groups)))}"] 34 | spot_price = "${var.spot_price}" 35 | user_data = "${var.user_data}" 36 | 37 | root_block_device { 38 | delete_on_termination = "${var.root_vol_del_on_term}" 39 | iops = "${var.root_vol_type == "io1" ? var.root_vol_iops : "0" }" 40 | volume_size = "${length(var.root_vol_size) > 0 ? var.root_vol_size : "8"}" 41 | volume_type = "${var.root_vol_type}" 42 | } 43 | 44 | lifecycle { 45 | create_before_destroy = true 46 | } 47 | } 48 | 49 | resource "aws_launch_configuration" "lc_ebs" { 50 | count = "${length(var.ebs_vol_device_name) > 0 ? 1 : 0}" 51 | 52 | associate_public_ip_address = "${var.associate_public_ip_address}" 53 | ebs_optimized = "${var.ebs_optimized}" 54 | enable_monitoring = "${var.enable_monitoring}" 55 | iam_instance_profile = "${var.instance_profile}" 56 | image_id = "${var.ami}" 57 | instance_type = "${var.instance_type}" 58 | key_name = "${var.key_name}" 59 | name_prefix = "${var.stack_item_label}-" 60 | placement_tenancy = "${var.placement_tenancy}" 61 | security_groups = ["${distinct(concat(list(aws_security_group.sg_asg.id), compact(var.security_groups)))}"] 62 | spot_price = "${var.spot_price}" 63 | user_data = "${var.user_data}" 64 | 65 | root_block_device { 66 | delete_on_termination = "${var.root_vol_del_on_term}" 67 | iops = "${var.root_vol_type == "io1" ? var.root_vol_iops : "0" }" 68 | volume_size = "${length(var.root_vol_size) > 0 ? var.root_vol_size : "0"}" 69 | volume_type = "${var.root_vol_type}" 70 | } 71 | 72 | ebs_block_device { 73 | delete_on_termination = "${var.ebs_vol_del_on_term}" 74 | device_name = "${var.ebs_vol_device_name}" 75 | encrypted = "${length(var.ebs_vol_snapshot_id) > 0 ? "" : var.ebs_vol_encrypted}" 76 | iops = "${var.ebs_vol_type == "io1" ? var.ebs_vol_iops : "0" }" 77 | snapshot_id = "${var.ebs_vol_snapshot_id}" 78 | volume_size = "${length(var.ebs_vol_snapshot_id) > 0 ? "0" : var.ebs_vol_size}" 79 | volume_type = "${var.ebs_vol_type}" 80 | } 81 | 82 | lifecycle { 83 | create_before_destroy = true 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /policy/variables.tf: -------------------------------------------------------------------------------- 1 | # Input Variables 2 | 3 | ## Resource tags 4 | variable "stack_item_fullname" { 5 | type = "string" 6 | 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." 7 | } 8 | 9 | variable "stack_item_label" { 10 | type = "string" 11 | 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." 12 | } 13 | 14 | ## ASG parameters 15 | variable "asg_name" { 16 | type = "string" 17 | description = "Name of the ASG to associate the alarm with." 18 | } 19 | 20 | ## Notification parameters 21 | variable "notifications" { 22 | type = "list" 23 | description = "List of events to associate with the auto scaling notification." 24 | default = ["autoscaling:EC2_INSTANCE_LAUNCH", "autoscaling:EC2_INSTANCE_TERMINATE", "autoscaling:EC2_INSTANCE_LAUNCH_ERROR", "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"] 25 | } 26 | 27 | ## Policy parameters 28 | variable "adjustment_type" { 29 | type = "string" 30 | description = "Specifies the scaling adjustment. Valid values are 'ChangeInCapacity', 'ExactCapacity' or 'PercentChangeInCapacity'." 31 | } 32 | 33 | variable "cooldown" { 34 | type = "string" 35 | description = "Seconds between auto scaling activities." 36 | } 37 | 38 | variable "scaling_adjustment" { 39 | type = "string" 40 | description = "The number of instances involved in a scaling action." 41 | } 42 | 43 | ## Monitor parameters 44 | variable "comparison_operator" { 45 | type = "string" 46 | description = "Arithmetic operation to use when comparing the thresholds. Valid values are 'GreaterThanOrEqualToThreshold', 'GreaterThanThreshold', 'LessThanThreshold' and 'LessThanOrEqualToThreshold'" 47 | } 48 | 49 | variable "evaluation_periods" { 50 | type = "string" 51 | description = "The number of periods over which data is compared to the specified threshold." 52 | } 53 | 54 | variable "metric_name" { 55 | type = "string" 56 | description = "Name for the alarm's associated metric." 57 | } 58 | 59 | variable "name_space" { 60 | type = "string" 61 | description = "The namespace for the alarm's associated metric." 62 | default = "AWS/EC2" 63 | } 64 | 65 | variable "period" { 66 | type = "string" 67 | description = "The period in seconds over which the specified statistic is applied." 68 | } 69 | 70 | variable "statistic" { 71 | type = "string" 72 | description = "The statistic to apply to the alarm's associated metric. Valid values are 'SampleCount', 'Average', 'Sum', 'Minimum' and 'Maximum'" 73 | default = "Average" 74 | } 75 | 76 | variable "threshold" { 77 | type = "string" 78 | description = "The value against which the specified statistic is compared." 79 | } 80 | 81 | variable "treat_missing_data" { 82 | type = "string" 83 | description = "You can specfy how alarms handle missing data points. Valid values are 'missing': the alarm looks back farther in time to find additional data points, 'notBreaching': treated as a data point that is within the threshold, 'breaching': treated as a data point that is breaching the threshold, 'ignore': the current alarm state is maintained." 84 | default = "missing" 85 | } 86 | 87 | variable "valid_missing_data" { 88 | type = "map" 89 | 90 | default = { 91 | missing = "missing" 92 | ignore = "ignore" 93 | breaching = "breaching" 94 | notBreaching = "notBreaching" 95 | } 96 | } 97 | 98 | variable "valid_statistics" { 99 | type = "map" 100 | 101 | default = { 102 | Average = "Average" 103 | Maximum = "Maximum" 104 | Minimum = "Minimum" 105 | SampleCount = "SampleCount" 106 | Sum = "Sum" 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /group/asg/main.tf: -------------------------------------------------------------------------------- 1 | # AWS Auto Scaling Group 2 | 3 | ## Creates auto scaling group 4 | locals { 5 | default_asg_tags = [ 6 | { 7 | key = "application" 8 | value = "${var.stack_item_fullname}" 9 | propagate_at_launch = true 10 | }, 11 | { 12 | key = "Name" 13 | value = "${length(var.asg_name_override) > 0 ? var.asg_name_override : var.stack_item_label}" 14 | propagate_at_launch = "${var.propagate_name_at_launch}" 15 | }, 16 | { 17 | key = "managed_by" 18 | value = "terraform" 19 | propagate_at_launch = true 20 | }, 21 | ] 22 | } 23 | 24 | resource "aws_autoscaling_group" "asg" { 25 | count = "${length(var.min_elb_capacity) > 0 || length(var.wait_for_elb_capacity) > 0 ? 0 : 1}" 26 | 27 | default_cooldown = "${length(var.default_cooldown) > 0 ? var.default_cooldown : "300"}" 28 | desired_capacity = "${length(var.desired_capacity) > 0 ? var.desired_capacity : var.min_size}" 29 | enabled_metrics = ["${compact(var.enabled_metrics)}"] 30 | force_delete = "${var.force_delete}" 31 | health_check_grace_period = "${length(var.hc_grace_period) > 0 ? var.hc_grace_period : "300"}" 32 | health_check_type = "EC2" 33 | launch_configuration = "${var.lc_id}" 34 | max_size = "${var.max_size}" 35 | metrics_granularity = "${var.metrics_granularity}" 36 | min_size = "${var.min_size}" 37 | name = "${length(var.asg_name_override) > 0 ? var.asg_name_override : var.stack_item_label}" 38 | placement_group = "${var.placement_group}" 39 | protect_from_scale_in = "${var.protect_from_scale_in}" 40 | suspended_processes = ["${compact(var.suspended_processes)}"] 41 | target_group_arns = ["${compact(var.target_group_arns)}"] 42 | termination_policies = ["${compact(var.termination_policies)}"] 43 | vpc_zone_identifier = ["${compact(var.subnets)}"] 44 | wait_for_capacity_timeout = "${length(var.wait_for_capacity_timeout) > 0 ? var.wait_for_capacity_timeout : "10m"}" 45 | 46 | tags = "${concat(local.default_asg_tags, var.additional_asg_tags)}" 47 | } 48 | 49 | resource "aws_autoscaling_group" "asg_elb" { 50 | count = "${length(var.min_elb_capacity) > 0 || length(var.wait_for_elb_capacity) > 0 ? 1 : 0}" 51 | 52 | default_cooldown = "${length(var.default_cooldown) > 0 ? var.default_cooldown : "300"}" 53 | desired_capacity = "${length(var.desired_capacity) > 0 ? var.desired_capacity : var.min_size}" 54 | enabled_metrics = ["${compact(var.enabled_metrics)}"] 55 | force_delete = "${var.force_delete}" 56 | health_check_grace_period = "${length(var.hc_grace_period) > 0 ? var.hc_grace_period : "300"}" 57 | health_check_type = "${length(var.hc_check_type) > 0 ? var.hc_check_type : "ELB"}" 58 | launch_configuration = "${var.lc_id}" 59 | load_balancers = ["${compact(var.load_balancers)}"] 60 | max_size = "${var.max_size}" 61 | metrics_granularity = "${var.metrics_granularity}" 62 | min_elb_capacity = "${length(var.min_elb_capacity) > 0 ? var.min_elb_capacity : "0"}" 63 | min_size = "${var.min_size}" 64 | name = "${length(var.asg_name_override) > 0 ? var.asg_name_override : var.stack_item_label}" 65 | placement_group = "${var.placement_group}" 66 | protect_from_scale_in = "${var.protect_from_scale_in}" 67 | suspended_processes = ["${compact(var.suspended_processes)}"] 68 | target_group_arns = ["${compact(var.target_group_arns)}"] 69 | termination_policies = ["${compact(var.termination_policies)}"] 70 | vpc_zone_identifier = ["${compact(var.subnets)}"] 71 | wait_for_capacity_timeout = "${length(var.wait_for_capacity_timeout) > 0 ? var.wait_for_capacity_timeout : "10m"}" 72 | wait_for_elb_capacity = "${length(var.wait_for_elb_capacity) > 0 ? var.wait_for_elb_capacity : "0"}" 73 | 74 | tags = "${concat(local.default_asg_tags, var.additional_asg_tags)}" 75 | } 76 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | #### Consider Implementing: 4 | * Consider coding `ebs_optimized` against list of [ebs-optimized instances](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html). 5 | * Expose ephemeral block device support. 6 | * Expose lifecycle hook support. 7 | * Expose scheduling support. 8 | * Added support for Autoscaling "StepScaling" policy. 9 | * Break notifications out to support aggregation of groups and notifications 10 | to a single SNS topic. 11 | * Extend multi-part user_data mechanism to support more use cases. 12 | 13 | ## 0.3.0 (March 24, 2017) 14 | 15 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 16 | * The following parameters were renamed: 17 | * `ebs_device_name` to `ebs_vol_device_name` 18 | * `ebs_snapshot_id` to `ebs_vol_snapshot_id` 19 | * The `min_adjustment_magnitude` parameter for SimpleScaling policies has 20 | been retired. 21 | 22 | #### IMPROVEMENTS / NEW FEATURES: 23 | * Conditional support for the following parameters: 24 | * `associate_public_ip_address` 25 | * `default_cooldown` 26 | * `desired_capacity` 27 | * `ebs_vol_encrypted` 28 | * `ebs_vol_size` 29 | * `enabled_metrics` 30 | * `enable_monitring` 31 | * `force_delete` 32 | * `instance_profile` 33 | * `key_name` 34 | * `placement_group` 35 | * `protect_from_scale_in` 36 | * `suspended_processes` 37 | * `termination_policies` 38 | * `root_vol_size` 39 | * `spot_price` 40 | * `user_data` 41 | * Support for specifying multiple security groups in addition to the one 42 | created by the module using the `security_groups` parameter. 43 | * Support for `io1` root & EBS volumes. 44 | * Support for EBS volume not not originating from a snapshot. 45 | * Added `managed_by=terraform` tag to ASG managed instances. 46 | * Instance `Name` tags can now be based on the instance-id by setting the 47 | `instance_based_naming_enabled` parameter to **true**. 48 | * Support for an arbitrary number of tags to be applied to each instance. 49 | 50 | #### BUG FIXES: 51 | * Fixed inconsistent tagging between `asg` and `asg_elb` resources 52 | 53 | ## 0.2.0 (May 16, 2016) 54 | 55 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 56 | * Resources names updated in several places for standardization. Will cause 57 | extra churn in existing environments. 58 | * The built in egress security group rule has been remove from the module (see [here](https://github.com/unifio/terraform-aws-asg/compare/v0.1.2...v0.2.0#diff-776572ed86400784bb739b64a2cbcb00L14) and [here](https://github.com/unifio/terraform-aws-asg/compare/v0.1.2...v0.2.0#diff-adb68aea6eb2a951e65c8971444cee02L14)). 59 | Be sure to declare your own egress rule when using the module (see [here](https://github.com/unifio/terraform-aws-asg/compare/v0.1.2...v0.2.0#diff-6f17df14965c642acbd9d68a62ea120eR148) and [here](https://github.com/unifio/terraform-aws-asg/compare/v0.1.2...v0.2.0#diff-7540fff78d0edcda5f9da593d378a2b3R82)) 60 | 61 | #### IMPROVEMENTS: 62 | * Introduced deterministic conditional logic for the following scenarios: 63 | * Specification of an EBS snapshot to associate with the launch configuration. 64 | * Specification of ELB(s) to associate with the auto scaling group. 65 | * Specification of percentage based simple auto scaling policies. 66 | * All variables explicitly typed per HashiCorp best practices. 67 | * Added name prefixing to security group and launch configuration resources. 68 | * Exposed `associate_public_ip_address` parameter on launch configuration. 69 | * Exposed `placement_tenancy` parameter on launch configuration. 70 | * Exposed root volume configuration parameters on launch configuration. 71 | * Exposed EBS volume configuration parameters on launch configuration. 72 | * Exposed `wait_for_capacity_timeout` parameter on auto scaling group. 73 | 74 | ## 0.1.2 (Apr 27, 2016) 75 | 76 | #### BACKWARDS INCOMPATIBILITIES / NOTES: 77 | * Changed the default value for `ebs_optimized` from `true` -> `false`. This 78 | setting is more compatible with the majority of instance types. 79 | 80 | #### IMPROVEMENTS: 81 | * Name for `aws_launch_configuration`. 82 | * Fixed name label for `aws_autoscaling_group`. 83 | * Fixed name label for auto-scaling group's security group. 84 | 85 | ## 0.1.1 (Dec 1, 2015) 86 | 87 | #### IMPROVEMENTS: 88 | * Updated template_file usage for 0.6.7 to remove deprecation warnings [GH-9] 89 | * Added ASG name to module outputs [GH-8] 90 | * Added default Name tag to auto scaling groups [GH-6] 91 | 92 | #### BUG FIXES: 93 | * Updated lifecycle hooks to prevent dependency cycles during destroy [GH-5] 94 | * Added proper lifecycle management to allow launch configuration updates [GH-2] 95 | * Removed `sg-` from ASG security group name [GH-1] 96 | 97 | ## 0.1.0 (Oct 26, 2015) 98 | 99 | * Initial Release 100 | -------------------------------------------------------------------------------- /group/main.tf: -------------------------------------------------------------------------------- 1 | # AWS Auto Scaling Configuration 2 | 3 | ## Set Terraform version constraint 4 | terraform { 5 | required_version = "> 0.8.0" 6 | } 7 | 8 | ## Creates cloudconfig fragments for tagging 9 | data "aws_region" "current" {} 10 | 11 | data "template_file" "name" { 12 | template = "${var.instance_based_naming_enabled == "true" ? file("${path.module}/templates/name.tpl") : ""}" 13 | 14 | vars { 15 | name_prefix = "${length(var.instance_name_prefix) > 0 ? var.instance_name_prefix : var.stack_item_label}" 16 | region = "${data.aws_region.current.name}" 17 | } 18 | } 19 | 20 | data "template_file" "tags" { 21 | count = "${length(keys(var.instance_tags))}" 22 | 23 | template = "${element(keys(var.instance_tags),count.index) != "" ? file("${path.module}/templates/tag.tpl") : ""}" 24 | 25 | vars { 26 | key = "${element(keys(var.instance_tags),count.index)}" 27 | region = "${data.aws_region.current.name}" 28 | value = "${lookup(var.instance_tags,element(keys(var.instance_tags),count.index))}" 29 | } 30 | } 31 | 32 | data "template_cloudinit_config" "cloud_config" { 33 | gzip = false 34 | base64_encode = false 35 | 36 | part { 37 | content_type = "text/cloud-config" 38 | content = "${var.user_data}" 39 | } 40 | 41 | part { 42 | content_type = "text/cloud-config" 43 | content = "${data.template_file.name.rendered}" 44 | merge_type = "list(append)+dict(recurse_array)+str()" 45 | } 46 | 47 | part { 48 | content_type = "text/cloud-config" 49 | content = "#cloud-config\nruncmd:\n${join("",data.template_file.tags.*.rendered)}" 50 | merge_type = "list(append)+dict(recurse_array)+str()" 51 | } 52 | } 53 | 54 | ## Creates launch configuration & security group 55 | module "lc" { 56 | source = "lc" 57 | 58 | ### Resource labels 59 | stack_item_fullname = "${var.stack_item_fullname}" 60 | stack_item_label = "${var.stack_item_label}" 61 | lc_sg_name_prefix_override = "${var.lc_sg_name_prefix_override}" 62 | 63 | ### VPC parameters 64 | vpc_id = "${var.vpc_id}" 65 | 66 | ### LC parameters 67 | ami = "${var.ami}" 68 | associate_public_ip_address = "${var.associate_public_ip_address}" 69 | ebs_optimized = "${var.ebs_optimized}" 70 | ebs_vol_del_on_term = "${var.ebs_vol_del_on_term}" 71 | ebs_vol_device_name = "${var.ebs_vol_device_name}" 72 | ebs_vol_encrypted = "${var.ebs_vol_encrypted}" 73 | ebs_vol_iops = "${var.ebs_vol_iops}" 74 | ebs_vol_size = "${var.ebs_vol_size}" 75 | ebs_vol_snapshot_id = "${var.ebs_vol_snapshot_id}" 76 | ebs_vol_type = "${var.ebs_vol_type}" 77 | enable_monitoring = "${var.enable_monitoring}" 78 | instance_profile = "${var.instance_profile}" 79 | instance_type = "${var.instance_type}" 80 | key_name = "${var.key_name}" 81 | placement_tenancy = "${var.placement_tenancy}" 82 | root_vol_del_on_term = "${var.root_vol_del_on_term}" 83 | root_vol_iops = "${var.root_vol_iops}" 84 | root_vol_size = "${var.root_vol_size}" 85 | root_vol_type = "${var.root_vol_type}" 86 | security_groups = ["${var.security_groups}"] 87 | spot_price = "${var.spot_price}" 88 | user_data = "${data.template_cloudinit_config.cloud_config.rendered}" 89 | } 90 | 91 | ## Creates auto scaling group 92 | module "asg" { 93 | source = "asg" 94 | 95 | ### Resource tags 96 | stack_item_label = "${var.stack_item_label}" 97 | stack_item_fullname = "${var.stack_item_fullname}" 98 | asg_name_override = "${var.asg_name_override}" 99 | propagate_name_at_launch = "${var.propagate_name_at_launch}" 100 | 101 | ### VPC parameters 102 | subnets = ["${var.subnets}"] 103 | 104 | ### LC parameters 105 | lc_id = "${module.lc.lc_id}" 106 | 107 | ### ASG parameters 108 | default_cooldown = "${var.default_cooldown}" 109 | desired_capacity = "${var.desired_capacity}" 110 | enabled_metrics = ["${var.enabled_metrics}"] 111 | force_delete = "${var.force_delete}" 112 | hc_check_type = "${var.hc_check_type}" 113 | hc_grace_period = "${var.hc_grace_period}" 114 | max_size = "${var.max_size}" 115 | metrics_granularity = "1Minute" 116 | min_size = "${var.min_size}" 117 | placement_group = "${var.placement_group}" 118 | protect_from_scale_in = "${var.protect_from_scale_in}" 119 | suspended_processes = ["${var.suspended_processes}"] 120 | termination_policies = ["${var.termination_policies}"] 121 | wait_for_capacity_timeout = "${var.wait_for_capacity_timeout}" 122 | additional_asg_tags = "${var.additional_asg_tags}" 123 | 124 | ### ELB parameters 125 | load_balancers = ["${var.load_balancers}"] 126 | min_elb_capacity = "${var.min_elb_capacity}" 127 | target_group_arns = ["${var.target_group_arns}"] 128 | wait_for_elb_capacity = "${var.wait_for_elb_capacity}" 129 | } 130 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | # Standard ASG Example 2 | 3 | ## Configures providers 4 | provider "aws" { 5 | region = "${var.region}" 6 | } 7 | 8 | ## Creates IAM role 9 | resource "aws_iam_role" "role" { 10 | name = "${var.stack_item_label}-${var.region}" 11 | path = "/" 12 | 13 | lifecycle { 14 | create_before_destroy = true 15 | } 16 | 17 | assume_role_policy = </dev/null) 105 | if [[ ${GET_DOCKER_HOST_IP} ]];then 106 | CONSUL_TEST_IP=${GET_DOCKER_HOST_IP} 107 | add_arg_simple "--add-host" "${TEST_HOST_LOCAL}:${CONSUL_TEST_IP}" 108 | fi 109 | fi 110 | } 111 | # add a volume host:docker mount. 112 | add_host_volume(){ 113 | local host_vol="${1%:*}" 114 | local dkr_vol="${1##*:}" 115 | add_arg "-v" "${host_vol}:${dkr_vol}" 116 | } 117 | 118 | # add envfiles for docker if they exist in working directory 119 | add_docker_envfiles(){ 120 | local envfiles="${1}" 121 | IFS=':' read -r -a arrenvs <<< "$envfiles" 122 | for i in "${arrenvs[@]}" 123 | do 124 | if [[ -r "${i}" ]];then 125 | add_arg "--env-file" "$(pwd)/${i}" 126 | fi 127 | done 128 | } 129 | usage () { 130 | echo "" 131 | echo "Usage : $0 [OPTIONS] [COMMANDS|task]" 132 | echo "Options:" 133 | echo " -l List available rake tasks " 134 | echo " -e FILE:FILE Envfiles for docker : separated " 135 | echo " -s FILE Local env file to source " 136 | echo " -d DNS Docker DNS " 137 | echo " -u USER Run Docker as user " 138 | echo " -O Use Wrapper without Covalence " 139 | echo " -T URL URL for Consul overload " 140 | echo " -R Leave intermediary containers " 141 | echo " -v VOL:MNT Add a volume mount to container " 142 | echo " -c AWS_DIR AWS credentials path " 143 | echo " -w DIR Host workspace to mount " 144 | echo " -E ENTRYPOINT Override entrypoint command " 145 | echo " -i DKR_IMG_NAME Docker container Image name " 146 | echo " -h View help. " 147 | echo " -r RAKEFILE Specify separate rakefile " 148 | echo " -D Turn on debug " 149 | echo " -H Environment dump " 150 | } 151 | # require at lest a task or -l to run 152 | if [ $# -lt 1 ]; then 153 | usage 154 | exit 1 155 | fi 156 | 157 | # Load local env file if provided/available 158 | # That way explicit options will overwrite 159 | # any env vars sourced in .env.covalence 160 | if [[ -r "${LOCAL_ENVFILE}" ]]; then 161 | . ./"${LOCAL_ENVFILE}" 162 | fi 163 | 164 | # Parse arguments and populate ENV vars respectively 165 | # See Environment Variable section or .env.covalence for 166 | # option details. 167 | while getopts ":le:s:d:OT:Rv:c:w:E:i:hr:DH" opt; do 168 | case $opt in 169 | l) 170 | LIST_RAKE_TASKS=1 171 | ;; 172 | e) 173 | LOAD_ENVFILE="$OPTARG" 174 | ;; 175 | s) 176 | LOCAL_ENVFILE="$OPTARG" 177 | ;; 178 | d) 179 | DOCKER_DNS="$OPTARG" 180 | ;; 181 | u) 182 | CONTAINER_USER="$OPTARG" 183 | ;; 184 | O) 185 | DOCKER_WRAPPER=1 186 | ;; 187 | T) 188 | TEST_HOST_LOCAL="$OPTARG" 189 | ;; 190 | R) 191 | DOCKER_RUN_TYPE="--it" 192 | ;; 193 | v) 194 | ADD_VOLUMES="$OPTARG" 195 | ;; 196 | c) 197 | AWS_CREDENTIAL_PATH="$OPTARG" 198 | ;; 199 | w) 200 | DOCKER_WORKSPACE="$OPTARG" 201 | ;; 202 | E) 203 | ENTRYPOINT="$OPTARG" 204 | ;; 205 | i) 206 | DOCKER_IMAGE_NAME="$OPTARG" 207 | ;; 208 | h) 209 | usage 210 | exit 0 211 | ;; 212 | D) 213 | S_DEBUG=1 214 | ;; 215 | H) 216 | DUMP_ENV=1 217 | ;; 218 | r) 219 | COVALENCE_RAKEFILE="$OPTARG" 220 | ;; 221 | \?) 222 | set +x 223 | echo "Invalid option: -$OPTARG" >&2 224 | usage 225 | exit 1 226 | ;; 227 | :) 228 | set +x 229 | echo "Option -$OPTARG requires an argument." >&2 230 | usage 231 | exit 1 232 | ;; 233 | esac 234 | done 235 | 236 | # Get rid of processed options from Array 237 | shift "$((OPTIND-1))" 238 | USER_ARGS=("${@}") 239 | 240 | if [[ "${COVALENCE_CONFIG}" ]]; then 241 | add_arg_simple "-e" "COVALENCE_CONFIG=${COVALENCE_CONFIG}" 242 | fi 243 | 244 | if [[ "${COVALENCE_TEST_ENVS}" ]]; then 245 | add_arg_simple "-e" "COVALENCE_TEST_ENVS=${COVALENCE_TEST_ENVS}" 246 | fi 247 | 248 | # Add the --rm or --it argument to the docker command array. 249 | if [[ "${DOCKER_RUN_TYPE}" ]]; then 250 | DOCKER_BASE_COMMANDS[3]="${DOCKER_RUN_TYPE}" 251 | fi 252 | 253 | if [[ "${DOCKER_DNS}" ]]; then 254 | add_arg "--dns" "${DOCKER_DNS}" 255 | fi 256 | 257 | if [[ "${CONTAINER_USER}" ]]; then 258 | add_arg "--user" "${CONTAINER_USER}" 259 | fi 260 | 261 | get_docker_host "$TEST_HOST_LOCAL" 262 | 263 | if [[ "$ADD_VOLUMES" ]];then 264 | add_host_volume "${ADD_VOLUMES}" 265 | fi 266 | 267 | if [[ -d "$AWS_CREDENTIAL_PATH" ]];then 268 | add_arg_simple "-v" "${AWS_CREDENTIAL_PATH}:${DOCKER_HOMEDIR}/.aws" 269 | fi 270 | 271 | if [[ -d "${SRC_ROOT}" ]];then 272 | add_arg_simple "-v" "${SRC_ROOT}:${DOCKER_WORKSPACE}" 273 | add_arg "-w" "${DOCKER_WORKSPACE}" 274 | fi 275 | 276 | if [[ "${LOAD_ENVFILE}" ]]; then 277 | add_docker_envfiles "${LOAD_ENVFILE}" 278 | fi 279 | 280 | if [[ "${ENTRYPOINT}" ]]; then 281 | ARGS+=("--entrypoint=${ENTRYPOINT}") 282 | fi 283 | # All options should be completed 284 | # Only image and task remain. 285 | 286 | if [[ $DOCKER_IMAGE_NAME ]];then 287 | ARGS+=("$DOCKER_IMAGE_NAME") 288 | fi 289 | 290 | #Check whether docker wrapper or covalence 291 | if [[ ! $DOCKER_WRAPPER ]]; then 292 | ARGS+=("rake") 293 | fi 294 | 295 | if [[ -r "${COVALENCE_RAKEFILE}" && ! $DOCKER_WRAPPER ]];then 296 | add_arg "-f" "${COVALENCE_RAKEFILE}" 297 | fi 298 | 299 | if [[ $LIST_RAKE_TASKS && ! $DOCKER_WRAPPER ]];then 300 | ARGS+=("-T") 301 | fi 302 | # Merged Commands for execution 303 | DOCKER_BASE_COMMANDS=(${DOCKER_BASE_COMMANDS[@]} ${ARGS[@]} ${USER_ARGS[@]}) 304 | 305 | if [[ $DUMP_ENV ]]; then 306 | echo "DOCKER_BASE_COMMANDS that would have been executed without -H" 307 | echo "${DOCKER_BASE_COMMANDS[@]}" 308 | # echo "ARGS array" 309 | # echo "${ARGS[@]}" 310 | # echo "USER_ARGS array" 311 | # echo "${USER_ARGS[@]}" 312 | # echo "" 313 | else 314 | # Execute the commands 315 | # If we are listing, remove the rake as user won't pass that in. 316 | if [[ $LIST_RAKE_TASKS && ! $DOCKER_WRAPPER ]];then 317 | "${DOCKER_BASE_COMMANDS[@]}" | sed -e "s/^rake //" 318 | else 319 | "${DOCKER_BASE_COMMANDS[@]}" 320 | fi 321 | fi 322 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform AWS Auto Scaling Module 2 | [![Circle CI](https://circleci.com/gh/unifio/terraform-aws-asg/tree/master.svg?style=svg)](https://circleci.com/gh/unifio/terraform-aws-asg/tree/master) 3 | 4 | Module stack supporting multiple deployment scenarios of an Auto Scaling Group 5 | to an AWS VPC. 6 | 7 | ## Prerequisites 8 | 9 | * Pre-configured AWS VPC 10 | 11 | ## Requirements 12 | 13 | * Terraform 0.8.0 or newer 14 | * AWS provider 15 | 16 | ## Group Module 17 | 18 | The group module will provision security group, launch configuration and auto 19 | scaling group resources. Both ELB and simple group configurations are 20 | supported. 21 | 22 | ## Input Variables 23 | 24 | **Resource labels** 25 | * `stack_item_fullname` - Long form descriptive name for this stack item. This 26 | value is used to create the **application** resource tag for resources created 27 | by this stack item. 28 | * `stack_item_label` - Short form identifier for this stack. This value is used 29 | to create the **Name** resource tag for resources created by this stack item, 30 | and also serves as a unique key for re-use. 31 | 32 | **Virtual Private Cloud (VPC) parameters** 33 | * `region` - AWS region to be utilized. 34 | * `subnets` - List of VPC subnets to associate with the auto scaling group. 35 | * `vpc_id` - ID of the target VPC. 36 | 37 | **Launch configuration parameters** 38 | * `ami` - Amazon Machine Image (AMI) to associate with the launch configuration. 39 | * `associate_public_ip_address` - (Optional) Flag for associating public IP 40 | addresses with instances managed by the auto scaling group. 41 | * `ebs_optimized` - (Default: **false**) Flag to enable Elastic Block Storage 42 | (EBS) optimization. 43 | * `enable_monitoring` - (Optional) Flag to enable detailed monitoring. 44 | * `instance_based_naming_enabled` - (Optional) Flag to enable dynamic name tags 45 | on instances. The default format is **stack_item_label-instance-id**. Requires 46 | the instance to have the AWS CLI installed and an instance profile applied with 47 | the **ec2:CreateTags** action granted for the given instance. 48 | * `instance_name_prefix` - (Optional) String to replace **stack_item_label** 49 | when `instance_based_naming_enabled` is set to **true**. 50 | * `instance_profile` - (Optional) IAM instance profile to associate with the 51 | launch configuration. 52 | * `instance_tags` - (Optional) A map of key/value pairs to be applied as tags 53 | to each instance. Requires the instance to have the AWS CLI installed and an 54 | instance profile applied with the **ec2:CreateTags** action granted for the 55 | given instance. 56 | * `instance_type` - EC2 instance type to associate with the launch 57 | configuration. 58 | * `key_name` - (Optional) SSH key pair to associate with the launch 59 | configuration. 60 | * `placement_tenancy` - (Default: **default**) The tenancy of the instance. 61 | Valid values are **default** or **dedicated**. 62 | * `security_groups` - (Optional) A list of associated security group IDs. 63 | * `spot_price` - (Optional) The price to use for reserving spot instances. 64 | * `user_data` - (Optional) Instance initialization data to associate with the 65 | launch configuration. 66 | 67 | **Block volume configuration** 68 | 69 | NOTES: Ephemeral block device support to be implemented in a future version. 70 | Module currently only offers support for a single additional volume in addition to 71 | **root**. All volume parameters are optional to the overall configuration, but 72 | those not marked optional here are required when specifying a volume. 73 | 74 | * `ebs_vol_del_on_term` - (Default: **true**) Whether the volume should be 75 | destroyed on instance termination. 76 | * `ebs_vol_device_name` - The name of the device to mount. 77 | * `ebs_vol_encrypted` - (Optional) Whether the volume should be encrypted or 78 | not. Value is ignored when `ebs_vol_snapshot_id` has been specified. 79 | * `ebs_vol_iops` - (Default: **2000**) The amount of provisioned IOPS. Value 80 | is ignored for any volume type other than `io1`. 81 | * `ebs_vol_snapshot_id` - (Optional) The Snapshot ID to mount. 82 | * `ebs_vol_size` - (Optional) The size of the volume in gigabytes. 83 | * `ebs_vol_type` - (Default: **gp2**) The type of volume. Valid values are 84 | **standard**, **gp2** or **io1**. 85 | * `root_vol_del_on_term` - (Default: **true**) Whether the volume should be 86 | destroyed on instance termination. 87 | * `root_vol_iops` - (Default: **2000**) The amount of provisioned IOPS. Value 88 | is ignored for any volume type other than `io1`. 89 | * `root_vol_size` - (Optional) The size of the volume in gigabytes. 90 | * `root_vol_type` - (Default: **gp2**) The type of volume. Valid values are 91 | **standard**, **gp2** or **io1**. 92 | 93 | **Auto scaling group parameters** 94 | 95 | * `default_cooldown` - (Optional) The amount of time, in seconds, after a 96 | scaling activity completes before another scaling activity can start. 97 | * `desired_capacity` - (Optional) The number of Amazon EC2 instances that 98 | should be running in the group. 99 | * `enabled_metrics` - (Optional) A list of metrics to collect. The allowed 100 | values are 'GroupMinSize', 'GroupMaxSize', 'GroupDesiredCapacity', 101 | 'GroupInServiceInstances', 'GroupPendingInstances', 'GroupStandbyInstances', 102 | 'GroupTerminatingInstances', 'GroupTotalInstances'. 103 | * `force_delete` - (Default: **false**) Flag to allow deletion of the auto 104 | scaling group without waiting for all instances in the pool to terminate. 105 | * `hc_check_type` - (Optional) Type of health check performed by the auto 106 | scaling group. Valid values are **ELB** or **EC2**. Automatically set to 107 | **EC2** when **min_elb_capacity** and **wait_for_elb_capacity** are unset 108 | and **ELB** when they are. 109 | * `hc_grace_period` - (Optional) Time allowed after an instance comes into 110 | service before checking health. 111 | * `load_balancers` - (Optional) List of load balancer names to associate with the auto 112 | scaling group. 113 | * `max_size` - Maximum number of instances allowed by the auto scaling group. 114 | * `min_elb_capacity` - (Optional) Minimum number of healthy instances attached 115 | to the ELB that must be maintained during updates. 116 | * `min_size` - Minimum number of instance to be maintained by the auto scaling 117 | group. 118 | * `placement_group` - (Optional) The name of the placement group into which 119 | you'll launch your instances, if any. 120 | * `protect_from_scale_in` - (Optional) Allows setting instance protection. The 121 | auto scaling group will not select instances with this setting for terminination 122 | during scale in events. 123 | * `suspended_processes` - (Optional) A list of processes to suspend for the 124 | auto scaling group. The allowed values are 'Launch', 'Terminate', 'HealthCheck', 125 | 'ReplaceUnhealthy', 'AZRebalance', 'AlarmNotification', 'ScheduledActions', 126 | 'AddToLoadBalancer'. Note that if you suspend either the 'Launch' or 127 | 'Terminate' process types, it can prevent your auto scaling group from 128 | functioning properly. 129 | * `termination_policies` - (Optional) A list of policies to decide how the 130 | instances in the auto scale group should be terminated. The allowed values are 131 | 'OldestInstance', 'NewestInstance', 'OldestLaunchConfiguration', 132 | 'ClosestToNextInstanceHour', 'Default'. 133 | * `wait_for_capacity_timeout` - (Default: **10m**) A maximum duration that 134 | Terraform should wait for ASG managed instances to become healthy before 135 | timing out. 136 | * `wait_for_elb_capacity` - Setting this will cause Terraform to wait for 137 | exactly this number of healthy instances in all attached load balancers on both 138 | create and update operations. (Takes precedence over 'min_elb_capacity' 139 | behavior.) 140 | 141 | ### Usage 142 | 143 | NOTE: These examples assume that valid AWS credentials have been provided as 144 | environment variables. 145 | 146 | **Common** 147 | ```js 148 | provider "aws" { 149 | region = "us-east-1" 150 | } 151 | 152 | data "template_file" "user_data" { 153 | template = "${file("../templates/user_data.tpl")}" 154 | } 155 | ``` 156 | 157 | **Basic ASG** 158 | ```js 159 | module "asg" { 160 | source = "github.com/unifio/terraform-aws-asg//group" 161 | 162 | # Resource tags 163 | stack_item_fullname = "Application stack" 164 | stack_item_label = "app-prod" 165 | 166 | # VPC parameters 167 | region = "us-east-1" 168 | subnets = "subnet-3315e85a,subnet-3bbaaf43,subnet-ec1326a6" 169 | vpc_id = "vpc-0f986c66" 170 | 171 | # LC parameters 172 | ami = "ami-c55673a0" 173 | enable_monitoring = true 174 | instance_based_naming_enabled = true 175 | instance_type = "m4.large" 176 | key_name = "ops" 177 | security_groups = "sg-c1afc0a8,sg-d4f906bd" 178 | spot price = "0.010" 179 | user_data = "${template_file.user_data.rendered}" 180 | 181 | # ASG parameters 182 | max_size = 2 183 | min_size = 2 184 | } 185 | ``` 186 | 187 | **ASG w/ ELB** 188 | ```js 189 | 190 | resource "aws_elb" "elb" { 191 | . 192 | . 193 | } 194 | 195 | resource "aws_iam_instance_profile" "terraform" { 196 | . 197 | . 198 | } 199 | 200 | module "asg" { 201 | source = "github.com/unifio/terraform-aws-asg//group" 202 | 203 | # Resource tags 204 | stack_item_fullname = "Application stack" 205 | stack_item_label = "app-prod" 206 | 207 | # VPC parameters 208 | region = "us-east-1" 209 | subnets = "subnet-3315e85a,subnet-3bbaaf43,subnet-ec1326a6" 210 | vpc_id = "vpc-0f986c66" 211 | 212 | # LC parameters 213 | ami = "ami-c55673a0" 214 | ebs_vol_device_name = "/dev/xvdb" 215 | ebs_vol_encrypted = true 216 | ebs_vol_size = 2 217 | ebs_vol_snapshot_id = "snap-08b95a31adefd036f" 218 | enable_monitoring = true 219 | instance_based_naming_enabled = true 220 | instance_name_prefix = "supercool" 221 | instance_tags = "${map("env","production")}" 222 | instance_type = "t2.medium" 223 | instance_profile = "${aws_iam_instance_profile.terraform.id}" 224 | key_name = "ops" 225 | user_data = "${template_file.user_data.rendered}" 226 | 227 | # ASG parameters 228 | desired_capacity = 2 229 | load_balancers = "${aws_elb.elb.id}" 230 | max_size = 3 231 | min_elb_capacity = 2 232 | min_size = 1 233 | } 234 | ``` 235 | 236 | ### Outputs 237 | 238 | * `asg_id` - ID of the auto scaling group 239 | * `asg_name` - Name of the auto scaling group 240 | * `lc_id` - ID of the launch configuration 241 | * `sg_id` - ID of the security group 242 | 243 | ## Policy Module 244 | 245 | The policy module will provision auto scaling policy, auto scaling 246 | notification, CloudWatch monitor and SNS topic resources. Both absolute and 247 | percentage based simple scaling schemes are supported. 248 | 249 | ### Input Variables 250 | 251 | **Resource labels** 252 | * `stack_item_fullname` - Long form descriptive name for this stack item. This 253 | value is used to create the **application** resource tag for resources created 254 | by this stack item. 255 | * `stack_item_label` - Short form identifier for this stack. This value is used 256 | to create the **Name** resource tag for resources created by this stack item, 257 | and also serves as a unique key for re-use. 258 | 259 | **Auto scaling group parameters** 260 | * `asg_name` - Name of the ASG to associate the alarm with. 261 | 262 | **Notification parameters** 263 | * `notifications` - List of events to associate with the auto scaling 264 | notification. 265 | * Defaults (_Comma separated String_): 266 | * **autoscaling:EC2_INSTANCE_LAUNCH** 267 | * **autoscaling:EC2_INSTANCE_TERMINATE** 268 | * **autoscaling:EC2_INSTANCE_LAUNCH_ERROR** 269 | * **autoscaling:EC2_INSTANCE_TERMINATE_ERROR** 270 | 271 | **Policy parameters** 272 | * `adjustment_type` - Specifies the scaling adjustment. Valid values are 273 | **ChangeInCapacity**, **ExactCapacity** and **PercentChangeInCapacity**. 274 | * `cooldown` - Seconds between auto scaling activities. 275 | * `scaling_adjustment` - The number of instances involved in a scaling action. 276 | 277 | **Monitor parameters** 278 | * `comparison_operator` - Arithmetic operation to use when comparing the 279 | thresholds. Valid values are **GreaterThanOrEqualToThreshold**, 280 | **GreaterThanThreshold**, **LessThanThreshold** and 281 | **LessThanOrEqualToThreshold**. 282 | * `evaluation_periods` - The number of periods over which data is compared to 283 | the specified threshold. 284 | * `metric_name` - Name for the alarm's associated metric. 285 | * `name_space` - (Default: **AWS/EC2**) The namespace for the alarm's 286 | associated metric. 287 | * `period` - The period in seconds over which the specified statistic is 288 | applied. 289 | * `statistic` - (Default: **Average**) The statistic to apply to the alarm's 290 | associated metric. Valid values are **SampleCount**, **Average**, **Sum**, 291 | **Minimum** and **Maximum**. 292 | * `threshold` - The value against which the specified statistic is compared. 293 | * [`treat_missing_data`](http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html#alarms-and-missing-data) - (Default: **missing**) How alarms handle missing 294 | data points. Valid values are: 295 | * **missing** - Missing (the alarm looks back farther in time to find additional data points) 296 | * **ignore** - Good ("Not Breaching," treated as a data point that is within the threshold) 297 | * **breaching** - Bad ("Breaching," treated as a data point that is breaching the threshold) 298 | * **notBreaching** - Ignored (the current alarm state is maintained) 299 | 300 | ### Usage 301 | 302 | NOTE: These examples assume that valid AWS credentials have been provided as 303 | environment variables. 304 | 305 | **Common** 306 | ```js 307 | provider "aws" { 308 | region = "us-east-1" 309 | } 310 | ``` 311 | 312 | **Absolute policy** 313 | ```js 314 | module "absolute_policy" { 315 | source = "github.com/unifio/terraform-aws-asg//policy" 316 | 317 | # Resource tags 318 | stack_item_fullname = "Application stack" 319 | stack_item_label = "app-prod" 320 | 321 | # ASG parameters 322 | asg_name = "example_asg" 323 | 324 | # Monitor parameters 325 | adjustment_type = "ExactCapacity" 326 | comparison_operator = "LessThanOrEqualToThreshold" 327 | cooldown = 300 328 | evaluation_periods = 2 329 | metric_name = "CPUUtilization" 330 | period = 120 331 | scaling_adjustment = 4 332 | threshold = 10 333 | treat_missing_data = "breaching" 334 | } 335 | ``` 336 | 337 | **Percentage policy** 338 | ```js 339 | module "percentage_policy" { 340 | source = "github.com/unifio/terraform-aws-asg//policy" 341 | 342 | # Resource tags 343 | stack_item_fullname = "application" 344 | stack_item_label = "ops" 345 | 346 | # ASG parameters 347 | asg_name = "example_asg" 348 | 349 | # Monitor parameters 350 | adjustment_type = "PercentChangeInCapacity" 351 | comparison_operator = "GreaterThanOrEqualToThreshold" 352 | cooldown = 300 353 | evaluation_periods = 2 354 | metric_name = "CPUUtilization" 355 | period = 120 356 | scaling_adjustment = 4 357 | threshold = 10 358 | treat_missing_data = "breaching" 359 | } 360 | ``` 361 | 362 | ### Outputs 363 | 364 | * `sns_arn` - Resource name of the Simple Notification Service (SNS) topic. 365 | 366 | ## Examples 367 | 368 | See the [examples](examples) directory for a complete set of example source 369 | files. 370 | 371 | ## License ## 372 | 373 | MPL 2.0. See LICENSE for full details. 374 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------