Skip to content

Commit 93f639f

Browse files
committed
At this point the api invoke url https://zbubkekd26.execute-api.us-east-1.amazonaws.com/dev works if I use it in, for example,
curl -X GET https://zbubkekd26.execute-api.us-east-1.amazonaws.com/dev/api/v0/users The next step I think is to step up a custom domain that the frontend app could use instead. Then have that point to the load balancer and the load balancer point to the backend?
1 parent 5c3c1af commit 93f639f

File tree

9 files changed

+286
-8
lines changed

9 files changed

+286
-8
lines changed

terraform/environments/development/main.tf

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,14 @@ module "dns" {
170170
backend_record_name = "api.dev.interviewprep.onyxdevtutorials.com"
171171
lb_dns_name = module.load_balancer.lb_dns_name
172172
lb_zone_id = module.load_balancer.lb_zone_id
173-
}
173+
api_invoke_url = replace(module.api_gateway.api_invoke_url, "/dev", "")
174+
}
175+
176+
module "api_gateway" {
177+
source = "../../modules/api_gateway"
178+
environment = var.environment
179+
# backend_dns_name = module.dns.backend_record_name
180+
cloudwatch_role_arn = module.iam.api_gateway_cloudwatch_role_arn
181+
lb_dns_name = module.load_balancer.lb_dns_name
182+
region = var.region
183+
}

terraform/environments/development/outputs.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,14 @@ output "frontend_target_group_arn" {
138138
output "backend_target_group_arn" {
139139
description = "The ARN of the backend target group"
140140
value = module.load_balancer.backend_target_group_arn
141+
}
142+
143+
# output "api_key_id" {
144+
# description = "The ID of the API Gateway API key"
145+
# value = module.api_gateway.api_key_id
146+
# }
147+
148+
output "api_invoke_url" {
149+
description = "The invoke URL of the API Gateway"
150+
value = module.api_gateway.api_invoke_url
141151
}

terraform/modules/api_gateway/main.tf

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# http://development-interview-prep-lb-585997728.us-east-1.elb.amazonaws.com:3000/{proxy}
2+
# curl -X GET http://development-interview-prep-lb-585997728.us-east-1.elb.amazonaws.com:3000/api/v0/products
3+
# https://zbubkekd26.execute-api.us-east-1.amazonaws.com/dev
4+
# curl -X GET https://zbubkekd26.execute-api.us-east-1.amazonaws.com/dev/api/v0/products
5+
6+
7+
resource "aws_api_gateway_rest_api" "api" {
8+
name = "${var.environment}-interview-prep-api"
9+
description = "API Gateway for Interview Prep ${var.environment} environment"
10+
}
11+
12+
resource "aws_api_gateway_resource" "proxy" {
13+
rest_api_id = aws_api_gateway_rest_api.api.id
14+
parent_id = aws_api_gateway_rest_api.api.root_resource_id
15+
path_part = "{proxy+}"
16+
17+
depends_on = [ aws_api_gateway_rest_api.api ]
18+
}
19+
20+
resource "aws_api_gateway_method" "proxy_method" {
21+
rest_api_id = aws_api_gateway_rest_api.api.id
22+
resource_id = aws_api_gateway_resource.proxy.id
23+
http_method = "ANY"
24+
authorization = "NONE"
25+
api_key_required = false
26+
request_parameters = {
27+
"method.request.path.proxy" = true
28+
}
29+
}
30+
31+
resource "aws_api_gateway_method" "proxy_options" {
32+
rest_api_id = aws_api_gateway_rest_api.api.id
33+
resource_id = aws_api_gateway_resource.proxy.id
34+
http_method = "OPTIONS"
35+
authorization = "NONE"
36+
api_key_required = false
37+
}
38+
39+
resource "aws_api_gateway_integration" "proxy_integration" {
40+
rest_api_id = aws_api_gateway_rest_api.api.id
41+
resource_id = aws_api_gateway_resource.proxy.id
42+
http_method = aws_api_gateway_method.proxy_method.http_method
43+
type = "HTTP_PROXY"
44+
integration_http_method = "ANY"
45+
uri = "http://${var.lb_dns_name}:3000/{proxy}"
46+
request_parameters = {
47+
"integration.request.path.proxy" = "method.request.path.proxy"
48+
}
49+
timeout_milliseconds = 29000
50+
}
51+
52+
resource "aws_api_gateway_integration" "proxy_options_integration" {
53+
rest_api_id = aws_api_gateway_rest_api.api.id
54+
resource_id = aws_api_gateway_resource.proxy.id
55+
http_method = aws_api_gateway_method.proxy_options.http_method
56+
type = "MOCK"
57+
request_templates = {
58+
"application/json" = "{\"statusCode\": 200}"
59+
}
60+
}
61+
62+
resource "aws_api_gateway_integration_response" "proxy_options_integration_response" {
63+
rest_api_id = aws_api_gateway_rest_api.api.id
64+
resource_id = aws_api_gateway_resource.proxy.id
65+
http_method = aws_api_gateway_method.proxy_options.http_method
66+
status_code = "200"
67+
response_parameters = {
68+
"method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'"
69+
"method.response.header.Access-Control-Allow-Methods" = "'GET,OPTIONS'"
70+
"method.response.header.Access-Control-Allow-Origin" = "'*'"
71+
}
72+
}
73+
74+
resource "aws_api_gateway_method_response" "proxy_options_response" {
75+
rest_api_id = aws_api_gateway_rest_api.api.id
76+
resource_id = aws_api_gateway_resource.proxy.id
77+
http_method = aws_api_gateway_method.proxy_options.http_method
78+
status_code = "200"
79+
response_parameters = {
80+
"method.response.header.Access-Control-Allow-Headers" = true
81+
"method.response.header.Access-Control-Allow-Methods" = true
82+
"method.response.header.Access-Control-Allow-Origin" = true
83+
}
84+
}
85+
86+
resource "aws_api_gateway_deployment" "api_deployment" {
87+
depends_on = [
88+
aws_api_gateway_integration.proxy_integration,
89+
aws_api_gateway_integration.proxy_options_integration,
90+
aws_api_gateway_integration_response.proxy_options_integration_response,
91+
aws_api_gateway_method_response.proxy_options_response
92+
]
93+
rest_api_id = aws_api_gateway_rest_api.api.id
94+
}
95+
96+
resource "aws_api_gateway_stage" "api_stage" {
97+
deployment_id = aws_api_gateway_deployment.api_deployment.id
98+
rest_api_id = aws_api_gateway_rest_api.api.id
99+
stage_name = "dev"
100+
101+
access_log_settings {
102+
destination_arn = aws_cloudwatch_log_group.api_gateway_log_group.arn
103+
format = "$context.requestId $context.identity.sourceIp $context.identity.userAgent $context.requestTime $context.httpMethod $context.resourcePath $context.status $context.protocol $context.responseLength"
104+
}
105+
}
106+
107+
resource "aws_api_gateway_method_settings" "api_method_settings" {
108+
rest_api_id = aws_api_gateway_rest_api.api.id
109+
stage_name = aws_api_gateway_stage.api_stage.stage_name
110+
method_path = "*/*"
111+
settings {
112+
metrics_enabled = true
113+
logging_level = "INFO"
114+
data_trace_enabled = true
115+
}
116+
}
117+
118+
resource "aws_cloudwatch_log_group" "api_gateway_log_group" {
119+
name = "/aws/api-gateway/interview-prep/${aws_api_gateway_rest_api.api.id}"
120+
retention_in_days = 7
121+
}
122+
123+
resource "aws_iam_role" "api_gateway_cloudwatch_role" {
124+
name = "${var.environment}-interview-prep-api-gateway-cloudwatch-role"
125+
assume_role_policy = jsonencode({
126+
Version = "2012-10-17",
127+
Statement = [
128+
{
129+
Effect = "Allow",
130+
Principal = {
131+
Service = "apigateway.amazonaws.com"
132+
},
133+
Action = "sts:AssumeRole"
134+
}
135+
]
136+
})
137+
}
138+
139+
resource "aws_iam_policy" "api_gateway_cloudwatch_policy" {
140+
name = "${var.environment}-interview-prep-api-gateway-cloudwatch-policy"
141+
description = "Policy for API Gateway to write logs to CloudWatch"
142+
policy = jsonencode({
143+
Version = "2012-10-17",
144+
Statement = [
145+
{
146+
Effect = "Allow",
147+
Action = [
148+
"logs:CreateLogGroup",
149+
"logs:CreateLogStream",
150+
"logs:PutLogEvents"
151+
],
152+
Resource = "*"
153+
}
154+
]
155+
})
156+
}
157+
158+
resource "aws_iam_role_policy_attachment" "api_gateway_cloudwatch_policy_attachment" {
159+
policy_arn = aws_iam_policy.api_gateway_cloudwatch_policy.arn
160+
role = aws_iam_role.api_gateway_cloudwatch_role.name
161+
}
162+
163+
# resource "aws_api_gateway_api_key" "api_key" {
164+
# name = "${var.environment}-interview-prep-api-key"
165+
# description = "API Key for Interview Prep ${var.environment} environment"
166+
# enabled = true
167+
# }
168+
169+
# resource "aws_api_gateway_usage_plan" "usage_plan" {
170+
# name = "${var.environment}-interview-prep-usage-plan"
171+
# description = "Usage Plan for Interview Prep ${var.environment} environment"
172+
# api_stages {
173+
# api_id = aws_api_gateway_rest_api.api.id
174+
# stage = aws_api_gateway_stage.api_stage.stage_name
175+
# }
176+
# }
177+
178+
# resource "aws_api_gateway_usage_plan_key" "usage_plan_key" {
179+
# key_id = aws_api_gateway_api_key.api_key.id
180+
# key_type = "API_KEY"
181+
# usage_plan_id = aws_api_gateway_usage_plan.usage_plan.id
182+
# }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
output "api_invoke_url" {
2+
value = "https://${aws_api_gateway_rest_api.api.id}.execute-api.${var.region}.amazonaws.com/dev"
3+
}
4+
5+
# output "api_key_id" {
6+
# value = aws_api_gateway_api_key.api_key.id
7+
# }
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
variable "environment" {
2+
description = "The environment to deploy the resources to"
3+
type = string
4+
}
5+
6+
variable "region" {
7+
description = "The region to deploy the resources to"
8+
type = string
9+
}
10+
11+
variable "cloudwatch_role_arn" {
12+
description = "The ARN of the CloudWatch role"
13+
type = string
14+
}
15+
16+
variable "lb_dns_name" {
17+
description = "The DNS name of the load balancer"
18+
type = string
19+
}

terraform/modules/dns/main.tf

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,15 @@ resource "aws_route53_record" "frontend" {
66
alias {
77
name = var.lb_dns_name
88
zone_id = var.lb_zone_id
9-
evaluate_target_health = true
9+
evaluate_target_health = false
1010
}
1111
}
1212

1313
resource "aws_route53_record" "backend" {
1414
zone_id = var.zone_id
1515
name = var.backend_record_name
16-
type = "A"
16+
type = "CNAME"
1717

18-
alias {
19-
name = var.lb_dns_name
20-
zone_id = var.lb_zone_id
21-
evaluate_target_health = true
22-
}
18+
records = [replace(var.api_invoke_url, "https://", "")]
19+
ttl = 300
2320
}

terraform/modules/dns/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@ variable "lb_dns_name" {
2121
variable "lb_zone_id" {
2222
description = "The hosted zone ID of the load balancer"
2323
type = string
24+
}
25+
26+
variable "api_invoke_url" {
27+
description = "The invoke URL of the API Gateway"
28+
type = string
29+
default = "placeholder"
2430
}

terraform/modules/iam/main.tf

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,45 @@ resource "aws_iam_role_policy_attachment" "ecs_full_access_attachment" {
258258
role = aws_iam_role.github_actions_role.name
259259
policy_arn = "arn:aws:iam::aws:policy/AmazonECS_FullAccess"
260260
}
261+
262+
resource "aws_iam_role" "api_gateway_cloudwatch_role" {
263+
name = "APIGatewayCloudWatchLogsRole"
264+
265+
assume_role_policy = jsonencode({
266+
Version = "2012-10-17",
267+
Statement = [
268+
{
269+
Effect = "Allow",
270+
Principal = {
271+
Service = "apigateway.amazonaws.com"
272+
},
273+
Action = "sts:AssumeRole"
274+
}
275+
]
276+
})
277+
}
278+
279+
resource "aws_iam_policy" "api_gateway_cloudwatch_policy" {
280+
name = "APIGatewayCloudWatchLogsPolicy"
281+
description = "Policy for API Gateway CloudWatch Logs"
282+
283+
policy = jsonencode({
284+
Version = "2012-10-17",
285+
Statement = [
286+
{
287+
Effect = "Allow",
288+
Action = [
289+
"logs:CreateLogGroup",
290+
"logs:CreateLogStream",
291+
"logs:PutLogEvents"
292+
],
293+
Resource = "*"
294+
}
295+
]
296+
})
297+
}
298+
299+
resource "aws_iam_role_policy_attachment" "api_gateway_cloudwatch_policy_attachment" {
300+
role = aws_iam_role.api_gateway_cloudwatch_role.name
301+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
302+
}

terraform/modules/iam/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,9 @@ output "lambda_exec_role_arn" {
1616
output "vpc_flow_log_role_arn" {
1717
description = "The ARN of the VPC flow log role"
1818
value = aws_iam_role.vpc_flow_log_role.arn
19+
}
20+
21+
output "api_gateway_cloudwatch_role_arn" {
22+
description = "The ARN of the API Gateway CloudWatch role"
23+
value = aws_iam_role.api_gateway_cloudwatch_role.arn
1924
}

0 commit comments

Comments
 (0)