From e1769a8be8c70a1bf8273c5e642a01c4f2233e4a Mon Sep 17 00:00:00 2001 From: Brandon O'Connor Date: Wed, 8 Mar 2017 18:13:09 -0800 Subject: [PATCH 1/2] fixed up the variables example --- example/variables.tf | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/example/variables.tf b/example/variables.tf index 550f4ef..bd94eae 100644 --- a/example/variables.tf +++ b/example/variables.tf @@ -1,7 +1,3 @@ -variable "aws_access_key" {} - -variable "aws_secret_key" {} - variable "aws_region" { default = "us-west-2" } @@ -10,7 +6,7 @@ variable "alb_name" { default = "example_alb" } -variable "alb_security_groups" { +variable "security_group_id_list" { default = ["sg-edcd9784", "sg-edcd9785"] } @@ -18,26 +14,30 @@ variable "certificate_arn" { default = "arn:aws:iam::123456789012:server-certificate/ProdServerCert" } -variable "subnets" { +variable "public_subnet_ids" { default = ["subnet-1a2b3c4d", "subnet-1a2b3c4e", "subnet-1a2b3c4f"] } -variable "backend_port" { +variable "instance_port" { default = "8080" } -variable "backend_protocol" { - default = "http" +variable "instance_protocol" { + default = "HTTPS" } variable "health_check_target" { default = "HTTPS:443/health" } -variable "log_bucket" { +variable "log_bucket_name" { default = "my_log_bucket" } variable "log_prefix" { default = "example_alb" } + +variable "vpc_id" { + default = "vpc-12345678" +} From fc04be0bfd22a1b096369aea1f90d99479bd37d1 Mon Sep 17 00:00:00 2001 From: Brandon O'Connor Date: Thu, 9 Mar 2017 13:28:50 -0800 Subject: [PATCH 2/2] Initial tagged release. Variable switches working * HTTP/HTTPS support enabled via variable conditionals. * stickiness now works as a variable conditional. * README expanded. * Added various files to support testing. Not there yet. * experimented with a submodule to allow logging to exist or not. Couldn't get it working but left commented out code. --- .gitignore | 3 ++ Gemfile | 3 ++ README.md | 36 +++++++++------ alb/main.tf | 24 ++++++++++ alb/outputs.tf | 24 ++++++++++ alb/variables.tf | 28 ++++++++++++ example/main.tf | 22 ++++----- example/variables.tf | 36 +++++++-------- main.tf | 104 +++++++++++++++++-------------------------- variables.tf | 51 ++++++++++++--------- 10 files changed, 206 insertions(+), 125 deletions(-) create mode 100644 Gemfile create mode 100644 alb/main.tf create mode 100644 alb/outputs.tf create mode 100644 alb/variables.tf diff --git a/.gitignore b/.gitignore index cc90d6c..04c9434 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.tfvars* *.tfstate* .terraform +**/Gemfile.lock +**/inspec.lock +*.gem diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..60c9162 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org/' do + gem 'kitchen-terraform', '~> 0.6' +end diff --git a/README.md b/README.md index 7d7dbed..a0daf74 100644 --- a/README.md +++ b/README.md @@ -27,28 +27,38 @@ For an example of using ALB with ECS look no further than the [hashicorp example ## Input Variables --------------- -* `lc_name` - The launch configuration name +* `alb_is_internal` - Determines if the ALB is externally facing or internal. (Optional; default: false) +* `alb_name` - Name of the ALB as it appears in the AWS console. (Optional; default: my_alb) +* `alb_protocols` - A comma delimited list of protocols the ALB will accept for incoming connections. Only HTTP and HTTPS are supported. (Optional; default: HTTPS) +* `alb_security_groups` - A comma delimited list of security groups to attach to the ALB. (Required) +* `backend_port` - Port on which the backing instances serve traffic. (Optional; default: 80) +* `backend_protocol` - Protocol the backing instances use. (Optional; default: HTTP) +* `certificate_arn` - . (Required if using HTTPS in `alb_protocols`) +* `cookie_duration` - If sticky sessions via cookies are desired, set this variable to a value from 2 - 604800 seconds. (Optional) +* `health_check_path` - Path for the load balancer to health check instances. (Optional; default: /) +* `log_bucket` - S3 bucket where access logs should land. (Required) +* `log_prefix` - S3 prefix within the `log_bucket` where logs should land. (Optional) +* `subnets` - ALB will be created in the subnets in this list. (Required) +* `vpc_id` - Resources will be created in the VPC with this `id`. (Required) ## Outputs ------- -- `alb_id` +* `alb_id` - `id` of the ALB created. +* `alb_dns_name` - DNS CNAME of the ALB created. +* `alb_zone_id` - Route53 `zone_id` of the newly minted ALB. +* `target_group_arn` - `arn` of the target group. Useful for passing to your Auto Scaling group module. ## Usage example: A full example set is contained in the [example directory](example/). Here's the gist: 1. Set the input variables from above in [variables.tf](example/variables.tf). 2. Define the ALB module using the following in your [main.tf](example/main.tf): ``` -module "my_web_alb" { - source = "github.com/brandoconnor/tf_aws_alb" - alb_name = "${var.alb_name}" - subnet_azs = "${var.subnet_azs}" - backend_port = "${var.backend_port}" - backend_protocol = "${var.backend_protocol}" - ssl_certificate_id = "${var.ssl_certificate_id}" - health_check_target = "${var.health_check_target}" - aws_access_key = "${var.aws_access_key}" - aws_secret_key = "${var.aws_secret_key}" - aws_region = "${var.aws_region}" +module "alb" { + source = "github.com/brandoconnor/tf_aws_alb" + alb_security_groups = "${var.security_group_id_list}" + log_bucket = "${var.log_bucket}" + subnets = "${var.subnet_id_list}" + vpc_id = "${var.vpc_id}" } ``` 3. Always `terraform plan` to see your change before running `terraform apply`. diff --git a/alb/main.tf b/alb/main.tf new file mode 100644 index 0000000..df1627b --- /dev/null +++ b/alb/main.tf @@ -0,0 +1,24 @@ +### ALB resources with a switch - logging enabled/disabled + +resource "aws_alb" "alb_loging" { + name = "${var.alb_name}" + subnets = ["${split(",", var.subnets)}"] + security_groups = ["${split(",", var.alb_security_groups)}"] + internal = "${var.alb_is_internal}" + + access_logs { + bucket = "${var.log_bucket}" + prefix = "${var.log_prefix}" + } + + count = "${var.log_bucket != "" && var.log_prefix != "" ? 1 : 0}" +} + +resource "aws_alb" "alb_nologing" { + name = "${var.alb_name}" + subnets = ["${split(",", var.subnets)}"] + security_groups = ["${split(",", var.alb_security_groups)}"] + internal = "${var.alb_is_internal}" + + count = "${(var.log_bucket == "" || var.log_prefix == "") ? 1 : 0}" +} diff --git a/alb/outputs.tf b/alb/outputs.tf new file mode 100644 index 0000000..f781a18 --- /dev/null +++ b/alb/outputs.tf @@ -0,0 +1,24 @@ +# this approach is not working just yet +output "arn" { + value = "${coalesce(aws_alb.alb_loging.arn, aws_alb.alb_nologing.arn) }" + + /*value = "${aws_alb.alb_loging.arn}"*/ +} + +output "dns_name" { + value = "${aws_alb.alb_loging.dns_name}" + + /*value = "${coalesce(list("aws_alb.alb_loging.dns_name", "aws_alb.alb_nologing.dns_name")) }"*/ +} + +output "id" { + value = "${aws_alb.alb_loging.id}" + + /*value = "${coalesce(list("aws_alb.alb_loging.dns_name", "aws_alb.alb_nologing.dns_name")) }"*/ +} + +output "zone_id" { + value = "${aws_alb.alb_loging.zone_id}" + + /*value = "${coalesce(list("aws_alb.alb_loging.dns_name", "aws_alb.alb_nologing.dns_name")) }"*/ +} diff --git a/alb/variables.tf b/alb/variables.tf new file mode 100644 index 0000000..2072732 --- /dev/null +++ b/alb/variables.tf @@ -0,0 +1,28 @@ +/* +Module variables +*/ + +variable "alb_is_internal" { + description = "Determines if the ALB is internal. Default: false" + default = false +} + +variable "alb_name" { + description = "The name of the ALB as will show in the AWS EC2 ELB console." +} + +variable "alb_security_groups" { + description = "A comma separated string of security groups with which we associate the ALB. e.g. 'sg-edcd9784,sg-edcd9785'" +} + +variable "log_bucket" { + description = "S3 bucket for storing ALB access logs." +} + +variable "log_prefix" { + description = "S3 prefix within the log_bucket under which logs are stored." +} + +variable "subnets" { + description = "A comma delimited list of subnets to associate with the ALB. e.g. 'subnet-1a2b3c4d,subnet-1a2b3c4e,subnet-1a2b3c4f'" +} diff --git a/example/main.tf b/example/main.tf index 22982f6..212c82e 100644 --- a/example/main.tf +++ b/example/main.tf @@ -1,13 +1,15 @@ -module "external_alb" { +provider "aws" { + allowed_account_ids = ["${var.aws_account_id}"] + region = "${var.aws_region}" + access_key = "${var.aws_access_key}" + secret_key = "${var.aws_secret_key}" + region = "${var.aws_region}" +} + +module "alb" { source = "github.com/brandoconnor/tf_aws_alb" - alb_name = "${var.alb_name}" - backend_port = "${var.instance_port}" - backend_protocol = "${var.instance_protocol}" - health_check_target = "${var.health_check_target}" - alb_security_groups = "${join(",", var.security_group_id_list)}" - log_bucket = "${var.log_bucket_name}-${var.aws_region}" - log_prefix = "${var.log_prefix}" - certificate_arn = "${var.certificate_arn}" - subnets = "${join(",", var.public_subnet_ids)}" + alb_security_groups = "${var.security_group_id_list}" + log_bucket = "${var.log_bucket}" + subnets = "${var.subnet_id_list}" vpc_id = "${var.vpc_id}" } diff --git a/example/variables.tf b/example/variables.tf index bd94eae..e8d34a9 100644 --- a/example/variables.tf +++ b/example/variables.tf @@ -1,43 +1,39 @@ -variable "aws_region" { - default = "us-west-2" +variable "aws_account_id" { + default = "123456789012" } -variable "alb_name" { - default = "example_alb" +variable "aws_access_key" { + default = "access_key_id_here" } -variable "security_group_id_list" { - default = ["sg-edcd9784", "sg-edcd9785"] +variable "aws_secret_key" { + default = "secret_access_key_here" } -variable "certificate_arn" { - default = "arn:aws:iam::123456789012:server-certificate/ProdServerCert" +variable "aws_region" { + default = "us-west-2" } -variable "public_subnet_ids" { - default = ["subnet-1a2b3c4d", "subnet-1a2b3c4e", "subnet-1a2b3c4f"] +variable "security_group_id_list" { + default = ["sg-edcd9784", "sg-edcd9785"] } -variable "instance_port" { - default = "8080" +variable "security_group_id_list" { + default = ["sg-edcd9784", "sg-edcd9785"] } -variable "instance_protocol" { - default = "HTTPS" +variable "security_group_id_list" { + default = ["sg-edcd9784", "sg-edcd9785"] } -variable "health_check_target" { - default = "HTTPS:443/health" +variable "subnet_id_list" { + default = ["subnet-1a2b3c4d", "subnet-1a2b3c4e", "subnet-1a2b3c4f"] } variable "log_bucket_name" { default = "my_log_bucket" } -variable "log_prefix" { - default = "example_alb" -} - variable "vpc_id" { default = "vpc-12345678" } diff --git a/main.tf b/main.tf index 303fec8..f4555c2 100644 --- a/main.tf +++ b/main.tf @@ -1,21 +1,29 @@ ### ALB resources # TODO: -# need health check -# internal or external -# with logging or without logging (perhaps even submodule locally?) +# support not logging +/* +# will return to this approach later +module "alb" { + source = "./alb" + alb_name = "${var.alb_name}" + alb_security_groups = "${var.alb_security_groups}" + log_bucket = "${var.log_bucket}" + log_prefix = "${var.log_prefix}" + subnets = "${var.subnets}" +} +*/ resource "aws_alb" "main" { name = "${var.alb_name}" subnets = ["${split(",", var.subnets)}"] security_groups = ["${split(",", var.alb_security_groups)}"] + internal = "${var.alb_is_internal}" - /* - access_logs { - bucket = "${var.log_bucket}" - prefix = "${var.log_prefix}" - }*/ - count = 1 + access_logs { + bucket = "${var.log_bucket}" + prefix = "${var.log_prefix}" + } } resource "aws_alb_target_group" "target_group" { @@ -23,11 +31,30 @@ resource "aws_alb_target_group" "target_group" { port = "${var.backend_port}" protocol = "${upper(var.backend_protocol)}" vpc_id = "${var.vpc_id}" + + health_check { + interval = 30 + path = "${var.health_check_path}" + port = "traffic-port" + healthy_threshold = 3 + unhealthy_threshold = 3 + timeout = 5 + protocol = "${var.backend_protocol}" + } + + stickiness { + type = "lb_cookie" + cookie_duration = "${var.cookie_duration}" + enabled = "${ var.cookie_duration == 1 ? false : true}" + } } -# add listeners using count based on http/https vars +/* +aws_alb.main becomes module.alb in submodulelandia +*/ + resource "aws_alb_listener" "front_end_http" { - load_balancer_arn = "${aws_alb.main.id}" + load_balancer_arn = "${aws_alb.main.arn}" port = "80" protocol = "HTTP" @@ -35,10 +62,12 @@ resource "aws_alb_listener" "front_end_http" { target_group_arn = "${aws_alb_target_group.target_group.id}" type = "forward" } + + count = "${trimspace(element(split(",", var.alb_protocols), 1)) == "HTTP" || trimspace(element(split(",", var.alb_protocols), 2)) == "HTTP" ? 1 : 0}" } resource "aws_alb_listener" "front_end_https" { - load_balancer_arn = "${aws_alb.main.id}" + load_balancer_arn = "${aws_alb.main.arn}" port = "443" protocol = "HTTPS" certificate_arn = "${var.certificate_arn}" @@ -48,55 +77,6 @@ resource "aws_alb_listener" "front_end_https" { target_group_arn = "${aws_alb_target_group.target_group.id}" type = "forward" } -} -/* -resource "aws_elb" "elb" { - name = "${var.elb_name}" - subnets = ["${split(",", var.subnet_azs)}"] - internal = "${var.elb_is_internal}" - security_groups = ["${split(",", var.elb_security_groups)}"] - - access_logs { - bucket = "${var.log_bucket}" - bucket_prefix = "${var.log_prefix}" - interval = 5 - } - - health_check { - healthy_threshold = 2 - unhealthy_threshold = 2 - timeout = 3 - target = "${var.health_check_target}" - interval = 30 - } - - cross_zone_load_balancing = true - - tags { - Name = "${var.name}" - App = "${var.app}" - Creator = "${var.creator}" - Group = "${var.group}" - Environment = "${var.environment_tag}" - Ops_Environment = "${var.operational_environment}" - } -} - -resource "aws_lb_cookie_stickiness_policy" "http_stickiness" { - name = "httpstickiness" - load_balancer = "${aws_elb.elb.id}" - lb_port = 80 - cookie_expiration_period = 600 - depends_on = ["aws_elb.elb"] + count = "${trimspace(element(split(",", var.alb_protocols), 1)) == "HTTPS" || trimspace(element(split(",", var.alb_protocols), 2)) == "HTTPS" ? 1 : 0}" } - -resource "aws_lb_cookie_stickiness_policy" "https_stickiness" { - name = "httpsstickiness" - load_balancer = "${aws_elb.elb.id}" - lb_port = 443 - cookie_expiration_period = 600 - depends_on = ["aws_elb.elb"] -} -*/ - diff --git a/variables.tf b/variables.tf index 4ff58fc..278f610 100644 --- a/variables.tf +++ b/variables.tf @@ -2,50 +2,61 @@ Module variables */ -variable "alb_name" { - description = "The name of the ALB as will show in the AWS EC2 ELB console." +variable "alb_is_internal" { + description = "Determines if the ALB is internal. Default: false" + default = false } -variable "alb_security_groups" { - description = "A comma separated string of security groups with which we associate the ALB. e.g. 'sg-edcd9784,sg-edcd9785'" +variable "alb_name" { + description = "The name of the ALB as will show in the AWS EC2 ELB console." + default = "my_alb" } -variable "certificate_arn" { - description = "The ARN of the SSL Certificate. e.g. 'arn:aws:iam::123456789012:server-certificate/ProdServerCert'" +variable "alb_protocols" { + description = "A comma delimited list of the protocols the ALB accepts. e.g.: HTTPS" + default = "HTTPS" } -variable "subnets" { - description = "A comma delimited list of subnets to associate with the ALB. e.g. 'subnet-1a2b3c4d,subnet-1a2b3c4e,subnet-1a2b3c4f'" +variable "alb_security_groups" { + description = "A comma separated string of security groups with which we associate the ALB. e.g. 'sg-edcd9784,sg-edcd9785'" } variable "backend_port" { description = "The port the service on the EC2 instances listen on." + default = 80 } variable "backend_protocol" { - description = "The protocol the backend service speaks. Options: http, https, tcp, ssl (secure tcp)." + description = "The protocol the backend service speaks. Options: HTTP, HTTPS, TCP, SSL (secure tcp)." + default = "HTTP" } -variable "health_check_target" { - description = "The URL the ELB should use for health checks. e.g. HTTPS:443/health" +variable "certificate_arn" { + description = "The ARN of the SSL Certificate. e.g. 'arn:aws:iam::123456789012:server-certificate/ProdServerCert'" } -variable "vpc_id" { - description = "" +variable "health_check_path" { + description = "The URL the ELB should use for health checks. e.g. /health" + default = "/" } variable "log_bucket" { - default = "" + description = "S3 bucket for storing ALB access logs." } variable "log_prefix" { - default = "" + description = "S3 prefix within the log_bucket under which logs are stored." } -/* -variable "alb_is_internal" { - description = "Determines if the ALB is internal. Default: false" - default = false +variable "cookie_duration" { + description = "If load balancer connection stickiness is desired, set this to the duration that cookie should be valid. If no stickiness is wanted, leave it blank. e.g.: 300" + default = "1" } -*/ +variable "subnets" { + description = "A comma delimited list of subnets to associate with the ALB. e.g. 'subnet-1a2b3c4d,subnet-1a2b3c4e,subnet-1a2b3c4f'" +} + +variable "vpc_id" { + description = "VPC id where the ALB and other resources will be deployed." +}