---
title: Automate SaaS Tenant Provisioning on AWS
description: Assume 8 state transitions and 200 approved signups in a month: 1,600 transitions. Step Functions Standard includes 4,000 free transitions a month in US East (N. Virginia) at $0.000025 after that. The workflow marks READY only after the health check.
url: https://www.factualminds.com/blog/automate-saas-tenant-provisioning-aws/
datePublished: 2026-10-19T00:00:00.000Z
dateModified: 2026-10-19T00:00:00.000Z
author: palaniappan-p
category: Cloud Architecture
tags: saas, aws-step-functions, aws-secrets-manager, amazon-dynamodb, multi-tenant
---

# Automate SaaS Tenant Provisioning on AWS

> Assume 8 state transitions and 200 approved signups in a month: 1,600 transitions. Step Functions Standard includes 4,000 free transitions a month in US East (N. Virginia) at $0.000025 after that. The workflow marks READY only after the health check.

On **19 October 2026**, the slow part of a new SaaS customer is still someone creating the tenant by hand, then forgetting the secret. The [silo, pool, and bridge post](https://www.factualminds.com/blog/saas-multi-tenancy-on-aws-silo-vs-pool-vs-bridge-model/) chooses the isolation model. The [pattern](https://www.factualminds.com/patterns/multi-tenant-saas-on-aws/) is the reference. This page starts after that choice, and only for a **pool**: one shared table, one item per tenant. Shard-key pain on an existing pool is the Aurora Limitless decision.

> **Reproduce this** — Copy the [provision states](https://www.factualminds.com/examples/architecture-blog-2026/tenant-provisioning/provision-states.md). If the sheet's first row is not a conditional put, you do not have idempotency yet.

## Who this is for

The platform engineer who gets a Slack message that says the contract is signed, and the CTO who wants that message to become a tenant without a console session.

## What you get, and what you do not

You get one item, one secret, a health check, and a status. You do not get a new account, a new VPC, or a silo database. You do not get a user who can sign in. [Cognito for SaaS](https://www.factualminds.com/blog/aws-cognito-authentication-for-saas-applications/) is that problem.

**Opinionated take:** pool item plus secret, Standard workflow, READY last. The trade-off is that a tenant who contracted for a dedicated database is out of scope. Sell them the isolation article's answer, then a different workflow.

## Architecture

Your billing or signup system emits a custom EventBridge event only after a person approves the tenant. Step Functions Standard runs the states on the sheet. Express is the wrong type: you want exactly-once workflow execution and a history you can read when a signup fails at 2 a.m. The [welcome page](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) states that Standard workflows are exactly-once and can run for up to a year. Express is at-least-once and stops at five minutes.

```mermaid
flowchart LR
  approved[SignupApproved] --> workflow[StepFunctionsStandard]
  workflow --> item[ConditionalPut]
  item --> secret[SecretsManager]
  secret --> health[HealthCheck]
  health --> ready[StatusReady]
  health --> failed[StatusFailed]
```

## Walkthrough

**1. Accept only an authorised event.** The event carries `tenantId`, a plan code, and an approver id. Reject a missing approver before any write. The event pattern is yours. AWS does not define a tenant-signup schema.

**2. Conditional put.** Context: DynamoDB `PutItem`, condition expression `attribute_not_exists(tenantId)`.

```json
{
  "TableName": "tenants",
  "Item": {
    "tenantId": { "S": "ten_01" },
    "status": { "S": "RECEIVED" }
  },
  "ConditionExpression": "attribute_not_exists(tenantId)"
}
```

On `ConditionalCheckFailedException`, read the item. `READY` means stop as success. Anything else is a conflict a person looks at. Do not overwrite `READY`.

**3. Create the secret** with a name that includes the tenant id, so a retry can see the name. The resource policy, if you use one, allows only the application role that already assumes tenant context. Do not put the secret value in the state payload. Pass the ARN.

**4. Health check, then READY.** The check reads the item and the secret ARN and calls whatever "ready" means for your app: a row exists, the secret resolves. Only then update status to `READY`.

**5. Compensate.** If the secret create succeeds and the health check fails, delete that secret and set `FAILED`. If the conditional put never succeeded, delete nothing. Do not run a shared-schema drop.

## IAM

The workflow role can `dynamodb:PutItem` and `dynamodb:UpdateItem` on the tenants table, `secretsmanager:CreateSecret` and `secretsmanager:DeleteSecret` on the name prefix you use, and `secretsmanager:GetSecretValue` only for the health-check function. `iam:PassRole` is not required if you are not creating roles. Do not grant `secretsmanager:DeleteSecret` on `*`.

## Failure and retries

Retry `CreateSecret` on throttling. Do not retry a conditional-check failure. A replayed event is handled by the read-after-conflict branch. Record the execution ARN on the item so support can open one history.

## Cost, as an assumption

[Step Functions pricing](https://aws.amazon.com/step-functions/pricing/) in US East (N. Virginia): **$0.000025** per state transition, **4,000** free per month. Assume 8 transitions and 200 signups: **1,600** transitions, inside the free tier. Secrets Manager has its own per-secret monthly charge. Price that from the Secrets Manager pricing page. Two hundred secrets are the bill that matters, not the state machine. Not a measured saving.

## Testing

Send one event. Expect `READY` and one secret. Send it again. Expect no second secret. Force the health check to fail. Expect `FAILED` and no leftover secret from that execution. Send an event with no approver id. Expect no item.

> **What broke** — Lab, 19 October 2026. Not a customer account. The health check treated a DynamoDB 200 as ready before the secret write had been read, because the check ran in parallel with the create. Detection: the app logged a missing secret for a READY tenant. Recovery: put the check after the create, in series, and read the secret. Lesson: parallel "it exists" checks race the write.

## Production checklist

- Isolation model is already written down. This workflow says pool.
- Approver id is required.
- READY is the last write.
- Compensation deletes only the secret this execution created.
- State payloads do not contain secret strings.
- A replay of a READY tenant is a no-op.

## What This Post Doesn't Cover

Silo and bridge provisioning. Per-tenant KMS keys. Cognito user pools. Offboarding. That workflow is the [retention article](https://www.factualminds.com/blog/saas-tenant-offboarding-data-retention-aws/).

## What to Do This Week

1. Confirm the tenant is supposed to be pool. If not, stop and use the isolation article.
2. Implement the conditional put and the replay test before you create a secret.
3. Add the health check only after replay is boring.

Running this for a customer is [managed services](https://www.factualminds.com/services/aws-managed-services/) work when the signup path already exists and the gap is the workflow, not a new product.

## FAQ

### When should we not automate provisioning this way?
Skip it when you have not chosen pool, silo, or bridge. That decision is the other article. Also skip it when every tenant is a separate AWS account. Account vending is a different control plane. This workflow writes one item and one secret in an account you already operate.

### What could go wrong if READY is set before the health check?
The product tells the customer to sign in, and the config the app reads is missing. Set READY only after the check returns. A failure marks FAILED and deletes the secret this execution created. It does not drop a schema other tenants use.

### What if the same signup event arrives twice?
The first write is a conditional put on tenantId. If status is already READY, the execution stops without creating a second secret. If status is FAILED, a new authorised event may retry. An unauthorised duplicate must not.

### Does this replace Cognito?
No. Cognito, or whatever issues the token, still authenticates the user. This workflow stores the tenant id your authorizer already expects. The Cognito article owns the sign-in design.

---

*Source: https://www.factualminds.com/blog/automate-saas-tenant-provisioning-aws/*
