Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion documentation/docs/api-reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ Options:

- `--protocol, -p TEXT`: Agent server protocol (HTTP or MCP)

- `--vpc`: Enable VPC networking mode for secure access to private resources

- `--subnets TEXT`: Comma-separated list of subnet IDs (required when --vpc is enabled)

- `--security-groups TEXT`: Comma-separated list of security group IDs (required when --vpc is enabled)

Subcommands:

- `list`: List configured agents
Expand All @@ -57,6 +63,25 @@ agentcore configure -e agent.py --region us-east-1
# 3. AWS CLI configured region
```

**VPC Networking:**

When enabled, agents run within your VPC for secure access to private resources:

- **Requirements:**
- All subnets must be in the same VPC
- Subnets must be in supported Availability Zones
- Security groups must allow required egress traffic
- Automatically creates `AWSServiceRoleForBedrockAgentCoreNetwork` service-linked role if needed

- **Validation:**
- Validates subnets belong to the same VPC
- Checks subnet availability zones are supported
- Verifies security groups exist and are properly configured

- **Network Immutability:**
- VPC configuration cannot be changed after initial deployment
- To modify network settings, create a new agent configuration

### Launch

Deploy agents to AWS or run locally.
Expand Down Expand Up @@ -122,7 +147,7 @@ Your formatted response here

### Status

Get Bedrock AgentCore status including config and runtime details.
Get Bedrock AgentCore status including config and runtime details, and VPC configuration.

```bash
agentcore status [OPTIONS]
Expand All @@ -134,6 +159,19 @@ Options:

- `--verbose, -v`: Verbose JSON output of config, agent, and endpoint status

**Status Output:**

The status command displays:
- Agent configuration details
- Runtime endpoint information
- VPC networking configuration (when enabled):
- VPC ID
- Subnet IDs and Availability Zones
- Security Group IDs
- Network mode indicator
- CloudWatch log group information
- Deployment status

## Gateway Commands

Access gateway subcommands:
Expand Down Expand Up @@ -195,6 +233,21 @@ agentcore configure --entrypoint agent_example.pt
# Configure with execution role
agentcore configure --entrypoint agent_example.py --execution-role arn:aws:iam::123456789012:role/MyRole

# Configure with VPC networking
agentcore configure \
--entrypoint agent_example.py \
--vpc \
--subnets subnet-0abc123,subnet-0def456 \
--security-groups sg-0xyz789

# Configure with VPC and custom execution role
agentcore configure \
--entrypoint agent_example.py \
--execution-role arn:aws:iam::123456789012:role/MyAgentRole \
--vpc \
--subnets subnet-0abc123,subnet-0def456,subnet-0ghi789 \
--security-groups sg-0xyz789,sg-0uvw012

# List configured agents
agentcore configure list

Expand Down
100 changes: 95 additions & 5 deletions src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,19 @@ def configure(
help="Comma-separated list of allowed request headers "
"(Authorization or X-Amzn-Bedrock-AgentCore-Runtime-Custom-*)",
),
vpc: bool = typer.Option(
False, "--vpc", help="Enable VPC networking mode (requires --subnets and --security-groups)"
),
subnets: Optional[str] = typer.Option(
None,
"--subnets",
help="Comma-separated list of subnet IDs (e.g., subnet-abc123,subnet-def456). Required with --vpc.",
),
security_groups: Optional[str] = typer.Option(
None,
"--security-groups",
help="Comma-separated list of security group IDs (e.g., sg-xyz789). Required with --vpc.",
),
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
region: Optional[str] = typer.Option(None, "--region", "-r"),
protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP or A2A)"),
Expand All @@ -277,6 +290,60 @@ def configure(
if protocol and protocol.upper() not in ["HTTP", "MCP", "A2A"]:
_handle_error("Error: --protocol must be either HTTP or MCP or A2A")

# Validate VPC configuration
vpc_subnets = None
vpc_security_groups = None

if vpc:
# VPC mode requires both subnets and security groups
if not subnets or not security_groups:
_handle_error(
"VPC mode requires both --subnets and --security-groups.\n"
"Example: agentcore configure --entrypoint my_agent.py --vpc "
"--subnets subnet-abc123,subnet-def456 --security-groups sg-xyz789"
)

# Parse and validate subnet IDs - UPDATED VALIDATION
vpc_subnets = [s.strip() for s in subnets.split(",") if s.strip()]
for subnet_id in vpc_subnets:
# Format: subnet-{8-17 hex characters}
if not subnet_id.startswith("subnet-"):
_handle_error(
f"Invalid subnet ID format: {subnet_id}\nSubnet IDs must start with 'subnet-' (e.g., subnet-abc123)"
)
# Check minimum length (subnet- + at least 8 chars)
if len(subnet_id) < 15: # "subnet-" (7) + 8 chars = 15
_handle_error(
f"Invalid subnet ID format: {subnet_id}\nSubnet ID is too short. Expected format: subnet-xxxxxxxx"
)

# Parse and validate security group IDs - UPDATED VALIDATION
vpc_security_groups = [sg.strip() for sg in security_groups.split(",") if sg.strip()]
for sg_id in vpc_security_groups:
# Format: sg-{8-17 hex characters}
if not sg_id.startswith("sg-"):
_handle_error(
f"Invalid security group ID format: {sg_id}\n"
f"Security group IDs must start with 'sg-' (e.g., sg-abc123)"
)
# Check minimum length (sg- + at least 8 chars)
if len(sg_id) < 11: # "sg-" (3) + 8 chars = 11
_handle_error(
f"Invalid security group ID format: {sg_id}\n"
f"Security group ID is too short. Expected format: sg-xxxxxxxx"
)

_print_success(
f"VPC mode enabled with {len(vpc_subnets)} subnets and {len(vpc_security_groups)} security groups"
)

elif subnets or security_groups:
# Error: VPC resources provided without --vpc flag
_handle_error(
"The --subnets and --security-groups flags require --vpc flag.\n"
"Use: agentcore configure --entrypoint my_agent.py --vpc --subnets ... --security-groups ..."
)

console.print("[cyan]Configuring Bedrock AgentCore...[/cyan]")

# Create configuration manager early for consistent prompting
Expand Down Expand Up @@ -399,6 +466,9 @@ def configure(
protocol=protocol.upper() if protocol else None,
non_interactive=non_interactive,
source_path=source_path,
vpc_enabled=vpc,
vpc_subnets=vpc_subnets,
vpc_security_groups=vpc_security_groups,
)

# Prepare authorization info for summary
Expand All @@ -412,6 +482,10 @@ def configure(
headers = request_header_config.get("requestHeaderAllowlist", [])
headers_info = f"Request Headers Allowlist: [dim]{len(headers)} headers configured[/dim]\n"

network_info = "Public"
if vpc:
network_info = f"VPC ({len(vpc_subnets)} subnets, {len(vpc_security_groups)} security groups)"

execution_role_display = "Auto-create" if not result.execution_role else result.execution_role
memory_info = "Short-term memory (30-day retention)"
if disable_memory:
Expand All @@ -429,6 +503,7 @@ def configure(
f"ECR Repository: [cyan]"
f"{'Auto-create' if result.auto_create_ecr else result.ecr_repository or 'N/A'}"
f"[/cyan]\n"
f"Network Mode: [cyan]{network_info}[/cyan]\n"
f"Authorization: [cyan]{auth_info}[/cyan]\n\n"
f"{headers_info}\n"
f"Memory: [cyan]{memory_info}[/cyan]\n\n"
Expand Down Expand Up @@ -983,11 +1058,6 @@ def status(

# Determine overall status
endpoint_status = endpoint_data.get("status", "Unknown") if endpoint_data else "Not Ready"
# memory_info = ""
# if hasattr(status_json["config"], "memory_id") and status_json["config"].get("memory_id"):
# memory_type = status_json["config"].get("memory_type", "Short-term")
# memory_id = status_json["config"].get("memory_id")
# memory_info = f"Memory: [cyan]{memory_type}[/cyan] ([dim]{memory_id}[/dim])\n"
if endpoint_status == "READY":
status_text = "Ready - Agent deployed and endpoint available"
else:
Expand All @@ -1005,6 +1075,26 @@ def status(
f"Account: [dim]{status_json['config'].get('account', 'Not available')}[/dim]\n\n"
)

# Add network information
network_mode = status_json.get("agent", {}).get("networkConfiguration", {}).get("networkMode")
if network_mode == "VPC":
# Get VPC info from agent response (not config)
network_config = (
status_json.get("agent", {}).get("networkConfiguration", {}).get("networkModeConfig", {})
)
vpc_subnets = network_config.get("subnets", [])
vpc_security_groups = network_config.get("securityGroups", [])
subnet_count = len(vpc_subnets)
sg_count = len(vpc_security_groups)
vpc_id = status_json.get("config", {}).get("network_vpc_id", "unknown")
if vpc_id:
panel_content += f"Network: [cyan]VPC[/cyan] ([dim]{vpc_id}[/dim])\n"
panel_content += f" {subnet_count} subnets, {sg_count} security groups\n\n"
else:
panel_content += "Network: [cyan]VPC[/cyan]\n\n"
else:
panel_content += "Network: [cyan]Public[/cyan]\n\n"

# Add memory status with proper provisioning indication
if "memory_id" in status_json.get("config", {}) and status_json["config"]["memory_id"]:
memory_type = status_json["config"].get("memory_type", "Unknown")
Expand Down
Loading