Skip to content

Commit 28f5550

Browse files
authored
Make currently active region a terraform parameter (#359)
Allows for easily switching the region currently serving traffic <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Multi-region support: frontend now routes to region-specific endpoints and honors a selectable active region at runtime. * Secondary-region compute and queue components added to enable regional failover and lower latency; CDN origins and routing are region-aware. * **Chores** * Deployment tooling updated to accept and validate a configured active region in prod and QA plans; build scripts now propagate the active-region setting. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 3fb0821 commit 28f5550

File tree

7 files changed

+142
-50
lines changed

7 files changed

+142
-50
lines changed

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
prod_aws_account = 298118738376
22
dev_aws_account = 427040638965
33
current_aws_account := $(shell aws sts get-caller-identity --query Account --output text)
4+
current_active_region = "us-east-2"
45

56
src_directory_root = src/
67
dist_ui_directory_root = dist_ui/
@@ -48,14 +49,14 @@ local:
4849
deploy_prod:
4950
@echo "Deploying Terraform..."
5051
terraform -chdir=terraform/envs/prod init -lockfile=readonly
51-
terraform -chdir=terraform/envs/prod plan -out=tfplan
52+
terraform -chdir=terraform/envs/prod plan -out=tfplan -var="current_active_region=$(current_active_region)"
5253
terraform -chdir=terraform/envs/prod apply -auto-approve tfplan
5354
rm terraform/envs/prod/tfplan
5455

5556
deploy_qa:
5657
@echo "Deploying Terraform..."
5758
terraform -chdir=terraform/envs/qa init -lockfile=readonly
58-
terraform -chdir=terraform/envs/qa plan -out=tfplan
59+
terraform -chdir=terraform/envs/qa plan -out=tfplan -var="current_active_region=$(current_active_region)"
5960
terraform -chdir=terraform/envs/qa apply -auto-approve tfplan
6061
rm terraform/envs/qa/tfplan
6162

terraform/envs/prod/main.tf

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ locals {
3737
main = module.sqs_queues.main_queue_arn
3838
sqs = module.sqs_queues.sales_email_queue_arn
3939
}
40+
queue_arns_usw2 = {
41+
main = module.sqs_queues_usw2.main_queue_arn
42+
sqs = module.sqs_queues_usw2.sales_email_queue_arn
43+
}
4044
DynamoReplicationRegions = toset(["us-west-2"])
45+
deployment_env = "prod"
4146
}
4247

4348
module "sqs_queues" {
@@ -93,7 +98,7 @@ module "lambdas" {
9398
region = "us-east-2"
9499
source = "../../modules/lambdas"
95100
ProjectId = var.ProjectId
96-
RunEnvironment = "prod"
101+
RunEnvironment = local.deployment_env
97102
CurrentOriginVerifyKey = module.origin_verify.current_origin_verify_key
98103
PreviousOriginVerifyKey = module.origin_verify.previous_origin_verify_key
99104
PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time
@@ -102,14 +107,21 @@ module "lambdas" {
102107
}
103108

104109
module "frontend" {
105-
source = "../../modules/frontend"
106-
BucketPrefix = local.primary_bucket_prefix
107-
CoreLambdaHost = module.lambdas.core_function_url
110+
source = "../../modules/frontend"
111+
BucketPrefix = local.primary_bucket_prefix
112+
CoreLambdaHost = {
113+
"us-east-2" = module.lambdas.core_function_url
114+
"us-west-2" = module.lambdas_usw2.core_function_url
115+
}
116+
CoreSlowLambdaHost = {
117+
"us-east-2" = module.lambdas.core_slow_function_url
118+
"us-west-2" = module.lambdas_usw2.core_slow_function_url
119+
}
120+
CurrentActiveRegion = var.current_active_region
108121
OriginVerifyKey = module.origin_verify.current_origin_verify_key
109122
ProjectId = var.ProjectId
110123
CoreCertificateArn = var.CoreCertificateArn
111124
CorePublicDomain = var.CorePublicDomain
112-
CoreSlowLambdaHost = module.lambdas.core_slow_function_url
113125
IcalPublicDomain = var.IcalPublicDomain
114126
LinkryPublicDomain = var.LinkryPublicDomain
115127
LinkryEdgeFunctionArn = module.lambdas.linkry_redirect_function_arn
@@ -133,6 +145,39 @@ resource "aws_lambda_event_source_mapping" "queue_consumer" {
133145
function_response_types = ["ReportBatchItemFailures"]
134146
}
135147

148+
// Multi-Region Failover: us-west-2
149+
150+
module "lambdas_usw2" {
151+
region = "us-west-2"
152+
source = "../../modules/lambdas"
153+
ProjectId = var.ProjectId
154+
RunEnvironment = local.deployment_env
155+
CurrentOriginVerifyKey = module.origin_verify.current_origin_verify_key
156+
PreviousOriginVerifyKey = module.origin_verify.previous_origin_verify_key
157+
PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time
158+
LogRetentionDays = var.LogRetentionDays
159+
EmailDomain = var.EmailDomain
160+
}
161+
162+
module "sqs_queues_usw2" {
163+
region = "us-west-2"
164+
depends_on = [module.lambdas_usw2]
165+
source = "../../modules/sqs"
166+
resource_prefix = var.ProjectId
167+
core_sqs_consumer_lambda_name = module.lambdas_usw2.core_sqs_consumer_lambda_name
168+
}
169+
170+
resource "aws_lambda_event_source_mapping" "queue_consumer_usw2" {
171+
region = "us-west-2"
172+
depends_on = [module.lambdas_usw2, module.sqs_queues_usw2]
173+
for_each = local.queue_arns_usw2
174+
batch_size = 5
175+
event_source_arn = each.value
176+
function_name = module.lambdas_usw2.core_sqs_consumer_lambda_arn
177+
function_response_types = ["ReportBatchItemFailures"]
178+
}
179+
180+
136181
// This section last: moved records into modules
137182
moved {
138183
from = aws_dynamodb_table.app_audit_log

terraform/envs/prod/variables.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,13 @@ variable "IcalPublicDomain" {
4848
type = string
4949
default = "ical.acm.illinois.edu"
5050
}
51+
52+
variable "current_active_region" {
53+
type = string
54+
description = "Currently active AWS region"
55+
56+
validation {
57+
condition = contains(["us-east-2", "us-west-2"], var.current_active_region)
58+
error_message = "Invalid value for current_active_region"
59+
}
60+
}

terraform/envs/qa/main.tf

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ provider "aws" {
3131
data "aws_caller_identity" "current" {}
3232
data "aws_region" "current" {}
3333

34-
locals {
35-
DynamoReplicationRegions = toset(["us-west-2"])
36-
}
3734

3835

3936
module "sqs_queues" {
@@ -53,6 +50,8 @@ locals {
5350
main = module.sqs_queues_usw2.main_queue_arn
5451
sqs = module.sqs_queues_usw2.sales_email_queue_arn
5552
}
53+
DynamoReplicationRegions = toset(["us-west-2"])
54+
deployment_env = "dev"
5655
}
5756

5857
module "dynamo" {
@@ -86,7 +85,7 @@ module "archival" {
8685
depends_on = [module.dynamo]
8786
source = "../../modules/archival"
8887
ProjectId = var.ProjectId
89-
RunEnvironment = "dev"
88+
RunEnvironment = local.deployment_env
9089
LogRetentionDays = var.LogRetentionDays
9190
MonitorTables = ["${var.ProjectId}-audit-log", "${var.ProjectId}-events", "${var.ProjectId}-room-requests"]
9291
BucketPrefix = local.primary_bucket_prefix
@@ -102,7 +101,7 @@ module "lambdas" {
102101
region = "us-east-2"
103102
source = "../../modules/lambdas"
104103
ProjectId = var.ProjectId
105-
RunEnvironment = "dev"
104+
RunEnvironment = local.deployment_env
106105
CurrentOriginVerifyKey = module.origin_verify.current_origin_verify_key
107106
PreviousOriginVerifyKey = module.origin_verify.previous_origin_verify_key
108107
PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time
@@ -111,10 +110,17 @@ module "lambdas" {
111110
}
112111

113112
module "frontend" {
114-
source = "../../modules/frontend"
115-
BucketPrefix = local.primary_bucket_prefix
116-
CoreLambdaHost = module.lambdas.core_function_url
117-
CoreSlowLambdaHost = module.lambdas.core_slow_function_url
113+
source = "../../modules/frontend"
114+
BucketPrefix = local.primary_bucket_prefix
115+
CoreLambdaHost = {
116+
"us-east-2" = module.lambdas.core_function_url
117+
"us-west-2" = module.lambdas_usw2.core_function_url
118+
}
119+
CoreSlowLambdaHost = {
120+
"us-east-2" = module.lambdas.core_slow_function_url
121+
"us-west-2" = module.lambdas_usw2.core_slow_function_url
122+
}
123+
CurrentActiveRegion = var.current_active_region
118124
OriginVerifyKey = module.origin_verify.current_origin_verify_key
119125
ProjectId = var.ProjectId
120126
CoreCertificateArn = var.CoreCertificateArn
@@ -138,7 +144,7 @@ module "lambdas_usw2" {
138144
region = "us-west-2"
139145
source = "../../modules/lambdas"
140146
ProjectId = var.ProjectId
141-
RunEnvironment = "dev"
147+
RunEnvironment = local.deployment_env
142148
CurrentOriginVerifyKey = module.origin_verify.current_origin_verify_key
143149
PreviousOriginVerifyKey = module.origin_verify.previous_origin_verify_key
144150
PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time

terraform/envs/qa/variables.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,13 @@ variable "PrioritySNSAlertArn" {
4949
type = string
5050
default = "arn:aws:sns:us-east-2:427040638965:infra-monitor-alerts"
5151
}
52+
53+
variable "current_active_region" {
54+
type = string
55+
description = "Currently active AWS region"
56+
57+
validation {
58+
condition = contains(["us-east-2", "us-west-2"], var.current_active_region)
59+
error_message = "Invalid value for current_active_region"
60+
}
61+
}

terraform/modules/frontend/main.tf

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,34 @@ resource "aws_cloudfront_distribution" "app_cloudfront_distribution" {
125125
origin_access_control_id = aws_cloudfront_origin_access_control.frontend_oac.id
126126
domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
127127
}
128-
origin {
129-
origin_id = "LambdaFunction"
130-
domain_name = var.CoreLambdaHost
131-
custom_origin_config {
132-
http_port = 80
133-
https_port = 443
134-
origin_protocol_policy = "https-only"
135-
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
128+
129+
# Dynamic origins for each region's Lambda function
130+
dynamic "origin" {
131+
for_each = var.CoreLambdaHost
132+
content {
133+
origin_id = "LambdaFunction-${origin.key}"
134+
domain_name = origin.value
135+
custom_origin_config {
136+
http_port = 80
137+
https_port = 443
138+
origin_protocol_policy = "https-only"
139+
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
140+
}
136141
}
137142
}
138-
origin {
139-
origin_id = "SlowLambdaFunction"
140-
domain_name = var.CoreSlowLambdaHost
141-
custom_origin_config {
142-
http_port = 80
143-
https_port = 443
144-
origin_protocol_policy = "https-only"
145-
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
143+
144+
# Dynamic origins for each region's Slow Lambda function
145+
dynamic "origin" {
146+
for_each = var.CoreSlowLambdaHost
147+
content {
148+
origin_id = "SlowLambdaFunction-${origin.key}"
149+
domain_name = origin.value
150+
custom_origin_config {
151+
http_port = 80
152+
https_port = 443
153+
origin_protocol_policy = "https-only"
154+
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
155+
}
146156
}
147157
}
148158
default_root_object = "index.html"
@@ -173,7 +183,7 @@ resource "aws_cloudfront_distribution" "app_cloudfront_distribution" {
173183
}
174184
ordered_cache_behavior {
175185
path_pattern = "/api/v1/syncIdentity"
176-
target_origin_id = "SlowLambdaFunction"
186+
target_origin_id = "SlowLambdaFunction-${var.CurrentActiveRegion}"
177187
viewer_protocol_policy = "redirect-to-https"
178188
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
179189
cached_methods = ["GET", "HEAD"]
@@ -187,7 +197,7 @@ resource "aws_cloudfront_distribution" "app_cloudfront_distribution" {
187197
}
188198
ordered_cache_behavior {
189199
path_pattern = "/api/v1/events*"
190-
target_origin_id = "LambdaFunction"
200+
target_origin_id = "LambdaFunction-${var.CurrentActiveRegion}"
191201
viewer_protocol_policy = "redirect-to-https"
192202
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
193203
cached_methods = ["GET", "HEAD"]
@@ -201,7 +211,7 @@ resource "aws_cloudfront_distribution" "app_cloudfront_distribution" {
201211
}
202212
ordered_cache_behavior {
203213
path_pattern = "/api/v1/organizations*"
204-
target_origin_id = "LambdaFunction"
214+
target_origin_id = "LambdaFunction-${var.CurrentActiveRegion}"
205215
viewer_protocol_policy = "redirect-to-https"
206216
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
207217
cached_methods = ["GET", "HEAD"]
@@ -215,7 +225,7 @@ resource "aws_cloudfront_distribution" "app_cloudfront_distribution" {
215225
}
216226
ordered_cache_behavior {
217227
path_pattern = "/api/*"
218-
target_origin_id = "LambdaFunction"
228+
target_origin_id = "LambdaFunction-${var.CurrentActiveRegion}"
219229
viewer_protocol_policy = "redirect-to-https"
220230
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
221231
cached_methods = ["GET", "HEAD"]
@@ -232,23 +242,28 @@ resource "aws_cloudfront_distribution" "app_cloudfront_distribution" {
232242

233243
resource "aws_cloudfront_distribution" "ical_cloudfront_distribution" {
234244
http_version = "http2and3"
235-
origin {
236-
origin_id = "LambdaFunction"
237-
domain_name = var.CoreLambdaHost
238-
origin_path = "/api/v1/ical"
239-
custom_origin_config {
240-
http_port = 80
241-
https_port = 443
242-
origin_protocol_policy = "https-only"
243-
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
245+
246+
# Dynamic origins for each region's Lambda function
247+
dynamic "origin" {
248+
for_each = var.CoreLambdaHost
249+
content {
250+
origin_id = "LambdaFunction-${origin.key}"
251+
domain_name = origin.value
252+
origin_path = "/api/v1/ical"
253+
custom_origin_config {
254+
http_port = 80
255+
https_port = 443
256+
origin_protocol_policy = "https-only"
257+
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
258+
}
244259
}
245260
}
246261
aliases = [var.IcalPublicDomain]
247262
enabled = true
248263
is_ipv6_enabled = true
249264
default_cache_behavior {
250265
compress = true
251-
target_origin_id = "LambdaFunction"
266+
target_origin_id = "LambdaFunction-${var.CurrentActiveRegion}"
252267
viewer_protocol_policy = "redirect-to-https"
253268
allowed_methods = ["GET", "HEAD"]
254269
cached_methods = ["GET", "HEAD"]

terraform/modules/frontend/variables.tf

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ variable "ProjectId" {
44
}
55

66
variable "CoreLambdaHost" {
7-
type = string
8-
description = "Host for Lambda Function URL"
7+
type = map(string)
8+
description = "Map of region to Lambda Function URL host"
99
}
1010

1111
variable "CoreSlowLambdaHost" {
12+
type = map(string)
13+
description = "Map of region to Slow Lambda Function URL host"
14+
}
15+
16+
variable "CurrentActiveRegion" {
1217
type = string
13-
description = "Host for Slow Lambda Function URL"
18+
description = "Currently active AWS region for primary routing"
1419
}
1520

1621
variable "CorePublicDomain" {

0 commit comments

Comments
 (0)