I am using Terraform to build my architecture, specifically an API Gateway with CloudWatch Logs integration. However, I am encountering an issue where I cannot see the API Gateway CloudWatch logs. I have integrated the apigateway-sns and can see the Lambda logs subscribed to the SNS topic, but I am unable to view the CloudWatch logs for the API Gateway methods.
From the console, I noticed that the "CloudWatch Logs" option is unchecked. I can manually check it from the console, but I want to configure it through Terraform. I have checked my Terraform code, but I can’t figure out where I am making a mistake.
resource "aws_api_gateway_rest_api" "main" {
name = "gitlab-slack-api-${var.environment}"
description = "Gitlab Slack notifications integration"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_cloudwatch_log_group" "main_api_gw" {
name = "/aws/api-gw/${aws_api_gateway_rest_api.main.name}"
retention_in_days = 14
}
resource "aws_api_gateway_resource" "resource" {
path_part = "slack"
parent_id = aws_api_gateway_rest_api.main.root_resource_id
rest_api_id = aws_api_gateway_rest_api.main.id
}
resource "aws_api_gateway_method" "method" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.resource.id
http_method = "POST"
authorization = "NONE"
request_parameters = {
"method.request.querystring.TopicArn" = false
"method.request.querystring.Message" = false
}
}
resource "aws_api_gateway_integration" "integration" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
type = "AWS"
integration_http_method = "POST"
uri = "arn:aws:apigateway:${data.aws_region.region.name}:sns:action/Publish"
credentials = aws_iam_role.api_gateway_role.arn
request_parameters = {
"integration.request.querystring.TopicArn" = "'${aws_sns_topic.sns_topic_name.arn}'"
"integration.request.querystring.Message" = "method.request.body"
}
request_templates = {
"application/json" = <<EOF
{
"Message": "$input.body",
"TopicArn": "$input.params('TopicArn')"
}
EOF
}
}
resource "aws_api_gateway_method_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
status_code = "200"
}
resource "aws_api_gateway_integration_response" "integration_response" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
status_code = aws_api_gateway_method_response.response_200.status_code
}
resource "aws_api_gateway_deployment" "deployment" {
depends_on = [aws_api_gateway_integration.integration]
rest_api_id = aws_api_gateway_rest_api.main.id
stage_name = var.environment
}
resource "aws_api_gateway_stage" "prod" {
depends_on = [aws_api_gateway_deployment.deployment]
stage_name = var.environment
rest_api_id = aws_api_gateway_rest_api.main.id
deployment_id = aws_api_gateway_deployment.deployment.id
access_log_settings {
destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
format = jsonencode({
requestId = "$context.requestId"
requestTime = "$context.requestTime"
requestTimeEpoch = "$context.requestTimeEpoch"
path = "$context.path"
method = "$context.httpMethod"
status = "$context.status"
responseLength = "$context.responseLength"
})
}
}
resource "aws_api_gateway_method_settings" "all" {
rest_api_id = aws_api_gateway_rest_api.test_api_gateway.id
stage_name = aws_api_gateway_stage.prod.stage_name
method_path = "*/*"
settings {
logging_level = "INFO"
}
}
resource "aws_iam_policy" "api_gateway_sns_policy" {
name = "ApiGatewaySNSAccessPolicy"
description = "IAM policy for API Gateway to invoke SNS"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["sns:Publish"]
Resource = aws_sns_topic.sns_topic_name.arn
}
]
})
}
resource "aws_iam_role" "api_gateway_role" {
name = "ApiGatewaySNSAccessRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = { Service = "apigateway.amazonaws.com" }
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "api_gateway_role_policy_attachment" {
role = aws_iam_role.api_gateway_role.name
policy_arn = aws_iam_policy.api_gateway_sns_policy.arn
}
output "api_gateway_endpoint_url" {
value = aws_api_gateway_deployment.deployment.invoke_url
}
output "api_gateway_endpoint_arn" {
value = aws_api_gateway_rest_api.main.arn
}
2
Answers
Found solution: ApiGate needs a role to allow CloudWatch to publish logs, some how Marko's policy did not work(don't why). I have used the AWS managed role AmazonAPIGatewayPushToCloudWatchLogs and the resource aws_api_gateway_method_settings for enabling the logs(INFO, ERROR,OFF). Now I can see the API Gateway logs in CloudWatch.
Thank you Marko, put some effort on the issue.
Here is Terraform:
Ref:
Took Ref from [here]/https://github.com/darren-reddick/terraform-aws-transfer/blob/master/apigateway.tf) as well
You can enable logging for API Gateway in the Stage settings, for which a terraform resource exists. Based on the example from the documentation and your code, the following should enable you to configure logging:
In order to avoid circular dependency, you would need to define the stage name as a variable, which in your case seems to be
var.environment
. This is a part of the log groupname
argument andstage_name
argument of the API Gateway stage resource. Additionally, the name of the log group has to be like this, as this is the default API Gateway naming convention:The
format
argument is required and can take in one of"JSON"/"XML"/"CLF"/"CSV"
, which you can read more about in the documentation.EDIT: Since the original post didn’t show all the code, and the stage resource was present, referencing the log group properly, what would need to change is the log group name (as described above) and the IAM role permissions. Besides having SNS allowed in the permissions policy, CloudWatch logs also need to be added: