From 7e6dfa2782ec84df178a2383350f8ea8baeecdf8 Mon Sep 17 00:00:00 2001 From: Jordan Sim-Smith Date: Thu, 14 Nov 2024 15:28:03 +1300 Subject: [PATCH] Deploy subfootball tracker --- .../infra/.terraform.lock.hcl | 63 +++++ subfootball_tracker_api/infra/main.tf | 215 ++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 subfootball_tracker_api/infra/.terraform.lock.hcl create mode 100644 subfootball_tracker_api/infra/main.tf diff --git a/subfootball_tracker_api/infra/.terraform.lock.hcl b/subfootball_tracker_api/infra/.terraform.lock.hcl new file mode 100644 index 0000000..fa94363 --- /dev/null +++ b/subfootball_tracker_api/infra/.terraform.lock.hcl @@ -0,0 +1,63 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.75.1" + constraints = "~> 5.61" + hashes = [ + "h1:uz55I4t3Pqy3p+82NZ35mkUA9mZ5yu4pS6beZMI8wpA=", + "zh:1075825e7311a8d2d233fd453a173910e891b0320e8a7698af44d1f90b02621d", + "zh:203c5d09a03fcaa946defb8459f01227f2fcda07df768f74777beb328d6751ae", + "zh:21bc79ccb09bfdeb711a3a5226c6c4a457ac7c4bb781dbda6ade7be38461739f", + "zh:2bac969855b62a0ff6716954be29387a1f9793626059122cda4681206396e309", + "zh:4b65ea5b51058f05b9ec8797f76184e19e5b38a609029fe2226af3fa4ad289b3", + "zh:5065d7df357fb3ee2b0a2520bbcff6335c0c47bfb9e8e9932bad088c3ab7efd3", + "zh:678a4015a4cd26af5c2b30dfd9290b8a01e900668fa0fec6585dfd1838f1cebd", + "zh:6ddc5dfdd4a0dddca027db99a7bfa9a0978933119d63af81acb6020728405119", + "zh:98c0d48b09842c444dbcbddd279e5b5b1e44113951817a8ecc28896bb4ad1dd7", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:aad169fea072842c0b54f1ff95f1ec6558d6c5af3ea4c159308583db59003b09", + "zh:bd2625ed8e1ff29ac6ed3a810d7b68a090add5fcb2fce4122669bd37e1eb9f1d", + "zh:c6f57625e26a6ef1ffb49bfa0e6148496ad12d80c857f6bb222e21f293a2a78a", + "zh:c7cd085326c5eb88804b11a4bc0fbc8376f06138f4b9624fb25cd06ea8687cdd", + "zh:f60c98139f983817d4d08f4138b1e53f31f91176ff638631e8dd38b6de36fce0", + ] +} + +provider "registry.terraform.io/hashicorp/external" { + version = "2.3.4" + hashes = [ + "h1:cCabxnWQ5fX1lS7ZqgUzsvWmKZw9FA7NRxAZ94vcTcc=", + "zh:037fd82cd86227359bc010672cd174235e2d337601d4686f526d0f53c87447cb", + "zh:0ea1db63d6173d01f2fa8eb8989f0809a55135a0d8d424b08ba5dabad73095fa", + "zh:17a4d0a306566f2e45778fbac48744b6fd9c958aaa359e79f144c6358cb93af0", + "zh:298e5408ab17fd2e90d2cd6d406c6d02344fe610de5b7dae943a58b958e76691", + "zh:38ecfd29ee0785fd93164812dcbe0664ebbe5417473f3b2658087ca5a0286ecb", + "zh:59f6a6f31acf66f4ea3667a555a70eba5d406c6e6d93c2c641b81d63261eeace", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:ad0279dfd09d713db0c18469f585e58d04748ca72d9ada83883492e0dd13bd58", + "zh:c69f66fd21f5e2c8ecf7ca68d9091c40f19ad913aef21e3ce23836e91b8cbb5f", + "zh:d4a56f8c48aa86fc8e0c233d56850f5783f322d6336f3bf1916e293246b6b5d4", + "zh:f2b394ebd4af33f343835517e80fc876f79361f4688220833bc3c77655dd2202", + "zh:f31982f29f12834e5d21e010856eddd19d59cd8f449adf470655bfd19354377e", + ] +} + +provider "registry.terraform.io/hashicorp/local" { + version = "2.5.2" + hashes = [ + "h1:IyFbOIO6mhikFNL/2h1iZJ6kyN3U00jgkpCLUCThAfE=", + "zh:136299545178ce281c56f36965bf91c35407c11897f7082b3b983d86cb79b511", + "zh:3b4486858aa9cb8163378722b642c57c529b6c64bfbfc9461d940a84cd66ebea", + "zh:4855ee628ead847741aa4f4fc9bed50cfdbf197f2912775dd9fe7bc43fa077c0", + "zh:4b8cd2583d1edcac4011caafe8afb7a95e8110a607a1d5fb87d921178074a69b", + "zh:52084ddaff8c8cd3f9e7bcb7ce4dc1eab00602912c96da43c29b4762dc376038", + "zh:71562d330d3f92d79b2952ffdda0dad167e952e46200c767dd30c6af8d7c0ed3", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:805f81ade06ff68fa8b908d31892eaed5c180ae031c77ad35f82cb7a74b97cf4", + "zh:8b6b3ebeaaa8e38dd04e56996abe80db9be6f4c1df75ac3cccc77642899bd464", + "zh:ad07750576b99248037b897de71113cc19b1a8d0bc235eb99173cc83d0de3b1b", + "zh:b9f1c3bfadb74068f5c205292badb0661e17ac05eb23bfe8bd809691e4583d0e", + "zh:cc4cbcd67414fefb111c1bf7ab0bc4beb8c0b553d01719ad17de9a047adff4d1", + ] +} diff --git a/subfootball_tracker_api/infra/main.tf b/subfootball_tracker_api/infra/main.tf new file mode 100644 index 0000000..937f27c --- /dev/null +++ b/subfootball_tracker_api/infra/main.tf @@ -0,0 +1,215 @@ +terraform { + backend "s3" { + bucket = "jordansimsmith-terraform" + key = "subfootball_tracker_api/infra/terraform.tfstate" + region = "ap-southeast-2" + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.61" + } + } + + required_version = ">= 1.9.0" +} + +provider "aws" { + region = "ap-southeast-2" +} + +locals { + application_id = "subfootball_tracker_api" + tags = { + application_id = local.application_id + } + + lambdas = { + update_page_content = { + target = "//subfootball_tracker_api:update-page-content-handler_deploy.jar" + handler = "com.jordansimsmith.subfootballtracker.UpdatePageContentHandler" + } + } + + subscriptions = ["jordansimsmith@gmail.com"] +} + +data "aws_iam_policy_document" "lambda_sts_allow_policy_document" { + statement { + effect = "Allow" + + principals { + identifiers = ["lambda.amazonaws.com"] + type = "Service" + } + + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role" "lambda_role" { + name = "${local.application_id}_lambda_exec" + assume_role_policy = data.aws_iam_policy_document.lambda_sts_allow_policy_document.json + tags = local.tags +} + +resource "aws_iam_role_policy_attachment" "lambda_basic" { + role = aws_iam_role.lambda_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" +} + +resource "aws_dynamodb_table" "subfootball_tracker" { + name = "subfootball_tracker" + billing_mode = "PAY_PER_REQUEST" + hash_key = "pk" + range_key = "sk" + + attribute { + name = "pk" + type = "S" + } + + attribute { + name = "sk" + type = "S" + } + + point_in_time_recovery { + enabled = true + } + + deletion_protection_enabled = true + + tags = local.tags +} + +data "aws_iam_policy_document" "lambda_dynamodb" { + statement { + effect = "Allow" + + resources = [ + aws_dynamodb_table.subfootball_tracker.arn + ] + + actions = [ + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:BatchWriteItem", + "dynamodb:GetItem", + "dynamodb:BatchGetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:ConditionCheckItem", + ] + } +} + +resource "aws_iam_policy" "lambda_dynamodb" { + name = "${local.application_id}_lambda_dynamodb" + policy = data.aws_iam_policy_document.lambda_dynamodb.json + tags = local.tags +} + +resource "aws_iam_role_policy_attachment" "lambda_dynamodb" { + role = aws_iam_role.lambda_role.name + policy_arn = aws_iam_policy.lambda_dynamodb.arn +} + +resource "aws_sns_topic" "page_content_updates" { + name = "${local.application_id}_page_content_updates" +} + +resource "aws_sns_topic_subscription" "page_content_updates" { + for_each = toset(local.subscriptions) + topic_arn = aws_sns_topic.page_content_updates.arn + protocol = "email" + endpoint = each.value +} + +data "aws_iam_policy_document" "lambda_sns" { + statement { + effect = "Allow" + + resources = [ + aws_sns_topic.page_content_updates.arn, + ] + + actions = [ + "SNS:Publish", + "SNS:GetTopicAttributes", + ] + } + + statement { + effect = "Allow" + + resources = [ + "*" + ] + + actions = [ + "SNS:ListTopics" + ] + } +} + +resource "aws_iam_policy" "lambda_sns" { + name = "${local.application_id}_lambda_sns" + policy = data.aws_iam_policy_document.lambda_sns.json + tags = local.tags +} + +resource "aws_iam_role_policy_attachment" "lambda_sns" { + role = aws_iam_role.lambda_role.name + policy_arn = aws_iam_policy.lambda_sns.arn +} + +data "external" "handler_location" { + for_each = local.lambdas + + program = ["bash", "../../tools/terraform/resolve_location.sh"] + + query = { + target = each.value.target + } +} + +data "local_file" "handler_file" { + for_each = local.lambdas + + filename = data.external.handler_location[each.key].result.location +} + +resource "aws_lambda_function" "lambda" { + for_each = local.lambdas + + filename = data.local_file.handler_file[each.key].filename + function_name = "${local.application_id}_${each.key}" + role = aws_iam_role.lambda_role.arn + source_code_hash = data.local_file.handler_file[each.key].content_base64sha256 + handler = each.value.handler + runtime = "java17" + memory_size = 1024 + timeout = 30 + tags = local.tags +} + +resource "aws_cloudwatch_event_rule" "trigger" { + name = "${local.application_id}_trigger" + schedule_expression = "rate(1 hour)" +} + +resource "aws_cloudwatch_event_target" "trigger" { + rule = aws_cloudwatch_event_rule.trigger.name + target_id = "lambda" + arn = aws_lambda_function.lambda["update_page_content"].arn +} + +resource "aws_lambda_permission" "cloudwatch_trigger" { + statement_id = "AllowExecutionFromCloudWatch" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.lambda["update_page_content"].function_name + principal = "events.amazonaws.com" + source_arn = aws_cloudwatch_event_rule.trigger.arn +} \ No newline at end of file