@aws/nx-plugin 1.0.0-rc.68 → 1.0.0-rc.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,366 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as url from 'node:url';
4
+ import { CfnOutput, Lazy, Names, Stack } from 'aws-cdk-lib';
5
+ import * as agentcore from 'aws-cdk-lib/aws-bedrockagentcore';
6
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
7
+ import * as iam from 'aws-cdk-lib/aws-iam';
8
+ import { Construct, IConstruct } from 'constructs';
9
+ import { suppressRules } from '../../../core/checkov<% if (esm) { %>.js<% } %>';
10
+ import { RuntimeConfig } from '../../../core/runtime-config<% if (esm) { %>.js<% } %>';
11
+ import { findWorkspaceRoot } from '../../../core/workspace<% if (esm) { %>.js<% } %>';
12
+
13
+ // Checkov rules flagging IAM statements whose resource cannot be scoped.
14
+ const WILDCARD_IAM_RULES = ['CKV_AWS_107', 'CKV_AWS_111', 'CKV_AWS_356'];
15
+
16
+ // Read at synth time, anchored on the workspace root so it still resolves once
17
+ // this file compiles to dist/.
18
+ const systemPrompt = fs.readFileSync(
19
+ path.join(
20
+ findWorkspaceRoot(url.fileURLToPath(new URL(<% if (esm) { %>import.meta.url<% } else { %>require('url').pathToFileURL(__filename).href<% } %>))),
21
+ '<%- promptPathFromCdk %>',
22
+ ),
23
+ 'utf-8',
24
+ );
25
+
26
+ /**
27
+ * Properties for the <%- nameClassName %> Harness. Any native Harness field
28
+ * supplied here takes precedence over the generated defaults.
29
+ */
30
+ export interface <%- nameClassName %>Props
31
+ extends Partial<
32
+ Omit<agentcore.CfnHarnessProps, 'executionRoleArn' | 'allowedTools'>
33
+ > {
34
+ /** Existing execution role to use instead of the generated role. */
35
+ executionRole?: iam.IRole;
36
+ /** Model resources the generated execution role may invoke. */
37
+ modelResourceArns?: string[];
38
+ /** Tools the Harness may use. Deploys with none unless supplied here. */
39
+ allowedTools?: agentcore.CfnHarnessProps['allowedTools'];
40
+ /**
41
+ * Run the Harness in a VPC so it can reach private resources. Selects private
42
+ * subnets with egress unless `vpcSubnets` narrows it, and creates a security
43
+ * group unless `securityGroups` supplies them.
44
+ */
45
+ vpc?: ec2.IVpc;
46
+ /** Subnets to place the Harness in. Requires `vpc`. */
47
+ vpcSubnets?: ec2.SubnetSelection;
48
+ /** Security groups for the Harness. Requires `vpc`. */
49
+ securityGroups?: ec2.ISecurityGroup[];
50
+ }
51
+
52
+ /** A managed Amazon Bedrock AgentCore Harness, using IAM inbound auth. */
53
+ export class <%- nameClassName %>
54
+ extends Construct
55
+ implements iam.IGrantable, ec2.IConnectable
56
+ {
57
+ public readonly harness: agentcore.CfnHarness;
58
+ public readonly executionRole: iam.IRole;
59
+ private readonly _connections?: ec2.Connections;
60
+
61
+ constructor(scope: Construct, id: string, props?: <%- nameClassName %>Props) {
62
+ super(scope, id);
63
+
64
+ const stack = Stack.of(this);
65
+ const {
66
+ executionRole,
67
+ modelResourceArns = [
68
+ `arn:${stack.partition}:bedrock:*::foundation-model/*`,
69
+ `arn:${stack.partition}:bedrock:${stack.region}:${stack.account}:*`,
70
+ ],
71
+ vpc,
72
+ vpcSubnets,
73
+ securityGroups,
74
+ ...harnessProps
75
+ } = props ?? {};
76
+
77
+ if (!vpc && (vpcSubnets || securityGroups)) {
78
+ throw new Error(
79
+ 'vpcSubnets and securityGroups require vpc to be supplied.',
80
+ );
81
+ }
82
+
83
+ if (vpc) {
84
+ this._connections = new ec2.Connections({
85
+ securityGroups: securityGroups ?? [
86
+ new ec2.SecurityGroup(this, 'SecurityGroup', {
87
+ vpc,
88
+ description: `Harness ${id}`,
89
+ }),
90
+ ],
91
+ });
92
+ }
93
+
94
+ // Harness names allow ASCII letters, digits and underscores, must start
95
+ // with a letter, and are at most 40 characters.
96
+ const uniqueName = Names.uniqueResourceName(this, {
97
+ maxLength: 39,
98
+ separator: '_',
99
+ allowedSpecialCharacters: '_',
100
+ });
101
+ const harnessName =
102
+ harnessProps.harnessName ??
103
+ (/^[A-Za-z]/.test(uniqueName) ? uniqueName : `H${uniqueName}`);
104
+
105
+ this.executionRole =
106
+ executionRole ??
107
+ new iam.Role(this, 'ExecutionRole', {
108
+ assumedBy: new iam.ServicePrincipal('bedrock-agentcore.amazonaws.com', {
109
+ conditions: {
110
+ StringEquals: { 'aws:SourceAccount': stack.account },
111
+ ArnLike: {
112
+ 'aws:SourceArn': `arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:*`,
113
+ },
114
+ },
115
+ }),
116
+ });
117
+
118
+ if (!executionRole) {
119
+ // Baseline permissions from the AgentCore Harness security guidance:
120
+ // https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html
121
+ [
122
+ new iam.PolicyStatement({
123
+ sid: 'BedrockModelInvocation',
124
+ actions: [
125
+ 'bedrock:InvokeModel',
126
+ 'bedrock:InvokeModelWithResponseStream',
127
+ ],
128
+ resources: modelResourceArns,
129
+ }),
130
+ new iam.PolicyStatement({
131
+ sid: 'EcrPublicTokenAccess',
132
+ actions: ['ecr-public:GetAuthorizationToken'],
133
+ resources: ['*'],
134
+ }),
135
+ new iam.PolicyStatement({
136
+ sid: 'StsForEcrPublicPull',
137
+ actions: ['sts:GetServiceBearerToken'],
138
+ resources: ['*'],
139
+ }),
140
+ new iam.PolicyStatement({
141
+ sid: 'XRayTracingAccess',
142
+ actions: [
143
+ 'xray:PutTraceSegments',
144
+ 'xray:PutTelemetryRecords',
145
+ 'xray:GetSamplingRules',
146
+ 'xray:GetSamplingTargets',
147
+ ],
148
+ resources: ['*'],
149
+ }),
150
+ new iam.PolicyStatement({
151
+ sid: 'CloudWatchLogsGroup',
152
+ actions: ['logs:CreateLogGroup', 'logs:DescribeLogStreams'],
153
+ resources: [
154
+ `arn:${stack.partition}:logs:${stack.region}:${stack.account}:log-group:/aws/bedrock-agentcore/runtimes/*`,
155
+ ],
156
+ }),
157
+ new iam.PolicyStatement({
158
+ sid: 'CloudWatchLogsDescribeGroups',
159
+ actions: ['logs:DescribeLogGroups'],
160
+ resources: [
161
+ `arn:${stack.partition}:logs:${stack.region}:${stack.account}:log-group:*`,
162
+ ],
163
+ }),
164
+ new iam.PolicyStatement({
165
+ sid: 'CloudWatchLogsStream',
166
+ actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
167
+ resources: [
168
+ `arn:${stack.partition}:logs:${stack.region}:${stack.account}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*`,
169
+ ],
170
+ }),
171
+ new iam.PolicyStatement({
172
+ sid: 'CloudWatchMetricsPublish',
173
+ actions: ['cloudwatch:PutMetricData'],
174
+ resources: ['*'],
175
+ conditions: {
176
+ StringEquals: { 'cloudwatch:namespace': 'bedrock-agentcore' },
177
+ },
178
+ }),
179
+ new iam.PolicyStatement({
180
+ sid: 'AgentCoreWorkloadIdentity',
181
+ actions: [
182
+ 'bedrock-agentcore:GetWorkloadAccessToken',
183
+ 'bedrock-agentcore:GetWorkloadAccessTokenForJWT',
184
+ ],
185
+ resources: [
186
+ `arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:workload-identity-directory/default`,
187
+ `arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:workload-identity-directory/default/workload-identity/harness_${harnessName}-*`,
188
+ ],
189
+ }),
190
+ new iam.PolicyStatement({
191
+ sid: 'AgentCoreBrowserDefault',
192
+ actions: [
193
+ 'bedrock-agentcore:StartBrowserSession',
194
+ 'bedrock-agentcore:StopBrowserSession',
195
+ 'bedrock-agentcore:GetBrowserSession',
196
+ 'bedrock-agentcore:ListBrowserSessions',
197
+ 'bedrock-agentcore:UpdateBrowserStream',
198
+ 'bedrock-agentcore:ConnectBrowserAutomationStream',
199
+ 'bedrock-agentcore:ConnectBrowserLiveViewStream',
200
+ ],
201
+ resources: [
202
+ `arn:${stack.partition}:bedrock-agentcore:${stack.region}:aws:browser/*`,
203
+ ],
204
+ }),
205
+ new iam.PolicyStatement({
206
+ sid: 'AgentCoreCodeInterpreterDefault',
207
+ actions: [
208
+ 'bedrock-agentcore:StartCodeInterpreterSession',
209
+ 'bedrock-agentcore:StopCodeInterpreterSession',
210
+ 'bedrock-agentcore:GetCodeInterpreterSession',
211
+ 'bedrock-agentcore:ListCodeInterpreterSessions',
212
+ 'bedrock-agentcore:InvokeCodeInterpreter',
213
+ ],
214
+ resources: [
215
+ `arn:${stack.partition}:bedrock-agentcore:${stack.region}:aws:code-interpreter/*`,
216
+ ],
217
+ }),
218
+ ].forEach((statement) =>
219
+ this.executionRole.addToPrincipalPolicy(statement),
220
+ );
221
+
222
+ // These statements take no resource-level permission.
223
+ const isPolicy = (c: IConstruct) => c instanceof iam.Policy;
224
+ suppressRules(
225
+ this.executionRole,
226
+ WILDCARD_IAM_RULES,
227
+ 'EcrPublicTokenAccess: ecr-public:GetAuthorizationToken has no resource-level permission.',
228
+ isPolicy,
229
+ );
230
+ suppressRules(
231
+ this.executionRole,
232
+ WILDCARD_IAM_RULES,
233
+ 'StsForEcrPublicPull: sts:GetServiceBearerToken has no resource-level permission.',
234
+ isPolicy,
235
+ );
236
+ suppressRules(
237
+ this.executionRole,
238
+ WILDCARD_IAM_RULES,
239
+ 'XRayTracingAccess: X-Ray segment and sampling APIs have no resource-level permission.',
240
+ isPolicy,
241
+ );
242
+ }
243
+
244
+ // Rendered lazily so security groups added through `connections` after
245
+ // construction are still picked up.
246
+ const environment: agentcore.CfnHarnessProps['environment'] | undefined = vpc
247
+ ? Lazy.any({
248
+ produce: () => ({
249
+ ...(harnessProps.environment as
250
+ | agentcore.CfnHarness.HarnessEnvironmentProviderProperty
251
+ | undefined),
252
+ agentCoreRuntimeEnvironment: {
253
+ ...(
254
+ harnessProps.environment as
255
+ | agentcore.CfnHarness.HarnessEnvironmentProviderProperty
256
+ | undefined
257
+ )?.agentCoreRuntimeEnvironment,
258
+ networkConfiguration: {
259
+ networkMode: 'VPC',
260
+ networkModeConfig: {
261
+ subnets: vpc.selectSubnets(
262
+ vpcSubnets ?? {
263
+ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
264
+ },
265
+ ).subnetIds,
266
+ securityGroups: this._connections!.securityGroups.map(
267
+ (group) => group.securityGroupId,
268
+ ),
269
+ },
270
+ },
271
+ },
272
+ }),
273
+ })
274
+ : harnessProps.environment;
275
+
276
+ this.harness = new agentcore.CfnHarness(this, 'Harness', {
277
+ model: {
278
+ bedrockModelConfig: {
279
+ modelId: 'global.anthropic.claude-sonnet-4-6',
280
+ },
281
+ },
282
+ systemPrompt: [{ text: systemPrompt }],
283
+ ...harnessProps,
284
+ // Set after harnessProps so the deployed name always matches the
285
+ // execution role's workload-identity resource pattern.
286
+ harnessName,
287
+ executionRoleArn: this.executionRole.roleArn,
288
+ // Carries the VPC network configuration when `vpc` is supplied, and is
289
+ // the caller's own `environment` otherwise.
290
+ environment,
291
+ });
292
+
293
+ // The Harness provisions managed memory unless `memory` is supplied, and
294
+ // the service names it, so the grant scopes to the resulting ARN.
295
+ if (!executionRole && harnessProps.memory === undefined) {
296
+ this.executionRole.addToPrincipalPolicy(
297
+ new iam.PolicyStatement({
298
+ sid: 'AgentCoreManagedMemory',
299
+ actions: [
300
+ 'bedrock-agentcore:CreateEvent',
301
+ 'bedrock-agentcore:DeleteEvent',
302
+ 'bedrock-agentcore:GetEvent',
303
+ 'bedrock-agentcore:ListEvents',
304
+ 'bedrock-agentcore:RetrieveMemoryRecords',
305
+ ],
306
+ resources: [
307
+ this.harness.attrMemoryManagedMemoryConfigurationArn,
308
+ ],
309
+ }),
310
+ );
311
+ }
312
+
313
+ const rc = RuntimeConfig.ensure(this);
314
+ rc.set('agentcore', 'harnesses', {
315
+ ...rc.get('agentcore').harnesses,
316
+ <%- nameClassName %>: this.harness.attrArn,
317
+ });
318
+
319
+ // Materialises the AppConfig resources so the entry above is deployed and
320
+ // the chat script's RUNTIME_CONFIG_APP_ID lookup resolves.
321
+ void rc.appConfigApplicationId;
322
+
323
+ new CfnOutput(this, 'HarnessArn', { value: this.harness.attrArn });
324
+ }
325
+
326
+ public get grantPrincipal(): iam.IPrincipal {
327
+ return this.executionRole.grantPrincipal;
328
+ }
329
+
330
+ /**
331
+ * Network connections for this Harness, for granting access to resources such
332
+ * as a database. Only available when the Harness runs in a VPC.
333
+ */
334
+ public get connections(): ec2.Connections {
335
+ if (!this._connections) {
336
+ throw new Error(
337
+ 'Connections are only available when the Harness runs in a VPC. Supply the vpc prop.',
338
+ );
339
+ }
340
+ return this._connections;
341
+ }
342
+
343
+ /** The deployed Harness ARN. */
344
+ public get harnessArn(): string {
345
+ return this.harness.attrArn;
346
+ }
347
+
348
+ /** Add permissions required by tools, skills, memory, or custom models. */
349
+ public addToRolePolicy(
350
+ statement: iam.PolicyStatement,
351
+ ): iam.AddToPrincipalPolicyResult {
352
+ return this.executionRole.addToPrincipalPolicy(statement);
353
+ }
354
+
355
+ /** Grant an IAM principal permission to invoke this Harness. */
356
+ public grantInvokeAccess(grantee: iam.IGrantable): iam.Grant {
357
+ return iam.Grant.addToPrincipal({
358
+ grantee,
359
+ actions: [
360
+ 'bedrock-agentcore:InvokeHarness',
361
+ 'bedrock-agentcore:InvokeAgentRuntime',
362
+ ],
363
+ resourceArns: [this.harness.attrArn],
364
+ });
365
+ }
366
+ }
@@ -0,0 +1,282 @@
1
+ terraform {
2
+ required_version = ">= 1.0"
3
+
4
+ required_providers {
5
+ aws = {
6
+ source = "hashicorp/aws"
7
+ version = "<%- awsProviderVersion %>"
8
+ }
9
+ random = {
10
+ source = "hashicorp/random"
11
+ version = "<%- randomProviderVersion %>"
12
+ }
13
+ }
14
+ }
15
+
16
+ variable "model_id" {
17
+ description = "Amazon Bedrock model or inference profile used by default."
18
+ type = string
19
+ default = "global.anthropic.claude-sonnet-4-6"
20
+ }
21
+
22
+ variable "model_resource_arns" {
23
+ description = "Bedrock model and inference-profile ARNs the execution role may invoke. Defaults to every foundation model plus this account's Bedrock resources in the deployment region; replace with narrower ARNs to restrict baseline model access."
24
+ type = set(string)
25
+ default = null
26
+ }
27
+
28
+ variable "additional_execution_role_policy_statements" {
29
+ description = "Additional least-privilege IAM policy statements required by configured optional capabilities (e.g. customer-owned Gateways, memory, custom browsers or code interpreters, skills, secrets). Sid and Condition are optional; null fields are omitted from the rendered policy."
30
+ type = list(object({
31
+ Sid = optional(string)
32
+ Effect = string
33
+ Action = list(string)
34
+ Resource = list(string)
35
+ Condition = optional(any)
36
+ }))
37
+ default = []
38
+ }
39
+
40
+ data "aws_caller_identity" "current" {}
41
+ data "aws_partition" "current" {}
42
+ data "aws_region" "current" {}
43
+
44
+ resource "random_id" "unique_suffix" {
45
+ byte_length = 4
46
+ }
47
+
48
+ locals {
49
+ account_id = data.aws_caller_identity.current.account_id
50
+ partition = data.aws_partition.current.partition
51
+ region = data.aws_region.current.region
52
+
53
+ # Capped at 31 characters so the suffix keeps the deployed Harness name
54
+ # within its 40-character limit.
55
+ harness_name_prefix = "<%- harnessNamePrefix %>"
56
+ harness_name = "${local.harness_name_prefix}_${random_id.unique_suffix.hex}"
57
+
58
+ # Set var.model_resource_arns to narrow the default model access.
59
+ model_resource_arns = var.model_resource_arns != null ? var.model_resource_arns : [
60
+ "arn:${local.partition}:bedrock:*::foundation-model/*",
61
+ "arn:${local.partition}:bedrock:${local.region}:${local.account_id}:*",
62
+ ]
63
+ }
64
+
65
+ resource "aws_iam_role" "execution_role" {
66
+ name = "${local.harness_name_prefix}-HarnessRole-${random_id.unique_suffix.hex}"
67
+
68
+ assume_role_policy = jsonencode({
69
+ Version = "2012-10-17"
70
+ Statement = [{
71
+ Effect = "Allow"
72
+ Principal = {
73
+ Service = "bedrock-agentcore.amazonaws.com"
74
+ }
75
+ Action = "sts:AssumeRole"
76
+ Condition = {
77
+ StringEquals = {
78
+ "aws:SourceAccount" = local.account_id
79
+ }
80
+ ArnLike = {
81
+ "aws:SourceArn" = "arn:${local.partition}:bedrock-agentcore:${local.region}:${local.account_id}:*"
82
+ }
83
+ }
84
+ }]
85
+ })
86
+ }
87
+
88
+ # Baseline permissions per the AgentCore Harness security guidance; extend for
89
+ # customer-owned resources via var.additional_execution_role_policy_statements.
90
+ # https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html
91
+ resource "aws_iam_role_policy" "execution_role" {
92
+ name = "${local.harness_name_prefix}-HarnessPolicy"
93
+ role = aws_iam_role.execution_role.id
94
+
95
+ policy = jsonencode({
96
+ Version = "2012-10-17"
97
+ Statement = concat([
98
+ {
99
+ Sid = "BedrockModelInvocation"
100
+ Effect = "Allow"
101
+ Action = [
102
+ "bedrock:InvokeModel",
103
+ "bedrock:InvokeModelWithResponseStream",
104
+ ]
105
+ Resource = local.model_resource_arns
106
+ },
107
+ {
108
+ #checkov:skip=CKV_AWS_355:EcrPublicTokenAccess requires a wildcard resource; ecr-public:GetAuthorizationToken has no resource-level permission
109
+ Sid = "EcrPublicTokenAccess"
110
+ Effect = "Allow"
111
+ Action = ["ecr-public:GetAuthorizationToken"]
112
+ Resource = ["*"]
113
+ },
114
+ {
115
+ #checkov:skip=CKV_AWS_355:StsForEcrPublicPull requires a wildcard resource; sts:GetServiceBearerToken has no resource-level permission
116
+ Sid = "StsForEcrPublicPull"
117
+ Effect = "Allow"
118
+ Action = ["sts:GetServiceBearerToken"]
119
+ Resource = ["*"]
120
+ },
121
+ {
122
+ #checkov:skip=CKV_AWS_355:XRayTracingAccess requires a wildcard resource; the X-Ray segment and sampling APIs have no resource-level permission
123
+ #checkov:skip=CKV_AWS_290:XRayTracingAccess requires a wildcard resource; the X-Ray segment and sampling APIs have no resource-level permission
124
+ Sid = "XRayTracingAccess"
125
+ Effect = "Allow"
126
+ Action = [
127
+ "xray:PutTraceSegments",
128
+ "xray:PutTelemetryRecords",
129
+ "xray:GetSamplingRules",
130
+ "xray:GetSamplingTargets",
131
+ ]
132
+ Resource = ["*"]
133
+ },
134
+ {
135
+ Sid = "CloudWatchLogsGroup"
136
+ Effect = "Allow"
137
+ Action = [
138
+ "logs:CreateLogGroup",
139
+ "logs:DescribeLogStreams",
140
+ ]
141
+ Resource = [
142
+ "arn:${local.partition}:logs:${local.region}:${local.account_id}:log-group:/aws/bedrock-agentcore/runtimes/*",
143
+ ]
144
+ },
145
+ {
146
+ Sid = "CloudWatchLogsDescribeGroups"
147
+ Effect = "Allow"
148
+ Action = ["logs:DescribeLogGroups"]
149
+ Resource = ["arn:${local.partition}:logs:${local.region}:${local.account_id}:log-group:*"]
150
+ },
151
+ {
152
+ Sid = "CloudWatchLogsStream"
153
+ Effect = "Allow"
154
+ Action = [
155
+ "logs:CreateLogStream",
156
+ "logs:PutLogEvents",
157
+ ]
158
+ Resource = [
159
+ "arn:${local.partition}:logs:${local.region}:${local.account_id}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*",
160
+ ]
161
+ },
162
+ {
163
+ #checkov:skip=CKV_AWS_355:CloudWatchMetricsPublish requires a wildcard resource; cloudwatch:PutMetricData is scoped by the namespace condition instead
164
+ #checkov:skip=CKV_AWS_290:CloudWatchMetricsPublish requires a wildcard resource; cloudwatch:PutMetricData is scoped by the namespace condition instead
165
+ Sid = "CloudWatchMetricsPublish"
166
+ Effect = "Allow"
167
+ Action = ["cloudwatch:PutMetricData"]
168
+ Resource = ["*"]
169
+ Condition = {
170
+ StringEquals = {
171
+ "cloudwatch:namespace" = "bedrock-agentcore"
172
+ }
173
+ }
174
+ },
175
+ {
176
+ Sid = "AgentCoreWorkloadIdentity"
177
+ Effect = "Allow"
178
+ Action = [
179
+ "bedrock-agentcore:GetWorkloadAccessToken",
180
+ "bedrock-agentcore:GetWorkloadAccessTokenForJWT",
181
+ ]
182
+ Resource = [
183
+ "arn:${local.partition}:bedrock-agentcore:${local.region}:${local.account_id}:workload-identity-directory/default",
184
+ "arn:${local.partition}:bedrock-agentcore:${local.region}:${local.account_id}:workload-identity-directory/default/workload-identity/harness_${local.harness_name}-*",
185
+ ]
186
+ },
187
+ # The service names the managed memory from the harness name plus a
188
+ # generated suffix, so this scopes to that prefix.
189
+ {
190
+ Sid = "AgentCoreManagedMemory"
191
+ Effect = "Allow"
192
+ Action = [
193
+ "bedrock-agentcore:CreateEvent",
194
+ "bedrock-agentcore:DeleteEvent",
195
+ "bedrock-agentcore:GetEvent",
196
+ "bedrock-agentcore:ListEvents",
197
+ "bedrock-agentcore:RetrieveMemoryRecords",
198
+ ]
199
+ Resource = [
200
+ "arn:${local.partition}:bedrock-agentcore:${local.region}:${local.account_id}:memory/${local.harness_name}-*",
201
+ ]
202
+ },
203
+ {
204
+ Sid = "AgentCoreBrowserDefault"
205
+ Effect = "Allow"
206
+ Action = [
207
+ "bedrock-agentcore:StartBrowserSession",
208
+ "bedrock-agentcore:StopBrowserSession",
209
+ "bedrock-agentcore:GetBrowserSession",
210
+ "bedrock-agentcore:ListBrowserSessions",
211
+ "bedrock-agentcore:UpdateBrowserStream",
212
+ "bedrock-agentcore:ConnectBrowserAutomationStream",
213
+ "bedrock-agentcore:ConnectBrowserLiveViewStream",
214
+ ]
215
+ Resource = [
216
+ "arn:${local.partition}:bedrock-agentcore:${local.region}:aws:browser/*",
217
+ ]
218
+ },
219
+ {
220
+ Sid = "AgentCoreCodeInterpreterDefault"
221
+ Effect = "Allow"
222
+ Action = [
223
+ "bedrock-agentcore:StartCodeInterpreterSession",
224
+ "bedrock-agentcore:StopCodeInterpreterSession",
225
+ "bedrock-agentcore:GetCodeInterpreterSession",
226
+ "bedrock-agentcore:ListCodeInterpreterSessions",
227
+ "bedrock-agentcore:InvokeCodeInterpreter",
228
+ ]
229
+ Resource = [
230
+ "arn:${local.partition}:bedrock-agentcore:${local.region}:aws:code-interpreter/*",
231
+ ]
232
+ },
233
+ ], [
234
+ # Null Sid/Condition fields are dropped from the rendered policy.
235
+ for statement in var.additional_execution_role_policy_statements :
236
+ { for key, value in statement : key => value if value != null }
237
+ ])
238
+ })
239
+ }
240
+
241
+ # Omitting authorizer_configuration uses IAM inbound authorization; add a
242
+ # custom_jwt_authorizer block to change that.
243
+ resource "aws_bedrockagentcore_harness" "this" {
244
+ harness_name = local.harness_name
245
+ execution_role_arn = aws_iam_role.execution_role.arn
246
+
247
+ model {
248
+ bedrock_model_config {
249
+ model_id = var.model_id
250
+ }
251
+ }
252
+
253
+ # Read at plan time; the walk up from path.module reaches the workspace root.
254
+ system_prompt {
255
+ text = file("${path.module}/../../../../../../../<%- promptPathFromTerraform %>")
256
+ }
257
+
258
+ depends_on = [aws_iam_role_policy.execution_role]
259
+ }
260
+
261
+ module "add_harness_arn_to_runtime_config" {
262
+ source = "../../../core/runtime-config/entry"
263
+
264
+ namespace = "agentcore"
265
+ key = "harnesses"
266
+ value = { "<%- nameClassName %>" = aws_bedrockagentcore_harness.this.arn }
267
+ }
268
+
269
+ output "harness_id" {
270
+ description = "ID of the Amazon Bedrock AgentCore Harness"
271
+ value = aws_bedrockagentcore_harness.this.harness_id
272
+ }
273
+
274
+ output "harness_arn" {
275
+ description = "ARN of the Amazon Bedrock AgentCore Harness"
276
+ value = aws_bedrockagentcore_harness.this.arn
277
+ }
278
+
279
+ output "execution_role_arn" {
280
+ description = "ARN of the IAM role assumed by the Harness"
281
+ value = aws_iam_role.execution_role.arn
282
+ }