From e012092ccc14336fd4d6c6f1ed81711bfd1d3212 Mon Sep 17 00:00:00 2001 From: Florian0410 Date: Mon, 26 Jul 2021 10:05:26 +0200 Subject: [PATCH] Feat/add nlb (#182) * Feat/add nlb compatibilty * Add example for NLB * Fix example, add scheduled actions Add suggestion using new security group Co-authored-by: Yonatan Koren * Auto Format * Add configs to fixture Co-authored-by: Yonatan Koren * syntax clean * Auto Format Co-authored-by: Yonatan Koren Co-authored-by: cloudpossebot <11232728+cloudpossebot@users.noreply.github.com> --- README.md | 2 +- README.yaml | 2 +- docs/terraform.md | 2 +- examples/nlb/context.tf | 202 +++++++++++++++++++++++++ examples/nlb/fixtures.us-east-2.tfvars | 97 ++++++++++++ examples/nlb/main.tf | 118 +++++++++++++++ examples/nlb/outputs.tf | 104 +++++++++++++ examples/nlb/variables.tf | 181 ++++++++++++++++++++++ examples/nlb/versions.tf | 14 ++ main.tf | 41 +++-- outputs.tf | 1 - test/src/Makefile | 2 + test/src/examples_nlb_test.go | 68 +++++++++ variables.tf | 4 +- 14 files changed, 818 insertions(+), 20 deletions(-) create mode 100644 examples/nlb/context.tf create mode 100644 examples/nlb/fixtures.us-east-2.tfvars create mode 100644 examples/nlb/main.tf create mode 100644 examples/nlb/outputs.tf create mode 100644 examples/nlb/variables.tf create mode 100644 examples/nlb/versions.tf create mode 100644 test/src/examples_nlb_test.go diff --git a/README.md b/README.md index 59238730..b02e3e64 100644 --- a/README.md +++ b/README.md @@ -361,7 +361,7 @@ Available targets: | [scheduled\_actions](#input\_scheduled\_actions) | Define a list of scheduled actions |
list(object({
name = string
minsize = string
maxsize = string
desiredcapacity = string
starttime = string
endtime = string
recurrence = string
suspend = bool
}))
| `[]` | no | | [security\_group\_description](#input\_security\_group\_description) | The Security Group description. | `string` | `"Elastic Beanstalk environment Security Group"` | no | | [security\_group\_enabled](#input\_security\_group\_enabled) | Whether to create Security Group. | `bool` | `true` | no | -| [security\_group\_rules](#input\_security\_group\_rules) | A list of maps of Security Group rules.
The values of map is fully complated with `aws_security_group_rule` resource.
To get more info see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule . | `list(any)` |
[
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow all outbound traffic",
"from_port": 0,
"protocol": "-1",
"to_port": 65535,
"type": "egress"
}
]
| no | +| [security\_group\_rules](#input\_security\_group\_rules) | A list of maps of Security Group rules.
The values of map is fully complated with `aws_security_group_rule` resource.
To get more info see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule . | `list(any)` |
[
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow all outbound traffic",
"from_port": 0,
"protocol": "-1",
"to_port": 65535,
"type": "egress"
}
]
| no | | [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Whether to create a default Security Group with unique name beginning with the normalized prefix. | `bool` | `false` | no | | [security\_groups](#input\_security\_groups) | A list of Security Group IDs to associate with EC2 instances. | `list(string)` | `[]` | no | | [solution\_stack\_name](#input\_solution\_stack\_name) | Elastic Beanstalk stack, e.g. Docker, Go, Node, Java, IIS. For more info, see https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html | `string` | n/a | yes | diff --git a/README.yaml b/README.yaml index 0496efd9..add275fe 100644 --- a/README.yaml +++ b/README.yaml @@ -207,4 +207,4 @@ contributors: - name: "Chris Green" github: "DirectRoot" - name: "Vladimir Syromyatnikov" - github: "SweetOps" \ No newline at end of file + github: "SweetOps" diff --git a/docs/terraform.md b/docs/terraform.md index dcb9953b..5197825b 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -128,7 +128,7 @@ | [scheduled\_actions](#input\_scheduled\_actions) | Define a list of scheduled actions |
list(object({
name = string
minsize = string
maxsize = string
desiredcapacity = string
starttime = string
endtime = string
recurrence = string
suspend = bool
}))
| `[]` | no | | [security\_group\_description](#input\_security\_group\_description) | The Security Group description. | `string` | `"Elastic Beanstalk environment Security Group"` | no | | [security\_group\_enabled](#input\_security\_group\_enabled) | Whether to create Security Group. | `bool` | `true` | no | -| [security\_group\_rules](#input\_security\_group\_rules) | A list of maps of Security Group rules.
The values of map is fully complated with `aws_security_group_rule` resource.
To get more info see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule . | `list(any)` |
[
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow all outbound traffic",
"from_port": 0,
"protocol": "-1",
"to_port": 65535,
"type": "egress"
}
]
| no | +| [security\_group\_rules](#input\_security\_group\_rules) | A list of maps of Security Group rules.
The values of map is fully complated with `aws_security_group_rule` resource.
To get more info see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule . | `list(any)` |
[
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow all outbound traffic",
"from_port": 0,
"protocol": "-1",
"to_port": 65535,
"type": "egress"
}
]
| no | | [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Whether to create a default Security Group with unique name beginning with the normalized prefix. | `bool` | `false` | no | | [security\_groups](#input\_security\_groups) | A list of Security Group IDs to associate with EC2 instances. | `list(string)` | `[]` | no | | [solution\_stack\_name](#input\_solution\_stack\_name) | Elastic Beanstalk stack, e.g. Docker, Go, Node, Java, IIS. For more info, see https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html | `string` | n/a | yes | diff --git a/examples/nlb/context.tf b/examples/nlb/context.tf new file mode 100644 index 00000000..81f99b4e --- /dev/null +++ b/examples/nlb/context.tf @@ -0,0 +1,202 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.24.1" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" +} + +variable "environment" { + type = string + default = null + description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = "Solution name, e.g. 'app' or 'jenkins'" +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = "Additional attributes (e.g. `1`)" +} + +variable "tags" { + type = map(string) + default = {} + description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`" +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`." +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The naming order of the id output and Name tag. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 5 elements, but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for default, which is `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + The letter case of label keys (`tag` names) (i.e. `name`, `namespace`, `environment`, `stage`, `attributes`) to use in `tags`. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + The letter case of output label values (also used in `tags` and `id`). + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/examples/nlb/fixtures.us-east-2.tfvars b/examples/nlb/fixtures.us-east-2.tfvars new file mode 100644 index 00000000..4cd3ebf7 --- /dev/null +++ b/examples/nlb/fixtures.us-east-2.tfvars @@ -0,0 +1,97 @@ +region = "us-east-2" + +availability_zones = ["us-east-2a", "us-east-2b"] + +namespace = "eg" + +stage = "test" + +name = "elastic-beanstalk-env" + +description = "Test elastic-beanstalk-environment" + +tier = "WebServer" + +environment_type = "LoadBalanced" + +loadbalancer_type = "network" + +availability_zone_selector = "Any 2" + +instance_type = "t3.micro" + +autoscale_min = 1 + +autoscale_max = 2 + +wait_for_ready_timeout = "20m" + +force_destroy = true + +rolling_update_enabled = true + +rolling_update_type = "Health" + +updating_min_in_service = 0 + +updating_max_batch = 1 + +application_port = 80 + +root_volume_size = 8 + +root_volume_type = "gp2" + +autoscale_measure_name = "CPUUtilization" + +autoscale_statistic = "Average" + +autoscale_unit = "Percent" + +autoscale_lower_bound = 20 + +autoscale_lower_increment = -1 + +autoscale_upper_bound = 80 + +autoscale_upper_increment = 1 + +elb_scheme = "public" + +// https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html +// https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html#platforms-supported.docker +solution_stack_name = "64bit Amazon Linux 2018.03 v2.12.17 running Docker 18.06.1-ce" + +version_label = "" + +dns_zone_id = "Z3SO0TKDDQ0RGG" + +// https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html +additional_settings = [ + { + namespace = "aws:elasticbeanstalk:managedactions" + name = "ManagedActionsEnabled" + value = "false" + } +] + +env_vars = { + "DB_HOST" = "xxxxxxxxxxxxxx" + "DB_USERNAME" = "yyyyyyyyyyyyy" + "DB_PASSWORD" = "zzzzzzzzzzzzzzzzzzz" + "ANOTHER_ENV_VAR" = "123456789" +} + +// https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-autoscaling-scheduledactions.html +scheduled_actions = [ + { + name = "Refreshinstances" + minsize = "1" + maxsize = "2" + desiredcapacity = "2" + starttime = "2015-05-14T07:00:00Z" + endtime = "2016-01-12T07:00:00Z" + recurrence = "*/20 * * * *" + suspend = false + } +] diff --git a/examples/nlb/main.tf b/examples/nlb/main.tf new file mode 100644 index 00000000..bfbee5b3 --- /dev/null +++ b/examples/nlb/main.tf @@ -0,0 +1,118 @@ +provider "aws" { + region = var.region +} + +module "vpc" { + source = "cloudposse/vpc/aws" + version = "0.18.1" + cidr_block = "172.16.0.0/16" + + context = module.this.context +} + +module "subnets" { + source = "cloudposse/dynamic-subnets/aws" + version = "0.33.0" + availability_zones = var.availability_zones + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = true + nat_instance_enabled = false + + context = module.this.context +} + +module "elastic_beanstalk_application" { + source = "cloudposse/elastic-beanstalk-application/aws" + version = "0.8.0" + description = "Test elastic_beanstalk_application" + + context = module.this.context +} + +module "elastic_beanstalk_environment" { + source = "../../" + description = var.description + region = var.region + availability_zone_selector = var.availability_zone_selector + dns_zone_id = var.dns_zone_id + + wait_for_ready_timeout = var.wait_for_ready_timeout + elastic_beanstalk_application_name = module.elastic_beanstalk_application.elastic_beanstalk_application_name + environment_type = var.environment_type + loadbalancer_type = var.loadbalancer_type + elb_scheme = var.elb_scheme + tier = var.tier + version_label = var.version_label + force_destroy = var.force_destroy + + instance_type = var.instance_type + root_volume_size = var.root_volume_size + root_volume_type = var.root_volume_type + + autoscale_min = var.autoscale_min + autoscale_max = var.autoscale_max + autoscale_measure_name = var.autoscale_measure_name + autoscale_statistic = var.autoscale_statistic + autoscale_unit = var.autoscale_unit + autoscale_lower_bound = var.autoscale_lower_bound + autoscale_lower_increment = var.autoscale_lower_increment + autoscale_upper_bound = var.autoscale_upper_bound + autoscale_upper_increment = var.autoscale_upper_increment + + vpc_id = module.vpc.vpc_id + loadbalancer_subnets = module.subnets.public_subnet_ids + application_subnets = module.subnets.private_subnet_ids + security_group_rules = [ + { + type = "egress" + from_port = 0 + to_port = 65535 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + source_security_group_id = null + description = "Allow all outbound traffic" + }, + { + type = "ingress" + from_port = 0 + to_port = 65535 + protocol = "-1" + cidr_blocks = [] + source_security_group_id = module.vpc.vpc_default_security_group_id + description = "Allow all inbound traffic from trusted Security Groups" + }, + ] + + rolling_update_enabled = var.rolling_update_enabled + rolling_update_type = var.rolling_update_type + updating_min_in_service = var.updating_min_in_service + updating_max_batch = var.updating_max_batch + + application_port = var.application_port + + # https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html + # https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html#platforms-supported.docker + solution_stack_name = var.solution_stack_name + + additional_settings = var.additional_settings + env_vars = var.env_vars + + extended_ec2_policy_document = data.aws_iam_policy_document.minimal_s3_permissions.json + prefer_legacy_ssm_policy = false + prefer_legacy_service_policy = false + scheduled_actions = var.scheduled_actions + context = module.this.context +} + +data "aws_iam_policy_document" "minimal_s3_permissions" { + statement { + sid = "AllowS3OperationsOnElasticBeanstalkBuckets" + actions = [ + "s3:ListAllMyBuckets", + "s3:GetBucketLocation" + ] + resources = ["*"] + } +} diff --git a/examples/nlb/outputs.tf b/examples/nlb/outputs.tf new file mode 100644 index 00000000..e1fb1997 --- /dev/null +++ b/examples/nlb/outputs.tf @@ -0,0 +1,104 @@ +output "public_subnet_cidrs" { + value = module.subnets.public_subnet_cidrs + description = "Public subnet CIDRs" +} + +output "private_subnet_cidrs" { + value = module.subnets.private_subnet_cidrs + description = "Private subnet CIDRs" +} + +output "vpc_cidr" { + value = module.vpc.vpc_cidr_block + description = "VPC ID" +} + +output "elastic_beanstalk_application_name" { + value = module.elastic_beanstalk_application.elastic_beanstalk_application_name + description = "Elastic Beanstalk Application name" +} + +output "elastic_beanstalk_environment_hostname" { + value = module.elastic_beanstalk_environment.hostname + description = "DNS hostname" +} + +output "elastic_beanstalk_environment_id" { + description = "ID of the Elastic Beanstalk environment" + value = module.elastic_beanstalk_environment.id +} + +output "elastic_beanstalk_environment_name" { + value = module.elastic_beanstalk_environment.name + description = "Name" +} + +output "elastic_beanstalk_environment_security_group_id" { + value = module.elastic_beanstalk_environment.security_group_id + description = "Security group id" +} + +output "elastic_beanstalk_environment_elb_zone_id" { + value = module.elastic_beanstalk_environment.elb_zone_id + description = "ELB zone id" +} + +output "elastic_beanstalk_environment_ec2_instance_profile_role_name" { + value = module.elastic_beanstalk_environment.ec2_instance_profile_role_name + description = "Instance IAM role name" +} + +output "elastic_beanstalk_environment_tier" { + description = "The environment tier" + value = module.elastic_beanstalk_environment.tier +} + +output "elastic_beanstalk_environment_application" { + description = "The Elastic Beanstalk Application specified for this environment" + value = module.elastic_beanstalk_environment.application +} + +output "elastic_beanstalk_environment_setting" { + description = "Settings specifically set for this environment" + value = module.elastic_beanstalk_environment.setting +} + +output "elastic_beanstalk_environment_all_settings" { + description = "List of all option settings configured in the environment. These are a combination of default settings and their overrides from setting in the configuration" + value = module.elastic_beanstalk_environment.all_settings +} + +output "elastic_beanstalk_environment_endpoint" { + description = "Fully qualified DNS name for the environment" + value = module.elastic_beanstalk_environment.endpoint +} + +output "elastic_beanstalk_environment_autoscaling_groups" { + description = "The autoscaling groups used by this environment" + value = module.elastic_beanstalk_environment.autoscaling_groups +} + +output "elastic_beanstalk_environment_instances" { + description = "Instances used by this environment" + value = module.elastic_beanstalk_environment.instances +} + +output "elastic_beanstalk_environment_launch_configurations" { + description = "Launch configurations in use by this environment" + value = module.elastic_beanstalk_environment.launch_configurations +} + +output "elastic_beanstalk_environment_load_balancers" { + description = "Elastic Load Balancers in use by this environment" + value = module.elastic_beanstalk_environment.load_balancers +} + +output "elastic_beanstalk_environment_queues" { + description = "SQS queues in use by this environment" + value = module.elastic_beanstalk_environment.queues +} + +output "elastic_beanstalk_environment_triggers" { + description = "Autoscaling triggers in use by this environment" + value = module.elastic_beanstalk_environment.triggers +} diff --git a/examples/nlb/variables.tf b/examples/nlb/variables.tf new file mode 100644 index 00000000..e3c447ae --- /dev/null +++ b/examples/nlb/variables.tf @@ -0,0 +1,181 @@ +variable "region" { + type = string + description = "AWS region" +} + +variable "availability_zones" { + type = list(string) + description = "List of availability zones" +} + +variable "description" { + type = string + description = "Short description of the Environment" +} + +variable "environment_type" { + type = string + description = "Environment type, e.g. 'LoadBalanced' or 'SingleInstance'. If setting to 'SingleInstance', `rolling_update_type` must be set to 'Time', `updating_min_in_service` must be set to 0, and `loadbalancer_subnets` will be unused (it applies to the ELB, which does not exist in SingleInstance environments)" +} + +variable "loadbalancer_type" { + type = string + description = "Load Balancer type, e.g. 'application' or 'classic'" +} + +variable "dns_zone_id" { + type = string + description = "Route53 parent zone ID. The module will create sub-domain DNS record in the parent zone for the EB environment" +} + +variable "availability_zone_selector" { + type = string + description = "Availability Zone selector" +} + +variable "instance_type" { + type = string + description = "Instances type" +} + +variable "autoscale_min" { + type = number + description = "Minumum instances to launch" +} + +variable "autoscale_max" { + type = number + description = "Maximum instances to launch" +} + +variable "solution_stack_name" { + type = string + description = "Elastic Beanstalk stack, e.g. Docker, Go, Node, Java, IIS. For more info, see https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html" +} + +variable "wait_for_ready_timeout" { + type = string + description = "The maximum duration to wait for the Elastic Beanstalk Environment to be in a ready state before timing out" +} + +variable "tier" { + type = string + description = "Elastic Beanstalk Environment tier, e.g. 'WebServer' or 'Worker'" +} + +variable "version_label" { + type = string + description = "Elastic Beanstalk Application version to deploy" +} + +variable "force_destroy" { + type = bool + description = "Force destroy the S3 bucket for load balancer logs" +} + +variable "rolling_update_enabled" { + type = bool + description = "Whether to enable rolling update" +} + +variable "rolling_update_type" { + type = string + description = "`Health` or `Immutable`. Set it to `Immutable` to apply the configuration change to a fresh group of instances" +} + +variable "updating_min_in_service" { + type = number + description = "Minimum number of instances in service during update" +} + +variable "updating_max_batch" { + type = number + description = "Maximum number of instances to update at once" +} + +variable "application_port" { + type = number + description = "Port application is listening on" +} + +variable "root_volume_size" { + type = number + description = "The size of the EBS root volume" +} + +variable "root_volume_type" { + type = string + description = "The type of the EBS root volume" +} + +variable "autoscale_measure_name" { + type = string + description = "Metric used for your Auto Scaling trigger" +} + +variable "autoscale_statistic" { + type = string + description = "Statistic the trigger should use, such as Average" +} + +variable "autoscale_unit" { + type = string + description = "Unit for the trigger measurement, such as Bytes" +} + +variable "autoscale_lower_bound" { + type = number + description = "Minimum level of autoscale metric to remove an instance" +} + +variable "autoscale_lower_increment" { + type = number + description = "How many Amazon EC2 instances to remove when performing a scaling activity." +} + +variable "autoscale_upper_bound" { + type = number + description = "Maximum level of autoscale metric to add an instance" +} + +variable "autoscale_upper_increment" { + type = number + description = "How many Amazon EC2 instances to add when performing a scaling activity" +} + +variable "elb_scheme" { + type = string + description = "Specify `internal` if you want to create an internal load balancer in your Amazon VPC so that your Elastic Beanstalk application cannot be accessed from outside your Amazon VPC" +} + +variable "additional_settings" { + type = list(object({ + namespace = string + name = string + value = string + })) + + description = "Additional Elastic Beanstalk setttings. For full list of options, see https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html" + default = [] +} + +variable "env_vars" { + type = map(string) + default = {} + description = "Map of custom ENV variables to be provided to the application running on Elastic Beanstalk, e.g. env_vars = { DB_USER = 'admin' DB_PASS = 'xxxxxx' }" +} + +variable "scheduled_actions" { + type = list(object({ + name = string + minsize = string + maxsize = string + desiredcapacity = string + starttime = string + endtime = string + recurrence = string + suspend = bool + })) + default = [] + description = "Define a list of scheduled actions" +} diff --git a/examples/nlb/versions.tf b/examples/nlb/versions.tf new file mode 100644 index 00000000..971ae244 --- /dev/null +++ b/examples/nlb/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 0.13.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 2.0" + } + null = { + source = "hashicorp/null" + version = ">= 2.0" + } + } +} diff --git a/main.tf b/main.tf index 76ad6802..328e75bd 100755 --- a/main.tf +++ b/main.tf @@ -397,6 +397,7 @@ locals { value = "true" }, ] + alb_settings = [ { namespace = "aws:elbv2:loadbalancer" @@ -442,6 +443,23 @@ locals { namespace = "aws:elbv2:listener:443" name = "SSLPolicy" value = var.loadbalancer_type == "application" ? var.loadbalancer_ssl_policy : "" + }, + ###===================== Application Load Balancer Health check settings =====================================================### + # The Application Load Balancer health check does not take into account the Elastic Beanstalk health check path + # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-applicationloadbalancer.html + # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-applicationloadbalancer.html#alb-default-process.config + { + namespace = "aws:elasticbeanstalk:environment:process:default" + name = "HealthCheckPath" + value = var.healthcheck_url + } + ] + + nlb_settings = [ + { + namespace = "aws:elbv2:listener:default" + name = "ListenerEnabled" + value = var.http_listener_enabled } ] @@ -462,16 +480,6 @@ locals { name = "LoadBalancerType" value = var.loadbalancer_type }, - - ###===================== Application Load Balancer Health check settings =====================================================### - # The Application Load Balancer health check does not take into account the Elastic Beanstalk health check path - # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-applicationloadbalancer.html - # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-applicationloadbalancer.html#alb-default-process.config - { - namespace = "aws:elasticbeanstalk:environment:process:default" - name = "HealthCheckPath" - value = var.healthcheck_url - }, { namespace = "aws:elasticbeanstalk:environment:process:default" name = "Port" @@ -480,12 +488,16 @@ locals { { namespace = "aws:elasticbeanstalk:environment:process:default" name = "Protocol" - value = "HTTP" + value = var.loadbalancer_type == "network" ? "TCP" : "HTTP" } ] + # Select elb configuration depending on loadbalancer_type + elb_settings_nlb = var.loadbalancer_type == "network" ? concat(local.nlb_settings, local.generic_elb_settings) : [] + elb_settings_alb = var.loadbalancer_type == "application" ? concat(local.alb_settings, local.generic_elb_settings) : [] + elb_setting_classic = var.loadbalancer_type == "classic" ? concat(local.classic_elb_settings, local.generic_elb_settings) : [] # If the tier is "WebServer" add the elb_settings, otherwise exclude them - elb_settings_final = var.tier == "WebServer" ? var.loadbalancer_type == "application" ? concat(local.alb_settings, local.generic_elb_settings) : concat(local.classic_elb_settings, local.generic_elb_settings) : [] + elb_settings_final = var.tier == "WebServer" ? concat(local.elb_settings_nlb, local.elb_settings_alb, local.elb_setting_classic) : [] } # @@ -962,7 +974,7 @@ data "aws_elb_service_account" "main" { } data "aws_iam_policy_document" "elb_logs" { - count = var.tier == "WebServer" && var.environment_type == "LoadBalanced" ? 1 : 0 + count = var.tier == "WebServer" && var.environment_type == "LoadBalanced" && var.loadbalancer_type != "network" ? 1 : 0 statement { sid = "" @@ -988,7 +1000,8 @@ resource "aws_s3_bucket" "elb_logs" { #bridgecrew:skip=BC_AWS_S3_13:Skipping `Enable S3 Bucket Logging` check until bridgecrew will support dynamic blocks (https://github.com/bridgecrewio/checkov/issues/776). #bridgecrew:skip=BC_AWS_S3_14:Skipping `Ensure all data stored in the S3 bucket is securely encrypted at rest` check until bridgecrew will support dynamic blocks (https://github.com/bridgecrewio/checkov/issues/776). #bridgecrew:skip=CKV_AWS_52:Skipping `Ensure S3 bucket has MFA delete enabled` due to issue in terraform (https://github.com/hashicorp/terraform-provider-aws/issues/629). - count = var.tier == "WebServer" && var.environment_type == "LoadBalanced" ? 1 : 0 + #bridgecrew:skip=BC_AWS_S3_16:Skipping since adding count condition sounds like to trigger an error. (https://github.com/cloudposse/terraform-aws-elastic-beanstalk-environment/pull/182) + count = var.tier == "WebServer" && var.environment_type == "LoadBalanced" && var.loadbalancer_type != "network" ? 1 : 0 bucket = "${module.this.id}-eb-loadbalancer-logs" acl = "private" force_destroy = var.force_destroy diff --git a/outputs.tf b/outputs.tf index c2870a26..694c4633 100644 --- a/outputs.tf +++ b/outputs.tf @@ -92,4 +92,3 @@ output "triggers" { description = "Autoscaling triggers in use by this environment" value = aws_elastic_beanstalk_environment.default.triggers } - diff --git a/test/src/Makefile b/test/src/Makefile index 2707cd23..0f4ce526 100644 --- a/test/src/Makefile +++ b/test/src/Makefile @@ -17,6 +17,8 @@ init: test: init go mod download go test -v -timeout 60m -run TestExamplesComplete + go test -v -timeout 60m -run TestExamplesNlb + ## Run tests in docker container docker/test: diff --git a/test/src/examples_nlb_test.go b/test/src/examples_nlb_test.go new file mode 100644 index 00000000..a93c725a --- /dev/null +++ b/test/src/examples_nlb_test.go @@ -0,0 +1,68 @@ +package test + +import ( + "fmt" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/stretchr/testify/assert" +) + +// Test the Terraform module in examples/nlb using Terratest. +func TestExamplesNlb(t *testing.T) { + t.Parallel() + + // Give this S3 Bucket a unique ID for a name tag so we can distinguish it from any other Buckets provisioned + // in your AWS account + attributes := []string{strings.ToLower(random.UniqueId())} + + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../../examples/nlb", + Upgrade: true, + // Variables to pass to our Terraform code using -var-file options + VarFiles: []string{"fixtures.us-east-2.tfvars"}, + Vars: map[string]interface{}{ + "attributes": attributes, + }, + } + + // At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.InitAndApply(t, terraformOptions) + + // Run `terraform output` to get the value of an output variable + vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr") + // Verify we're getting back the outputs we expect + assert.Equal(t, "172.16.0.0/16", vpcCidr) + + // Run `terraform output` to get the value of an output variable + privateSubnetCidrs := terraform.OutputList(t, terraformOptions, "private_subnet_cidrs") + // Verify we're getting back the outputs we expect + assert.Equal(t, []string{"172.16.0.0/19", "172.16.32.0/19"}, privateSubnetCidrs) + + // Run `terraform output` to get the value of an output variable + publicSubnetCidrs := terraform.OutputList(t, terraformOptions, "public_subnet_cidrs") + // Verify we're getting back the outputs we expect + assert.Equal(t, []string{"172.16.96.0/19", "172.16.128.0/19"}, publicSubnetCidrs) + + // Run `terraform output` to get the value of an output variable + expectedName := fmt.Sprintf("eg-test-elastic-beanstalk-env-%s", attributes[0]) + elasticBeanstalkApplicationName := terraform.Output(t, terraformOptions, "elastic_beanstalk_application_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, expectedName, elasticBeanstalkApplicationName) + + // Run `terraform output` to get the value of an output variable + elasticBeanstalkEnvironmentName := terraform.Output(t, terraformOptions, "elastic_beanstalk_environment_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, expectedName, elasticBeanstalkEnvironmentName) + + // Run `terraform output` to get the value of an output variable + elasticBeanstalkEnvironmentHostname := terraform.Output(t, terraformOptions, "elastic_beanstalk_environment_hostname") + // Verify we're getting back the outputs we expect + assert.Equal(t, "elastic-beanstalk-env.testing.cloudposse.co", elasticBeanstalkEnvironmentHostname) +} diff --git a/variables.tf b/variables.tf index 9aa47e1d..19446961 100755 --- a/variables.tf +++ b/variables.tf @@ -75,8 +75,8 @@ variable "security_group_rules" { } ] description = <<-EOT - A list of maps of Security Group rules. - The values of map is fully complated with `aws_security_group_rule` resource. + A list of maps of Security Group rules. + The values of map is fully complated with `aws_security_group_rule` resource. To get more info see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule . EOT }