Skip to content

Commit

Permalink
Merge pull request #64 from jonhoo/terraform
Browse files Browse the repository at this point in the history
Terraform all the stuff
  • Loading branch information
jonhoo authored Dec 30, 2023
2 parents 4e9f5dd + 838df92 commit a63fd24
Show file tree
Hide file tree
Showing 14 changed files with 4,231 additions and 0 deletions.
3 changes: 3 additions & 0 deletions infra/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.terraform/
terraform.tfstate.*backup
lambda_function_payload.zip
44 changes: 44 additions & 0 deletions infra/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 96 additions & 0 deletions infra/apigw.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
resource "aws_apigatewayv2_api" "www" {
name = "wewerewondering"
protocol_type = "HTTP"
}

data "aws_iam_policy_document" "apigw_assume" {
statement {
principals {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "apigw_cw" {
name = "wewerewondering-api-gw"
description = "Allows API Gateway to push logs to CloudWatch Logs."
assume_role_policy = data.aws_iam_policy_document.apigw_assume.json
managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"]
}

resource "aws_api_gateway_account" "www" {
cloudwatch_role_arn = aws_iam_role.apigw_cw.arn
}

resource "aws_apigatewayv2_stage" "www" {
api_id = aws_apigatewayv2_api.www.id
name = "$default"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.apigw.arn
format = jsonencode({
"requestId" : "$context.requestId",
"ip" : "$context.identity.sourceIp",
"requestTime" : "$context.requestTime",
"httpMethod" : "$context.httpMethod",
"routeKey" : "$context.routeKey",
"status" : "$context.status",
"protocol" : "$context.protocol",
"responseLength" : "$context.responseLength"
})
}
default_route_settings {
throttling_burst_limit = 250
throttling_rate_limit = 50
}
}

resource "aws_apigatewayv2_integration" "www" {
api_id = aws_apigatewayv2_api.www.id
integration_type = "AWS_PROXY"
integration_method = "POST"
integration_uri = aws_lambda_function.www.invoke_arn
payload_format_version = "2.0"
}

resource "aws_apigatewayv2_route" "api_event_post" {
api_id = aws_apigatewayv2_api.www.id
route_key = "POST /api/event"
target = "integrations/${aws_apigatewayv2_integration.www.id}"
}

resource "aws_apigatewayv2_route" "api_event_eid_post" {
api_id = aws_apigatewayv2_api.www.id
route_key = "POST /api/event/{eid}"
target = "integrations/${aws_apigatewayv2_integration.www.id}"
}

resource "aws_apigatewayv2_route" "api_event_eid_get" {
api_id = aws_apigatewayv2_api.www.id
route_key = "GET /api/event/{eid}"
target = "integrations/${aws_apigatewayv2_integration.www.id}"
}

resource "aws_apigatewayv2_route" "api_route" {
for_each = {
get_eeq = "GET /api/event/{eid}/questions",
get_eeqs = "GET /api/event/{eid}/questions/{secret}",
post_toggle = "POST /api/event/{eid}/questions/{secret}/{qid}/toggle/{property}",
get_q = "GET /api/questions/{qids}",
post_vote = "POST /api/vote/{qid}/{updown}",
}

api_id = aws_apigatewayv2_api.www.id
route_key = each.value
target = "integrations/${aws_apigatewayv2_integration.www.id}"
}

resource "aws_lambda_permission" "www" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.www.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_stage.www.execution_arn}/*"
}
236 changes: 236 additions & 0 deletions infra/athena.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
locals {
athena = "wewerewondering-${data.aws_region.current.name}-athena"
db = "default"
tbl = "cloudfront_logs"
}

resource "aws_s3_bucket" "athena" {
bucket = local.athena

# TODO: lifecycle configuration
# https://docs.aws.amazon.com/athena/latest/ug/querying.html#query-results-specify-location
}

resource "aws_s3_bucket_ownership_controls" "athena" {
bucket = aws_s3_bucket.athena.id

rule {
object_ownership = "BucketOwnerEnforced"
}
}

resource "aws_glue_catalog_database" "default" {
name = local.db
}

# https://docs.aws.amazon.com/athena/latest/ug/cloudfront-logs.html
resource "aws_glue_catalog_table" "cf_logs" {
name = local.tbl
database_name = local.db
table_type = "EXTERNAL_TABLE"
owner = "hadoop"
parameters = {
EXTERNAL = "TRUE"
"skip.header.line.count" = 2
}
depends_on = [aws_glue_catalog_database.default]
storage_descriptor {
input_format = "org.apache.hadoop.mapred.TextInputFormat"
location = "s3://${aws_s3_bucket.logs.id}/"
output_format = "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"

columns {
name = "date"
type = "date"
}
columns {
name = "time"
type = "string"
}
columns {
name = "location"
type = "string"
}
columns {
name = "bytes"
type = "bigint"
}
columns {
name = "request_ip"
type = "string"
}
columns {
name = "method"
type = "string"
}
columns {
name = "host"
type = "string"
}
columns {
name = "uri"
type = "string"
}
columns {
name = "status"
type = "int"
}
columns {
name = "referrer"
type = "string"
}
columns {
name = "user_agent"
type = "string"
}
columns {
name = "query_string"
type = "string"
}
columns {
name = "cookie"
type = "string"
}
columns {
name = "result_type"
type = "string"
}
columns {
name = "request_id"
type = "string"
}
columns {
name = "host_header"
type = "string"
}
columns {
name = "request_protocol"
type = "string"
}
columns {
name = "request_bytes"
type = "bigint"
}
columns {
name = "time_taken"
type = "float"
}
columns {
name = "xforwarded_for"
type = "string"
}
columns {
name = "ssl_protocol"
type = "string"
}
columns {
name = "ssl_cipher"
type = "string"
}
columns {
name = "response_result_type"
type = "string"
}
columns {
name = "http_version"
type = "string"
}
columns {
name = "fle_status"
type = "string"
}
columns {
name = "fle_encrypted_fields"
type = "int"
}
columns {
name = "c_port"
type = "int"
}
columns {
name = "time_to_first_byte"
type = "float"
}
columns {
name = "x_edge_detailed_result_type"
type = "string"
}
columns {
name = "sc_content_type"
type = "string"
}
columns {
name = "sc_content_len"
type = "bigint"
}
columns {
name = "sc_range_start"
type = "bigint"
}
columns {
name = "sc_range_end"
type = "bigint"
}

ser_de_info {
parameters = {
"field.delim" = "\t"
"serialization.format" = "\t"
}
serialization_library = "org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe"
}
}
}

resource "aws_athena_workgroup" "www" {
name = "primary"

configuration {
enforce_workgroup_configuration = false
publish_cloudwatch_metrics_enabled = false

result_configuration {
output_location = "s3://${aws_s3_bucket.athena.bucket}/"
}
}
}

resource "aws_athena_named_query" "common_errs" {
name = "Common errors"
workgroup = aws_athena_workgroup.www.name
database = local.db
query = <<-EOF
SELECT
request_ip,
method,
uri,
status,
COUNT(*) AS n
FROM "${local.db}"."${local.tbl}"
WHERE status >= 400
AND from_iso8601_timestamp(concat(to_iso8601("date"), 'T', time)) > current_timestamp - interval '14' day
GROUP BY status, method, uri, request_ip
HAVING COUNT(*) > 1
ORDER BY n DESC;
EOF
}

resource "aws_athena_named_query" "recent_errs" {
name = "Recent errors"
workgroup = aws_athena_workgroup.www.name
database = local.db
query = <<-EOF
SELECT
from_iso8601_timestamp(concat(to_iso8601("date"), 'T', time)) AT TIME ZONE 'Europe/Oslo' as "when",
request_ip,
method,
uri,
status
FROM "${local.db}"."${local.tbl}"
WHERE status >= 400
AND status <= 599
AND from_iso8601_timestamp(concat(to_iso8601("date"), 'T', time)) > current_timestamp - interval '8' hour
ORDER BY "when" DESC
LIMIT 25;
EOF
}
Loading

0 comments on commit a63fd24

Please sign in to comment.