Skip to content

Deploying AI Agent Runtimes in DuploCloud Multi-Tenant Environments

How auto-provisioning Infrastructure-as-Code collides with DuploCloud's prefix-enforced tenancy — and the patterns that bridge the two without weakening compliance isolation.

Author: Ross RasmussenCreated: 2026-03-27

🔬 Scope — this is provider- and tool-agnostic on purpose. The motivating workload is a managed AI-agent runtime (a containerized agent with its own artifact store, registry, execution roles, log groups, and optional VPC attachment), but every friction point below applies to any IaC toolkit that auto-generates AWS resources with globally-unique names. DuploCloud is fixed — it's our platform — while the IaC layer is treated as a variable; Pulumi and SST get an honorable mention as the tools we expect to standardize on.

TL;DR

The collision

Auto-naming vs. prefix-enforcement

Agent-runtime toolkits expect broad permissions to mint generically-named resources. DuploCloud denies anything missing the duploservices-<tenant>- prefix. Out-of-the-box deploys fail at the IAM boundary.

The root cause

Two correct designs, opposite assumptions

It's not a bug in either system. Declarative IaC assumes unbounded permissions; prescriptive platform engineering enforces namespace + IAM isolation. The friction is structural.

The fix

Pre-provision, then point the toolkit at it

Stop letting the toolkit create primitives. Provision compliant resources via DuploCloud-aware IaC, then pass their ARNs in. Scale up to name-rewriting hooks and a hybrid pipeline as complexity grows.

Question & scope

We want to run managed AI-agent runtimes inside our DuploCloud-governed AWS accounts. Most vendor and open-source agent toolkits ship a one-command "launch" experience that quietly provisions a fleet of supporting AWS resources — an artifact bucket, a container registry, build and execution IAM roles, log groups, and (for private data access) VPC networking.

The question this doc answers: why do those one-command deploys fail inside a DuploCloud tenant, and what are the engineering patterns to make them succeed without punching holes in the tenancy model? The analysis holds regardless of which agent runtime or which IaC engine (CDK, Terraform, Pulumi, SST, raw CloudFormation) generates the resources.

Background

What a managed agent runtime provisions

Independent of vendor, a "launch"-style agent toolkit walks a predictable provisioning lifecycle. It abstracts the AWS plumbing behind a single command, which is exactly why it assumes broad account permissions:

  1. Config parse — reads local project config (entrypoint, runtime version, memory/state strategy, target region).
  2. Resource synthesis — an IaC layer (CDK, Pulumi, SST, Terraform, or a hand-rolled CloudFormation emitter) maps the logical agent to physical AWS resources.
  3. Build pipeline — source + dependencies are uploaded to an auto-named S3 bucket; a build service compiles a (commonly ARM64) container image.
  4. Registry push — the image lands in a newly created ECR repository.
  5. IAM role synthesis — least-privilege execution and build roles are minted automatically.
  6. Endpoint instantiation — the runtime endpoint mounts the image, attaches the role, and wires up CloudWatch log groups for tracing/observability.

Every bolded artifact is created with a tool-chosen, globally-unique name. That single assumption — the deploying principal may freely create generically-named buckets, repos, roles, and log groups — is the catalyst for all the friction below. [1][2]

DuploCloud's tenancy model

DuploCloud is a DevOps-as-a-Service platform that enforces security, compliance, and multi-tenant isolation by default, rather than leaving it to per-resource hand-authoring. [3][4] Two concepts matter here:

  • Infrastructure ≈ a VPC (routing, subnets, NAT across AZs). [5]
  • Tenant (a.k.a. Project) — the fundamental isolation boundary. A tenant maps to a dedicated Kubernetes namespace (duploservices-<tenant>), gets auto-generated default-deny security groups, and assigns workloads an auto-generated instance profile that is the IAM boundary — no long-lived keys, no hand-written JSON policies. [5][6]
mermaid
flowchart TB
    subgraph Infra["Infrastructure (VPC)"]
      subgraph TA["Tenant A"]
        WA["Workloads<br/>ns: duploservices-tenant-a"]
        RA["Resources<br/>duploservices-tenant-a-*"]
      end
      subgraph TB2["Tenant B"]
        WB["Workloads<br/>ns: duploservices-tenant-b"]
        RB["Resources<br/>duploservices-tenant-b-*"]
      end
    end
    WA -. "default-deny" .-x WB
    style TA fill:#e6f3ff,color:#000
    style TB2 fill:#f3e6ff,color:#000

The cryptographic linchpin: resource prefixes

Nearly every AWS resource a tenant provisions — S3, SQS, KMS, ECR, IAM roles — must be prefixed duploservices-<tenant>-. This is not a naming convention; it's the enforcement mechanism. DuploCloud's permission boundaries and resource-based policies use IAM condition keys to deny any action whose target ARN lacks the prefix. [5][6]

json
{
  "Effect": "Deny",
  "Action": ["s3:CreateBucket"],
  "Resource": "*",
  "Condition": {
    "StringNotLike": {
      "s3:BucketName": "duploservices-<tenant>-*"
    }
  }
}

Because enforcement is string-pattern-matching, tenant names must be globally unique and cannot be substrings of one another (a tenant dev blocks dev2) to avoid regex collisions during policy evaluation. [5]

Just-in-time access closes the last loophole

Developers rarely hold persistent console or programmatic access. They operate through the DuploCloud portal, its Terraform provider, or JIT credential federation — short-lived STS credentials bound by the tenant's iam:PermissionsBoundary. [7] The practical consequence: a launch command run from a developer laptop is constrained by the exact same boundary as the in-cloud workloads. There is no "it works locally" escape hatch.

Findings

When an auto-provisioning agent toolkit meets a DuploCloud tenant, the failures are immediate and systemic. Toggle between the friction inventory and the governance rules it violates:

1. S3 & ECR naming collisions. The toolkit synthesizes an artifact bucket (agent-code-<account>-<region>) and a registry (agent-<name>). JIT credentials are bounded by the tenant policy, so s3:CreateBucket and ecr:CreateRepository return AccessDeniedException — the requested names lack the duploservices-<tenant>- prefix. [8]

2. IAM role synthesis vs. permission boundaries. Auto-generated build and execution roles are an IaC best practice in an unmanaged account, but DuploCloud restricts iam:CreateRole and requires every new role to (a) carry the tenant prefix and (b) attach a specific managed iam:PermissionsBoundary. Stock synthesizers append neither, so the stack rolls back on creation. [8]

3. Log-group routing. Agent runtimes hardcode CloudWatch log paths (e.g. /aws/vendedlogs/... or a runtime-specific prefix). DuploCloud centralizes ingestion (OpenSearch + Filebeat, Wazuh SIEM) keyed on its own prefixes (duplo-<infra>-awslogs-<account>). Logs may write successfully yet never reach the tenant's dashboard — a fractured observability story. [9]

4. Account-level service-linked roles. Agents needing private data (RDS, internal APIs) attach to a VPC, which depends on a service-linked role to manage ENIs. SLRs are immutable, account-level constructs, and tenant admins lack iam:CreateServiceLinkedRole. The deploy blocks unless an account administrator pre-provisions the SLR. [10]

Friction summary

Toolkit default behaviorDuploCloud requirementResulting failure
Auto-names artifact S3 bucketduploservices-<tenant>- prefix requiredAccessDenied on s3:CreateBucket
Auto-names ECR repositoryduploservices-<tenant>- prefix requiredAccessDenied on ecr:CreateRepository
Synthesizes generic IAM rolesiam:CreateRole restricted; boundary requiredStack rollback on role creation
Hardcodes CloudWatch log pathsSIEM expects duplo-<infra>-awslogs-*Broken observability; logs unrouted
Mints a network service-linked roleTenant lacks iam:CreateServiceLinkedRoleDeploy blocked on VPC attach

Options & tradeoffs

Resolving the friction means abandoning the "black box" one-command deploy. Three strategies, in increasing order of complexity and robustness — they compose; most teams land on a blend.

① Pre-provision + override

Provision compliant resources via DuploCloud-aware IaC, then pass their ARNs to the toolkit so it skips its own creation logic. Least invasive; right for simple agents.

② Name-rewriting hooks

Keep the toolkit's IaC but intercept synthesis to inject the tenant prefix on every generated resource. Right when you manage the IaC program directly.

③ Hybrid pipeline

CI orchestrates compliant foundation (IaC) → extracts ARNs → drives the agent deploy headlessly. Right for multi-account, audited, enterprise scale.

Strategy ① — Pre-provision compliant resources, override the toolkit

Bypass the toolkit's resource creation entirely. Most launch-style CLIs (and the IaC programs underneath them) accept overrides for the execution role, build role, registry, and artifact bucket. Provision those up front through DuploCloud's portal/Terraform provider — which inherently applies the duploservices-<tenant>- prefix — then point the toolkit at the ARNs. [5]

Phase A — foundation (DuploCloud-aware IaC). Create the artifact bucket, the registry, and an execution role attached to (or derived from) the tenant instance profile, with the required permission boundary. DuploCloud prepends the prefix automatically:

hcl
resource "duplocloud_s3_bucket" "agent_artifacts" {
  tenant_id = duplocloud_tenant.dev.tenant_id
  name      = "agent-artifacts"   # becomes duploservices-dev-agent-artifacts
}

Phase B — point the toolkit at it. Invoke the agent deploy with explicit ARNs so its synthesizer skips the role/registry/bucket creation steps:

bash
agent-deploy configure \
  --name           duploservices-dev-support-agent \
  --execution-role arn:aws:iam::123456789012:role/duploservices-dev-agent-role \
  --build-role     arn:aws:iam::123456789012:role/duploservices-dev-build-role \
  --ecr            duploservices-dev-agent-repo \
  --artifact-bucket duploservices-dev-agent-artifacts

With creation suppressed, the deploy compiles and ships the agent data plane without ever tripping the IAM boundary. The exact flag names differ per toolkit; the principle — own the primitives, lend them to the toolkit — is universal.

Strategy ② — Rewrite generated names at synthesis time

For complex agents (multiple gateways, custom evaluators, VPC-attached tools), teams often manage the IaC program directly rather than through the CLI wrapper. That re-exposes the auto-naming problem — and hardcoding a prefix into every resource declaration is brittle and misses nested resources.

The durable fix is a synthesis-time transform that injects the duploservices-<tenant>- prefix into the physical name of every generated resource, centrally, just before the template is emitted. Each IaC engine has its own hook for this:

  • AWS CDK — an Aspect (the Visitor pattern) that visits every L1 construct and rewrites RoleName/BucketName/etc. via addPropertyOverride. Target L1 nodes specifically; L2 proxies don't always reflect post-instantiation edits. [11][13]
  • Pulumi — a stack transformation (registerStackTransformation) that mutates each resource's name/props as it's registered, or a global auto-naming convention. Pulumi's transforms are the cleanest fit here because they're first-class and run on every resource. [12]
  • SST / CDK-based stacks — SST sits on CDK, so the same Aspect mechanism applies to the underlying stack; SST's own component props can seed the prefix at the construct level. [14]
  • Terraform — there is no AST hook; the equivalent is a naming module + name_prefix arguments + a policy-as-code gate (OPA/Sentinel/tflint) that fails the plan if any resource name lacks the prefix.

Watch the 64-character IAM role-name limit: the transform must truncate the generated logical id before prepending the prefix, or deployment fails AWS-side validation. [13]

Pulumi / SST honorable mention. We expect to standardize on Pulumi and/or SST for new infrastructure. Both express the prefix rule as ordinary program code — a Pulumi stack transformation or an SST construct default — rather than the escape-hatch overrides CDK requires. That makes the duploservices-<tenant>- invariant a reusable library function instead of per-stack boilerplate, which is the main reason to prefer them here.

Strategy ③ — Hybrid CI/CD pipeline

For audited, multi-account scale, combine the strengths: IaC owns compliant foundation; the agent toolkit owns the rapidly-iterating data plane. A CI runner assumes DuploCloud JIT credentials via OIDC, then:

mermaid
flowchart LR
    A["IaC apply<br/>(tenant, bucket, roles, ECR)"] --> B["Extract ARNs<br/>from stack outputs"]
    B --> C["Inject ARNs into<br/>agent deploy config"]
    C --> D["Headless agent deploy<br/>--non-interactive"]
    style A fill:#e6f3ff,color:#000
    style D fill:#e6ffe6,color:#000
  1. Foundationpulumi up / terraform apply provisions the prefixed tenant resources and boundary-bound roles.
  2. Output extraction — the runner reads role ARNs and registry/bucket names from stack outputs.
  3. Config injection — those values are passed to the agent deploy via override flags (or by templating its config file with jq).
  4. Headless deploy — the agent toolkit runs non-interactively, compiling and shipping the runtime against the injected, compliant infrastructure.

This decouples the governance lifecycle (slow, audited, IaC-managed) from the application lifecycle (fast, iterative, toolkit-managed) — each moves at its own cadence without violating the boundary.

Advanced friction: networking, observability, identity

Solving naming + IAM unblocks the deploy. Three secondary frictions remain for production-grade agents:

  • VPC / networking. Retrieve the tenant's auto-generated, default-deny security-group IDs (Terraform data source or portal API) and pass them explicitly to the agent's network config so its ENIs inherit the tenant posture. Still requires the account-level network service-linked role to be pre-provisioned by an administrator. [10]
  • Observability. Instrument the agent with the AWS Distro for OpenTelemetry and point the exporter at log groups matching DuploCloud's expected prefixes, rather than accepting the toolkit's hardcoded paths — so traces and tool-call latencies flow into the tenant's OpenSearch/Wazuh dashboards. [9]
  • Identity / OAuth. Pre-provision any Cognito user pools / app clients with the duploservices- prefix, then pass the pool + client IDs into the agent's JWT authorizer config instead of letting the toolkit mint generic identity resources (which the naming boundary would reject anyway).

Recommendation

Start with Strategy ① for the first agent, design for Strategy ③. Pre-provision a compliant artifact bucket, registry, and boundary-bound roles via DuploCloud-aware IaC, and pass their ARNs into the agent toolkit — this gets a working deploy fastest and proves the boundary interactions end to end.

Encode the duploservices-<tenant>- prefix as a reusable IaC primitive, not per-stack boilerplate. This is the strongest argument for Pulumi or SST over raw CDK: a stack transformation (Pulumi) or a construct default (SST) makes the prefix invariant a one-line library call, so Strategy ② stops being bespoke per agent. As the number of agents grows, fold ① + ② into a Strategy ③ hybrid pipeline so governance and application lifecycles can move independently.

Pre-provision account-level prerequisites once. The network service-linked role and the SIEM-compatible log-group prefixes are account-wide concerns — have an administrator establish them before the first VPC-attached agent, so tenant deploys never block on them.

Risks & open questions

HighToolkit drift breaks overrides

A new toolkit version may add a resource we don't override, reintroducing an auto-named primitive that the boundary rejects.

Mitigation: pin toolkit versions; gate deploys with a policy-as-code check that fails on any non-prefixed resource in the plan.

MedSilent observability gaps

Logs can write successfully to a non-routed group, so a deploy looks healthy while the SIEM sees nothing.

Mitigation: assert the expected duplo-<infra>-awslogs-* group exists and receives events as a post-deploy smoke check.

MedRole-name truncation collisions

Truncating long logical ids to fit the 64-char IAM limit (after prefixing) can produce duplicate role names within a tenant.

Mitigation: append a short deterministic hash suffix during truncation.

LowSLR provisioning lag

The network service-linked role is an account-level prerequisite outside tenant control; a missing SLR blocks the first VPC-attached agent.

Mitigation: provision it once as part of account bootstrap; document it in the infra runbook.

Open questions

Pulumi or SST — or both? SST's higher-level runtime components may cover more of the lifecycle out of the box, while Pulumi gives finer transform control. Which becomes the default, and where do they coexist?

Where does the prefix-injection library live? A shared Pulumi/SST package consumed by every agent stack, vs. copied per repo. Ownership and versioning need a decision before the second agent ships.

Do we want a thin internal wrapper around the agent toolkit? A small CLI that bakes in the DuploCloud overrides + prefix transform would make every team's deploy compliant-by-default — at the cost of another internal tool to maintain. Worth it once we have 3+ agents?

Sources

  1. Provisioning Secure and Compliant Applications on AWS with DevSecOps and DuploCloud — AWS Partner Network blog, https://aws.amazon.com/blogs/apn/provisioning-secure-and-compliant-applications-on-aws-with-devsecops-and-duplocloud/
  2. Optimize multi-account serverless deployments using IaC and CI workflows — AWS Prescriptive Guidance, https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/optimize-multi-account-serverless-deployments.html
  3. Platform Engineering for Cloud Deployment and Operations — DuploCloud, https://duplocloud.com/white-papers/platform-engineering/
  4. How the DuploCloud platform allows customers to build SaaS on AWS — AWS APN blog, https://aws.amazon.com/blogs/apn/how-the-duplocloud-platform-allows-customers-to-build-saas-on-aws/
  5. Tenant architecture — DuploCloud Docs, https://github.com/duplocloud/docs/blob/main/automation-platform/application-focused-interface-duplocloud-architecture/tenant.md
  6. IAM — DuploCloud Documentation, https://docs.duplocloud.com/docs/automation-platform/security-and-compliance/access-control/iam
  7. AWS FAQ (JIT access) — DuploCloud Documentation, https://docs.duplocloud.com/docs/automation-platform/overview/aws-faq
  8. Permissions boundaries for IAM entities — AWS IAM User Guide, https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html
  9. Central Logging — DuploCloud Documentation, https://docs.duplocloud.com/docs/automation-platform/overview/use-cases/central-logging
  10. Resource: duplocloud_tenant — Terraform Registry, https://registry.terraform.io/providers/duplocloud/duplocloud/latest/docs/resources/tenant
  11. Aspects and the AWS CDK — AWS CDK Developer Guide, https://docs.aws.amazon.com/cdk/v2/guide/aspects.html
  12. Stack transformations — Pulumi Documentation, https://www.pulumi.com/docs/concepts/options/transformations/
  13. Customize default role names using CDK aspects and escape hatches — AWS Prescriptive Guidance, https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/customize-default-role-names-by-using-aws-cdk-aspects-and-escape-hatches.html
  14. Infrastructure as Code with SST — SST Documentation, https://sst.dev/docs/

Internal documentation — gated behind Cloudflare Access.