resource "aws_lb_target_group" "app" { # We prepare blue/green target groups for deploying the ECS Service via CodeDeploy. for_each = toset(["blue", "green"]) ... }
action { # Specify the Target Group ARN bound to the ECS Service target_group_arn = element(aws_ecs_service.app.load_balancer[*].target_group_arn, 0) type = "forward" }
condition { # NOTE: Control by domain name host_header { values = [ "example.com", ] } }
# Make sure only internal IPs can still access even during maintenance condition { source_ip { values = office_ips } }
# NOTE: Ignore the parts changed by Blue/Green Deployment via CodeDeploy lifecycle { ignore_changes = [ action, ] } }
action { target_group_arn = aws_lb_target_group.app["blue"].arn type = "forward" }
condition { host_header { # NOTE: Control by domain name values = [ "example.com", ] } }
# NOTE: Ignore the parts changed by Blue/Green Deployment via CodeDeploy lifecycle { ignore_changes = [ action, ] } }
When maintenance mode is enabled, only internal IPs can connect to the server, and all other requests get a 503.
When maintenance mode is disabled, the rules above are removed.
The maintenance response is returned as application/json because it assumes communication with a native application. You can also choose text/html and so on.
Why var.maintenance_mode is used to toggle maintenance mode
Managing maintenance_mode outside of the Terraform that manages the ALB resources has the benefit of letting you manage maintenance toggling and resource updates on separate lifecycles.
For example, the intended usage is as follows:
Set maintenance_mode = true on Terraform Cloud and run terraform apply to enter maintenance mode
Apply infrastructure changes with Terraform
Why the ALB default action is set to 404
This strays a bit from the maintenance topic, but the reason is that we want to disallow requests to anything other than the specified domain.
Allowing access via a direct IP address or via the ALB domain name is, in my understanding, undesirable both for security and for SEO.
This also strays from the maintenance topic, but the reason is that we want to unify the default action across the production and staging environments.
A configuration like the following is sometimes seen:
Production environment → default_action { allow {} }
Staging environment → default_action { block {} }
When the default action differs per environment, an added rule can unintentionally block or allow requests, leading to accidents. Unifying it is one measure to prevent that, and is the better choice.
Summary
We were able to implement maintenance methods that leverage the characteristics of each resource.
ALB uses listener rules
CloudFront uses WAF
If you want a richer maintenance page, routing to S3 is also a good option.
That’s all. I hope you find this helpful.
Switching to Maintenance Mode with AWS ALB / CloudFront