Skip to content

Commit 5231766

Browse files
committed
Merge branch 'main' of https://github.com/bcgov/nr-bcws-wfprev into feature/WFPREV-403
2 parents d4dfaa7 + 36249fd commit 5231766

File tree

9 files changed

+199
-5
lines changed

9 files changed

+199
-5
lines changed

.github/workflows/terragrunt-deploy.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ jobs:
134134

135135
- uses: peter-murray/terragrunt-github-action@v1.0.0
136136
with:
137-
terragrunt_version: ${{ env.TG_VERSION }}
137+
terragrunt_version: ${{ env.TG_VERSION }}
138138

139139
- name: Terragrunt Apply
140140
working-directory: ${{env.TG_SRC_PATH}}
@@ -179,6 +179,9 @@ jobs:
179179
# GDB Extractor Lambda Image
180180
WFPREV_GDB_EXTRACTOR_DIGEST: ${{ steps.getDigestGDB.outputs.GDB_EXTRACTOR_IMAGE }}
181181

182+
# AWS
183+
AWS_ALERT_EMAIL_LIST: ${{ vars.AWS_ALERT_EMAIL_LIST }}
184+
182185
#liquibase
183186
COMMAND: ${{ steps.liquibaseCommand.outputs.LIQUIBASE_COMMAND }}
184187
PROXY_COUNT: ${{ steps.changeLogCount.outputs.PROXY_COUNT }}

terraform/alarms.tf

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
resource "aws_cloudwatch_metric_alarm" "high_latency" {
2+
alarm_name = "ALB-High-Latency"
3+
comparison_operator = "GreaterThanThreshold"
4+
evaluation_periods = 1
5+
metric_name = "TargetResponseTime"
6+
namespace = "AWS/ApplicationELB"
7+
period = 60 # 1 min
8+
statistic = "Average"
9+
threshold = 1 # 1 second
10+
11+
dimensions = {
12+
LoadBalancer = aws_lb.wfprev_main.arn_suffix
13+
}
14+
15+
alarm_description = "ALB target response time is high"
16+
alarm_actions = [aws_sns_topic.alb_alerts.arn]
17+
}
18+
19+
resource "aws_cloudwatch_metric_alarm" "unhealthy_targets" {
20+
alarm_name = "ALB-Unhealthy-Targets"
21+
comparison_operator = "GreaterThanThreshold"
22+
evaluation_periods = 1
23+
metric_name = "UnHealthyHostCount"
24+
namespace = "AWS/ApplicationELB"
25+
period = 60
26+
statistic = "Average"
27+
threshold = 0
28+
29+
dimensions = {
30+
TargetGroup = aws_alb_target_group.wfprev_api.arn_suffix
31+
LoadBalancer = aws_lb.wfprev_main.arn_suffix
32+
}
33+
34+
alarm_description = "ALB has unhealthy targets"
35+
alarm_actions = [aws_sns_topic.alb_alerts.arn]
36+
}
37+
38+
resource "aws_sns_topic" "alb_alerts" {
39+
name = "wfprev-alb-alerts"
40+
}
41+
42+
# List of emails
43+
locals {
44+
alert_emails = split(",", var.AWS_ALERT_EMAIL_LIST)
45+
}
46+
47+
# Create subscriptions for each email
48+
resource "aws_sns_topic_subscription" "alb_alerts_emails" {
49+
for_each = toset(local.alert_emails)
50+
51+
topic_arn = aws_sns_topic.alb_alerts.arn
52+
protocol = "email"
53+
endpoint = trim(each.key, " ")
54+
}

terraform/alb.tf

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ resource "aws_lb" "wfprev_main" {
3434
Environment = "${var.TARGET_ENV}"
3535
}
3636

37+
access_logs {
38+
bucket = aws_s3_bucket.alb_logs.bucket
39+
prefix = "wfprev-${var.TARGET_ENV}"
40+
enabled = true
41+
}
42+
3743
}
3844

3945
//////////////////////////
@@ -45,10 +51,27 @@ resource "aws_lb_listener" "wfprev_main" {
4551
protocol = "HTTP"
4652

4753
default_action {
48-
type = "fixed-response"
54+
type = "redirect"
55+
redirect {
56+
port = "443"
57+
protocol = "HTTPS"
58+
status_code = "HTTP_301" # Any HTTP request gets a 301 redirect to HTTPS
59+
}
60+
}
61+
}
62+
63+
resource "aws_lb_listener" "wfprev_main_https" {
64+
load_balancer_arn = aws_lb.wfprev_main.arn
65+
port = "443"
66+
protocol = "HTTPS"
67+
ssl_policy = "ELBSecurityPolicy-2016-08"
68+
certificate_arn = aws_acm_certificate_validation.domain_certificate_validation_ca.certificate_arn
69+
70+
default_action {
71+
type = "fixed-response"
4972
fixed_response {
5073
content_type = "text/plain"
51-
status_code = 404
74+
status_code = 404
5275
}
5376
}
5477
}
@@ -81,14 +104,22 @@ resource "aws_alb_target_group" "wfprev_api" {
81104
target_type = "ip"
82105
deregistration_delay = 30
83106

107+
stickiness {
108+
enabled = true
109+
type = "lb_cookie"
110+
cookie_duration = 3600 # 1 hour session persistence
111+
}
112+
84113
health_check {
85114
healthy_threshold = "2"
86-
interval = "300"
115+
interval = "30"
87116
protocol = "HTTP"
88117
matcher = "200"
89-
timeout = "3"
118+
timeout = "5"
90119
path = "/${aws_apigatewayv2_stage.wfprev_stage.name}/actuator/health"
91120
unhealthy_threshold = "2"
92121
}
93122
}
94123

124+
125+

terraform/certificate_manager.tf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,20 @@ resource "aws_acm_certificate_validation" "domain_certificate_validation" {
1616

1717
provider = aws.aws-us
1818
}
19+
20+
//NOTE: CA certificate is needed for ALB listener
21+
resource "aws_acm_certificate" "wfprev_domain_certificate_ca" {
22+
domain_name = data.aws_route53_zone.wfprev_route53_zone.name
23+
validation_method = "DNS"
24+
25+
lifecycle {
26+
create_before_destroy = true
27+
}
28+
29+
provider = aws # The default provider (ca-central-1)
30+
}
31+
32+
resource "aws_acm_certificate_validation" "domain_certificate_validation_ca" {
33+
certificate_arn = aws_acm_certificate.wfprev_domain_certificate_ca.arn
34+
validation_record_fqdns = [for record in aws_route53_record.cert_validation_record_ca : record.fqdn]
35+
}

terraform/cloudfront.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ resource "aws_cloudfront_distribution" "wfprev_app_distribution" {
107107
restriction_type = "none"
108108
}
109109
}
110+
111+
logging_config {
112+
bucket = data.aws_s3_bucket.cloudfront_logs.bucket_domain_name
113+
include_cookies = false
114+
prefix = "cloudfront-logs/"
115+
}
110116
}
111117

112118
resource "aws_cloudfront_function" "rewrite_uri" {
@@ -123,6 +129,10 @@ resource "aws_cloudfront_function" "rewrite_uri" {
123129
EOF
124130
}
125131

132+
data "aws_s3_bucket" "cloudfront_logs" {
133+
bucket = "wfprev-${var.TARGET_ENV}-cloudfront-logs"
134+
}
135+
126136
output "cloudfront_distribution_id" {
127137
value = aws_cloudfront_distribution.wfprev_app_distribution.id
128138
}

terraform/route53_dns.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,24 @@ resource "aws_route53_record" "cert_validation_record" {
1818
ttl = 300
1919
}
2020

21+
// ca-central-1 cert validation for ALB listener
22+
resource "aws_route53_record" "cert_validation_record_ca" {
23+
for_each = {
24+
for dvo in aws_acm_certificate.wfprev_domain_certificate_ca.domain_validation_options : dvo.domain_name => {
25+
name = dvo.resource_record_name
26+
record = dvo.resource_record_value
27+
type = dvo.resource_record_type
28+
}
29+
}
30+
31+
name = each.value.name
32+
type = each.value.type
33+
records = [each.value.record]
34+
ttl = 300
35+
zone_id = data.aws_route53_zone.wfprev_route53_zone.zone_id
36+
allow_overwrite = true
37+
}
38+
2139
resource "aws_route53_record" "wfprev_vanity_url" {
2240
zone_id = data.aws_route53_zone.wfprev_route53_zone.id
2341
name = "wfprev-${var.TARGET_ENV}.${var.gov_domain}"

terraform/s3.tf

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,35 @@ resource "aws_s3_bucket" "wfprev_site_bucket" {
1313
}
1414
}
1515

16+
resource "aws_s3_bucket" "alb_logs" {
17+
bucket = "wfprev-${var.TARGET_ENV}-alb-logs-bucket"
18+
19+
force_destroy = true
20+
21+
tags = {
22+
Environment = var.TARGET_ENV
23+
}
24+
}
25+
26+
resource "aws_s3_bucket_lifecycle_configuration" "alb_logs_lifecycle" {
27+
bucket = aws_s3_bucket.alb_logs.id
28+
29+
rule {
30+
id = "expire-alb-logs"
31+
status = "Enabled"
32+
33+
filter {} # Empty filter means apply to all objects
34+
35+
expiration {
36+
days = 90
37+
}
38+
39+
noncurrent_version_expiration {
40+
noncurrent_days = 30
41+
}
42+
}
43+
}
44+
1645
# Uploading assets. This shouldn't be needed because we'll push them up from the
1746
# github action, vs having terraform fetch them
1847
#resource "aws_s3_object" "upload-assets" {
@@ -58,6 +87,31 @@ resource "aws_s3_bucket_policy" "wfprev_site_bucket_policy" {
5887
})
5988
}
6089

90+
resource "aws_s3_bucket_policy" "alb_logs_policy" {
91+
bucket = aws_s3_bucket.alb_logs.id
92+
93+
policy = jsonencode({
94+
Version = "2012-10-17",
95+
Statement = [
96+
{
97+
Sid = "AWSALBLoggingPermissions"
98+
Effect = "Allow"
99+
Principal = {
100+
Service = [
101+
"logdelivery.elasticloadbalancing.amazonaws.com",
102+
"elasticloadbalancing.amazonaws.com"
103+
],
104+
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
105+
}
106+
Action = "s3:PutObject"
107+
Resource = "${aws_s3_bucket.alb_logs.arn}/*"
108+
}
109+
]
110+
})
111+
}
112+
113+
data "aws_caller_identity" "current" {}
114+
61115
output "s3_bucket_name" {
62116
value = aws_s3_bucket.wfprev_site_bucket.bucket
63117
}

terraform/terragrunt.hcl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ WFPREV_GDB_FUNCTION_NAME = "${get_env("WFPREV_GDB_FUNCTION_NAME")}"
4545
OPENMAPS_URL = "${get_env("OPENMAPS_URL")}"
4646
server_count = "${get_env("server_count")}"
4747
48+
# AWS
4849
TARGET_AWS_ACCOUNT_ID = "${get_env("TARGET_AWS_ACCOUNT_ID")}"
50+
AWS_ALERT_EMAIL_LIST = "${get_env("AWS_ALERT_EMAIL_LIST")}"
4951
5052
# client
5153
WEBADE_OAUTH2_WFPREV_UI_CLIENT_SECRET = "${get_env("WEBADE_OAUTH2_WFPREV_UI_CLIENT_SECRET")}"

terraform/variables.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ variable "AWS_REGION" {
8282
default = "ca-central-1"
8383
}
8484

85+
variable "AWS_ALERT_EMAIL_LIST" {
86+
type = string
87+
description = "Comma-separated list of email addresses for AWS alerts"
88+
}
89+
8590
variable "WEBADE_OAUTH2_REST_CLIENT_ID" {
8691
type = string
8792
default = ""

0 commit comments

Comments
 (0)