Iqraa.tech

04 – AWS Multi-Tenant Agentic AI: Isolation and Cost Architecture

Multi-tenant agents turn one agentic AI system into a SaaS serving many customers from shared infrastructure. Siloed, pooled, or hybrid deployment determines your unit economics and security.

04 – AWS Multi-Tenant Agentic AI: Isolation and Cost Architecture — title card

Multi-Tenant Agents: What You’ll Learn

This guide maps the AWS-recommended deployment models, tenant-context propagation patterns, and isolation primitives across siloed, pooled, and hybrid topologies on Bedrock AgentCore.

By the end you should be able to justify a siloed, pooled, or hybrid topology for a given tenant mix, and trace a tenant identifier through every hop of a request from JWT to memory write.

You should also be able to explain the layered isolation controls — IAM, KMS, microVM, MCP credentials — that a security review will ask about. The goal is a mental checklist for any new tenant-facing agent feature, not one fixed blueprint.

Multi-Tenant Agents: The Core Challenge

Unlike a stateless web request, an agent invocation carries tenant-specific memory, tenant-scoped tools, and tenant-aware prompts. Every layer — API entry, LLM call, tool execution — must know which tenant it serves.

Three forces shape every architecture: isolation (tenant A never sees tenant B’s data), context (the agent knows which tenant is requesting), and attribution (every token is billable to the triggering tenant). No universally best pattern — only the best fit for your workload and regulatory environment.

The reason agents are harder to isolate than plain web apps is that an LLM call is not a single, auditable database query — it is a chain of tool invocations, memory reads, and prompt assemblies, and any one of those hops can silently pull in the wrong tenant’s data if the identifier is dropped.

A missing tenant_id at the tool layer does not throw an error; it just returns whatever the tool finds, which makes these bugs more dangerous because they fail silently instead of loudly.

Siloed vs Pooled Agent Deployment

The first and most consequential decision: a separate agent instance per tenant (siloed), or one shared instance with tenant context injected at runtime (pooled)?

This decision cascades into almost every later architecture choice — how you provision infrastructure, how you bill, how you patch, and how a security auditor will scope their review. Getting it wrong early is expensive to reverse because migrating live tenants between topologies means rebuilding the isolation boundary underneath production traffic, so it deserves more upfront analysis than most teams give it.

Dimension Siloed Pooled
Isolation Strong (no shared state) Weak (relies on context scoping)
Per-tenant cost High Low
Scaling Linear (N = N instances) Sublinear (1 serves all)
Customization Trivial Needs conditional logic
Upgrade velocity Slow Fast
Operational toil High Low
Best fit Enterprise, regulated Consumer, PLG

A hybrid model bridges them: most tenants share a pooled agent, but high-tier or regulated tenants get dedicated siloed instances, routed at API Gateway by the JWT tier claim.

When siloed wins

Siloed is right for regulated industries requiring per-tenant compute isolation, enterprise contracts with per-tenant fine-tuning or knowledge bases, and B2B SaaS with few high-value tenants where the cost premium is trivial relative to revenue.

The tell-tale sign you need siloed is a customer contract clause that names a specific isolation guarantee — dedicated compute, a named encryption key, or a right to audit the runtime — because those clauses are much easier to satisfy with a physically separate agent instance than with logical scoping inside a shared one.

When pooled wins

Pooled dominates consumer and PLG segments where a dedicated instance per tenant would sink the idle-cost baseline. It also wins when tenant customization is uniform — a new tenant is just a registry row, not a provisioning project.

Idle cost is the deciding variable: a siloed agent instance still consumes baseline compute even when a tenant sends zero requests overnight, and at thousands of low-usage tenants that idle cost dwarfs the actual inference spend. Pooled collapses that baseline to near zero because the shared instance is already running for other tenants’ traffic.

When hybrid wins

Hybrid is the 2026 default for enterprise SaaS: free tiers pooled, enterprise tiers siloed. The cost is running two topologies in parallel; the upside is matching isolation to willingness-to-pay. Most Bedrock AgentCore agents ship hybrid from day one.

Running two topologies means maintaining two deployment pipelines, two sets of IAM policies, and two on-call runbooks, which is real operational overhead. Teams that pick hybrid successfully treat the pooled and siloed paths as the same codebase with a routing decision at the edge, not two forked implementations that drift apart over time.

Three deployment models — siloed, pooled, hybrid.

Injecting Tenant Context into Agents

How does a pooled agent know which tenant is requesting? Tenant context injection attaches a tenant identifier to every step of execution. Done right, the agent behaves as if per-tenant. Done wrong, you ship a data leak. The canonical AWS pattern:

1. Caller authenticates → Cognito / OIDC issues a JWT
2. JWT carries a custom claim: { "tenant_id": "acme-corp" }
3. API Gateway authorizer validates JWT, extracts tenant_id
4. API Gateway forwards request to Lambda with tenant_id in header
5. Lambda reads tenant_id, scopes all DynamoDB queries with it
6. Lambda invokes Bedrock Agent with tenant_id in sessionMetadata
7. Bedrock Agent's tools receive tenant_id, scope their own queries
8. Memory store (Bedrock AgentCore Memory) keys sessions by tenant_id
9. Response returns to caller; no cross-tenant state leaked

The most common mistake is stopping at the API layer — validating tenant at API Gateway but forgetting to scope the DynamoDB query, S3 prefix, or Knowledge Base retrieval. Bedrock AgentCore exposes session metadata as a first-class concept: attach tenant_id once and it propagates to every tool call, memory write, and observability event. The Lambda authorizer that extracts tenant_id from the JWT:

Session metadata matters because it removes the temptation to pass tenant_id as an ordinary function argument that a future refactor could quietly drop. Once tenant_id lives on the session object, every downstream component — the tool executor, the memory writer, the trace exporter — reads it from the same place, so there is exactly one code path to audit instead of dozens of call sites that each need to remember to forward the value correctly.

import json, jwt  # PyJWT
TENANT_CLAIM = "custom:tenant_id"
JWT_AUDIENCE = "agent-api"
def lambda_handler(event, context):
    auth_header = event["headers"].get("authorization", "")
    token = auth_header.replace("Bearer ", "")
    try:
        # Verify signature + audience against Cognito JWKS
        decoded = jwt.decode(
            token,
            algorithms=["RS256"],
            audience=JWT_AUDIENCE,
            jwks_url="https://cognito-idp.us-east-1.amazonaws.com/"
                     + os.environ["USER_POOL_ID"] + "/.well-known/jwks.json",
        )
        tenant_id = decoded[TENANT_CLAIM]
        tier = decoded.get("custom:tier", "free")
    except jwt.PyJWTError as e:
        return {"principalId": "denied", "policyDocument": deny_policy()}
    # Allow + propagate tenant_id / tier to integration via context
    return {
        "principalId": tenant_id,
        "policyDocument": allow_policy(event["methodArn"]),
        "context": {"tenant_id": tenant_id, "tier": tier},
    }
Tenant context resolved at the perimeter, enforced at each interaction.

Tenant Onboarding and Lifecycle Management

Multi-tenant agents need a control plane separate from the application plane: the control plane handles onboarding, tiering changes, and offboarding; the application plane serves real-time requests. They communicate through a shared tenant registry (typically DynamoDB global tables) and use different IAM roles so a control-plane bug cannot serve tenant traffic.

A reference onboarding flow on Bedrock AgentCore:

import boto3
control_plane = boto3.client('events')  # EventBridge for lifecycle events
agentcore = boto3.client('bedrock-agentcore')
def onboard_tenant(tenant_id: str, tier: str, admin_email: str):
    # 1. Create tenant-scoped IAM role
    role_arn = iam.create_role(
        RoleName=f"agent-tenant-{tenant_id}",
        AssumeRolePolicyDocument=trust_policy_for_agentcore(),
        Description=f"Tenant {tenant_id} ({tier})",
    )['Role']['Arn']
    # 2. Provision tenant-scoped memory in AgentCore
    memory_id = agentcore.create_memory(
        name=f"mem-{tenant_id}",
        strategy='semantic',
        encryptionKeyArn=tenant_kms_key(tenant_id),
    )['memoryId']
    # 3. Register tenant in control-plane registry
    dynamodb.put_item(
        TableName='Tenants',
        Item={'tenant_id': {'S': tenant_id},
              'tier': {'S': tier},
              'role_arn': {'S': role_arn},
              'memory_id': {'S': memory_id},
              'status': {'S': 'ACTIVE'}},
    )
    # 4. Emit lifecycle event for downstream systems (billing, analytics)
    control_plane.put_events(Entries=[{
        'Source': 'agent.control-plane',
        'DetailType': 'TenantOnboarded',
        'Detail': json.dumps({'tenant_id': tenant_id, 'tier': tier}),
    }])
    return {'tenant_id': tenant_id, 'role_arn': role_arn, 'memory_id': memory_id}

Every resource is tenant-scoped by name (IAM role, memory ID, registry record), so cost attribution, audit, and offboarding become trivial prefix-matching operations.

The onboarding function above also illustrates why the control plane and application plane need separate IAM roles: the control plane needs iam:CreateRole and bedrock-agentcore:CreateMemory permissions to provision new tenants, but the application plane that actually serves chat requests should never hold those permissions — a compromised or buggy request handler with IAM-creation rights is a far bigger blast radius than one that can only read a tenant’s own memory and DynamoDB rows.

The control plane orchestrates tenant onboarding atomically.

Isolation, Security, and Data Ownership

A single cross-tenant leak is market-ending. AWS recommends a layered model. Identity: every tenant gets its own IAM role. Bedrock AgentCore‘s per-tenant session tokens stop even a misconfigured tool at the IAM boundary. Data: DynamoDB composite keys with tenant_id, S3 prefixes include tenant_id, Knowledge Bases per-tenant or partitioned. Compute: AgentCore’s microVM runtime gives each session a hardened, ephemeral environment.

Regulated workloads add per-tenant KMS customer-managed keys: each tenant’s data is encrypted with a key only that tenant can authorize. The MCP protocol adds another surface — a pooled agent calling an MCP server must pass tenant-scoped IAM credentials explicitly, never trusted to defaults.

Per-tenant KMS keys also give you a clean offboarding lever: scheduling a key for deletion cryptographically shreds every object encrypted under it, even if a stray copy of the data survives somewhere in a backup or log you forgot to purge. That is a much stronger guarantee than relying on a delete script to find and remove every row, because the guarantee holds even against your own mistakes.

An MCP client passes tenant-scoped IAM credentials to the MCP server.

Cost Attribution and Resource Management

Every Bedrock inference, AgentCore session minute, Lambda invocation, and DynamoDB read must attribute to a tenant. The cleanest pattern is tag-based attribution: tag every control-plane resource with Tenant={tenant_id} at creation, and Cost Explorer breaks spend per tenant automatically. For per-request costs (tokens, session minutes), the agent emits a telemetry event with tenant_id.

The noisy neighbor problem — one tenant’s burst saturating model concurrency or DynamoDB capacity — is the dark side of pooled. The fix is per-tenant token-bucket throttling at API Gateway or AgentCore Gateway. Tiered resource allocation (smaller model and shorter memory retention for free tier; larger model and dedicated capacity for premium) is config in the control-plane registry.

Token-bucket throttling works well here because it tolerates short bursts — a tenant running a legitimate batch job for a few seconds — while still capping the sustained rate that would otherwise starve every other tenant sharing the same model endpoint. Setting the bucket size and refill rate per tier, rather than globally, means a premium tenant’s burst allowance does not have to be sized down to protect free-tier capacity.

Tenant Tiering and Pricing Strategy

Three pricing models dominate multi-tenant agents in 2026. Subscription (flat fee with tiered feature gates) is simplest but risks heavy users eroding margin. Usage-based (per invocation, per thousand tokens, per task) aligns revenue to cost but risks bill shock — mitigate with per-tenant spending ceilings.

Outcome-based (pay only when the agent delivers a measurable result) is boldest and most aligned with the agent’s value, but requires strong attribution connecting agent actions to business metrics. Most products start with subscription or usage-based and migrate to outcome-based once attribution matures.

The practical reason most teams delay outcome-based pricing is that it needs a causal link between a specific agent action and a business result — a resolved support ticket, a closed deal — and that link is often ambiguous when a human also touched the workflow. Usage-based pricing sidesteps the ambiguity by billing on a countable signal (tokens, invocations) that the agent platform already measures precisely.

Observability for Multi-Tenant Agents

Aggregate metrics hide problems — p95 looks fine while one tenant suffers a 10x regression. Tag every observability event with tenant_id. AWS X-Ray supports annotation-based filtering; Bedrock AgentCore emits per-session traces with tenant metadata automatically. Three metrics matter: error rate (signals data issues), token efficiency (drops signal prompt drift), and guardrail intervention rate (high rates suggest tier upgrade or policy review).

Annotation-based filtering in X-Ray means you can slice a trace query down to a single tenant_id and replay exactly what that tenant experienced, which turns a vague support ticket like “the agent is slow for us” into a concrete trace you can inspect end to end. Without tenant tagging on every span, the same investigation means grepping through shared logs hoping the right request stands out.

Migrating Between Deployment Models

Most systems do not start in their final topology. A common trajectory: siloed for the first 10 enterprise customers, hit a cost ceiling around 50 tenants, migrate to hybrid, then consolidate to pooled-with-strong-isolation once per-tenant memory primitives mature.

Siloed to pooled is highest-risk because isolation shifts from physical to logical. Audit every tool for tenant scoping, every IAM role for least privilege, and write regression tests that attempt cross-tenant access. Ramp behind a feature flag (5% → two weeks → 100%).

Pooled to siloed is mechanically simple but expensive if frequent — automate it. Siloed to hybrid requires the control plane to track each tenant’s deployment model. Pick the topology you expect at 24 months and build toward it; stability beats theoretical optimality.

The feature-flag ramp matters more than it sounds: migrating a tenant from siloed to pooled at 100% on day one means any isolation gap surfaces in production against a real customer immediately. Ramping through 5%, then a subset of low-risk tenants for two weeks, then everyone, gives the chaos tests and monitoring dashboards time to catch a leak before it reaches a tenant who would notice and escalate.

Compliance and Audit for Multi-Tenant Agents

SOC 2, HIPAA, ISO 27001, FedRAMP — the answer depends on the audit trail. Three artifacts matter: tenant-scoped access logs (CloudTrail and AgentCore traces tagged with tenant_id), isolation test reports (weekly chaos tests attempting cross-tenant access), and tenant offboarding certificates (signed records of memory deletion, IAM revocation, KMS destruction).

CloudTrail inherits tenant tags; AgentCore emits per-session traces with tenant metadata; Bedrock Guardrails records every intervention. Build the audit pipeline as part of the control plane from day one — produce artifacts continuously, not on demand.

Building the audit pipeline early also avoids a familiar failure mode: an auditor asks for six months of tenant-scoped access logs, and the team discovers CloudTrail tagging was only turned on three weeks ago. Continuous artifact generation costs almost nothing at write time — it is a tag and a log line — but retrofitting it after the fact means the gap in history simply cannot be recovered.

Multi-Tenant Agents in Practice: A Worked Example

A SaaS “AI Customer Success Agent” for mid-market B2B. Each tenant has its own CRM, churn playbook, and chat branding. The company chooses hybrid: free-tier tenants share a pooled Bedrock AgentCore agent (Strands supervisor + three MCP tools); premium tenants get a siloed agent with Claude Sonnet, per-tenant memory, and tighter IAM. API Gateway routes by JWT tier.

from strands import Agent
from strands_tools import http_request
import boto3, os
# Bedrock AgentCore session carries tenant_id implicitly via session metadata
def build_agent(tenant_id: str, tier: str) -> Agent:
    model = "anthropic.claude-3-5-sonnet" if tier == "premium" else "anthropic.claude-3-5-haiku"
    system_prompt = f"""You are the Customer Success Agent for tenant {tenant_id}. Always scope CRM queries with tenant_id='{tenant_id}'. Never reveal data from other tenants.
Cite CRM record IDs in every answer."""
    return Agent(
        model=model,
        tools=[http_request, crm_lookup_tool(tenant_id), playbook_search_tool(tenant_id)],
        system_prompt=system_prompt,
    )
# Pooled mode: build once per request from cached agent instances by (tenant_id, tier)
# Siloed mode: long-running dedicated instance per tenant
agent = agent_pool.get_or_build(tenant_id, tier)
result = agent(customer_question)

The system prompt carries the tenant identifier; the CRM lookup tool receives tenant_id as a closure so it cannot query another tenant’s data. Success is measured by per-tenant margin, p95 latency by tier, cross-tenant leak incidents (target: zero), and onboarding time (under 5 minutes).

Closing over tenant_id in the tool constructor rather than reading it from the prompt text is the important design detail here: even if a malicious or careless prompt tried to ask the agent to look up a different tenant’s account, the tool itself has no code path to honor that request because the identifier it queries with was fixed when the tool was built, not parsed from the model’s output.

Multi-Tenant Agents: Common Mistakes to Avoid

Most cross-tenant incidents trace back to one of a handful of repeatable mistakes rather than an exotic new failure mode. Reviewing a pooled agent design against this list before launch catches the majority of isolation gaps a chaos test would otherwise have to find the hard way.


Multi-Tenant Agents: Best Practices

These practices are the operational checklist that follows from everything above: they are not new ideas so much as the concrete, repeatable version of the isolation, context-propagation, and attribution principles this guide has walked through. Treat them as the minimum bar for a production multi-tenant agent, not an aspirational list.


Multi-Tenant Agents: Frequently Asked Questions

When should I choose siloed over pooled?

Choose siloed for enterprise or regulated tenants, when customization is a differentiator, or when tenant count is small (under 50). Pooled wins above a few hundred tenants and for free tiers. If you are unsure, default to pooled and carve out siloed exceptions for the specific tenants whose contracts demand it, rather than starting siloed for everyone and paying the operational cost of migrating later.

How does Bedrock AgentCore support multi-tenancy?

Per-tenant session metadata, per-tenant memory, a gateway enforcing per-tenant rate limits, and MCP tool credentials passing tenant-scoped IAM roles. One deployment serves many tenants with strong isolation. The session metadata is the connective piece — because it travels with the invocation automatically, memory, tools, and observability all inherit the same tenant scope without extra plumbing in application code.

What is the noisy neighbor problem in multi-tenant agents?

One tenant’s bursty requests saturate shared capacity — model concurrency, DynamoDB read units, AgentCore session limits — degrading latency for others. Fix: per-tenant throttling at API Gateway or AgentCore Gateway. The problem is specific to pooled deployments; a siloed tenant can only ever exhaust their own dedicated capacity, which is exactly why regulated or usage-spiky tenants often justify the siloed cost premium.

Can multi-tenant agents be HIPAA-compliant?

Yes, with per-tenant KMS keys, per-tenant AgentCore Memory, tenant-scoped IAM roles, and BAAs with AWS for HIPAA-eligible services (Bedrock, AgentCore, S3, DynamoDB). The isolation and audit-trail requirements described throughout this guide are not extra work bolted on for HIPAA — they are the same controls a well-run multi-tenant agent needs regardless of vertical, just enforced without exception.

How do I attribute Bedrock token cost to a specific tenant?

Tag Bedrock invocations at the API layer with tenant_id (as request metadata), then aggregate per-request token counts in CloudWatch or a billing pipeline. Cost Explorer shows tag breakdowns natively. For usage-based billing, emit the token count and tenant_id as a structured event at invocation time rather than trying to reconstruct cost later from aggregate logs, since per-request granularity is what a billing dispute ultimately requires.

/0

AWS Lesson 4 Quiz: Multi-Tenant Agentic AI

Test your understanding of siloed vs pooled multi-tenant agent deployment, tenant context injection, isolation, and noisy-neighbor policies.

Add at least one question to start

Your score is

0%

Multi-Tenant Agents: Key Takeaways

Multi-tenant agents on AWS give SaaS unit economics with per-tenant isolation. Choose hybrid by default, propagate tenant context through every layer, lean on AgentCore’s per-tenant primitives. Multi-tenant agentic AI on AWS succeeds when tenant isolation is enforced at IAM, AgentCore Memory, and KMS — not only at the application layer. Every multi-tenant design choice — pooled or siloed, shared or dedicated AgentCore — flows from the compliance tier and cost ceiling the tenant contract demands.

Continue Learning

Exit mobile version