Skip to content

add initial terraform implementation#174

Draft
Rayna-Yu wants to merge 2 commits intomainfrom
159-create-lambda-and-api-gateway-terraform
Draft

add initial terraform implementation#174
Rayna-Yu wants to merge 2 commits intomainfrom
159-create-lambda-and-api-gateway-terraform

Conversation

@Rayna-Yu
Copy link
Copy Markdown
Contributor

@Rayna-Yu Rayna-Yu commented Mar 22, 2026

ℹ️ Issue

Closes

📝 Description

Write a short summary of what you added. Why is it important? Any member of C4C should be able to read this and understand your contribution -- not just your team members.

Briefly list the changes made to the code:

  1. Added lambda.tf
  2. followed the docs https://www.notion.so/Research-Lambda-Deployment-81-2a899c51d64f80dc930bf16bb0e43a97 and created terraform modules

✔️ Verification

What steps did you take to verify your changes work? These should be clear enough for someone to be able to clone the branch and follow the steps themselves.

Provide screenshots of any new components, styling changes, or pages.

Checked that the terraform plan in the checks tab to ensure that everything is being created.

🏕️ (Optional) Future Work / Notes

Did you notice anything ugly during the course of this ticket? Any bugs, design challenges, or unexpected behavior? Write it down so we can clean it up in a future ticket!

Rayna-Yu and others added 2 commits March 21, 2026 23:02
  - Auto-formatted .tf files with terraform fmt
  - Updated README.md with terraform-docs

  Co-authored-by: Rayna-Yu <Rayna-Yu@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

Terraform Plan 📖 infrastructure/aws

Terraform Initialization ⚙️success

Terraform Validation 🤖success

Terraform Plan 📖success

Show Plan
data.archive_file.lambda_placeholder: Reading...
data.archive_file.lambda_placeholder: Read complete after 0s [id=96878a51e358033297a32b882fd5223cc95fb8a7]
data.aws_caller_identity.current: Reading...
aws_s3_bucket.reports_bucket: Refreshing state... [id=c4c-branch-generated-reports20251030194253425700000001]
aws_cognito_user_pool.branch_user_pool: Refreshing state... [id=us-east-2_CxTueqe6g]
data.aws_caller_identity.current: Read complete after 0s [id=489881683177]
data.infisical_secrets.rds_folder: Reading...
data.infisical_secrets.rds_folder: Read complete after 0s
aws_db_instance.branch_rds: Refreshing state... [id=db-AMMYFTORW6XJGRELV7WQZCNHQI]
aws_cognito_user_pool_client.branch_client: Refreshing state... [id=570i6ocj0882qu0ditm4vrr60f]
aws_s3_bucket_public_access_block.reports_bucket_public_access: Refreshing state... [id=c4c-branch-generated-reports20251030194253425700000001]
aws_s3_bucket_policy.reports_bucket_policy: Refreshing state... [id=c4c-branch-generated-reports20251030194253425700000001]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_iam_role.lambda_role will be created
  + resource "aws_iam_role" "lambda_role" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = "lambda.amazonaws.com"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "branch-lambda-role"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = (known after apply)
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # aws_iam_role_policy_attachment.lambda_basic will be created
  + resource "aws_iam_role_policy_attachment" "lambda_basic" {
      + id         = (known after apply)
      + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      + role       = "aws_iam_role.lambda_role.name"
    }

  # aws_lambda_function.functions["donors"] will be created
  + resource "aws_lambda_function" "functions" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + function_name                  = "branch-donors"
      + handler                        = "handler.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + region                         = "us-east-2"
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "nodejs20.x"
      + s3_bucket                      = (known after apply)
      + s3_key                         = "donors/initial.zip"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 30
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "DB_HOST"     = "terraform-20251009192006341800000001.cexlbyrdcxre.us-east-2.rds.amazonaws.com"
              + "DB_NAME"     = "branch_rds"
              + "DB_PASSWORD" = (sensitive value)
              + "DB_PORT"     = "5432"
              + "DB_USER"     = (sensitive value)
              + "NODE_ENV"    = "production"
            }
        }

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # aws_lambda_function.functions["expenditures"] will be created
  + resource "aws_lambda_function" "functions" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + function_name                  = "branch-expenditures"
      + handler                        = "handler.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + region                         = "us-east-2"
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "nodejs20.x"
      + s3_bucket                      = (known after apply)
      + s3_key                         = "expenditures/initial.zip"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 30
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "DB_HOST"     = "terraform-20251009192006341800000001.cexlbyrdcxre.us-east-2.rds.amazonaws.com"
              + "DB_NAME"     = "branch_rds"
              + "DB_PASSWORD" = (sensitive value)
              + "DB_PORT"     = "5432"
              + "DB_USER"     = (sensitive value)
              + "NODE_ENV"    = "production"
            }
        }

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # aws_lambda_function.functions["projects"] will be created
  + resource "aws_lambda_function" "functions" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + function_name                  = "branch-projects"
      + handler                        = "handler.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + region                         = "us-east-2"
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "nodejs20.x"
      + s3_bucket                      = (known after apply)
      + s3_key                         = "projects/initial.zip"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 30
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "DB_HOST"     = "terraform-20251009192006341800000001.cexlbyrdcxre.us-east-2.rds.amazonaws.com"
              + "DB_NAME"     = "branch_rds"
              + "DB_PASSWORD" = (sensitive value)
              + "DB_PORT"     = "5432"
              + "DB_USER"     = (sensitive value)
              + "NODE_ENV"    = "production"
            }
        }

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # aws_lambda_function.functions["reports"] will be created
  + resource "aws_lambda_function" "functions" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + function_name                  = "branch-reports"
      + handler                        = "handler.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + region                         = "us-east-2"
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "nodejs20.x"
      + s3_bucket                      = (known after apply)
      + s3_key                         = "reports/initial.zip"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 30
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "DB_HOST"     = "terraform-20251009192006341800000001.cexlbyrdcxre.us-east-2.rds.amazonaws.com"
              + "DB_NAME"     = "branch_rds"
              + "DB_PASSWORD" = (sensitive value)
              + "DB_PORT"     = "5432"
              + "DB_USER"     = (sensitive value)
              + "NODE_ENV"    = "production"
            }
        }

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # aws_lambda_function.functions["users"] will be created
  + resource "aws_lambda_function" "functions" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + function_name                  = "branch-users"
      + handler                        = "handler.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 256
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + region                         = "us-east-2"
      + reserved_concurrent_executions = -1
      + role                           = (known after apply)
      + runtime                        = "nodejs20.x"
      + s3_bucket                      = (known after apply)
      + s3_key                         = "users/initial.zip"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 30
      + version                        = (known after apply)

      + environment {
          + variables = {
              + "DB_HOST"     = "terraform-20251009192006341800000001.cexlbyrdcxre.us-east-2.rds.amazonaws.com"
              + "DB_NAME"     = "branch_rds"
              + "DB_PASSWORD" = (sensitive value)
              + "DB_PORT"     = "5432"
              + "DB_USER"     = (sensitive value)
              + "NODE_ENV"    = "production"
            }
        }

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # aws_s3_bucket.lambda_deployments will be created
  + resource "aws_s3_bucket" "lambda_deployments" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "branch-lambda-deployments-489881683177"
      + bucket_domain_name          = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_region               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = "us-east-2"
      + request_payer               = (known after apply)
      + tags_all                    = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket_server_side_encryption_configuration.lambda_deployments will be created
  + resource "aws_s3_bucket_server_side_encryption_configuration" "lambda_deployments" {
      + bucket = (known after apply)
      + id     = (known after apply)
      + region = "us-east-2"

      + rule {
          + apply_server_side_encryption_by_default {
              + sse_algorithm     = "AES256"
                # (1 unchanged attribute hidden)
            }
        }
    }

  # aws_s3_bucket_versioning.lambda_deployments will be created
  + resource "aws_s3_bucket_versioning" "lambda_deployments" {
      + bucket = (known after apply)
      + id     = (known after apply)
      + region = "us-east-2"

      + versioning_configuration {
          + mfa_delete = (known after apply)
          + status     = "Enabled"
        }
    }

  # aws_s3_object.lambda_placeholder["donors"] will be created
  + resource "aws_s3_object" "lambda_placeholder" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = (known after apply)
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_type           = "application/zip"
      + etag                   = (known after apply)
      + force_destroy          = false
      + id                     = (known after apply)
      + key                    = "donors/initial.zip"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-2"
      + server_side_encryption = (known after apply)
      + source                 = "${path.module}/lambda-placeholder.zip"
      + storage_class          = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

  # aws_s3_object.lambda_placeholder["expenditures"] will be created
  + resource "aws_s3_object" "lambda_placeholder" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = (known after apply)
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_type           = "application/zip"
      + etag                   = (known after apply)
      + force_destroy          = false
      + id                     = (known after apply)
      + key                    = "expenditures/initial.zip"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-2"
      + server_side_encryption = (known after apply)
      + source                 = "${path.module}/lambda-placeholder.zip"
      + storage_class          = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

  # aws_s3_object.lambda_placeholder["projects"] will be created
  + resource "aws_s3_object" "lambda_placeholder" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = (known after apply)
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_type           = "application/zip"
      + etag                   = (known after apply)
      + force_destroy          = false
      + id                     = (known after apply)
      + key                    = "projects/initial.zip"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-2"
      + server_side_encryption = (known after apply)
      + source                 = "${path.module}/lambda-placeholder.zip"
      + storage_class          = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

  # aws_s3_object.lambda_placeholder["reports"] will be created
  + resource "aws_s3_object" "lambda_placeholder" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = (known after apply)
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_type           = "application/zip"
      + etag                   = (known after apply)
      + force_destroy          = false
      + id                     = (known after apply)
      + key                    = "reports/initial.zip"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-2"
      + server_side_encryption = (known after apply)
      + source                 = "${path.module}/lambda-placeholder.zip"
      + storage_class          = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

  # aws_s3_object.lambda_placeholder["users"] will be created
  + resource "aws_s3_object" "lambda_placeholder" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = (known after apply)
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_type           = "application/zip"
      + etag                   = (known after apply)
      + force_destroy          = false
      + id                     = (known after apply)
      + key                    = "users/initial.zip"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-2"
      + server_side_encryption = (known after apply)
      + source                 = "${path.module}/lambda-placeholder.zip"
      + storage_class          = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

Plan: 15 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

Pushed by: @nourshoreibah, Action: pull_request

@Rayna-Yu Rayna-Yu marked this pull request as ready for review March 25, 2026 02:26
@Rayna-Yu Rayna-Yu requested a review from nourshoreibah as a code owner March 25, 2026 02:26
@github-actions github-actions bot requested a review from denniwang March 25, 2026 02:26
github-actions bot added a commit that referenced this pull request Mar 25, 2026
Copy link
Copy Markdown
Collaborator

@nourshoreibah nourshoreibah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic to create the lambdas mostly looks good. just a few comments

What's missing is the API gateway set up. these lambdas would be completely isolated without any API gateway to expose them. I'd recommend doing this in a separate api_gateway.tf file

The idea is you would add every lambda function as an integration to the gateway then make each one a route using a for each


# Attach basic execution policy for CloudWatch Logs
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = "aws_iam_role.lambda_role.name"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm this should be quoted? i think this might error on apply

# 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"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also just wanting to confirm this is correct arn for the aws managed role

# 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"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the double "$" here? is this escaping the first "$"? please confirm

# Define all Lambda functions in one place
locals {
lambda_functions = toset([
"projects",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think auth is missing here? also what do you think of generating this list dynamically from the list of folders in lambdas? open to either design just curious

@Rayna-Yu Rayna-Yu marked this pull request as draft March 26, 2026 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants