awslabs / awslabs/loom

Enable multi-account deployments

Open
#16 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Python
Stars
183
Forks
41
Avg merge
8h 6m
Merged PRs (30d)
2

Description

## Overview

Add the ability to deploy agents across AWS accounts with Loom operating in a shared services account. Today, Loom deploys agents exclusively into the account where the backend runs — `AWS_ACCOUNT_ID` is a single environment variable, boto3 clients use the default credential chain, and IAM roles are created in the deployment account only. This issue introduces a deployment target model where admins register additional accounts and users select them when deploying agents.

Cross-account deployments require an STS AssumeRole flow: Loom's backend assumes a pre-provisioned role in the target account to create IAM execution roles, upload artifacts, manage secrets, and call AgentCore APIs. This issue includes the cross-account role setup to simplify enablement for new accounts.

## Requirements

### R1: Account Registration and Management

Admins should be able to register additional AWS accounts as deployment targets. Each account registration includes:

- **Account ID** (required): The 12-digit AWS account ID.
- **Display name** (required): A human-readable name (e.g., "Production - Payments").
- **Line of business** (required): Organizational grouping (e.g., "Payments", "Risk", "Platform").
- **Environment** (optional): Deployment tier label (e.g., "dev", "staging", "production").
- **Cross-account role ARN** (required): The IAM role in the target account that Loom will assume for deployments (`arn:aws:iam::{account_id}:role/loom-deployment-role`).
- **External ID** (required): A unique identifier for the cross-account trust relationship to prevent confused deputy attacks.
- **Supported regions** (required): List of AWS regions where AgentCore deployments are allowed in this account.
- **Enabled flag**: Whether this account is currently available as a deployment target.

Admins should be able to list, update, and deactivate registered accounts. Deactivating an account should not affect existing deployed agents — it only prevents new deployments to that account.

The Loom backend's own account should be automatically available as a deployment target (the "local" account) without requiring explicit registration.

**Backend changes:**
- New model: `backend/app/models/account.py` — `DeploymentAccount` table with the fields above.
- New router: `backend/app/routers/accounts.py` — CRUD endpoints for account management (`POST /api/accounts`, `GET /api/accounts`, `PATCH /api/accounts/{id}`, `DELETE /api/accounts/{id}`).
- Admin-only access: Account management endpoints should be restricted to admin users.

### R2: Deployment Target Selection

Users should be able to select a target account when deploying an agent (both custom agent and managed agent harness flows). The selection should support browsing by metadata — not just raw account IDs:

- **Account selector**: Dropdown or searchable list showing display name, line of business, environment, and account ID.
- **Filtering**: Filter accounts by line of business and environment.
- **Region selector**: After selecting an account, show only the regions that account supports.
- **Default behavior**: If no target account is selected, deploy to the local account (current behavior).

The selected target account ID and region should be stored on the agent record and used throughout the deployment lifecycle (role creation, artifact upload, AgentCore API calls, invocation).

**Frontend changes:**
- `frontend/src/components/AgentRegistrationForm.tsx` — Add deployment target section above existing fields for both "Custom Agent" and "Managed Agent" forms.
- New component for the account selector with metadata display and filtering.
- `frontend/src/api/accounts.ts` — API client for account CRUD endpoints.

**Backend changes:**
- `backend/app/models/agent.py` — Add `target_account_id` field (nullable; null means local account). Add `cross_account_role_arn` field to store the role used for this deployment.
- `backend/app/routers/agents.py` — Accept `target_account_id` in deploy requests. Look up the `DeploymentAccount` record to resolve the cross-account role ARN, external ID, and validate the requested region is supported.

### R3: Cross-Account Deployment Execution

When deploying to a non-local account, the backend must assume the target account's cross-account role before performing deployment operations. This affects the following operations:

1. **IAM role creation** (`backend/app/services/iam.py`): Create the agent's execution role in the target account using assumed credentials.
2. **Artifact upload** (`backend/app/services/deployment.py`): Upload the agent zip to an S3 bucket in the target account. The target account must have a designated artifact bucket (stored in `DeploymentAccount` or discoverable via convention, e.g., `loom-artifacts-{account_id}`).
3. **Secret management** (`backend/app/services/deployment.py`): Store agent secrets in the target account's Secrets Manager.
4. **AgentCore API calls** (`backend/app/services/deployment.py`, `backend/app/services/agentcore.py`): Call `bedrock-agentcore-control` and `bedrock-agentcore` APIs in the target account/region.
5. **Agent invocation** (`backend/app/routers/agents.py`): When invoking a cross-account agent, assume the role to call `invoke_agent_runtime` or `invoke_harness` in the target account.

**Implementation approach:**
- New utility: `backend/app/services/sts.py` — STS AssumeRole helper that accepts a role ARN and external ID, returns a boto3 `Session` with temporary credentials. Handle credential caching and expiration (STS tokens are valid for up to 1 hour).
- Modify all boto3 client construction in deployment and invocation paths to accept an optional `session` parameter. When a session is provided (cross-account), use it instead of the default credential chain.
- The cross-account role assumption should happen at the start of the deployment background task and the assumed session should be passed through to all downstream operations.

### R4: Cross-Account Role Setup

Provide a CloudFormation template that target account admins can deploy to set up the required cross-account trust relationship. This template should create:

1. **Deployment role** (`loom-deployment-role`): The role that Loom's backend assumes.
- Trust policy: Allow `sts:AssumeRole` from Loom's backend account with the external ID condition.
- Permissions: IAM role creation/management, S3 artifact bucket access, Secrets Manager access, AgentCore control plane and data plane API access, CloudWatch Logs and X-Ray for observability.
2. **Artifact S3 bucket** (optional): A bucket for storing agent deployment artifacts, with a lifecycle policy to clean up old artifacts.
3. **Outputs**: Export the role ARN, external ID, and bucket name for easy registration in Loom.

**Files:**
- New template: `iac/cross-account/template.yaml` — CloudFormation template parameterized with the Loom backend account ID and external ID.
- New makefile target: `make cross-account.deploy` — Deploy the cross-account setup template to a target account.
- Documentation in the template describing the permissions granted and the setup flow.

The setup flow should be:
1. Admin deploys the CloudFormation template in the target account, providing Loom's account ID.
2. Template outputs the role ARN and external ID.
3. Admin registers the account in Loom using these values.
4. Loom validates the cross-account role assumption works before marking the account as active.

### R5: Agent Lifecycle Operations Across Accounts

All existing agent lifecycle operations must work correctly for cross-account agents:

- **Refresh** (`POST /api/agents/{id}/refresh`): Assume the target account role before calling `get_agent_runtime` or `get_harness` to fetch status.
- **Delete** (`DELETE /api/agents/{id}`): Assume the target account role before deleting the runtime/harness, endpoint, execution role, secrets, and artifacts in the target account.
- **Update/Redeploy**: If supported, assume the target account role before updating the runtime configuration.

The agent record already stores `region` and `account_id` (parsed from the ARN). For cross-account agents, look up the `DeploymentAccount` record by `target_account_id` to get the cross-account role ARN needed for these operations.

Handle the case where a `DeploymentAccount` record has been deactivated or deleted but agents still exist in that account — lifecycle operations should still work using the `cross_account_role_arn` stored on the agent record itself, not just the account registration.

## Implementation Guidance

### Database Schema

**New table: `deployment_accounts`**
```
id: UUID (primary key)
account_id: String (unique, indexed)
display_name: String
line_of_business: String (indexed)
environment: String (nullable)
cross_account_role_arn: String
external_id: String
supported_regions: JSON (list of strings)
artifact_bucket: String (nullable)
enabled: Boolean (default true)
created_at: DateTime
updated_at: DateTime
```

**Agent table additions:**
```
target_account_id: String (nullable, FK to deployment_accounts.account_id)
cross_account_role_arn: String (nullable — snapshot at deploy time for resilience)
```

### STS Session Management

```python
# backend/app/services/sts.py
def get_cross_account_session(role_arn: str, external_id: str, region: str) -> boto3.Session:
"""Assume a cross-account role and return a boto3 Session with temporary credentials."""
sts = boto3.client("sts")
response = sts.assume_role(
RoleArn=role_arn,
ExternalId=external_id,
RoleSessionName="loom-deployment",
DurationSeconds=3600,
)
credentials = response["Credentials"]
return boto3.Session(
aws_access_key_id=credentials["AccessKeyId"],
aws_secret_access_key=credentials["SecretAccessKey"],
aws_session_token=credentials["SessionToken"],
region_name=region,
)
```

### IAM Permissions for Loom Backend

The Loom backend's ECS task role (`backend/iac/ecs.yaml`) needs `sts:AssumeRole` permission scoped to the cross-account deployment roles. Add a policy that allows assuming roles matching a naming convention (e.g., `arn:aws:iam::*:role/loom-deployment-role`) or use a resource tag condition.

## Out of Scope

- Multi-region Loom backend deployment (Loom itself runs in one account/region; it deploys agents cross-account).
- Cross-account artifact sharing via S3 bucket policies (each target account has its own bucket).
- Federated identity or SSO integration for account access.
- Cost allocation or chargeback per target account.
- Automated cross-account role rotation or credential vending.

Contributor guide

Open the contributing guide

Research direction

Start by reading the existing account and agent models and routers, then trace deployment and invocation through backend/app/services/iam.py, deployment.py, agentcore.py, and the background task paths. Review iac/cross-account/template.yaml and backend/iac/ecs.yaml; done means registered targets, assumed-role deployment and lifecycle operations, target selection, and the documented setup flow work across accounts.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, python
Domain
backend, cloud, infrastructure, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.