@@ -110,11 +110,14 @@ async function main() {
110110 memoryName ?: string ;
111111 containerUri ?: string ;
112112 hasDockerfile ?: boolean ;
113+ dockerfileName ?: string ;
114+ harnessDir ?: string ;
113115 tools ?: { type: string ; name : string }[];
114116 apiKeyArn ?: string ;
115117 }[] = [];
116118 for (const entry of specAny .harnesses ?? []) {
117- const harnessPath = path.resolve(projectRoot , entry .path , 'harness .json ');
119+ const harnessDir = path.resolve(projectRoot , entry .path );
120+ const harnessPath = path.resolve(harnessDir , 'harness .json ');
118121 try {
119122 const harnessSpec = JSON.parse(fs .readFileSync (harnessPath , 'utf -8'));
120123 harnessConfigs.push({
@@ -123,6 +126,8 @@ async function main() {
123126 memoryName : harnessSpec .memory ?.name ,
124127 containerUri : harnessSpec .containerUri ,
125128 hasDockerfile : !!harnessSpec .dockerfile ,
129+ dockerfileName : harnessSpec .dockerfile ,
130+ harnessDir ,
126131 tools : harnessSpec .tools ,
127132 apiKeyArn : harnessSpec .model ?.apiKeyArn ,
128133 });
@@ -296,10 +301,26 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/lib/cdk-stack.ts shou
296301 AgentCoreMcp ,
297302 type AgentCoreProjectSpec ,
298303 type AgentCoreMcpSpec ,
304+ ContainerSourceAssetFromPath ,
305+ AgentEcrRepository ,
306+ ContainerBuildProject ,
307+ ContainerImageBuilder ,
299308} from '@aws/agentcore-cdk';
300309import { CfnOutput , Stack , type StackProps } from 'aws-cdk-lib';
301310import { Construct } from 'constructs';
302311
312+ export interface HarnessConfig {
313+ name : string ;
314+ executionRoleArn ?: string ;
315+ memoryName ?: string ;
316+ containerUri ?: string ;
317+ hasDockerfile ?: boolean ;
318+ dockerfileName ?: string ;
319+ harnessDir ?: string ;
320+ tools ?: { type: string ; name : string }[];
321+ apiKeyArn ?: string ;
322+ }
323+
303324export interface AgentCoreStackProps extends StackProps {
304325 /**
305326 * The AgentCore project specification containing agents, memories, and credentials.
@@ -316,15 +337,7 @@ export interface AgentCoreStackProps extends StackProps {
316337 /**
317338 * Harness role configurations. Each entry creates an IAM execution role for a harness.
318339 */
319- harnesses ?: {
320- name: string ;
321- executionRoleArn ?: string ;
322- memoryName ?: string ;
323- containerUri ?: string ;
324- hasDockerfile ?: boolean ;
325- tools ?: { type: string ; name : string }[];
326- apiKeyArn ?: string ;
327- }[];
340+ harnesses ?: HarnessConfig [];
328341}
329342
330343/**
@@ -342,10 +355,46 @@ export class AgentCoreStack extends Stack {
342355
343356 const { spec , mcpSpec , credentials , harnesses } = props ;
344357
358+ // Build container images for harnesses that specify a dockerfile (no containerUri).
359+ // Produces CDK outputs consumed by the imperative harness deployer.
360+ const harnessesForCdk = harnesses ? [... harnesses ] : [];
361+ if (harnesses ) {
362+ for (let i = 0 ; i < harnesses .length ; i ++) {
363+ const h = harnesses [i ]! ;
364+ if (h .hasDockerfile && ! h .containerUri && h .harnessDir ) {
365+ const pascalName = h .name .replace (/ (^ | _)([a-z ] )/ g , (_ : string , __ : string , c : string ) => c .toUpperCase ());
366+ const sourceAsset = new ContainerSourceAssetFromPath (this , \` Harness\$ { pascalName } SourceAsset\` , {
367+ sourcePath : h .harnessDir ,
368+ } );
369+ const ecrRepo = new AgentEcrRepository(this, \` Harness\$ { pascalName } EcrRepo\` , {
370+ projectName : spec .name ,
371+ agentName : \` harness-\$ { h .name } \` ,
372+ });
373+ const buildProject = ContainerBuildProject.getOrCreate(this);
374+ buildProject.grantPushTo(ecrRepo.repository);
375+ sourceAsset.asset.grantRead(buildProject.role);
376+
377+ const builder = new ContainerImageBuilder(this, \` Harness\$ { pascalName } ContainerBuild\` , {
378+ buildProject ,
379+ sourceAsset ,
380+ repository : ecrRepo ,
381+ dockerfile : h .dockerfileName ?? ' Dockerfile' ,
382+ });
383+
384+ new CfnOutput (this , \` Harness\$ { pascalName } ContainerUriOutput\` , {
385+ value : builder .containerUri ,
386+ } );
387+
388+ // Pass the built containerUri to the harness role construct so it gets ECR pull permissions
389+ harnessesForCdk[i] = { ... h , containerUri : builder .containerUri } ;
390+ }
391+ }
392+ }
393+
345394 // Create AgentCoreApplication with all agents and harness roles
346395 this.application = new AgentCoreApplication(this, 'Application', {
347396 spec ,
348- harnesses ,
397+ harnesses : harnessesForCdk . length > 0 ? harnessesForCdk : undefined ,
349398 } );
350399
351400 // Create AgentCoreMcp if there are gateways configured
0 commit comments