diff --git a/infrastructure/aws/README.md b/infrastructure/aws/README.md index c52d8c9..c011548 100644 --- a/infrastructure/aws/README.md +++ b/infrastructure/aws/README.md @@ -11,6 +11,7 @@ | Name | Version | |------|---------| +| [archive](#provider\_archive) | n/a | | [aws](#provider\_aws) | 6.14.1 | | [infisical](#provider\_infisical) | n/a | @@ -25,7 +26,18 @@ No modules. | [aws_cognito_user_pool.branch_user_pool](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/cognito_user_pool) | resource | | [aws_cognito_user_pool_client.branch_client](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/cognito_user_pool_client) | resource | | [aws_db_instance.branch_rds](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/db_instance) | resource | +| [aws_iam_role.lambda_role](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.lambda_basic](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/iam_role_policy_attachment) | resource | +| [aws_lambda_function.functions](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/lambda_function) | resource | +| [aws_s3_bucket.lambda_deployments](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_bucket) | resource | | [aws_s3_bucket.reports_bucket](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_policy.reports_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_bucket_policy) | resource | +| [aws_s3_bucket_public_access_block.reports_bucket_public_access](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.lambda_deployments](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_versioning.lambda_deployments](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_bucket_versioning) | resource | +| [aws_s3_object.lambda_placeholder](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/resources/s3_object) | resource | +| [archive_file.lambda_placeholder](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/6.14.1/docs/data-sources/caller_identity) | data source | | [infisical_secrets.rds_folder](https://registry.terraform.io/providers/infisical/infisical/latest/docs/data-sources/secrets) | data source | ## Inputs @@ -40,9 +52,8 @@ No modules. | Name | Description | |------|-------------| -| [cognito\_client\_id](#output\_cognito\_client\_id) | Cognito User Pool Client ID | | [cognito\_region](#output\_cognito\_region) | AWS Region for Cognito | | [cognito\_user\_pool\_arn](#output\_cognito\_user\_pool\_arn) | Cognito User Pool ARN | | [cognito\_user\_pool\_endpoint](#output\_cognito\_user\_pool\_endpoint) | Cognito User Pool Endpoint | -| [cognito\_user\_pool\_id](#output\_cognito\_user\_pool\_id) | Cognito User Pool ID | +| [reports\_bucket\_name](#output\_reports\_bucket\_name) | Name of the S3 bucket for generated reports | diff --git a/infrastructure/aws/lambda.tf b/infrastructure/aws/lambda.tf new file mode 100644 index 0000000..2453f6e --- /dev/null +++ b/infrastructure/aws/lambda.tf @@ -0,0 +1,107 @@ +# IAM role for Lambda functions +resource "aws_iam_role" "lambda_role" { + name = "branch-lambda-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { Service = "lambda.amazonaws.com" } + }] + }) +} + +# Attach basic execution policy for CloudWatch Logs +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" +} + +# Get AWS account ID for unique bucket naming +data "aws_caller_identity" "current" {} + +resource "aws_s3_bucket" "lambda_deployments" { + bucket = "branch-lambda-deployments-${data.aws_caller_identity.current.account_id}" +} + +resource "aws_s3_bucket_versioning" "lambda_deployments" { + bucket = aws_s3_bucket.lambda_deployments.id + + versioning_configuration { + status = "Enabled" + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "lambda_deployments" { + bucket = aws_s3_bucket.lambda_deployments.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +# Define all Lambda functions in one place +locals { + lambda_functions = toset([ + "projects", + "reports", + "users", + "donors", + "expenditures" + ]) +} + +# Minimal placeholder that will be replaced by GitHub Actions on first deployment +data "archive_file" "lambda_placeholder" { + type = "zip" + output_path = "$${path.module}/lambda-placeholder.zip" + source { + content = "exports.handler = async () => ({ statusCode: 200, body: JSON.stringify({ message: 'Placeholder - will be replaced by CI/CD' }) });" + filename = "handler.js" + } +} + +# This allows Terraform to create the Lambda functions initially +resource "aws_s3_object" "lambda_placeholder" { + for_each = local.lambda_functions + + bucket = aws_s3_bucket.lambda_deployments.id + key = "${each.key}/initial.zip" + source = data.archive_file.lambda_placeholder.output_path + + content_type = "application/zip" +} + +# Create all Lambda functions with a single resource block +resource "aws_lambda_function" "functions" { + for_each = local.lambda_functions + + function_name = "branch-${each.key}" + runtime = "nodejs20.x" + handler = "handler.handler" + timeout = 30 + memory_size = 256 + role = aws_iam_role.lambda_role.arn + + # Use S3 for deployment (initial placeholder, replaced by GitHub Actions) + s3_bucket = aws_s3_bucket.lambda_deployments.id + s3_key = aws_s3_object.lambda_placeholder[each.key].key + + # Prevent Terraform from reverting code deployments made by GitHub Actions + lifecycle { + ignore_changes = [s3_key] + } + + environment { + variables = { + NODE_ENV = "production" + DB_HOST = aws_db_instance.branch_rds.address + DB_USER = data.infisical_secrets.rds_folder.secrets["username"].value + DB_PASSWORD = data.infisical_secrets.rds_folder.secrets["password"].value + DB_PORT = try(data.infisical_secrets.rds_folder.secrets["db_port"].value, "5432") + DB_NAME = try(data.infisical_secrets.rds_folder.secrets["db_name"].value, aws_db_instance.branch_rds.db_name) + } + } +} \ No newline at end of file