github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/examples/terraform-redeploy-example/main.tf (about) 1 # --------------------------------------------------------------------------------------------------------------------- 2 # PIN TERRAFORM VERSION TO >= 0.12 3 # The examples have been upgraded to 0.12 syntax 4 # --------------------------------------------------------------------------------------------------------------------- 5 6 terraform { 7 # This module is now only being tested with Terraform 0.13.x. However, to make upgrading easier, we are setting 8 # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it 9 # forwards compatible with 0.13.x code. 10 required_version = ">= 0.12.26" 11 } 12 13 # --------------------------------------------------------------------------------------------------------------------- 14 # DEPLOY AN AUTO SCALING GROUP (ASG) WITH AN APPLICATION LOAD BALANCER (ALB) IN FRONT OF IT 15 # --------------------------------------------------------------------------------------------------------------------- 16 17 provider "aws" { 18 region = var.aws_region 19 } 20 21 # --------------------------------------------------------------------------------------------------------------------- 22 # CREATE THE ASG 23 # --------------------------------------------------------------------------------------------------------------------- 24 25 resource "aws_autoscaling_group" "web_servers" { 26 # Note that we intentionally depend on the Launch Configuration name so that creating a new Launch Configuration 27 # (e.g. to deploy a new AMI) creates a new Auto Scaling Group. This will allow for rolling deployments. 28 name = aws_launch_configuration.web_servers.name 29 30 launch_configuration = aws_launch_configuration.web_servers.name 31 32 min_size = 3 33 max_size = 3 34 desired_capacity = 3 35 min_elb_capacity = 3 36 37 # Deploy into all the subnets (and therefore AZs) available 38 vpc_zone_identifier = data.aws_subnet_ids.default.ids 39 40 # Automatically register this ASG's Instances in the ALB and use the ALB's health check to determine when an Instance 41 # needs to be replaced 42 health_check_type = "ELB" 43 44 target_group_arns = [aws_alb_target_group.web_servers.arn] 45 46 tag { 47 key = "Name" 48 value = var.instance_name 49 propagate_at_launch = true 50 } 51 52 # To support rolling deployments, we tell Terraform to create a new ASG before deleting the old one. Note: as 53 # soon as you set create_before_destroy = true in one resource, you must also set it in every resource that it 54 # depends on, or you'll get an error about cyclic dependencies (especially when removing resources). 55 lifecycle { 56 create_before_destroy = true 57 } 58 59 # This needs to be here to ensure the ALB has at least one listener rule before the ASG is created. Otherwise, on the 60 # very first deployment, the ALB won't bother doing any health checks, which means min_elb_capacity will not be 61 # achieved, and the whole deployment will fail. 62 depends_on = [aws_alb_listener.http] 63 } 64 65 # --------------------------------------------------------------------------------------------------------------------- 66 # CREATE THE LAUNCH CONFIGURATION 67 # This is a "template" that defines the configuration for each EC2 Instance in the ASG 68 # --------------------------------------------------------------------------------------------------------------------- 69 70 resource "aws_launch_configuration" "web_servers" { 71 image_id = data.aws_ami.ubuntu.id 72 instance_type = var.instance_type 73 security_groups = [aws_security_group.web_server.id] 74 user_data = data.template_file.user_data.rendered 75 key_name = var.key_pair_name 76 77 # When used with an aws_autoscaling_group resource, the aws_launch_configuration must set create_before_destroy to 78 # true. Note: as soon as you set create_before_destroy = true in one resource, you must also set it in every resource 79 # that it depends on, or you'll get an error about cyclic dependencies (especially when removing resources). 80 lifecycle { 81 create_before_destroy = true 82 } 83 } 84 85 # --------------------------------------------------------------------------------------------------------------------- 86 # CREATE THE USER DATA SCRIPT THAT WILL RUN DURING BOOT ON THE EC2 INSTANCE 87 # --------------------------------------------------------------------------------------------------------------------- 88 89 data "template_file" "user_data" { 90 template = file("${path.module}/user-data/user-data.sh") 91 92 vars = { 93 instance_text = var.instance_text 94 instance_port = var.instance_port 95 } 96 } 97 98 # --------------------------------------------------------------------------------------------------------------------- 99 # FOR THIS EXAMPLE, WE JUST RUN A PLAIN UBUNTU 16.04 AMI 100 # --------------------------------------------------------------------------------------------------------------------- 101 102 data "aws_ami" "ubuntu" { 103 most_recent = true 104 owners = ["099720109477"] # Canonical 105 106 filter { 107 name = "virtualization-type" 108 values = ["hvm"] 109 } 110 111 filter { 112 name = "architecture" 113 values = ["x86_64"] 114 } 115 116 filter { 117 name = "image-type" 118 values = ["machine"] 119 } 120 121 filter { 122 name = "name" 123 values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"] 124 } 125 } 126 127 # --------------------------------------------------------------------------------------------------------------------- 128 # CREATE A SECURITY GROUP TO CONTROL WHAT TRAFFIC CAN GO IN AND OUT OF THE EC2 INSTANCE 129 # --------------------------------------------------------------------------------------------------------------------- 130 131 resource "aws_security_group" "web_server" { 132 name = var.instance_name 133 vpc_id = data.aws_vpc.default.id 134 135 # This is here because aws_launch_configuration.web_servers sets create_before_destroy to true and depends on this 136 # resource 137 lifecycle { 138 create_before_destroy = true 139 } 140 } 141 142 resource "aws_security_group_rule" "web_server_allow_http_inbound" { 143 type = "ingress" 144 from_port = var.instance_port 145 to_port = var.instance_port 146 protocol = "tcp" 147 security_group_id = aws_security_group.web_server.id 148 cidr_blocks = ["0.0.0.0/0"] 149 } 150 151 resource "aws_security_group_rule" "web_server_allow_ssh_inbound" { 152 type = "ingress" 153 from_port = var.ssh_port 154 to_port = var.ssh_port 155 protocol = "tcp" 156 security_group_id = aws_security_group.web_server.id 157 cidr_blocks = ["0.0.0.0/0"] 158 } 159 160 resource "aws_security_group_rule" "web_server_allow_all_outbound" { 161 type = "egress" 162 from_port = 0 163 to_port = 0 164 protocol = "-1" 165 security_group_id = aws_security_group.web_server.id 166 cidr_blocks = ["0.0.0.0/0"] 167 } 168 169 # --------------------------------------------------------------------------------------------------------------------- 170 # CREATE AN ALB TO DISTRIBUTE TRAFFIC ACROSS THE ASG 171 # --------------------------------------------------------------------------------------------------------------------- 172 173 resource "aws_alb" "web_servers" { 174 name = var.instance_name 175 security_groups = [aws_security_group.alb.id] 176 subnets = data.aws_subnet_ids.default.ids 177 178 # This is here because aws_alb_listener.http depends on this resource and sets create_before_destroy to true 179 lifecycle { 180 create_before_destroy = true 181 } 182 } 183 184 # --------------------------------------------------------------------------------------------------------------------- 185 # CREATE AN ALB LISTENER FOR HTTP REQUESTS 186 # --------------------------------------------------------------------------------------------------------------------- 187 188 resource "aws_alb_listener" "http" { 189 load_balancer_arn = aws_alb.web_servers.arn 190 port = var.alb_port 191 protocol = "HTTP" 192 193 default_action { 194 type = "forward" 195 target_group_arn = aws_alb_target_group.web_servers.arn 196 } 197 198 # This is here because aws_autoscaling_group.web_servers depends on this resource and sets create_before_destroy 199 # to true 200 lifecycle { 201 create_before_destroy = true 202 } 203 } 204 205 # --------------------------------------------------------------------------------------------------------------------- 206 # CREATE AN ALB TARGET GROUP FOR THE ASG 207 # This target group will perform health checks on the web servers in the ASG 208 # --------------------------------------------------------------------------------------------------------------------- 209 210 resource "aws_alb_target_group" "web_servers" { 211 depends_on = [aws_alb.web_servers] 212 213 name = var.instance_name 214 port = var.instance_port 215 protocol = "HTTP" 216 vpc_id = data.aws_vpc.default.id 217 218 # Give existing connections 10 seconds to complete before deregistering an instance. The default delay is 300 seconds 219 # (5 minutes), which significantly slows down redeploys. In theory, the ALB should deregister the instance as long as 220 # there are no open connections; in practice, it waits the full five minutes every time. If your requests are 221 # generally processed quickly, set this to something lower (such as 10 seconds) to keep redeploys fast. 222 deregistration_delay = 10 223 224 health_check { 225 path = "/" 226 interval = 15 227 healthy_threshold = 2 228 unhealthy_threshold = 2 229 timeout = 5 230 } 231 232 # This is here because aws_autoscaling_group.web_servers depends on this resource and sets create_before_destroy 233 # to true 234 lifecycle { 235 create_before_destroy = true 236 } 237 } 238 239 # --------------------------------------------------------------------------------------------------------------------- 240 # CREATE AN ALB LISTENER RULE TO SEND ALL REQUESTS TO THE ASG 241 # --------------------------------------------------------------------------------------------------------------------- 242 243 resource "aws_alb_listener_rule" "send_all_to_web_servers" { 244 listener_arn = aws_alb_listener.http.arn 245 priority = 100 246 247 action { 248 type = "forward" 249 target_group_arn = aws_alb_target_group.web_servers.arn 250 } 251 252 condition { 253 path_pattern { 254 values = ["*"] 255 } 256 } 257 } 258 259 # --------------------------------------------------------------------------------------------------------------------- 260 # CREATE A SECURITY GROUP TO CONTROL WHAT TRAFFIC CAN GO IN AND OUT OF THE ALB 261 # --------------------------------------------------------------------------------------------------------------------- 262 263 resource "aws_security_group" "alb" { 264 name = "${var.instance_name}-alb" 265 vpc_id = data.aws_vpc.default.id 266 } 267 268 resource "aws_security_group_rule" "alb_allow_http_inbound" { 269 type = "ingress" 270 from_port = var.alb_port 271 to_port = var.alb_port 272 protocol = "tcp" 273 security_group_id = aws_security_group.alb.id 274 cidr_blocks = ["0.0.0.0/0"] 275 } 276 277 # We need to allow outbound connections from the ALB so it can perform health checks 278 resource "aws_security_group_rule" "allow_all_outbound" { 279 type = "egress" 280 from_port = 0 281 to_port = 0 282 protocol = "-1" 283 security_group_id = aws_security_group.alb.id 284 cidr_blocks = ["0.0.0.0/0"] 285 } 286 287 # --------------------------------------------------------------------------------------------------------------------- 288 # DEPLOY INTO THE DEFAULT VPC AND SUBNETS 289 # To keep this example simple, we are deploying into the Default VPC and its subnets. In real-world usage, you should 290 # deploy into a custom VPC and private subnets. 291 # --------------------------------------------------------------------------------------------------------------------- 292 293 data "aws_vpc" "default" { 294 default = true 295 } 296 297 data "aws_subnet_ids" "default" { 298 vpc_id = data.aws_vpc.default.id 299 } 300