diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 333d563..16cbedc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,12 +2,14 @@ # See http://pre-commit.com/hooks.html for more hooks repos: - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.7.0 + rev: v1.7.1 + sha: 091f8b15d7b458e5a0aca642483deb2205e7db02 hooks: - id: terraform_fmt - - id: terraform_docs + # - id: terraform_docs - repo: git://github.com/pre-commit/pre-commit-hooks rev: v1.2.3 + sha: 92e1570c282e3c69a1f8b5b8dd8d286fe27cfaa7 hooks: - id: check-merge-conflict - id: trailing-whitespace diff --git a/CHANGELOG.md b/CHANGELOG.md index 34f271f..d014163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [[v3.4.0](https://github.com/terraform-aws-modules/terraform-aws-alb/compare/v3.3.1...v3.4.0)] - 2018-05-17] + +### Changed + +* resources supporting the not logging scenario added. Outputs now accommodate. +* reorganized the resource explosion to separate files. +* tests reorganized to confine cruft. +* `terraform-docs` now supported and generating documentation. (Kiitos, @antonbabenko 🍒) + ## [[v3.3.1](https://github.com/terraform-aws-modules/terraform-aws-alb/compare/v3.3.0...v3.3.1)] - 2018-05-06] ### Changed diff --git a/README.md b/README.md index ecc7e41..10c28f8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # terraform-aws-alb A Terraform module containing common configurations for an AWS Application Load -Balancer (ALB) running over HTTP/HTTPS. Available through the [terraform registry](https://registry.terraform.io/modules/terraform-aws-modules/alb/aws). +Balancer (ALB) running over HTTP/HTTPS. Available through the [Terraform registry](https://registry.terraform.io/modules/terraform-aws-modules/alb/aws). | Branch | Build status | | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -31,11 +31,11 @@ to the ASG immediately or will result in failure. The value of `target_group[n][ ## Why ALB instead of ELB -The use-case presented here appears almost identical to how one would use an ELB -but we inherit a few bonuses by moving to ALB like the ability to leverage WAF. +ALB has the ability to replace what several ELBs can do by routing based on URI matchers. +Additionally, operating at layer 7 opens the ability to shape traffic using WAF. [AWS's documentation](https://aws.amazon.com/elasticloadbalancing/applicationloadbalancer/) has a more exhaustive set of reasons. Alternatively, if using ALB with ECS look no further than -the [Hashicorp example](https://github.com/terraform-providers/terraform-provider-aws/blob/master/examples/ecs-alb). +the [HashiCorp example](https://github.com/terraform-providers/terraform-provider-aws/blob/master/examples/ecs-alb). ## Usage example @@ -60,7 +60,52 @@ module "alb" { } ``` - +## Testing + +This module has been packaged with [awspec](https://github.com/k1LoW/awspec) tests through [kitchen](https://kitchen.ci/) and [kitchen-terraform](https://newcontext-oss.github.io/kitchen-terraform/). To run them: + +1. Install [rvm](https://rvm.io/rvm/install) and the ruby version specified in the [Gemfile](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/Gemfile). +2. Install bundler and the gems from our Gemfile: + + ```bash + gem install bundler && bundle install + ``` + +3. Ensure your AWS environment is configured (i.e. credentials and region) for test and set TF_VAR_region to a valid AWS region (e.g. `export TF_VAR_region=${AWS_REGION}`). +4. Test using `bundle exec kitchen test` from the root of the repo. + +## Doc generation + +Documentation should be modified within `main.tf` and generated using [terraform-docs](https://github.com/segmentio/terraform-docs). +Generate them like so: + +```bash +terraform-docs md ./ | cat -s | ghead -n -1 > README.md +``` + +## Contributing + +Report issues/questions/feature requests on in the [issues](https://github.com/terraform-aws-modules/terraform-aws-alb/issues/new) section. + +Full contributing [guidelines are covered here](https://github.com/terraform-aws-modules/terraform-aws-alb/blob/master/CONTRIBUTING.md). + +## IAM Permissions + +Testing and using this repo requires a minimum set of IAM permissions. Test permissions +are listed in the [alb_test_fixture README](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/examples/alb_test_fixture/README.md). + +## Change log + +The [changelog](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/CHANGELOG.md) captures all important release notes. + +## Authors + +Created and maintained by [Brandon O'Connor](https://github.com/brandoconnor) - brandon@atscale.run. +Many thanks to [the contributors listed here](https://github.com/terraform-aws-modules/terraform-aws-alb/graphs/contributors)! + +## License + +MIT Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/LICENSE) for full details. ## Inputs @@ -76,14 +121,15 @@ module "alb" { | https_listeners_count | A manually provided count/length of the https_listeners list of maps since the list cannot be computed. | string | `0` | no | | idle_timeout | The time in seconds that the connection is allowed to be idle. | string | `60` | no | | ip_address_type | The type of IP addresses used by the subnets for your load balancer. The possible values are ipv4 and dualstack. | string | `ipv4` | no | -| listener_ssl_policy_default | The security policy if using HTTPS externally on the load balancer. See: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html | string | `ELBSecurityPolicy-2016-08` | no | +| listener_ssl_policy_default | The security policy if using HTTPS externally on the load balancer. [See](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html). | string | `ELBSecurityPolicy-2016-08` | no | | load_balancer_create_timeout | Timeout value when creating the ALB. | string | `10m` | no | | load_balancer_delete_timeout | Timeout value when deleting the ALB. | string | `10m` | no | | load_balancer_is_internal | Boolean determining if the load balancer is internal or externally facing. | string | `false` | no | | load_balancer_name | The resource name and Name tag of the load balancer. | string | - | yes | | load_balancer_update_timeout | Timeout value when updating the ALB. | string | `10m` | no | -| log_bucket_name | S3 bucket (externally created) for storing load balancer access logs. | string | - | yes | +| log_bucket_name | S3 bucket (externally created) for storing load balancer access logs. Required if logging_enabled is true. | string | `` | no | | log_location_prefix | S3 prefix within the log_bucket_name under which logs are stored. | string | `` | no | +| logging_enabled | Controls if the ALB will log requests to S3. | string | `true` | no | | security_groups | The security groups to attach to the load balancer. e.g. ["sg-edcd9784","sg-edcd9785"] | list | - | yes | | subnets | A list of subnets to associate with the load balancer. e.g. ['subnet-1a2b3c4d','subnet-1a2b3c4e','subnet-1a2b3c4f'] | list | - | yes | | tags | A map of tags to add to all resources | string | `` | no | @@ -107,43 +153,3 @@ module "alb" { | target_group_arn_suffixes | ARN suffixes of our target groups - can be used with CloudWatch. | | target_group_arns | ARNs of the target groups. Useful for passing to your Auto Scaling group. | | target_group_names | Name of the target group. Useful for passing to your CodeDeploy Deployment Group. | - - - -## Testing - -This module has been packaged with [awspec](https://github.com/k1LoW/awspec) tests through [kitchen](https://kitchen.ci/) and [kitchen-terraform](https://newcontext-oss.github.io/kitchen-terraform/). To run them: - -1. Install [rvm](https://rvm.io/rvm/install) and the ruby version specified in the [Gemfile](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/Gemfile). -2. Install bundler and the gems from our Gemfile: - - ```bash - gem install bundler && bundle install - ``` - -3. Ensure your AWS environment is configured (i.e. credentials and region) for test and set TF_VAR_region to a valid AWS region (e.g. `export TF_VAR_region=${AWS_REGION}`). -4. Test using `bundle exec kitchen test` from the root of the repo. - -## Contributing - -Report issues/questions/feature requests on in the [issues](https://github.com/terraform-aws-modules/terraform-aws-alb/issues/new) section. - -Full contributing [guidelines are covered here](https://github.com/terraform-aws-modules/terraform-aws-alb/blob/master/CONTRIBUTING.md). - -## IAM Permissions - -Testing and using this repo requires a minimum set of IAM permissions. Test permissions -are listed in the [alb_test_fixture README](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/examples/alb_test_fixture/README.md). - -## Change log - -The [changelog](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/CHANGELOG.md) captures all important release notes. - -## Authors - -Created and maintained by [Brandon O'Connor](https://github.com/brandoconnor) - brandon@atscale.run. -Many thanks to [the contributors listed here](https://github.com/terraform-aws-modules/terraform-aws-alb/graphs/contributors)! - -## License - -MIT Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/LICENSE) for full details. diff --git a/alb_no_logs.tf b/alb_no_logs.tf new file mode 100644 index 0000000..49df34f --- /dev/null +++ b/alb_no_logs.tf @@ -0,0 +1,86 @@ +resource "aws_lb" "application_no_logs" { + load_balancer_type = "application" + name = "${var.load_balancer_name}" + internal = "${var.load_balancer_is_internal}" + security_groups = ["${var.security_groups}"] + subnets = ["${var.subnets}"] + idle_timeout = "${var.idle_timeout}" + enable_deletion_protection = "${var.enable_deletion_protection}" + enable_http2 = "${var.enable_http2}" + ip_address_type = "${var.ip_address_type}" + tags = "${merge(var.tags, map("Name", var.load_balancer_name))}" + + timeouts { + create = "${var.load_balancer_create_timeout}" + delete = "${var.load_balancer_delete_timeout}" + update = "${var.load_balancer_update_timeout}" + } + + count = "${var.logging_enabled ? 0 : 1}" +} + +resource "aws_lb_target_group" "main_no_logs" { + name = "${lookup(var.target_groups[count.index], "name")}" + vpc_id = "${var.vpc_id}" + port = "${lookup(var.target_groups[count.index], "backend_port")}" + protocol = "${upper(lookup(var.target_groups[count.index], "backend_protocol"))}" + deregistration_delay = "${lookup(var.target_groups[count.index], "deregistration_delay", lookup(var.target_groups_defaults, "deregistration_delay"))}" + target_type = "${lookup(var.target_groups[count.index], "target_type", lookup(var.target_groups_defaults, "target_type"))}" + + health_check { + interval = "${lookup(var.target_groups[count.index], "health_check_interval", lookup(var.target_groups_defaults, "health_check_interval"))}" + path = "${lookup(var.target_groups[count.index], "health_check_path", lookup(var.target_groups_defaults, "health_check_path"))}" + port = "${lookup(var.target_groups[count.index], "health_check_port", lookup(var.target_groups_defaults, "health_check_port"))}" + healthy_threshold = "${lookup(var.target_groups[count.index], "health_check_healthy_threshold", lookup(var.target_groups_defaults, "health_check_healthy_threshold"))}" + unhealthy_threshold = "${lookup(var.target_groups[count.index], "health_check_unhealthy_threshold", lookup(var.target_groups_defaults, "health_check_unhealthy_threshold"))}" + timeout = "${lookup(var.target_groups[count.index], "health_check_timeout", lookup(var.target_groups_defaults, "health_check_timeout"))}" + protocol = "${upper(lookup(var.target_groups[count.index], "healthcheck_protocol", lookup(var.target_groups[count.index], "backend_protocol")))}" + matcher = "${lookup(var.target_groups[count.index], "health_check_matcher", lookup(var.target_groups_defaults, "health_check_matcher"))}" + } + + stickiness { + type = "lb_cookie" + cookie_duration = "${lookup(var.target_groups[count.index], "cookie_duration", lookup(var.target_groups_defaults, "cookie_duration"))}" + enabled = "${lookup(var.target_groups[count.index], "stickiness_enabled", lookup(var.target_groups_defaults, "stickiness_enabled"))}" + } + + tags = "${merge(var.tags, map("Name", lookup(var.target_groups[count.index], "name")))}" + count = "${var.logging_enabled ? 0 : var.target_groups_count}" + depends_on = ["aws_lb.application_no_logs"] + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_lb_listener" "frontend_http_tcp_no_logs" { + load_balancer_arn = "${element(concat(aws_lb.application_no_logs.*.arn, list("")), 0)}" + port = "${lookup(var.http_tcp_listeners[count.index], "port")}" + protocol = "${lookup(var.http_tcp_listeners[count.index], "protocol")}" + count = "${var.logging_enabled ? 0 : var.http_tcp_listeners_count}" + + default_action { + target_group_arn = "${aws_lb_target_group.main_no_logs.*.id[lookup(var.http_tcp_listeners[count.index], "target_group_index", 0)]}" + type = "forward" + } +} + +resource "aws_lb_listener" "frontend_https_no_logs" { + load_balancer_arn = "${element(concat(aws_lb.application_no_logs.*.arn, list("")), 0)}" + port = "${lookup(var.https_listeners[count.index], "port")}" + protocol = "HTTPS" + certificate_arn = "${lookup(var.https_listeners[count.index], "certificate_arn")}" + ssl_policy = "${lookup(var.https_listeners[count.index], "ssl_policy", var.listener_ssl_policy_default)}" + count = "${var.logging_enabled ? 0 : var.https_listeners_count}" + + default_action { + target_group_arn = "${aws_lb_target_group.main_no_logs.*.id[lookup(var.https_listeners[count.index], "target_group_index", 0)]}" + type = "forward" + } +} + +resource "aws_lb_listener_certificate" "https_listener_no_logs" { + listener_arn = "${aws_lb_listener.frontend_https_no_logs.*.arn[lookup(var.extra_ssl_certs[count.index], "https_listener_index")]}" + certificate_arn = "${lookup(var.extra_ssl_certs[count.index], "certificate_arn")}" + count = "${var.logging_enabled ? 0 : var.extra_ssl_certs_count}" +} diff --git a/alb_w_logs.tf b/alb_w_logs.tf new file mode 100644 index 0000000..6f3569f --- /dev/null +++ b/alb_w_logs.tf @@ -0,0 +1,92 @@ +resource "aws_lb" "application" { + load_balancer_type = "application" + name = "${var.load_balancer_name}" + internal = "${var.load_balancer_is_internal}" + security_groups = ["${var.security_groups}"] + subnets = ["${var.subnets}"] + idle_timeout = "${var.idle_timeout}" + enable_deletion_protection = "${var.enable_deletion_protection}" + enable_http2 = "${var.enable_http2}" + ip_address_type = "${var.ip_address_type}" + tags = "${merge(var.tags, map("Name", var.load_balancer_name))}" + + access_logs { + enabled = true + bucket = "${var.log_bucket_name}" + prefix = "${var.log_location_prefix}" + } + + timeouts { + create = "${var.load_balancer_create_timeout}" + delete = "${var.load_balancer_delete_timeout}" + update = "${var.load_balancer_update_timeout}" + } + + count = "${var.logging_enabled ? 1 : 0}" +} + +resource "aws_lb_target_group" "main" { + name = "${lookup(var.target_groups[count.index], "name")}" + vpc_id = "${var.vpc_id}" + port = "${lookup(var.target_groups[count.index], "backend_port")}" + protocol = "${upper(lookup(var.target_groups[count.index], "backend_protocol"))}" + deregistration_delay = "${lookup(var.target_groups[count.index], "deregistration_delay", lookup(var.target_groups_defaults, "deregistration_delay"))}" + target_type = "${lookup(var.target_groups[count.index], "target_type", lookup(var.target_groups_defaults, "target_type"))}" + + health_check { + interval = "${lookup(var.target_groups[count.index], "health_check_interval", lookup(var.target_groups_defaults, "health_check_interval"))}" + path = "${lookup(var.target_groups[count.index], "health_check_path", lookup(var.target_groups_defaults, "health_check_path"))}" + port = "${lookup(var.target_groups[count.index], "health_check_port", lookup(var.target_groups_defaults, "health_check_port"))}" + healthy_threshold = "${lookup(var.target_groups[count.index], "health_check_healthy_threshold", lookup(var.target_groups_defaults, "health_check_healthy_threshold"))}" + unhealthy_threshold = "${lookup(var.target_groups[count.index], "health_check_unhealthy_threshold", lookup(var.target_groups_defaults, "health_check_unhealthy_threshold"))}" + timeout = "${lookup(var.target_groups[count.index], "health_check_timeout", lookup(var.target_groups_defaults, "health_check_timeout"))}" + protocol = "${upper(lookup(var.target_groups[count.index], "healthcheck_protocol", lookup(var.target_groups[count.index], "backend_protocol")))}" + matcher = "${lookup(var.target_groups[count.index], "health_check_matcher", lookup(var.target_groups_defaults, "health_check_matcher"))}" + } + + stickiness { + type = "lb_cookie" + cookie_duration = "${lookup(var.target_groups[count.index], "cookie_duration", lookup(var.target_groups_defaults, "cookie_duration"))}" + enabled = "${lookup(var.target_groups[count.index], "stickiness_enabled", lookup(var.target_groups_defaults, "stickiness_enabled"))}" + } + + tags = "${merge(var.tags, map("Name", lookup(var.target_groups[count.index], "name")))}" + count = "${var.logging_enabled ? var.target_groups_count : 0}" + depends_on = ["aws_lb.application"] + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_lb_listener" "frontend_http_tcp" { + load_balancer_arn = "${element(concat(aws_lb.application.*.arn, aws_lb.application_no_logs.*.arn), 0)}" + port = "${lookup(var.http_tcp_listeners[count.index], "port")}" + protocol = "${lookup(var.http_tcp_listeners[count.index], "protocol")}" + count = "${var.logging_enabled ? var.http_tcp_listeners_count : 0}" + + default_action { + target_group_arn = "${aws_lb_target_group.main.*.id[lookup(var.http_tcp_listeners[count.index], "target_group_index", 0)]}" + type = "forward" + } +} + +resource "aws_lb_listener" "frontend_https" { + load_balancer_arn = "${element(concat(aws_lb.application.*.arn, aws_lb.application_no_logs.*.arn), 0)}" + port = "${lookup(var.https_listeners[count.index], "port")}" + protocol = "HTTPS" + certificate_arn = "${lookup(var.https_listeners[count.index], "certificate_arn")}" + ssl_policy = "${lookup(var.https_listeners[count.index], "ssl_policy", var.listener_ssl_policy_default)}" + count = "${var.logging_enabled ? var.https_listeners_count : 0}" + + default_action { + target_group_arn = "${aws_lb_target_group.main.*.id[lookup(var.https_listeners[count.index], "target_group_index", 0)]}" + type = "forward" + } +} + +resource "aws_lb_listener_certificate" "https_listener" { + listener_arn = "${aws_lb_listener.frontend_https.*.arn[lookup(var.extra_ssl_certs[count.index], "https_listener_index")]}" + certificate_arn = "${lookup(var.extra_ssl_certs[count.index], "certificate_arn")}" + count = "${var.logging_enabled ? var.extra_ssl_certs_count : 0}" +} diff --git a/examples/alb_test_fixture/main.tf b/examples/alb_test_fixture/main.tf index 780ea1b..ad9be04 100644 --- a/examples/alb_test_fixture/main.tf +++ b/examples/alb_test_fixture/main.tf @@ -76,6 +76,12 @@ resource "aws_autoscaling_group" "test" { vpc_zone_identifier = ["${module.vpc.public_subnets}"] } +resource "aws_launch_configuration" "test" { + name_prefix = "test_lc" + image_id = "${data.aws_ami.ubuntu.id}" + instance_type = "t2.micro" +} + resource "aws_launch_configuration" "as_conf" { name = "web_config" image_id = "${data.aws_ami.ubuntu.id}" @@ -86,6 +92,7 @@ module "alb" { source = "../.." load_balancer_name = "test-alb-${random_string.suffix.result}" security_groups = ["${module.security_group.this_security_group_id}"] + logging_enabled = true log_bucket_name = "${aws_s3_bucket.log_bucket.id}" log_location_prefix = "${var.log_location_prefix}" subnets = "${module.vpc.public_subnets}" diff --git a/main.tf b/main.tf index b341dc8..30b3246 100644 --- a/main.tf +++ b/main.tf @@ -1,90 +1,111 @@ -resource "aws_lb" "application" { - load_balancer_type = "application" - name = "${var.load_balancer_name}" - internal = "${var.load_balancer_is_internal}" - security_groups = ["${var.security_groups}"] - subnets = ["${var.subnets}"] - idle_timeout = "${var.idle_timeout}" - enable_deletion_protection = "${var.enable_deletion_protection}" - enable_http2 = "${var.enable_http2}" - ip_address_type = "${var.ip_address_type}" - tags = "${merge(var.tags, map("Name", var.load_balancer_name))}" - - access_logs { - enabled = true - bucket = "${var.log_bucket_name}" - prefix = "${var.log_location_prefix}" - } - - timeouts { - create = "${var.load_balancer_create_timeout}" - delete = "${var.load_balancer_delete_timeout}" - update = "${var.load_balancer_update_timeout}" - } -} - -resource "aws_lb_target_group" "main" { - name = "${lookup(var.target_groups[count.index], "name")}" - vpc_id = "${var.vpc_id}" - port = "${lookup(var.target_groups[count.index], "backend_port")}" - protocol = "${upper(lookup(var.target_groups[count.index], "backend_protocol"))}" - deregistration_delay = "${lookup(var.target_groups[count.index], "deregistration_delay", lookup(var.target_groups_defaults, "deregistration_delay"))}" - target_type = "${lookup(var.target_groups[count.index], "target_type", lookup(var.target_groups_defaults, "target_type"))}" - - health_check { - interval = "${lookup(var.target_groups[count.index], "health_check_interval", lookup(var.target_groups_defaults, "health_check_interval"))}" - path = "${lookup(var.target_groups[count.index], "health_check_path", lookup(var.target_groups_defaults, "health_check_path"))}" - port = "${lookup(var.target_groups[count.index], "health_check_port", lookup(var.target_groups_defaults, "health_check_port"))}" - healthy_threshold = "${lookup(var.target_groups[count.index], "health_check_healthy_threshold", lookup(var.target_groups_defaults, "health_check_healthy_threshold"))}" - unhealthy_threshold = "${lookup(var.target_groups[count.index], "health_check_unhealthy_threshold", lookup(var.target_groups_defaults, "health_check_unhealthy_threshold"))}" - timeout = "${lookup(var.target_groups[count.index], "health_check_timeout", lookup(var.target_groups_defaults, "health_check_timeout"))}" - protocol = "${upper(lookup(var.target_groups[count.index], "healthcheck_protocol", lookup(var.target_groups[count.index], "backend_protocol")))}" - matcher = "${lookup(var.target_groups[count.index], "health_check_matcher", lookup(var.target_groups_defaults, "health_check_matcher"))}" - } - - stickiness { - type = "lb_cookie" - cookie_duration = "${lookup(var.target_groups[count.index], "cookie_duration", lookup(var.target_groups_defaults, "cookie_duration"))}" - enabled = "${lookup(var.target_groups[count.index], "stickiness_enabled", lookup(var.target_groups_defaults, "stickiness_enabled"))}" - } - - tags = "${merge(var.tags, map("Name", lookup(var.target_groups[count.index], "name")))}" - count = "${var.target_groups_count}" - depends_on = ["aws_lb.application"] - - lifecycle { - create_before_destroy = true - } -} - -resource "aws_lb_listener" "frontend_http_tcp" { - load_balancer_arn = "${aws_lb.application.arn}" - port = "${lookup(var.http_tcp_listeners[count.index], "port")}" - protocol = "${lookup(var.http_tcp_listeners[count.index], "protocol")}" - count = "${var.http_tcp_listeners_count}" - - default_action { - target_group_arn = "${aws_lb_target_group.main.*.id[lookup(var.http_tcp_listeners[count.index], "target_group_index", 0)]}" - type = "forward" - } -} - -resource "aws_lb_listener" "frontend_https" { - load_balancer_arn = "${aws_lb.application.arn}" - port = "${lookup(var.https_listeners[count.index], "port")}" - protocol = "HTTPS" - certificate_arn = "${lookup(var.https_listeners[count.index], "certificate_arn")}" - ssl_policy = "${lookup(var.https_listeners[count.index], "ssl_policy", var.listener_ssl_policy_default)}" - count = "${var.https_listeners_count}" - - default_action { - target_group_arn = "${aws_lb_target_group.main.*.id[lookup(var.https_listeners[count.index], "target_group_index", 0)]}" - type = "forward" - } -} - -resource "aws_lb_listener_certificate" "https_listener" { - listener_arn = "${aws_lb_listener.frontend_https.*.arn[lookup(var.extra_ssl_certs[count.index], "https_listener_index")]}" - certificate_arn = "${lookup(var.extra_ssl_certs[count.index], "certificate_arn")}" - count = "${var.extra_ssl_certs_count}" -} +/** +* # terraform-aws-alb + +* A Terraform module containing common configurations for an AWS Application Load +Balancer (ALB) running over HTTP/HTTPS. Available through the [Terraform registry](https://registry.terraform.io/modules/terraform-aws-modules/alb/aws). + +* | Branch | Build status | +* | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +* | master | [![build Status](https://travis-ci.org/terraform-aws-modules/terraform-aws-alb.svg?branch=master)](https://travis-ci.org/terraform-aws-modules/terraform-aws-alb) | + +* ## Assumptions + +** You want to create a set of resources around an application load balancer: namely associated target groups and listeners. +** You've created a Virtual Private Cloud (VPC) and subnets where you intend to put this ALB. +** You have one or more security groups to attach to the ALB. +** Additionally, if you plan to use an HTTPS listener, the ARN of an SSL certificate is required. + +* The module supports both (mutually exclusive): + +** Internal ALBs +** External ALBs + +* It's recommended you use this module with [terraform-aws-vpc](https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws), +* [terraform-aws-security-group](https://registry.terraform.io/modules/terraform-aws-modules/security-group/aws), and +* [terraform-aws-autoscaling](https://registry.terraform.io/modules/terraform-aws-modules/autoscaling/aws/). + +* Note: + +* It's strongly recommended that the autoscaling module is instantiated in the same +* state as the ALB module as in flight changes to active target groups need to be propagated +* to the ASG immediately or will result in failure. The value of `target_group[n][name]` also must change any time there are modifications to existing `target_groups`. + +* ## Why ALB instead of ELB + +* ALB has the ability to replace what several ELBs can do by routing based on URI matchers. +* Additionally, operating at layer 7 opens the ability to shape traffic using WAF. +* [AWS's documentation](https://aws.amazon.com/elasticloadbalancing/applicationloadbalancer/) has a more +* exhaustive set of reasons. Alternatively, if using ALB with ECS look no further than +* the [HashiCorp example](https://github.com/terraform-providers/terraform-provider-aws/blob/master/examples/ecs-alb). + +* ## Usage example + +* A full example leveraging other community modules is contained in the [examples/alb_test_fixture directory](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/examples/alb_test_fixture). Here's the gist of using it via the Terraform registry: + +* ```hcl +* module "alb" { +* source = "terraform-aws-modules/alb/aws" +* load_balancer_name = "my-alb" +* security_groups = ["sg-edcd9784", "sg-edcd9785"] +* log_bucket_name = "logs-us-east-2-123456789012" +* log_location_prefix = "my-alb-logs" +* subnets = ["subnet-abcde012", "subnet-bcde012a"] +* tags = "${map("Environment", "test")}" +* vpc_id = "vpc-abcde012" +* https_listeners = "${list(map("certificate_arn", "arn:aws:iam::123456789012:server-certificate/test_cert-123456789012", "port", 443))}" +* https_listeners_count = "1" +* http_tcp_listeners = "${list(map("port", "80", "protocol", "HTTP"))}" +* http_tcp_listeners_count = "1" +* target_groups = "${list(map("name", "foo", "backend_protocol", "HTTP", "backend_port", "80"))}" +* target_groups_count = "1" +* } +* ``` + +* ## Testing + +* This module has been packaged with [awspec](https://github.com/k1LoW/awspec) tests through [kitchen](https://kitchen.ci/) and [kitchen-terraform](https://newcontext-oss.github.io/kitchen-terraform/). To run them: + +* 1. Install [rvm](https://rvm.io/rvm/install) and the ruby version specified in the [Gemfile](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/Gemfile). +* 2. Install bundler and the gems from our Gemfile: +* +* ```bash +* gem install bundler && bundle install +* ``` +* +* 3. Ensure your AWS environment is configured (i.e. credentials and region) for test and set TF_VAR_region to a valid AWS region (e.g. `export TF_VAR_region=${AWS_REGION}`). +* 4. Test using `bundle exec kitchen test` from the root of the repo. + +* ## Doc generation + +* Documentation should be modified within `main.tf` and generated using [terraform-docs](https://github.com/segmentio/terraform-docs). +* Generate them like so: + +* ```bash +* terraform-docs md ./ | cat -s | ghead -n -1 > README.md +* ``` + +* ## Contributing + +* Report issues/questions/feature requests on in the [issues](https://github.com/terraform-aws-modules/terraform-aws-alb/issues/new) section. + +* Full contributing [guidelines are covered here](https://github.com/terraform-aws-modules/terraform-aws-alb/blob/master/CONTRIBUTING.md). + +* ## IAM Permissions + +* Testing and using this repo requires a minimum set of IAM permissions. Test permissions +* are listed in the [alb_test_fixture README](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/examples/alb_test_fixture/README.md). + +* ## Change log + +* The [changelog](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/CHANGELOG.md) captures all important release notes. + +* ## Authors + +* Created and maintained by [Brandon O'Connor](https://github.com/brandoconnor) - brandon@atscale.run. +* Many thanks to [the contributors listed here](https://github.com/terraform-aws-modules/terraform-aws-alb/graphs/contributors)! + +* ## License + +* MIT Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-alb/tree/master/LICENSE) for full details. +*/ + diff --git a/outputs.tf b/outputs.tf index f1c7e31..63fabfa 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,54 +1,54 @@ output "dns_name" { description = "The DNS name of the load balancer." - value = "${aws_lb.application.dns_name}" + value = "${element(concat(aws_lb.application.*.dns_name, aws_lb.application_no_logs.*.dns_name), 0)}" } output "http_tcp_listener_arns" { description = "The ARN of the TCP and HTTP load balancer listeners created." - value = "${slice(concat(aws_lb_listener.frontend_http_tcp.*.arn, list("")), 0, var.http_tcp_listeners_count)}" + value = "${slice(concat(aws_lb_listener.frontend_http_tcp.*.arn, aws_lb_listener.frontend_http_tcp_no_logs.*.arn), 0, var.http_tcp_listeners_count)}" } output "http_tcp_listener_ids" { description = "The IDs of the TCP and HTTP load balancer listeners created." - value = "${slice(concat(aws_lb_listener.frontend_http_tcp.*.id, list("")), 0, var.http_tcp_listeners_count)}" + value = "${slice(concat(aws_lb_listener.frontend_http_tcp.*.id, aws_lb_listener.frontend_http_tcp_no_logs.*.id), 0, var.http_tcp_listeners_count)}" } output "https_listener_arns" { description = "The ARNs of the HTTPS load balancer listeners created." - value = "${slice(concat(aws_lb_listener.frontend_https.*.arn, list("")), 0, var.https_listeners_count)}" + value = "${slice(concat(aws_lb_listener.frontend_https.*.arn, aws_lb_listener.frontend_https_no_logs.*.arn), 0, var.https_listeners_count)}" } output "https_listener_ids" { description = "The IDs of the load balancer listeners created." - value = "${slice(concat(aws_lb_listener.frontend_https.*.id, list("")), 0, var.https_listeners_count)}" + value = "${slice(concat(aws_lb_listener.frontend_https.*.id, aws_lb_listener.frontend_https_no_logs.*.id), 0, var.https_listeners_count)}" } output "load_balancer_arn_suffix" { description = "ARN suffix of our load balancer - can be used with CloudWatch." - value = "${aws_lb.application.arn_suffix}" + value = "${element(concat(aws_lb.application.*.arn_suffix, aws_lb.application_no_logs.*.arn_suffix), 0)}" } output "load_balancer_id" { description = "The ID and ARN of the load balancer we created." - value = "${aws_lb.application.id}" + value = "${element(concat(aws_lb.application.*.id, aws_lb.application_no_logs.*.id), 0)}" } output "load_balancer_zone_id" { description = "The zone_id of the load balancer to assist with creating DNS records." - value = "${aws_lb.application.zone_id}" + value = "${element(concat(aws_lb.application.*.zone_id, aws_lb.application_no_logs.*.zone_id), 0)}" } output "target_group_arns" { description = "ARNs of the target groups. Useful for passing to your Auto Scaling group." - value = "${slice(concat(aws_lb_target_group.main.*.arn, list("")), 0, var.target_groups_count)}" + value = "${slice(concat(aws_lb_target_group.main.*.arn, aws_lb_target_group.main_no_logs.*.arn), 0, var.target_groups_count)}" } output "target_group_arn_suffixes" { description = "ARN suffixes of our target groups - can be used with CloudWatch." - value = "${slice(concat(aws_lb_target_group.main.*.arn_suffix, list("")), 0, var.target_groups_count)}" + value = "${slice(concat(aws_lb_target_group.main.*.arn_suffix, aws_lb_target_group.main_no_logs.*.arn_suffix), 0, var.target_groups_count)}" } output "target_group_names" { description = "Name of the target group. Useful for passing to your CodeDeploy Deployment Group." - value = "${slice(concat(aws_lb_target_group.main.*.name, list("")), 0, var.target_groups_count)}" + value = "${slice(concat(aws_lb_target_group.main.*.name, aws_lb_target_group.main_no_logs.*.name), 0, var.target_groups_count)}" } diff --git a/test/integration/default/setup.rb b/test/integration/default/setup.rb new file mode 100644 index 0000000..294e2a1 --- /dev/null +++ b/test/integration/default/setup.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# rubocop:disable LineLength +state_file = 'terraform.tfstate.d/kitchen-terraform-default-aws/terraform.tfstate' +tf_state = JSON.parse(File.open(state_file).read) +@http_tcp_listener_arns = tf_state['modules'][0]['outputs']['http_tcp_listener_arns']['value'] +@https_listener_arns = tf_state['modules'][0]['outputs']['https_listener_arns']['value'] +@target_group_arns = tf_state['modules'][0]['outputs']['target_group_arns']['value'] +@http_tcp_listeners_count = tf_state['modules'][0]['outputs']['http_tcp_listeners_count']['value'] +@https_listeners_count = tf_state['modules'][0]['outputs']['https_listeners_count']['value'] +@target_groups_count = tf_state['modules'][0]['outputs']['target_groups_count']['value'] +# rubocop:enable LineLength +@alb_arn = tf_state['modules'][0]['outputs']['alb_id']['value'] +ALB_NAME = @alb_arn.split('/')[-2] +@region = tf_state['modules'][0]['outputs']['region']['value'] +ENV['AWS_REGION'] = @region +VPC_ID = tf_state['modules'][0]['outputs']['vpc_id']['value'] +SECURITY_GROUP_ID = tf_state['modules'][0]['outputs']['sg_id']['value'] +@count_cases = [[@target_group_arns, @target_groups_count], + [@http_tcp_listener_arns, @http_tcp_listeners_count], + [@https_listener_arns, @https_listeners_count]] diff --git a/test/integration/default/test_alb.rb b/test/integration/default/test_alb.rb index 53a1b5b..ed31261 100755 --- a/test/integration/default/test_alb.rb +++ b/test/integration/default/test_alb.rb @@ -1,36 +1,17 @@ # frozen_string_literal: true require 'awspec' +require_relative 'setup' -# rubocop:disable LineLength -state_file = 'terraform.tfstate.d/kitchen-terraform-default-aws/terraform.tfstate' -tf_state = JSON.parse(File.open(state_file).read) -http_tcp_listener_arns = tf_state['modules'][0]['outputs']['http_tcp_listener_arns']['value'] -https_listener_arns = tf_state['modules'][0]['outputs']['https_listener_arns']['value'] -target_group_arns = tf_state['modules'][0]['outputs']['target_group_arns']['value'] -http_tcp_listeners_count = tf_state['modules'][0]['outputs']['http_tcp_listeners_count']['value'] -https_listeners_count = tf_state['modules'][0]['outputs']['https_listeners_count']['value'] -target_groups_count = tf_state['modules'][0]['outputs']['target_groups_count']['value'] -# rubocop:enable LineLength -alb_arn = tf_state['modules'][0]['outputs']['alb_id']['value'] -alb_name = alb_arn.split('/')[-2] -region = tf_state['modules'][0]['outputs']['region']['value'] -ENV['AWS_REGION'] = region -vpc_id = tf_state['modules'][0]['outputs']['vpc_id']['value'] -security_group_id = tf_state['modules'][0]['outputs']['sg_id']['value'] -count_cases = [[target_group_arns, target_groups_count], - [http_tcp_listener_arns, http_tcp_listeners_count], - [https_listener_arns, https_listeners_count]] - -describe alb(alb_name) do +describe alb(ALB_NAME) do it { should exist } - its(:load_balancer_name) { should eq alb_name } - its(:vpc_id) { should eq vpc_id } + its(:load_balancer_name) { should eq ALB_NAME } + its(:vpc_id) { should eq VPC_ID } it { should belong_to_vpc('test-vpc') } its(:type) { should eq 'application' } its(:scheme) { should eq 'internet-facing' } its(:ip_address_type) { should eq 'ipv4' } - it { should have_security_group(security_group_id) } + it { should have_security_group(SECURITY_GROUP_ID) } end describe alb_target_group('foo') do @@ -41,11 +22,11 @@ its(:port) { should eq 8080 } end -target_group_arns.each do |tg_arn| +@target_group_arns.each do |tg_arn| tg_name = tg_arn.split('/')[-2] describe alb_target_group(tg_name) do it { should exist } - it { should belong_to_alb(alb_name) } + it { should belong_to_alb(ALB_NAME) } it { should belong_to_vpc('test-vpc') } its(:protocol) { should eq 'HTTP' } its(:health_check_protocol) { should eq 'HTTP' } @@ -59,7 +40,7 @@ end end -https_listener_arns.each do |listener| +@https_listener_arns.each do |listener| describe alb_listener(listener) do it { should exist } its(:protocol) { should eq 'HTTPS' } @@ -67,7 +48,7 @@ end end -http_tcp_listener_arns.each do |listener| +@http_tcp_listener_arns.each do |listener| describe alb_listener(listener) do it { should exist } its(:protocol) { should eq 'HTTP' } @@ -75,7 +56,7 @@ end end -count_cases.each do |test_case| +@count_cases.each do |test_case| describe test_case[0] do it 'should be predetermined length' do expect(test_case[0].length).to eq(test_case[1].to_i) diff --git a/variables.tf b/variables.tf index a901c43..f12b964 100644 --- a/variables.tf +++ b/variables.tf @@ -52,7 +52,7 @@ variable "ip_address_type" { } variable "listener_ssl_policy_default" { - description = "The security policy if using HTTPS externally on the load balancer. See: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html" + description = "The security policy if using HTTPS externally on the load balancer. [See](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html)." default = "ELBSecurityPolicy-2016-08" } @@ -80,8 +80,14 @@ variable "load_balancer_update_timeout" { default = "10m" } +variable "logging_enabled" { + description = "Controls if the ALB will log requests to S3." + default = true +} + variable "log_bucket_name" { - description = "S3 bucket (externally created) for storing load balancer access logs." + description = "S3 bucket (externally created) for storing load balancer access logs. Required if logging_enabled is true." + default = "" } variable "log_location_prefix" { diff --git a/version b/version index 15ee400..c219f72 100644 --- a/version +++ b/version @@ -1 +1 @@ -v3.3.1 +v3.4.0