Infrastructure

This page documents the AWS infrastructure provisioned by Terraform for the qa-automation service. All resources are defined in the deploy/ directory and are applied via GitHub Actions to each environment in the table below. Every environment-specific name is derived from var.environment (test today, staging next), so the same configuration deploys to any non-production environment. The infrastructure is managed using the fmtf-module-lambda internal module and the terraform-aws-modules/apigateway-v2 community module.

This service is deployed only to non-production environments and is never deployed to production.

Names below are shown for the test environment; substitute the environment prefix (test-, staging-, test/, staging/) elsewhere.

This service names its own API gateway qa.<env>.svc.floatme.io under the per-environment zone infra provides, so callers can address it without knowing its API ID. Peers will get the same treatment as each adopts a name; until then their URLs are resolved as described next.

Resources owned by other services are not looked up at plan time. Peer API gateways resolve to an empty URL when absent, and queues and job Lambdas are addressed by name convention, so this service can be planned and applied in a fresh environment before its peers exist. A second deploy fills in the API URLs once every peer has deployed. Infra-owned resources (DynamoDB tables, the Kinesis stream, secrets, VPC) are looked up and must exist first.

Deployment

GitHub Actions authenticates to AWS with OIDC, never static keys: pull requests plan with the read-only <env>-github-services-plan role, and pushes to main apply with <env>-github-services-deploy. Both roles, the permissions boundary the Lambda role carries, and the state bucket are created by floatme-corp/infrastructure (modules/github-services-ci). The apply workflow also accepts workflow_dispatch; re-running it is the second pass a fresh environment needs to resolve peer API URLs that did not exist on the first.

Environment AWS account Region Terraform state bucket Lambda artifact bucket

test (old account)

267052520423 (shared with prod)

us-west-2

floatme-test-media

floatme-test-media

test

299185625033

us-east-2

floatme-test-services-terraform-state

floatme-test-services-artifacts

staging

208295047603

us-east-2

floatme-staging-services-terraform-state

floatme-staging-media

The state key is terraform/qa/terraform.tfstate and the lock table terraform-locking in every environment. The two test targets share an environment name, so every per-target value is a deploy-env.yaml input: the artifact bucket is var.artifact_bucket (the fmtf-module-lambda default is the old account’s bucket, whose name is taken) and the Dynamo regions are var.dynamo_region and var.dynamo_legacy_region. deploy/.terraform.lock.hcl is committed so the provider plugin cache the devkit mounts at /cache is used and provider upgrades go through review.

Every AWS provider applies the organization’s cost-allocation tags as default tags, matching floatme-corp/infrastructure: floatme:environment (var.environment), floatme:application (qa), floatme:managed-by (terraform), and floatme:repository (floatme-corp/qa-automation). The org tag policy enumerates these keys and the allowed environment values; resource-level tags only add a name.

Lambda Functions

One Lambda function is deployed. It is built from the cmd/api entrypoint, packaged as a binary in dist/, and deployed using the internal fmtf-module-lambda module (v2.1.0), which also attaches the environment’s <env>-github-services-boundary permissions boundary to the function’s IAM role.

Function Trigger Timeout Memory Key IAM Permissions

test-qa-automation-api

API Gateway HTTP API (AWS IAM SigV4) — ANY /\{proxy+\}

300s

Module default

  • sqs:SendMessage — the four subscription-service collections queues, the LOC collections queue, and the transactions-service listener queue (<env>-txn-listener-eventbridge, for login events injected through POST /qa/test/event), addressed by name convention rather than looked up

  • kinesis:PutRecord, kinesis:PutRecords — <env>-payments Kinesis stream

  • dynamodb:GetItem/PutItem/Query/UpdateItem/DeleteItem — bypass, underwriting, subscriptions, subscriptions_history, collections tables

  • lambda:InvokeFunction — <env>-loc-service-loc-collections-job, addressed by name convention rather than looked up

  • execute-api:Invoke — every API Gateway in the deploying region and account (arn:aws:execute-api:<region>:<account>:*///*); test and prod share an account but sit in different regions, which is what keeps this scoped to one environment

  • secretsmanager:GetSecretValue — Auth0, RDS, Plaid, GrowthBook, Iterable, Iterable Mobile secrets

Environment Variables

The Lambda receives the following runtime configuration via environment variables:

Variable Value / Source

BYPASS_TABLE_NAME

requirements-bypass (default)

SUBSCRIPTION_TABLE_NAME

Hardcoded at deploy time

COLLECTION_TABLE_NAME

Hardcoded at deploy time

UNDERWRITING_TABLE_NAME

Hardcoded at deploy time

DYNAMO_REGION

var.dynamo_region (us-west-2 in the old test account, us-east-2 in the new one and staging)

DYNAMO_LEGACY_REGION

var.dynamo_legacy_region (us-west-2 in the old test account, us-east-1 in the new one, staging and prod) — region of the unprefixed FloatMeAPI-era tables

LOG_VERBOSITY

0 in prod, 1 otherwise

LOC_COLLECTIONS_JOB_FUNCTION_NAME

<env>-loc-service-loc-collections-job, the LOC collections job Lambda invoked by POST /qa/simulate/loc/collections

USER_SERVICE_URL / USER_SERVICE_REGION

Resolved from <env>-user-service API Gateway (empty until that peer has deployed)

ADMIN_SERVICE_URL / ADMIN_SERVICE_REGION

Resolved from <env>-admin-api-internal API Gateway (empty until that peer has deployed)

INSIGHT_SERVICE_URL / INSIGHT_SERVICE_REGION

Resolved from <env>-insight API Gateway (empty until that peer has deployed)

TXN_SERVICE_URL / TXN_SERVICE_REGION

Resolved from <env>-txn API Gateway (empty until that peer has deployed)

PAYMENTS_SERVICE_URL / PAYMENTS_SERVICE_REGION

Resolved from <env>-payments API Gateway (empty until that peer has deployed)

LOC_SERVICE_URL / LOC_SERVICE_REGION

Resolved from <env>-loc-service API Gateway (empty until that peer has deployed)

FLOAT_SERVICE_URL / FLOAT_SERVICE_REGION

Resolved from <env>-floats API Gateway (empty until that peer has deployed)

SUBSCRIPTION_SERVICE_URL / SUBSCRIPTION_SERVICE_REGION

Resolved from <env>-subscription-service API Gateway (empty until that peer has deployed)

UNDERWRITING_SERVICE_URL / UNDERWRITING_SERVICE_REGION

Resolved from <env>-underwriting API Gateway (empty until that peer has deployed)

PLAID_WEBHOOK_URL

https://plaid.webhooks.<env>.floatme.io, registered on every Plaid item the API creates

SM_AUTH0_NAME

test/auth0

SM_RDS_MAIN_NAME

test/rds/main

SM_PLAID_NAME

test/plaid

SM_GROWTHBOOK_NAME

test/growthbook

SM_ITERABLE_NAME

test/iterable

SM_ITERABLE_MOBILE_NAME

test/iterable-mobile

SUBSCRIPTION_COLLECTIONS_SCHEDULED_QUEUE_URL

<env>-subscription-service-collections-scheduled queue URL, built from the name

SUBSCRIPTION_COLLECTIONS_PAUSED_QUEUE_URL

<env>-subscription-service-collections-pause queue URL, built from the name

SUBSCRIPTION_COLLECTIONS_RETRY_QUEUE_URL

<env>-subscription-service-collections-retry queue URL, built from the name

SUBSCRIPTION_COLLECTIONS_NOTIFIER_QUEUE_URL

<env>-subscription-service-pre-subscription-notifier-worker queue URL, built from the name

API Gateway

The HTTP API (API Gateway v2) is provisioned using the terraform-aws-modules/apigateway-v2 module (v6.1.0).

Property Value

Name

test-qa-automation

Protocol

HTTP API (API Gateway v2)

Custom domain

qa.<env>.svc.floatme.io — an alias record in the <env>.svc.floatme.io Route 53 zone, using the zone’s wildcard certificate; both are owned by infrastructure/modules/service-domains and looked up by name here. The domain, mapping, and record are standalone resources rather than module inputs, because the module disables the execute-api endpoint for HTTP APIs when it creates a domain

Endpoint URL

The custom domain above; the auto-assigned execute-api URL also remains active (disable_execute_api_endpoint is not set) so existing callers keep working until they move to the name

Auth type

AWS_IAM (SigV4 request signing required on all routes)

Integration payload format

1.0

Integration timeout

30,000 ms (30s)

Routes

Method + Path Auth Backend

ANY /\{proxy+\}

AWS IAM (SigV4)

test-qa-automation-api Lambda

All test automation API calls must be signed with AWS Signature Version 4 using credentials that have execute-api:Invoke permission on this API.

DynamoDB Tables

The Lambda reads and writes to five DynamoDB tables. The environment-prefixed table lives in var.dynamo_region (us-west-2 in the old test account, us-east-2 in the new one and staging). The unprefixed FloatMeAPI-era tables live in var.dynamo_legacy_region (us-west-2 in the old test account, us-east-1 in the new one, staging and prod, mirroring where prod keeps them), reached through the aws.legacy_dynamo provider at deploy time and DYNAMO_LEGACY_REGION at runtime. These tables are pre-existing (looked up via data sources) and are not created by this service.

Table Region Purpose

requirements-bypass

var.dynamo_legacy_region

Stores requirement bypass rules used by test flows to skip eligibility checks (e.g., advance eligibility, KYC gates)

billing-activity

var.dynamo_legacy_region

Subscription billing activity records (current); read and written during subscription lifecycle tests

billing-activity-history

var.dynamo_legacy_region

Historical billing activity; queried during subscription history assertions in tests

<env>-underwriting

var.dynamo_region

Underwriting decision records used during underwriting flow tests

collection-history

var.dynamo_legacy_region

Float collection-attempt log (owned by float-service); seeded so a test user can be given a collection history the collections and underwriting flows read back as real — e.g. the ACHSENT day-before attempt an early advance is taken against

Secrets Manager

All secrets are stored in AWS Secrets Manager under the test/ path prefix and are referenced by name in the Lambda’s environment. The Lambda retrieves them at startup via secretsmanager:GetSecretValue.

Secret name (path) Environment variable Contents

test/auth0

SM_AUTH0_NAME

Auth0 credentials — client ID, client secret, and domain used to obtain tokens for test user authentication flows

test/rds/main

SM_RDS_MAIN_NAME

Primary RDS database credentials — host, port, username, and password for direct DB access during test setup and teardown

test/plaid

SM_PLAID_NAME

Plaid API credentials — client ID and secret for simulating bank link and transaction flows in the test environment

test/growthbook

SM_GROWTHBOOK_NAME

GrowthBook SDK key and API credentials used to read and override feature flag state during tests

test/iterable

SM_ITERABLE_NAME

Iterable server-side API key for verifying email and push notification events triggered by test flows

test/iterable-mobile

SM_ITERABLE_MOBILE_NAME

Iterable mobile (JWT) API key used to authenticate mobile-originated notification events in tests

test/datadog/terraform

(Terraform only — not injected into Lambda)

Datadog api_key and app_key used solely by the Terraform Datadog provider to provision the service definition

VPC

The Lambda runs inside the FloatMe VPC for its target (us-west-2 in the old test account, us-east-2 in the new one and staging). Subnet and security group resources are looked up by tag name at deploy time rather than hardcoded by ID.

Resource Details

Private Subnet 1

Tagged floatme Private Subnet 1 — first private subnet, no direct internet access, egress via NAT Gateway

Private Subnet 2

Tagged floatme Private Subnet 2 — second private subnet in a separate AZ for redundancy

Security Group

Tagged floatme PrivateSG — private security group permitting intra-VPC traffic; blocks inbound internet traffic

Placing the Lambda in private subnets ensures it can reach RDS and other internal services over the VPC network without exposing them to the public internet.

Datadog

No Datadog monitors or SLOs are provisioned for this service. The fmtf-module-lambda per-function monitors (error rate, throughput, latency) are disabled on the api module call: nobody acts on them for QA tooling, and the old and new test accounts both deploy under the test environment name, so identical monitors from the two accounts would be rejected by Datadog as duplicates.

A Datadog service definition (schema v2.2) is registered for the qa service, linking it to:

Datadog credentials (api_key, app_key) are sourced from test/datadog/terraform in Secrets Manager and are used only by the Terraform provider — they are not injected into the Lambda runtime.