diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..be130c2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.tfvars +*.tfstate* +.terraform +**/inspec.lock +*.gem +.kitchen +.kitchen.local.yml +Gemfile.lock diff --git a/.kitchen.yml b/.kitchen.yml new file mode 100644 index 00000000..0c0d3cd1 --- /dev/null +++ b/.kitchen.yml @@ -0,0 +1,22 @@ +--- +driver: + name: "terraform" + directory: "examples/test_fixtures" + +provisioner: + name: "terraform" + variable_files: + - "examples/test_fixtures/terraform.tfvars" + +platforms: + - name: "aws" + +verifier: + name: "awspec" + +suites: + - name: "default" + verifier: + name: "awspec" + patterns: + - "test/integration/default/local_alb.rb" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..1f47aa73 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +# See http://pre-commit.com for more information +# See http://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + sha: v0.9.2 + hooks: + - id: trailing-whitespace + # - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..46816ab1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Change Log +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/). + +## [1.0.2] - 2017-10-12 +### Added +* moved data sources to dedicated `data.tf` file. +* `aws_caller_identity` now used to gather account_id rather than using a variable. +* tests added for `target_group` and expanded for `alb`. +* input variables added for health checks, bucket policy, force_destroy_log_bucket - increasing flexibility. +* circle CI config and badge + +### Changed +* altered structure of module to conform to the new [Terraform registry standards](https://www.terraform.io/docs/registry/modules/publish.html#requirements) +* `principle_account_id` (sp) moved to a data source rather than variable map. Spelling corrected. +* removed redundant `/test/alb` directory which had module contents copied. Test kitchen now uses the module itself. +* pinned examples to provider and terraform versions to harden versioning. +* self signed cert added to the test fixtures, eliminating the need for manual upload and terraform.tfvars configuration. +* modules referenced in the test fixture are now sourced from the terraform registry. +* moved bucket_policy.json and template rending to locals + optional variable input. +* stringed list variables moved to native lists +* + +## [1.0.1] - 2017-09-14 +### Added +* tag maps can now be provided (thanks @kwach) + +### Changed +* optional S3 logging (thanks @marocchino) + +## [1.0.0] - 2017-03-16 +### Added +* Tests and fixtures for ALB components using awspec and test kitchen +* S3 log bucket and policy rendering for logging now in place +* root_principle_id added and referenced through a map for s3 bucket policy +* string lists moved to native list types +* default region removed + +### Changed +* Restructured project templates to alb dir to add testing. This is a breaking change so upping major version. +* Redundant examples dir removed +* Updated documentation + +## [0.1.0] - 2017-03-09 +### Added +* Initial release. diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..8c24c10c --- /dev/null +++ b/Gemfile @@ -0,0 +1,9 @@ +ruby '2.4.2' + +source 'https://rubygems.org/' do + gem 'test-kitchen' + gem 'kitchen-terraform' + gem 'awspec' + gem 'kitchen-verifier-awspec' + gem 'rhcl' +end diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..4180f37c --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 Brandon O'Connor - Run at Scale + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e7560b89..5d4ebf5d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,80 @@ # terraform-aws-alb -Terraform module which creates ALB resources on AWS +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). +## Assumptions +* You want to create a set of resources for the ALB: namely an associated target group and listener. +* You've created a Virtual Private Cloud (VPC) + subnets where you intend to put +this ALB. +* You have one or more security groups to attach to the ALB. +* You want to configure a listener for HTTPS/HTTP +* You've uploaded an SSL certificate to AWS IAM if using HTTPS -**WORK IN PROGRESS** +The module supports both (mutually exclusive): +* Internal IP ALBs +* External IP 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/). + +## 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. Those are best outlined in [AWS's +documentation](https://aws.amazon.com/elasticloadbalancing/applicationloadbalancer/). +For an example of using ALB with ECS look no further than the [hashicorp example](https://github.com/terraform-providers/terraform-provider-aws/blob/master/examples/ecs-alb). + +## Resources, inputs, outputs +[Resources](https://registry.terraform.io/modules/terraform-aws-modules/alb/aws?tab=resources), [inputs](https://registry.terraform.io/modules/terraform-aws-modules/alb/aws?tab=inputs), and [outputs](https://registry.terraform.io/modules/terraform-aws-modules/alb/aws?tab=outputs) documented in the terraform registry. + +## Usage example +A full example leveraging other community modules is contained in the [examples/test_fixtures directory](examples/test_fixtures). Here's the gist of using it via the Terraform registry: +``` +module "alb" { + source = "terraform-aws-modules/alb/aws" + vpc_id = "vpc-abcde012" + subnets = ["subnet-abcde012", "subnet-bcde012a"] + alb_security_groups = ["sg-edcd9784", "sg-edcd9785"] + certificate_arn = "arn:aws:iam::123456789012:server-certificate/test_cert-123456789012" + log_bucket = "logs-us-east-2-123456789012" + log_prefix = "my-alb-logs" + + tags { + "Terraform" = "true" + "Env" = "${terraform.workspace}" + } +} +``` +3. Always `terraform plan` to see your change before running `terraform apply`. +4. Win the day! + +## Testing +This module has been packaged with [awspec](https://github.com/k1LoW/awspec) tests through test kitchen. To run them: +1. Install [rvm](https://rvm.io/rvm/install) and the ruby version specified in the [Gemfile](Gemfile). +2. Install bundler and the gems from our Gemfile: +``` +gem install bundler; bundle install +``` +3. Configure variables in `test/fixtures/terraform.tfvars`. An example of how this should look is in [terraform.tfvars.example](test/fixtures/terraform.tfvars.example). +4. Test using `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) section. + +Pull requests are welcome! Ideally create a feature branch and issue for every +individual change made. These are the steps: + +1. Fork the repo to a personal space or org. +2. Create your feature branch from master (`git checkout -b my-new-feature`). +4. Commit your awesome changes (`git commit -am 'Added some feature'`). +5. Push to the branch (`git push origin my-new-feature`). +6. Create a new Pull Request and tell us about your changes. + +## Change log +The [changelog](CHANGELOG.md) captures all important release notes. + +## Authors +Created and maintained by [Brandon O'Connor](https://github.com/brandoconnor) - brandon@atscale.run. + +## License +MIT Licensed. See [LICENSE](LICENSE) for full details. diff --git a/data.tf b/data.tf new file mode 100644 index 00000000..269751bd --- /dev/null +++ b/data.tf @@ -0,0 +1,3 @@ +data "aws_caller_identity" "current" {} + +data "aws_elb_service_account" "main" {} diff --git a/examples/test_fixtures/README.md b/examples/test_fixtures/README.md new file mode 100644 index 00000000..d6f70b07 --- /dev/null +++ b/examples/test_fixtures/README.md @@ -0,0 +1,4 @@ +# test_fixtures example +This set of templates serves two purposes: +0. it shows developers how to use the module in a straightforward way as integrated with other terraform community supported modules. +1. serves as the test infrastructure for CI on the project. diff --git a/examples/test_fixtures/certs/example.crt.pem b/examples/test_fixtures/certs/example.crt.pem new file mode 100644 index 00000000..60964d9c --- /dev/null +++ b/examples/test_fixtures/certs/example.crt.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIETjCCAzagAwIBAgIJALi9NaeI/EcpMA0GCSqGSIb3DQEBBQUAMHcxCzAJBgNV +BAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzANBgNVBAcTBkxvbmRvbjEYMBYGA1UE +ChMPR2xvYmFsIFNlY3VyaXR5MRYwFAYDVQQLEw1JVCBEZXBhcnRtZW50MRQwEgYD +VQQDEwtleGFtcGxlLmNvbTAeFw0xNzA5MjcyMTIwNDlaFw0yNzA5MjUyMTIwNDla +MHcxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzANBgNVBAcTBkxvbmRv +bjEYMBYGA1UEChMPR2xvYmFsIFNlY3VyaXR5MRYwFAYDVQQLEw1JVCBEZXBhcnRt +ZW50MRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBALAthQw1LG3Q7n8jroaBneqLgSyBMXxpilrWG7oYaNcCt3dY4FF0 +RWp2R+suMB7ObaWZwzIPseYD1M1IJoyeafSCmH/UCHsIaXUyTE9Ml69hxTA+3R4e +mO1mPOQ71dheQ0iX34NviiwhQIDJYGRHPYZTeJ2Y/yWQUw3tthYrE9KvYWx6UhXw +0PbBdHgl6bE/cqARua+Y4jOZO4jRDIwzKOxtK86uhWNBdrrLVNMY6kaNdO40wiZk +b+Q2YrMyvVUUFdE2TljyLqYgPsTvb8Yxh6h9WGqnY8Fg1aYelp98NFd9fVw/Wuqx +3Ub0o8Kpnfi+u5Phg5PewF5OoQTGxRLVpJMCAwEAAaOB3DCB2TAdBgNVHQ4EFgQU +8pucEK8IGWVRbp8cndsPrMoo5mYwgakGA1UdIwSBoTCBnoAU8pucEK8IGWVRbp8c +ndsPrMoo5mahe6R5MHcxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzAN +BgNVBAcTBkxvbmRvbjEYMBYGA1UEChMPR2xvYmFsIFNlY3VyaXR5MRYwFAYDVQQL +Ew1JVCBEZXBhcnRtZW50MRQwEgYDVQQDEwtleGFtcGxlLmNvbYIJALi9NaeI/Ecp +MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAK2mwg87BWPuYPkQBESQ +wUiX1L37VGuEjewda1o697OPTD9tWM2IuVjAYKIVx/oTwBwgMzlY8KjfceRYfrTg +YEP9EQ+5KknzgFYC+/SF9ugVke5/JICKQoOqBLboETTTgeYpSlFfKz97nXCAbMVN +/lYB9TCUZ/SdA76ZpIMR0uYH2mCspChWtYjPV9Z8PEwK7EmFuTQS1X/1Oa7O03vC +SU4GiONs7MxJoCrMo/xB6yGDM5NiE6ZqljmQ2238GQ99/VyGMn5uVDpZmXH6dMln +ofEU4fh6sbJvs19KNz9Ql31F0U+hq593T50V8iV+TccBB5ifqfjOnFKmljDjFYeZ +0bg= +-----END CERTIFICATE----- diff --git a/examples/test_fixtures/certs/example.key.pem b/examples/test_fixtures/certs/example.key.pem new file mode 100644 index 00000000..6158a868 --- /dev/null +++ b/examples/test_fixtures/certs/example.key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAsC2FDDUsbdDufyOuhoGd6ouBLIExfGmKWtYbuhho1wK3d1jg +UXRFanZH6y4wHs5tpZnDMg+x5gPUzUgmjJ5p9IKYf9QIewhpdTJMT0yXr2HFMD7d +Hh6Y7WY85DvV2F5DSJffg2+KLCFAgMlgZEc9hlN4nZj/JZBTDe22FisT0q9hbHpS +FfDQ9sF0eCXpsT9yoBG5r5jiM5k7iNEMjDMo7G0rzq6FY0F2ustU0xjqRo107jTC +JmRv5DZiszK9VRQV0TZOWPIupiA+xO9vxjGHqH1YaqdjwWDVph6Wn3w0V319XD9a +6rHdRvSjwqmd+L67k+GDk97AXk6hBMbFEtWkkwIDAQABAoIBACHCNzJlpgPM+0Zl +gfXINIhS/weWIfNjDd3mFR4Nu1kn7hvybdlt3DdQPuuzyAi/KYeH9T1MgJxAs7A6 +WRis6kSuGaa07IMW045fevvfA1nZ9D0QbrJszoT/CD/7rzhsp5vrbirzXpiiLof/ +Dndop0NsDzqzrIB4LVIH8NJCouPF4eibhb4XQwvCkG05pvlkeK4OJugF/CpgrJlX +GiXDTZ8Lh/fBXeFpCfcBlgTCPkxcZtuNeddTlEJpY6q8QdofvfpjXmpfQ+63FL5t +GTpoWNtO+UIqwHFQgoH6zGR8bMBx0/FLqrJ8Cq6nj7+uLODGAf4+dJ0m+Cz5t3Om +aHgJWEECgYEA4nXD/FS+Wy69dVei8imp3/x+v0/T4LXf8l8NI/Dzvdg82xU1AgZH +OKqTC2z/z4e+5EAF27kJeOBdh7kqPAF9KMHtd3FsxiYK4RpDlR6JFzHH8h8+v8c/ +Egg0WiXgnqfePHbS+q5wxPloReJm+ue4eSBzR6qyQbtfH0Qp+NIHGkMCgYEAxyir +CMmI3v1u70V9NtNOlt5O1JNy47iUaLgvEJXBPsD+JYWgs4nY8gZcy+Yx1LBRqMbi +LojOUGKGK6jcLOHjVBW9WJoFtTDN59lba5ryNW9AQOJsdeeaPttpWiFID5K0KTno +kGHzxQjioSnP+mDV7jaXZSNcvYGWVRSabkRwb3ECgYA/mBqlbZbXPFTv8uBLaO/P +erSNPPmfDPQKuC6UfUG1elf8ngP4wZKWkzAf8UgVG2W760652UiTrU4WoyT9HN3s +6Cirdiq5qk070YhRA/YzkUM49xVD/cv0YGFzP1fIthNun1+4DeyVJAToOx/4LcJc +IYS+B21vkBKyUQ4IVdKwyQKBgGBq3+KxLwJFz58nFSelxTZlCeUAPW7hvXe1M5Pj +7FplNKUVvGcvJUiGrAZKu3Usp1v+bSH6OWfRSwN4DJ/t/BCJNdHfP0QpDIZiRFAk +A367DXBcLgYmyhYEQ0zPMAPaEj6jOmQsB8gsNQuxIm8k0m0xILpmFE/qnM0z3E/l +8kkBAoGAJJQgk6dl2rlbZGZvjhVKWNmA8LBJWg28r9/6VWSwYNHSPmcReGvhAflg +5vY9yRIuJXc1Dfq57dIpX8OaH2n/Txgs/895NvpUk/AlqQiMrCKUO3M4wAA0/kNx +RHgT16B9lRxvTaaH8Eh5/YMHp8afrOVEwVIMMCZlWii+PbKFyM4= +-----END RSA PRIVATE KEY----- diff --git a/examples/test_fixtures/data.tf b/examples/test_fixtures/data.tf new file mode 100644 index 00000000..28fa52d7 --- /dev/null +++ b/examples/test_fixtures/data.tf @@ -0,0 +1 @@ +data "aws_caller_identity" "fixtures" {} diff --git a/examples/test_fixtures/main.tf b/examples/test_fixtures/main.tf new file mode 100644 index 00000000..3e7abd10 --- /dev/null +++ b/examples/test_fixtures/main.tf @@ -0,0 +1,59 @@ +terraform { + required_version = "~> 0.10.6" +} + +provider "aws" { + region = "${var.aws_region}" + version = "~> 1.0.0" +} + +provider "template" { + version = "~> 1.0.0" +} + +resource "aws_iam_server_certificate" "fixture_cert" { + name = "test_cert-${data.aws_caller_identity.fixtures.account_id}" + certificate_body = "${file("${path.module}/../../../examples/test_fixtures/certs/example.crt.pem")}" + private_key = "${file("${path.module}/../../../examples/test_fixtures/certs/example.key.pem")}" + + lifecycle { + create_before_destroy = true + } +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + name = "my-vpc" + cidr = "10.0.0.0/16" + azs = ["us-east-2a", "us-east-2b", "us-east-2c"] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + enable_nat_gateway = true + single_nat_gateway = true + tags = {} +} + +module "security-group" { + source = "terraform-aws-modules/security-group/aws" + name = "my-sg-https" + vpc_id = "${module.vpc.vpc_id}" +} + +module "alb" { + source = "../../../" + alb_name = "my-alb" + alb_security_groups = ["${module.security-group.this_security_group_id}"] + aws_region = "${var.aws_region}" + vpc_id = "${module.vpc.vpc_id}" + subnets = "${module.vpc.public_subnets}" + certificate_arn = "${aws_iam_server_certificate.fixture_cert.arn}" + health_check_path = "/" + log_bucket = "logs-${var.aws_region}-${data.aws_caller_identity.fixtures.account_id}" + log_prefix = "${var.log_prefix}" + force_destroy_log_bucket = true + + tags { + "Terraform" = "true" + "Env" = "${terraform.workspace}" + } +} diff --git a/examples/test_fixtures/outputs.tf b/examples/test_fixtures/outputs.tf new file mode 100644 index 00000000..cc8aa0c6 --- /dev/null +++ b/examples/test_fixtures/outputs.tf @@ -0,0 +1,19 @@ +/* +Outputs used for tests +*/ + +output "principal_account_id" { + value = "${module.alb.principal_account_id}" +} + +output "vpc_id" { + value = "${module.vpc.vpc_id}" +} + +output "sg_id" { + value = "${module.security-group.this_security_group_id}" +} + +output "account_id" { + value = "${data.aws_caller_identity.fixtures.account_id}" +} diff --git a/examples/test_fixtures/variables.tf b/examples/test_fixtures/variables.tf new file mode 100644 index 00000000..46c20a06 --- /dev/null +++ b/examples/test_fixtures/variables.tf @@ -0,0 +1,7 @@ +variable "log_prefix" { + default = "my-alb-logs" +} + +variable "aws_region" { + default = "us-east-2" +} diff --git a/main.tf b/main.tf new file mode 100644 index 00000000..6b3b9a25 --- /dev/null +++ b/main.tf @@ -0,0 +1,91 @@ +### ALB resources + +resource "aws_alb" "main" { + name = "${var.alb_name}" + subnets = ["${var.subnets}"] + security_groups = ["${var.alb_security_groups}"] + internal = "${var.alb_is_internal}" + tags = "${merge(var.tags, map("Name", format("%s", var.alb_name)))}" + + access_logs { + bucket = "${var.log_bucket}" + prefix = "${var.log_prefix}" + enabled = "${var.log_bucket != ""}" + } +} + +data "aws_iam_policy_document" "bucket_policy" { + statement { + actions = [ + "s3:PutObject", + ] + + resources = [ + "arn:aws:s3:::${var.log_bucket}/${var.log_prefix}/AWSLogs/${data.aws_caller_identity.current.account_id}/*", + ] + + principals { + type = "AWS" + identifiers = ["${data.aws_elb_service_account.main.id}"] + } + } +} + +resource "aws_s3_bucket" "log_bucket" { + bucket = "${var.log_bucket}" + policy = "${var.bucket_policy == "" ? data.aws_iam_policy_document.bucket_policy.json : var.bucket_policy}" + force_destroy = "${var.force_destroy_log_bucket}" + count = "${var.log_bucket != "" ? 1 : 0}" + tags = "${merge(var.tags, map("Name", format("%s", var.log_bucket)))}" +} + +resource "aws_alb_target_group" "target_group" { + name = "${var.alb_name}-tg" + port = "${var.backend_port}" + protocol = "${upper(var.backend_protocol)}" + vpc_id = "${var.vpc_id}" + + health_check { + interval = "${var.health_check_interval}" + path = "${var.health_check_path}" + port = "${var.health_check_port}" + healthy_threshold = "${var.health_check_healthy_threshold}" + unhealthy_threshold = "${var.health_check_unhealthy_threshold}" + timeout = "${var.health_check_timeout}" + protocol = "${var.backend_protocol}" + } + + stickiness { + type = "lb_cookie" + cookie_duration = "${var.cookie_duration}" + enabled = "${ var.cookie_duration == 1 ? false : true}" + } + + tags = "${merge(var.tags, map("Name", format("%s-tg", var.alb_name)))}" +} + +resource "aws_alb_listener" "front_end_http" { + load_balancer_arn = "${aws_alb.main.arn}" + port = "80" + protocol = "HTTP" + count = "${contains(var.alb_protocols, "HTTP") ? 1 : 0}" + + default_action { + target_group_arn = "${aws_alb_target_group.target_group.id}" + type = "forward" + } +} + +resource "aws_alb_listener" "front_end_https" { + load_balancer_arn = "${aws_alb.main.arn}" + port = "443" + protocol = "HTTPS" + certificate_arn = "${var.certificate_arn}" + ssl_policy = "${var.security_policy}" + count = "${contains(var.alb_protocols, "HTTPS") ? 1 : 0}" + + default_action { + target_group_arn = "${aws_alb_target_group.target_group.id}" + type = "forward" + } +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 00000000..f70da806 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,24 @@ +output "alb_dns_name" { + description = "The DNS name of the ALB presumably to be used with a friendlier CNAME." + value = "${aws_alb.main.dns_name}" +} + +output "alb_id" { + description = "The ID of the ALB we created." + value = "${aws_alb.main.id}" +} + +output "alb_zone_id" { + description = "The zone_id of the ALB to assist with creating DNS records." + value = "${aws_alb.main.zone_id}" +} + +output "principal_account_id" { + description = "The AWS-owned account given permissions to write your ALB logs to S3." + value = "${data.aws_elb_service_account.main.id}" +} + +output "target_group_arn" { + description = "ARN of the target group. Useful for passing to your Auto Scaling group module." + value = "${aws_alb_target_group.target_group.arn}" +} diff --git a/test/integration/default/local_alb.rb b/test/integration/default/local_alb.rb new file mode 100755 index 00000000..fe42d0b7 --- /dev/null +++ b/test/integration/default/local_alb.rb @@ -0,0 +1,58 @@ +require 'awspec' +require 'Rhcl' + +ENV['AWS_REGION'] = 'us-east-2' +module_vars = Rhcl.parse(File.open('examples/test_fixtures/variables.tf')) +log_prefix = module_vars['variable']['log_prefix']['default'] +tf_state = JSON.parse(File.open('.kitchen/kitchen-terraform/default-aws/terraform.tfstate').read) +principal_account_id = tf_state['modules'][0]['outputs']['principal_account_id']['value'] +account_id = tf_state['modules'][0]['outputs']['account_id']['value'] +vpc_id = tf_state['modules'][0]['outputs']['vpc_id']['value'] +security_group_id = tf_state['modules'][0]['outputs']['sg_id']['value'] +account_id = tf_state['modules'][0]['outputs']['account_id']['value'] +# this must match the format in examples/test_fixtures/locals.tf +log_bucket = 'logs-' + module_vars['variable']['aws_region']['default'] + '-' + account_id +# subnet_ids = tf_state['modules'][0]['outputs']['subnet_ids']['value'] + +describe alb('my-alb') do + it { should exist } + its (:load_balancer_name) {should eq 'my-alb'} + its (:vpc_id) {should eq vpc_id} + it { should belong_to_vpc('my-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_subnet(subnet_id) } +end + +describe alb_target_group('my-alb-tg') do + it { should exist } + its(:health_check_path) { should eq '/' } + its(:health_check_port) { should eq 'traffic-port' } + its(:health_check_protocol) { should eq 'HTTP' } + it { should belong_to_alb('my-alb') } + it { should belong_to_vpc('my-vpc') } + end + +describe s3_bucket(log_bucket) do + it { should exist } + it { should have_object("#{log_prefix}/AWSLogs/#{account_id}/ELBAccessLogTestFile") } + it do + should have_policy <<-POLICY +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::#{principal_account_id}:root" + }, + "Action": "s3:PutObject", + "Resource": "arn:aws:s3:::#{log_bucket}/#{log_prefix}/AWSLogs/#{account_id}/*" + } + ] +} + POLICY + end +end diff --git a/variables.tf b/variables.tf new file mode 100644 index 00000000..a390d93d --- /dev/null +++ b/variables.tf @@ -0,0 +1,110 @@ +variable "alb_is_internal" { + description = "Boolean determining if the ALB is internal or externally facing." + default = false +} + +variable "alb_name" { + description = "The name of the ALB as will show in the AWS EC2 ELB console." +} + +variable "alb_protocols" { + description = "The protocols the ALB accepts. e.g.: [\"HTTPS\"]" + type = "list" + default = ["HTTPS"] +} + +variable "alb_security_groups" { + description = "The security groups with which we associate the ALB. e.g. [\"sg-edcd9784\",\"sg-edcd9785\"]" + type = "list" +} + +variable "aws_region" { + description = "AWS region to use." +} + +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)." + default = "HTTP" +} + +variable "bucket_policy" { + description = "A custom S3 bucket policy to apply to the log bucket. If not provided, a minimal policy will be generated from other variables." + default = "" +} + +variable "certificate_arn" { + description = "The ARN of the SSL Certificate. e.g. \"arn:aws:iam::123456789012:server-certificate/ProdServerCert\"" +} + +variable "cookie_duration" { + description = "If load balancer connection stickiness is desired, set this to the duration in seconds that cookie should be valid (e.g. 300). Otherwise, if no stickiness is desired, leave the default." + default = 1 +} + +variable "force_destroy_log_bucket" { + description = "If set to true and if the log bucket already exists, it will be destroyed and recreated." + default = false +} + +variable "health_check_healthy_threshold" { + description = "Number of consecutive positive health checks before a backend instance is considered healthy." + default = 3 +} + +variable "health_check_interval" { + description = "Interval in seconds on which the health check against backend hosts is tried." + default = 10 +} + +variable "health_check_path" { + description = "The URL the ELB should use for health checks. e.g. /health" +} + +variable "health_check_port" { + description = "The port used by the health check if different from the traffic-port." + default = "traffic-port" +} + +variable "health_check_timeout" { + description = "Seconds to leave a health check waiting before terminating it and calling the check unhealthy." + default = 5 +} + +variable "health_check_unhealthy_threshold" { + description = "Number of consecutive positive health checks before a backend instance is considered unhealthy." + default = 3 +} + +variable "log_bucket" { + description = "S3 bucket for storing ALB access logs. Setting this means the module will try to create the bucket." + default = "" +} + +variable "log_prefix" { + description = "S3 prefix within the log_bucket under which logs are stored." + default = "" +} + +variable "security_policy" { + description = "The security policy if using HTTPS externally on the ALB. See: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html" + default = "ELBSecurityPolicy-2016-08" +} + +variable "subnets" { + description = "A list of subnets to associate with the ALB. e.g. ['subnet-1a2b3c4d','subnet-1a2b3c4e','subnet-1a2b3c4f']" + type = "list" +} + +variable "tags" { + description = "A map of tags to add to all resources" + default = {} +} + +variable "vpc_id" { + description = "VPC id where the ALB and other resources will be deployed." +}