Skip to content

Commit 60b5946

Browse files
feat(import): extract and pass through executionRoleArn from starter toolkit YAML (#729)
Parse the `aws.execution_role` field from `.bedrock_agentcore.yaml` and propagate it through the import pipeline into `agentcore.json`. When present, CDK uses the existing IAM role instead of creating a new one. - Add optional `executionRoleArn` to AgentEnvSpecSchema - Add `executionRoleArn` to ParsedStarterToolkitAgent type - Extract `execution_role` from YAML aws config in parser - Pass through to AgentEnvSpec in toAgentEnvSpec()
1 parent 1631fdb commit 60b5946

File tree

6 files changed

+94
-0
lines changed

6 files changed

+94
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Tests for execution role import from starter toolkit YAML.
3+
*/
4+
import type { AgentEnvSpec } from '../../../../schema/schemas/agent-env';
5+
import type { ParsedStarterToolkitConfig } from '../types';
6+
import { parseStarterToolkitYaml } from '../yaml-parser';
7+
import * as path from 'node:path';
8+
import { describe, expect, it } from 'vitest';
9+
10+
const APP_DIR = 'app';
11+
12+
function toAgentEnvSpec(agent: ParsedStarterToolkitConfig['agents'][0]): AgentEnvSpec {
13+
const codeLocation = path.join(APP_DIR, agent.name);
14+
const entrypoint = path.basename(agent.entrypoint);
15+
const spec: AgentEnvSpec = {
16+
name: agent.name,
17+
build: agent.build,
18+
entrypoint: entrypoint as AgentEnvSpec['entrypoint'],
19+
codeLocation: codeLocation as AgentEnvSpec['codeLocation'],
20+
runtimeVersion: (agent.runtimeVersion ?? 'PYTHON_3_12') as AgentEnvSpec['runtimeVersion'],
21+
protocol: agent.protocol,
22+
networkMode: agent.networkMode,
23+
instrumentation: { enableOtel: agent.enableOtel },
24+
};
25+
if (agent.networkMode === 'VPC' && agent.networkConfig) {
26+
spec.networkConfig = agent.networkConfig;
27+
}
28+
if (agent.executionRoleArn) {
29+
spec.executionRoleArn = agent.executionRoleArn;
30+
}
31+
return spec;
32+
}
33+
34+
const FIXTURE = path.join(__dirname, 'fixtures', 'agent-with-execution-role.yaml');
35+
const FIXTURE_NO_ROLE = path.join(__dirname, 'fixtures', 'different-agent.yaml');
36+
37+
describe('parseStarterToolkitYaml: executionRoleArn', () => {
38+
it('extracts executionRoleArn from YAML with execution_role', () => {
39+
const parsed = parseStarterToolkitYaml(FIXTURE);
40+
expect(parsed.agents).toHaveLength(1);
41+
expect(parsed.agents[0]!.executionRoleArn).toBe('arn:aws:iam::123456789012:role/StarterToolkitExecutionRole');
42+
});
43+
44+
it('returns undefined executionRoleArn when execution_role is absent', () => {
45+
const parsed = parseStarterToolkitYaml(FIXTURE_NO_ROLE);
46+
expect(parsed.agents[0]!.executionRoleArn).toBeUndefined();
47+
});
48+
});
49+
50+
describe('toAgentEnvSpec: executionRoleArn', () => {
51+
it('includes executionRoleArn in spec when present', () => {
52+
const parsed = parseStarterToolkitYaml(FIXTURE);
53+
const spec = toAgentEnvSpec(parsed.agents[0]!);
54+
expect(spec.executionRoleArn).toBe('arn:aws:iam::123456789012:role/StarterToolkitExecutionRole');
55+
});
56+
57+
it('omits executionRoleArn from spec when absent', () => {
58+
const parsed = parseStarterToolkitYaml(FIXTURE_NO_ROLE);
59+
const spec = toAgentEnvSpec(parsed.agents[0]!);
60+
expect(spec.executionRoleArn).toBeUndefined();
61+
});
62+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
default_agent: my_agent
2+
agents:
3+
my_agent:
4+
name: my_agent
5+
entrypoint: main.py
6+
deployment_type: direct_code_deploy
7+
runtime_type: PYTHON_3_12
8+
source_path: null
9+
aws:
10+
account: '123456789012'
11+
region: us-west-2
12+
execution_role: arn:aws:iam::123456789012:role/StarterToolkitExecutionRole
13+
network_configuration:
14+
network_mode: PUBLIC
15+
protocol_configuration:
16+
server_protocol: HTTP
17+
observability:
18+
enabled: true
19+
bedrock_agentcore:
20+
agent_id: AGENT_ROLE_123
21+
agent_arn: arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/AGENT_ROLE_123
22+
memory:
23+
mode: NO_MEMORY

src/cli/commands/import/actions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ function toAgentEnvSpec(agent: ParsedStarterToolkitConfig['agents'][0]): AgentEn
6161
spec.networkConfig = agent.networkConfig;
6262
}
6363

64+
if (agent.executionRoleArn) {
65+
spec.executionRoleArn = agent.executionRoleArn;
66+
}
67+
6468
if (agent.authorizerType) {
6569
spec.authorizerType = agent.authorizerType;
6670
}

src/cli/commands/import/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export interface ParsedStarterToolkitAgent {
2222
authorizerType?: RuntimeAuthorizerType;
2323
/** Authorizer configuration (Custom JWT) */
2424
authorizerConfiguration?: AuthorizerConfig;
25+
/** ARN of the execution role from the starter toolkit deployment */
26+
executionRoleArn?: string;
2527
}
2628

2729
/**

src/cli/commands/import/yaml-parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ export function parseStarterToolkitYaml(filePath: string): ParsedStarterToolkitC
225225
physicalAgentId: bedrockConfig?.agent_id as string | undefined,
226226
physicalAgentArn: bedrockConfig?.agent_arn as string | undefined,
227227
...extractAuthorizerConfig(agentConfig.authorizer_configuration),
228+
executionRoleArn: (awsConfig?.execution_role as string) || undefined,
228229
});
229230

230231
// Extract memory config per agent — ensure mode is a non-empty string

src/schema/schemas/agent-env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ export const AgentEnvSpecSchema = z
194194
protocol: ProtocolModeSchema.optional(),
195195
/** Allowed request headers forwarded to the runtime at invocation time. */
196196
requestHeaderAllowlist: RequestHeaderAllowlistSchema.optional(),
197+
/** ARN of an existing IAM execution role to use instead of creating a new one. */
198+
executionRoleArn: z.string().optional(),
197199
/** Authorizer type for inbound requests. Defaults to AWS_IAM. */
198200
authorizerType: RuntimeAuthorizerTypeSchema.optional(),
199201
/** Authorizer configuration. Required when authorizerType is CUSTOM_JWT. */

0 commit comments

Comments
 (0)