Skip to content

Commit fceeff1

Browse files
Merge pull request #2 from jesseturner21/feat/import-execution-role
feat(import): extract and pass through executionRoleArn from starter toolkit YAML
2 parents 42d4db4 + 5f5aae0 commit fceeff1

6 files changed

Lines changed: 95 additions & 0 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
type: 'AgentCoreRuntime',
17+
name: agent.name,
18+
build: agent.build,
19+
entrypoint: entrypoint as AgentEnvSpec['entrypoint'],
20+
codeLocation: codeLocation as AgentEnvSpec['codeLocation'],
21+
runtimeVersion: (agent.runtimeVersion ?? 'PYTHON_3_12') as AgentEnvSpec['runtimeVersion'],
22+
protocol: agent.protocol,
23+
networkMode: agent.networkMode,
24+
instrumentation: { enableOtel: agent.enableOtel },
25+
};
26+
if (agent.networkMode === 'VPC' && agent.networkConfig) {
27+
spec.networkConfig = agent.networkConfig;
28+
}
29+
if (agent.executionRoleArn) {
30+
spec.executionRoleArn = agent.executionRoleArn;
31+
}
32+
return spec;
33+
}
34+
35+
const FIXTURE = path.join(__dirname, 'fixtures', 'agent-with-execution-role.yaml');
36+
const FIXTURE_NO_ROLE = path.join(__dirname, 'fixtures', 'different-agent.yaml');
37+
38+
describe('parseStarterToolkitYaml: executionRoleArn', () => {
39+
it('extracts executionRoleArn from YAML with execution_role', () => {
40+
const parsed = parseStarterToolkitYaml(FIXTURE);
41+
expect(parsed.agents).toHaveLength(1);
42+
expect(parsed.agents[0]!.executionRoleArn).toBe('arn:aws:iam::123456789012:role/StarterToolkitExecutionRole');
43+
});
44+
45+
it('returns undefined executionRoleArn when execution_role is absent', () => {
46+
const parsed = parseStarterToolkitYaml(FIXTURE_NO_ROLE);
47+
expect(parsed.agents[0]!.executionRoleArn).toBeUndefined();
48+
});
49+
});
50+
51+
describe('toAgentEnvSpec: executionRoleArn', () => {
52+
it('includes executionRoleArn in spec when present', () => {
53+
const parsed = parseStarterToolkitYaml(FIXTURE);
54+
const spec = toAgentEnvSpec(parsed.agents[0]!);
55+
expect(spec.executionRoleArn).toBe('arn:aws:iam::123456789012:role/StarterToolkitExecutionRole');
56+
});
57+
58+
it('omits executionRoleArn from spec when absent', () => {
59+
const parsed = parseStarterToolkitYaml(FIXTURE_NO_ROLE);
60+
const spec = toAgentEnvSpec(parsed.agents[0]!);
61+
expect(spec.executionRoleArn).toBeUndefined();
62+
});
63+
});
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
@@ -62,6 +62,10 @@ function toAgentEnvSpec(agent: ParsedStarterToolkitConfig['agents'][0]): AgentEn
6262
spec.networkConfig = agent.networkConfig;
6363
}
6464

65+
if (agent.executionRoleArn) {
66+
spec.executionRoleArn = agent.executionRoleArn;
67+
}
68+
6569
return spec;
6670
}
6771

src/cli/commands/import/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface ParsedStarterToolkitAgent {
2020
physicalAgentArn?: string;
2121
/** Whether this agent has a custom JWT authorizer configured (not imported) */
2222
hasAuthorizerConfig?: boolean;
23+
/** ARN of the execution role from the starter toolkit deployment */
24+
executionRoleArn?: string;
2325
}
2426

2527
/**

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ export function parseStarterToolkitYaml(filePath: string): ParsedStarterToolkitC
194194
physicalAgentArn: bedrockConfig?.agent_arn as string | undefined,
195195
hasAuthorizerConfig:
196196
agentConfig.authorizer_configuration != null && agentConfig.authorizer_configuration !== 'null',
197+
executionRoleArn: (awsConfig?.execution_role as string) || undefined,
197198
});
198199

199200
// 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
@@ -166,6 +166,8 @@ export const AgentEnvSpecSchema = z
166166
protocol: ProtocolModeSchema.optional(),
167167
/** Allowed request headers forwarded to the runtime at invocation time. */
168168
requestHeaderAllowlist: RequestHeaderAllowlistSchema.optional(),
169+
/** ARN of an existing IAM execution role to use instead of creating a new one. */
170+
executionRoleArn: z.string().optional(),
169171
tags: TagsSchema.optional(),
170172
})
171173
.superRefine((data, ctx) => {

0 commit comments

Comments
 (0)