diff --git a/alb-service/alb/main.tf b/alb-service/alb/main.tf new file mode 100644 index 00000000..08949e15 --- /dev/null +++ b/alb-service/alb/main.tf @@ -0,0 +1,179 @@ +/** + * The ELB module creates an ELB, security group + * a route53 record and a service healthcheck. + * It is used by the service module. + */ + +variable "name" { + description = "ELB name, e.g cdn" +} + +variable "subnet_ids" { + description = "Comma separated list of subnet IDs" +} + +variable "environment" { + description = "Environment tag, e.g prod" +} + +variable "port" { + description = "Instance port" +} + +variable "security_groups" { + description = "Comma separated list of security group IDs" +} + +variable "healthcheck" { + description = "Healthcheck path" +} + +variable "log_bucket" { + description = "S3 bucket name to write ELB logs into" +} + +variable "external_dns_name" { + description = "The subdomain under which the ELB is exposed externally, defaults to the task name" +} + +variable "internal_dns_name" { + description = "The subdomain under which the ELB is exposed internally, defaults to the task name" +} + +variable "external_zone_id" { + description = "The zone ID to create the record in" +} + +variable "internal_zone_id" { + description = "The zone ID to create the record in" +} + +variable "ssl_certificate_id" {} + +variable "vpc_id" { + description = "The id of the VPC." +} + +/** + * Resources. + */ + +# Create a new load balancer +resource "aws_alb" "main" { + name = "${var.name}" + internal = false + subnets = ["${split(",",var.subnet_ids)}"] + security_groups = ["${split(",",var.security_groups)}"] + + access_logs { + bucket = "${var.log_bucket}" + } +} + +resource "aws_alb_target_group" "main" { + name = "alb-target-${var.name}" + port = "${var.port}" + protocol = "HTTP" + vpc_id = "${var.vpc_id}" + depends_on = ["aws_alb.main"] + + stickiness { + type = "lb_cookie" + enabled = true + } + + health_check { + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 5 + protocol = "HTTP" + path = "${var.healthcheck}" + interval = 30 + } +} + +resource "aws_alb_listener" "service_https" { + load_balancer_arn = "${aws_alb.main.arn}" + port = "443" + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-2015-05" + certificate_arn = "${var.ssl_certificate_id}" + + default_action { + target_group_arn = "${aws_alb_target_group.main.arn}" + type = "forward" + } +} + +resource "aws_alb_listener" "service_http" { + load_balancer_arn = "${aws_alb.main.arn}" + port = "80" + protocol = "HTTP" + + default_action { + target_group_arn = "${aws_alb_target_group.main.arn}" + type = "forward" + } +} + +resource "aws_route53_record" "external" { + zone_id = "${var.external_zone_id}" + name = "${var.external_dns_name}" + type = "A" + + alias { + zone_id = "${aws_alb.main.zone_id}" + name = "${aws_alb.main.dns_name}" + evaluate_target_health = false + } +} + +resource "aws_route53_record" "internal" { + zone_id = "${var.internal_zone_id}" + name = "${var.internal_dns_name}" + type = "A" + + alias { + zone_id = "${aws_alb.main.zone_id}" + name = "${aws_alb.main.dns_name}" + evaluate_target_health = false + } +} + +/** + * Outputs. + */ + +// The ELB name. +output "name" { + value = "${aws_alb.main.name}" +} + +// The ELB ID. +output "id" { + value = "${aws_alb.main.id}" +} + +// The ELB dns_name. +output "dns" { + value = "${aws_alb.main.dns_name}" +} + +// FQDN built using the zone domain and name (external) +output "external_fqdn" { + value = "${aws_route53_record.external.fqdn}" +} + +// FQDN built using the zone domain and name (internal) +output "internal_fqdn" { + value = "${aws_route53_record.internal.fqdn}" +} + +// The zone id of the ELB +output "zone_id" { + value = "${aws_alb.main.zone_id}" +} + +output "target_group" { + value = "${aws_alb_target_group.main.arn}" +} diff --git a/alb-service/main.tf b/alb-service/main.tf new file mode 100644 index 00000000..22bb755b --- /dev/null +++ b/alb-service/main.tf @@ -0,0 +1,233 @@ +/** + * The web-service is similar to the `service` module, but the + * it provides a __public__ ALB instead. + * + * Usage: + * + * module "auth_service" { + * source = "github.com/segmentio/stack/service" + * name = "auth-service" + * image = "auth-service" + * cluster = "default" + * } + * + */ + +/** + * Required Variables. + */ + +variable "environment" { + description = "Environment tag, e.g prod" +} + +variable "image" { + description = "The docker image name, e.g nginx" +} + +variable "name" { + description = "The service name, if empty the service name is defaulted to the image name" + default = "" +} + +variable "version" { + description = "The docker image version" + default = "latest" +} + +variable "subnet_ids" { + description = "Comma separated list of subnet IDs that will be passed to the ALB module" +} + +variable "security_groups" { + description = "Comma separated list of security group IDs that will be passed to the ALB module" +} + +variable "port" { + description = "The container host port" +} + +variable "cluster" { + description = "The cluster name or ARN" +} + +variable "log_bucket" { + description = "The S3 bucket ID to use for the ALB" +} + +variable "ssl_certificate_id" { + description = "SSL Certificate ID to use" +} + +variable "iam_role" { + description = "IAM Role ARN to use" +} + +variable "external_dns_name" { + description = "The subdomain under which the ALB is exposed externally, defaults to the task name" + default = "" +} + +variable "internal_dns_name" { + description = "The subdomain under which the ALB is exposed internally, defaults to the task name" + default = "" +} + +variable "external_zone_id" { + description = "The zone ID to create the record in" +} + +variable "internal_zone_id" { + description = "The zone ID to create the record in" +} + +/** + * Options. + */ + +variable "healthcheck" { + description = "Path to a healthcheck endpoint" + default = "/" +} + +variable "container_port" { + description = "The container port" + default = 3000 +} + +variable "command" { + description = "The raw json of the task command" + default = "[]" +} + +variable "env_vars" { + description = "The raw json of the task env vars" + default = "[]" +} + +variable "desired_count" { + description = "The desired count" + default = 2 +} + +variable "memory" { + description = "The number of MiB of memory to reserve for the container" + default = 512 +} + +variable "cpu" { + description = "The number of cpu units to reserve for the container" + default = 512 +} + +variable "deployment_minimum_healthy_percent" { + description = "lower limit (% of desired_count) of # of running tasks during a deployment" + default = 100 +} + +variable "deployment_maximum_percent" { + description = "upper limit (% of desired_count) of # of running tasks during a deployment" + default = 200 +} + +variable vpc_id { + description = "The id of the VPC." +} + +/** + * Resources. + */ + +resource "aws_ecs_service" "main" { + name = "${module.task.name}" + cluster = "${var.cluster}" + task_definition = "${module.task.arn}" + desired_count = "${var.desired_count}" + iam_role = "${var.iam_role}" + deployment_minimum_healthy_percent = "${var.deployment_minimum_healthy_percent}" + deployment_maximum_percent = "${var.deployment_maximum_percent}" + depends_on = ["module.alb"] + + load_balancer { + target_group_arn = "${module.alb.target_group}" + container_name = "${module.task.name}" + container_port = "${var.container_port}" + } + + lifecycle { + create_before_destroy = true + } +} + +module "task" { + source = "../task" + + name = "${coalesce(var.name, replace(var.image, "/", "-"))}" + image = "${var.image}" + image_version = "${var.version}" + command = "${var.command}" + env_vars = "${var.env_vars}" + memory = "${var.memory}" + cpu = "${var.cpu}" + + ports = <