@deployfoundation/foundation-deploy 0.2.13 → 0.2.14
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.
package/dist/bin/app.js
CHANGED
|
@@ -1522,8 +1522,7 @@ function spansIngestionPolicy(account, region) {
|
|
|
1522
1522
|
Version: "2012-10-17",
|
|
1523
1523
|
Statement: [
|
|
1524
1524
|
statement("SpansFromXray", "aws/spans"),
|
|
1525
|
-
statement("ApplicationSignalsFromXray", "/aws/application-signals/data")
|
|
1526
|
-
statement("AgentCoreRuntimeSpansFromXray", "/aws/bedrock-agentcore/runtimes/*")
|
|
1525
|
+
statement("ApplicationSignalsFromXray", "/aws/application-signals/data")
|
|
1527
1526
|
]
|
|
1528
1527
|
});
|
|
1529
1528
|
}
|
|
@@ -1538,7 +1537,7 @@ function hasSpansIngestionPolicy(describeOutput) {
|
|
|
1538
1537
|
return false;
|
|
1539
1538
|
return policies.some((p) => {
|
|
1540
1539
|
const doc = String(p.policyDocument ?? "");
|
|
1541
|
-
return doc.includes("aws/spans") && doc.includes("logs:CreateLogStream")
|
|
1540
|
+
return doc.includes("aws/spans") && doc.includes("logs:CreateLogStream");
|
|
1542
1541
|
});
|
|
1543
1542
|
}
|
|
1544
1543
|
function parseDestination(text) {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import * as cdk from "aws-cdk-lib";
|
|
26
26
|
import * as ecr from "aws-cdk-lib/aws-ecr";
|
|
27
27
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
28
|
+
import * as logs from "aws-cdk-lib/aws-logs";
|
|
28
29
|
class FoundationAgent extends cdk.Stack {
|
|
29
30
|
repository;
|
|
30
31
|
executionRole;
|
|
@@ -67,6 +68,7 @@ class FoundationAgent extends cdk.Stack {
|
|
|
67
68
|
this.accessPoint = accessPoint;
|
|
68
69
|
this.executionRole = this.buildExecutionRole(props);
|
|
69
70
|
this.readOnlyRole = this.buildReadOnlyRole(props);
|
|
71
|
+
this.buildSpansIngestionPolicy();
|
|
70
72
|
const gateway = this.buildToolGateway(display);
|
|
71
73
|
this.webSearchUrl = gateway.getAtt("GatewayUrl").toString();
|
|
72
74
|
this.executionRole.addToPolicy(new iam.PolicyStatement({
|
|
@@ -318,6 +320,27 @@ class FoundationAgent extends cdk.Stack {
|
|
|
318
320
|
}));
|
|
319
321
|
return role;
|
|
320
322
|
}
|
|
323
|
+
buildSpansIngestionPolicy() {
|
|
324
|
+
return new logs.ResourcePolicy(this, "AgentSpansIngestion", {
|
|
325
|
+
resourcePolicyName: `${this.stackName}-AgentSpansFromXray`,
|
|
326
|
+
policyStatements: [
|
|
327
|
+
new iam.PolicyStatement({
|
|
328
|
+
sid: "AgentSpansFromXray",
|
|
329
|
+
principals: [new iam.ServicePrincipal("xray.amazonaws.com")],
|
|
330
|
+
actions: ["logs:PutLogEvents", "logs:CreateLogStream"],
|
|
331
|
+
resources: [
|
|
332
|
+
`arn:${this.partition}:logs:${this.region}:${this.account}:log-group:/aws/bedrock-agentcore/runtimes/${this.names.runtimeName}-*:*`
|
|
333
|
+
],
|
|
334
|
+
conditions: {
|
|
335
|
+
StringEquals: { "aws:SourceAccount": this.account },
|
|
336
|
+
ArnLike: {
|
|
337
|
+
"aws:SourceArn": `arn:${this.partition}:xray:${this.region}:${this.account}:*`
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
]
|
|
342
|
+
});
|
|
343
|
+
}
|
|
321
344
|
buildExecutionRole(props) {
|
|
322
345
|
const role = new iam.Role(this, "AgentExecutionRole", {
|
|
323
346
|
assumedBy: new iam.ServicePrincipal("bedrock-agentcore.amazonaws.com"),
|
package/dist/src/index.js
CHANGED
package/package.json
CHANGED
package/src/deploy/tracing.ts
CHANGED
|
@@ -82,11 +82,12 @@ export function spansIngestionPolicy(account: string, region: string): string {
|
|
|
82
82
|
Statement: [
|
|
83
83
|
statement("SpansFromXray", "aws/spans"),
|
|
84
84
|
statement("ApplicationSignalsFromXray", "/aws/application-signals/data"),
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
85
|
+
// NOT the runtime's own log group, which is where the agent's spans
|
|
86
|
+
// actually go (`x-aws-log-group`): that grant is per instance and ships
|
|
87
|
+
// with every deploy as the agent stack's `AgentSpansIngestion` policy.
|
|
88
|
+
// Owning it here made it a one-time `setup` side effect, and an instance
|
|
89
|
+
// that reached its account by `deploy` alone never got it — every export
|
|
90
|
+
// a 400 (Olivia, 2026-09-26).
|
|
90
91
|
],
|
|
91
92
|
});
|
|
92
93
|
}
|
|
@@ -102,11 +103,7 @@ export function hasSpansIngestionPolicy(describeOutput: string): boolean {
|
|
|
102
103
|
if (!Array.isArray(policies)) return false;
|
|
103
104
|
return policies.some((p) => {
|
|
104
105
|
const doc = String((p as { policyDocument?: unknown }).policyDocument ?? "");
|
|
105
|
-
return (
|
|
106
|
-
doc.includes("aws/spans") &&
|
|
107
|
-
doc.includes("logs:CreateLogStream") &&
|
|
108
|
-
doc.includes("/aws/bedrock-agentcore/runtimes/")
|
|
109
|
-
);
|
|
106
|
+
return doc.includes("aws/spans") && doc.includes("logs:CreateLogStream");
|
|
110
107
|
});
|
|
111
108
|
}
|
|
112
109
|
|
|
@@ -4,6 +4,7 @@ import type * as ec2 from "aws-cdk-lib/aws-ec2";
|
|
|
4
4
|
import * as ecr from "aws-cdk-lib/aws-ecr";
|
|
5
5
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
6
6
|
import type * as kms from "aws-cdk-lib/aws-kms";
|
|
7
|
+
import * as logs from "aws-cdk-lib/aws-logs";
|
|
7
8
|
import type * as s3 from "aws-cdk-lib/aws-s3";
|
|
8
9
|
import type * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
|
|
9
10
|
import type { Construct } from "constructs";
|
|
@@ -147,6 +148,7 @@ export class FoundationAgent extends cdk.Stack {
|
|
|
147
148
|
this.accessPoint = accessPoint;
|
|
148
149
|
this.executionRole = this.buildExecutionRole(props);
|
|
149
150
|
this.readOnlyRole = this.buildReadOnlyRole(props);
|
|
151
|
+
this.buildSpansIngestionPolicy();
|
|
150
152
|
|
|
151
153
|
// The gateway is independent of the runtime: it can (and does) exist
|
|
152
154
|
// during the phase-one bootstrap deploy.
|
|
@@ -523,6 +525,48 @@ export class FoundationAgent extends cdk.Stack {
|
|
|
523
525
|
return role;
|
|
524
526
|
}
|
|
525
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Let X-Ray write this runtime's spans into the runtime's own log group.
|
|
530
|
+
*
|
|
531
|
+
* The agent posts OTLP to the X-Ray endpoint with `x-aws-log-group` naming
|
|
532
|
+
* `/aws/bedrock-agentcore/runtimes/<runtime>-<endpoint>` (see
|
|
533
|
+
* `packages/agent/src/observability/otel.ts`). X-Ray, not the execution
|
|
534
|
+
* role, then calls `PutLogEvents` — as a role in an AWS service account —
|
|
535
|
+
* so the execution role's `AgentLogs` grant does not apply, and without a
|
|
536
|
+
* CloudWatch Logs RESOURCE policy every export is a 400 "not authorized to
|
|
537
|
+
* perform: logs:PutLogEvents" (Olivia, 2026-09-26). AWS: "If you set
|
|
538
|
+
* OTEL_EXPORTER_OTLP_TRACES_HEADERS to deliver spans to your own log group,
|
|
539
|
+
* you must also add an Amazon CloudWatch Logs resource policy … allow X-Ray
|
|
540
|
+
* (xray.amazonaws.com) to call logs:PutLogEvents on that log group"
|
|
541
|
+
* (bedrock-agentcore devguide, observability-configure).
|
|
542
|
+
*
|
|
543
|
+
* Logs resource policies are account-level (ten per Region), so the name is
|
|
544
|
+
* the stack's own and the grant covers only this runtime's endpoint groups,
|
|
545
|
+
* and only when X-Ray acts for this account. `CreateLogStream` rides along
|
|
546
|
+
* because X-Ray creates the `spans` stream on first delivery.
|
|
547
|
+
*/
|
|
548
|
+
private buildSpansIngestionPolicy(): logs.ResourcePolicy {
|
|
549
|
+
return new logs.ResourcePolicy(this, "AgentSpansIngestion", {
|
|
550
|
+
resourcePolicyName: `${this.stackName}-AgentSpansFromXray`,
|
|
551
|
+
policyStatements: [
|
|
552
|
+
new iam.PolicyStatement({
|
|
553
|
+
sid: "AgentSpansFromXray",
|
|
554
|
+
principals: [new iam.ServicePrincipal("xray.amazonaws.com")],
|
|
555
|
+
actions: ["logs:PutLogEvents", "logs:CreateLogStream"],
|
|
556
|
+
resources: [
|
|
557
|
+
`arn:${this.partition}:logs:${this.region}:${this.account}:log-group:/aws/bedrock-agentcore/runtimes/${this.names.runtimeName}-*:*`,
|
|
558
|
+
],
|
|
559
|
+
conditions: {
|
|
560
|
+
StringEquals: { "aws:SourceAccount": this.account },
|
|
561
|
+
ArnLike: {
|
|
562
|
+
"aws:SourceArn": `arn:${this.partition}:xray:${this.region}:${this.account}:*`,
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
}),
|
|
566
|
+
],
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
526
570
|
private buildExecutionRole(props: FoundationAgentProps): iam.Role {
|
|
527
571
|
const role = new iam.Role(this, "AgentExecutionRole", {
|
|
528
572
|
assumedBy: new iam.ServicePrincipal("bedrock-agentcore.amazonaws.com"),
|
|
@@ -574,9 +618,10 @@ export class FoundationAgent extends cdk.Stack {
|
|
|
574
618
|
],
|
|
575
619
|
}),
|
|
576
620
|
);
|
|
577
|
-
// Spans: the runtime SigV4-posts OTLP to the X-Ray endpoint
|
|
578
|
-
//
|
|
579
|
-
//
|
|
621
|
+
// Spans: the runtime SigV4-posts OTLP to the X-Ray endpoint. These actions
|
|
622
|
+
// only let it call X-Ray (none takes a resource ARN — X-Ray scopes
|
|
623
|
+
// ingestion by account); X-Ray's own write into the runtime log group is
|
|
624
|
+
// authorized by `AgentSpansIngestion`, not by `AgentLogs` above.
|
|
580
625
|
role.addToPolicy(
|
|
581
626
|
new iam.PolicyStatement({
|
|
582
627
|
sid: "TraceIngest",
|