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.
🔬 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
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.
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.
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:
- Config parse — reads local project config (entrypoint, runtime version, memory/state strategy, target region).
- Resource synthesis — an IaC layer (CDK, Pulumi, SST, Terraform, or a hand-rolled CloudFormation emitter) maps the logical agent to physical AWS resources.
- Build pipeline — source + dependencies are uploaded to an auto-named S3 bucket; a build service compiles a (commonly ARM64) container image.
- Registry push — the image lands in a newly created ECR repository.
- IAM role synthesis — least-privilege execution and build roles are minted automatically.
- 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]
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:#000The 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]
{
"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 behavior | DuploCloud requirement | Resulting failure |
|---|---|---|
| Auto-names artifact S3 bucket | duploservices-<tenant>- prefix required | AccessDenied on s3:CreateBucket |
| Auto-names ECR repository | duploservices-<tenant>- prefix required | AccessDenied on ecr:CreateRepository |
| Synthesizes generic IAM roles | iam:CreateRole restricted; boundary required | Stack rollback on role creation |
| Hardcodes CloudWatch log paths | SIEM expects duplo-<infra>-awslogs-* | Broken observability; logs unrouted |
| Mints a network service-linked role | Tenant lacks iam:CreateServiceLinkedRole | Deploy 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.
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.
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.
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:
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:
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-artifactsWith 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. viaaddPropertyOverride. Target L1 nodes specifically; L2 proxies don't always reflect post-instantiation edits. [11][13] - Pulumi — a stack transformation (
registerStackTransformation) that mutates each resource'sname/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_prefixarguments + 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:
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- Foundation —
pulumi up/terraform applyprovisions the prefixed tenant resources and boundary-bound roles. - Output extraction — the runner reads role ARNs and registry/bucket names from stack outputs.
- Config injection — those values are passed to the agent deploy via override flags (or by templating its config file with
jq). - 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
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.
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.
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.
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
- 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/
- 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
- Platform Engineering for Cloud Deployment and Operations — DuploCloud, https://duplocloud.com/white-papers/platform-engineering/
- 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/
- Tenant architecture — DuploCloud Docs, https://github.com/duplocloud/docs/blob/main/automation-platform/application-focused-interface-duplocloud-architecture/tenant.md
- IAM — DuploCloud Documentation, https://docs.duplocloud.com/docs/automation-platform/security-and-compliance/access-control/iam
- AWS FAQ (JIT access) — DuploCloud Documentation, https://docs.duplocloud.com/docs/automation-platform/overview/aws-faq
- Permissions boundaries for IAM entities — AWS IAM User Guide, https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html
- Central Logging — DuploCloud Documentation, https://docs.duplocloud.com/docs/automation-platform/overview/use-cases/central-logging
- Resource: duplocloud_tenant — Terraform Registry, https://registry.terraform.io/providers/duplocloud/duplocloud/latest/docs/resources/tenant
- Aspects and the AWS CDK — AWS CDK Developer Guide, https://docs.aws.amazon.com/cdk/v2/guide/aspects.html
- Stack transformations — Pulumi Documentation, https://www.pulumi.com/docs/concepts/options/transformations/
- 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
- Infrastructure as Code with SST — SST Documentation, https://sst.dev/docs/