diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ddb621 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# terraform-aws-stateful + +This terraform module creates an EC2 Cluster on AWS. + +The following resources will be created: + +- Elastic File System (EFS) +- Auto Scaling +- Security groups for (ALB/NLB,EC2,EFS) +- IAM roles and policies for the EC2 instances + +In addition, you have the option to create: + - Elastic Load Balancer + - ALB - An external Application Load Balancer + - NLB - An external Network Load Balancer + + - Route 53 (requires ALB) + - URL pointing to a hostname (NLB or ALB hostname) + +## Usage + +For deployment usage please see the `examples` folder. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| certificate\_arn | Certificate ARN to be used on the ALB | `any` | n/a | no | +| custom\_efs\_dir | Custom EFS mount point - e.g /home | `string` | `""` | no | +| enable\_alb | Wheter to enable application load balancer | `bool` | `false` | no | +| hosted\_zone | Route 53 hosted zone | `string` | `""` | no | +| hostname\_create | Wheter to create the hostnames on Route 53 | `bool` | `false` | no | +| hostnames | Hostnames to be created on Route 53 | `any` | n/a | no | +| instance\_count | Number of EC2 intances | `number` | `1` | no | +| instance\_type | EC2 instance type | `string` | `"t2.micro"` | no | +| instance\_volume\_size\_root | Volume root size | `number` | `16` | no | +| instances\_subnet | List of private subnet IDs for EC2 instances | `list` | n/a | yes | +| name | Name of this EC2/default cluster | `any` | n/a | yes | +| cluster_name | Name of the environment (dev/prod) | `any` | n/a | yes | +| public\_subnet\_ids | List of public subnet IDs for the ALB | `list` | `[]` | no | +| secure\_subnet\_ids | List of secure subnet IDs for EFS | `list` | n/a | yes | +| security\_group\_ids | Extra security groups for instances | `list` | `[]` | no | +| userdata | Extra commands to pass to userdata | `string` | `""` | no | +| vpc\_id | VPC ID to deploy the EC2/default cluster | `any` | n/a | yes | +| lb\_type| Either ALB, NLB, or EIP to enable | `string` | `""` | no | +| lb\_port| Port to be used in the security groups and in LB the health check | `number` | `0` | no | +| lb\_protocol| LB protocol - TCP or UDP | `number` | `""` | no | +| sg\_cidr\_blocks| LB protocol - TCP or UDP | `list` | `[]` | no | diff --git a/_data.tf b/_data.tf new file mode 100644 index 0000000..116a6dc --- /dev/null +++ b/_data.tf @@ -0,0 +1,28 @@ +data "aws_region" "current" {} + +data "aws_ami" "amazon-linux-2" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["amzn2-ami-hvm*"] + } + + filter { + name = "architecture" + values = ["x86_64"] + } +} + +data "aws_autoscaling_groups" "groups" { + filter { + name = "key" + values = ["Name"] + } + + filter { + name = "value" + values = ["${var.name}-*"] + } +} \ No newline at end of file diff --git a/_variables.tf b/_variables.tf new file mode 100644 index 0000000..b5544f9 --- /dev/null +++ b/_variables.tf @@ -0,0 +1,113 @@ +variable "name" { + description = "Name of this EC2 cluster" +} + +variable "cluster_name" { + description = "Environment name" +} + +variable "instance_type" { + default = "t2.micro" + description = "EC2 instance type" +} + +variable "on_demand_base_capacity" { + description = "on_demand_base_capacity" +} + +variable "on_demand_percentage" { + description = "on_demand_percentage" +} + +variable "instance_type" { + default = "t2.micro" + description = "EC2 instance type" +} + +variable "instance_count" { + default = 1 + description = "Number of EC2 intances" +} + +variable "lb_type" { + default = "" + description = "Either alb nlb or EIP to enable" +} + +variable "lb_port" { + type = number + default = 0 + description = "LB port" +} + +variable "lb_protocol" { + default = "" + description = "LB protocol" +} + +variable "sg_cidr_blocks" { + type = list + default = [] + description = "Which cidr blocks allowed to connect to the service" +} + +variable "certificate_arn" { + default = "" + description = "Certificate ARN to be used on the ALB" +} + +variable "userdata" { + default = "" + description = "Extra commands to pass to userdata" +} + +variable "instance_volume_size_root" { + default = 16 + description = "Volume root size" +} + +variable "custom_efs_dir" { + default = "" + description = "Custom EFS mount point - e.g /home" +} + +variable "instances_subnet" { + type = list + description = "List of private subnet IDs for EC2 instances" +} + +variable "public_subnet_ids" { + type = list + default = [] + description = "List of public subnet IDs for the ALB" +} + +variable "secure_subnet_ids" { + type = list + description = "List of secure subnet IDs for EFS" +} + +variable "vpc_id" { + description = "VPC ID to deploy the EC2/default cluster" +} + +variable "security_group_ids" { + type = list + default = [] + description = "Extra security groups for instances" +} + +variable "hostname_create" { + default = false + description = "Wheter to create the hostnames on Route 53" +} + +variable "hosted_zone" { + default = "" + description = "Route 53 hosted zone" +} + +variable "hostnames" { + default = [] + description = "Hostnames to be created on Route 53" +} \ No newline at end of file diff --git a/alb-sg.tf b/alb-sg.tf new file mode 100644 index 0000000..06e53ed --- /dev/null +++ b/alb-sg.tf @@ -0,0 +1,61 @@ +resource "aws_security_group" "alb" { + count = var.lb_type == "ALB" ? 1 : 0 + + name = "${var.name}-lb" + description = "SG for ALB" + vpc_id = var.vpc_id + + tags = { + Name = "${var.name}-lb" + } +} + +resource "aws_security_group_rule" "http_from_world_to_alb" { + count = var.lb_type == "ALB" ? 1 : 0 + + description = "HTTP Redirect ${var.name} ALB" + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "tcp" + security_group_id = aws_security_group.alb[0].id + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_security_group_rule" "https_from_world_to_alb" { + count = var.lb_type == "ALB" ? 1 : 0 + + description = "HTTPS ${var.name} ALB" + type = "ingress" + from_port = 443 + to_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.alb[0].id + cidr_blocks = ["0.0.0.0/0"] +} + +resource "aws_security_group_rule" "alb_to_nodes" { + count = var.lb_type == "ALB" ? 1 : 0 + + description = "Traffic to ${var.name} Nodes" + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + security_group_id = aws_security_group.alb[0].id + source_security_group_id = aws_security_group.default.id +} + +resource "aws_security_group_rule" "from_alb_to_nodes" { + count = var.lb_type == "ALB" ? 1 : 0 + + description = "from ALB" + type = "ingress" + from_port = 0 + to_port = 0 + protocol = "-1" + security_group_id = aws_security_group.default.id + source_security_group_id = aws_security_group.alb[0].id + + depends_on = [aws_security_group.default] +} \ No newline at end of file diff --git a/alb.tf b/alb.tf new file mode 100644 index 0000000..d94d186 --- /dev/null +++ b/alb.tf @@ -0,0 +1,63 @@ +resource "aws_lb" "alb" { + count = var.lb_type == "ALB" ? 1 : 0 + name = "alb-${var.name}" + internal = false + load_balancer_type = "application" + subnets = var.public_subnet_ids + + security_groups = [ + aws_security_group.alb[0].id, + ] + + idle_timeout = 400 + + tags = { + Name = "${var.name}" + } +} + +resource "aws_lb_target_group" "alb_tg" { + count = var.lb_type == "ALB" ? 1 : 0 + name = "tg-${var.name}" + port = 80 + protocol = "HTTP" + vpc_id = var.vpc_id +} + +resource "aws_lb_listener" "alb_http_listener" { + count = var.lb_type == "ALB" ? 1 : 0 + load_balancer_arn = aws_lb.alb[0].arn + port = "80" + protocol = "HTTP" + + default_action { + type = "redirect" + + redirect { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } +} + +resource "aws_lb_listener" "alb_https_listener" { + count = var.lb_type == "ALB" ? 1 : 0 + load_balancer_arn = aws_lb.alb[0].arn + port = "443" + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-2016-08" + certificate_arn = var.certificate_arn + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.alb_tg[0].arn + } +} + +resource "aws_autoscaling_attachment" "alb_asg_attachment" { + count = "${var.lb_type == "ALB" ? 1 : 0}" * var.instance_count + autoscaling_group_name = aws_autoscaling_group.asg[count.index].name + alb_target_group_arn = aws_lb_target_group.alb_tg[0].arn + depends_on = [aws_lb_target_group.alb_tg] +} \ No newline at end of file diff --git a/asg.tf b/asg.tf new file mode 100644 index 0000000..77b5371 --- /dev/null +++ b/asg.tf @@ -0,0 +1,30 @@ +resource "aws_autoscaling_group" "asg" { + count = var.instance_count + name = "${var.name}-${var.cluster_name}-${count.index}" + + min_size = 1 + max_size = 1 + + vpc_zone_identifier = var.instances_subnet + + launch_template { + name = aws_launch_template.default.name + version = "$Latest" + } + + tags = [ + map("key", "Name", "value", "${var.name}", "propagate_at_launch", true) + ] + + lifecycle { + create_before_destroy = true + } + + instances_distribution { + spot_instance_pools = 3 + on_demand_base_capacity = var.on_demand_base_capacity + on_demand_percentage_above_base_capacity = var.on_demand_percentage + } + + depends_on = [aws_efs_file_system.default] +} \ No newline at end of file diff --git a/ec2-iam.tf b/ec2-iam.tf new file mode 100644 index 0000000..6e5887d --- /dev/null +++ b/ec2-iam.tf @@ -0,0 +1,29 @@ +resource "aws_iam_instance_profile" "default" { + name = "${var.name}-${data.aws_region.current.name}" + role = aws_iam_role.default.name +} + +resource "aws_iam_role" "default" { + name = "${var.name}-${data.aws_region.current.name}" + + assume_role_policy = <> /etc/fstab +mount -a -t efs defaults + +echo "### SETUP AGENT" + +echo "### SETUP APACHE" +touch /var/www/html/index.html +systemctl restart httpd + +${eip} + +${userdata_extra} \ No newline at end of file