@deployfoundation/foundation-deploy 0.2.38 → 0.2.39
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 +3 -2
- package/dist/bin/foundation-deploy.js +503 -100
- package/dist/bin/release-account.js +1 -2
- package/dist/{chunk-j52g7bsc.js → chunk-6v0ewr5b.js} +18 -4
- package/dist/{chunk-2ra18dqx.js → chunk-7rd8c1th.js} +14 -1
- package/dist/chunk-dz91avrb.js +16 -0
- package/dist/src/index.js +5 -2
- package/package.json +1 -1
- package/src/artifacts.ts +33 -0
- package/src/deploy/release.ts +1 -35
- package/src/deploy-permissions.ts +37 -2
- package/src/index.ts +2 -0
- package/src/release/trust.ts +52 -0
- package/src/stacks/ci-stack.ts +2 -1
- package/src/stacks/pipeline-stack.ts +2 -1
- package/dist/chunk-8e2z4rhf.js +0 -442
|
@@ -17,13 +17,14 @@ import {
|
|
|
17
17
|
parseInstanceConfig,
|
|
18
18
|
parseSchedule,
|
|
19
19
|
provisionsIntegration,
|
|
20
|
+
releaseAccess,
|
|
20
21
|
releaseImageRepositoryArn,
|
|
21
22
|
releaseSource,
|
|
22
23
|
requiresAuthenticatedQueue,
|
|
23
24
|
scheduledJobBindings,
|
|
24
25
|
slackCommandPrefixes,
|
|
25
26
|
webhookJobBindings
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-7rd8c1th.js";
|
|
27
28
|
|
|
28
29
|
// src/stacks/agent-stack.ts
|
|
29
30
|
import * as cdk from "aws-cdk-lib";
|
|
@@ -1846,6 +1847,18 @@ function deployStatements(stack, names, targets, instance) {
|
|
|
1846
1847
|
const runtimeSecret = secretArn(names.secretRuntime);
|
|
1847
1848
|
const knockOauthClient = secretArn(names.secretKnockOauthClient);
|
|
1848
1849
|
return [
|
|
1850
|
+
new iam3.PolicyStatement({
|
|
1851
|
+
sid: "ReadFoundationRelease",
|
|
1852
|
+
actions: ["s3:GetObject"],
|
|
1853
|
+
resources: [`arn:${stack.partition}:s3:::${targets.release.bucket}/releases/*`]
|
|
1854
|
+
}),
|
|
1855
|
+
...targets.release.signingKeyArn === undefined ? [] : [
|
|
1856
|
+
new iam3.PolicyStatement({
|
|
1857
|
+
sid: "VerifyReleaseSignature",
|
|
1858
|
+
actions: ["kms:Verify"],
|
|
1859
|
+
resources: [targets.release.signingKeyArn]
|
|
1860
|
+
})
|
|
1861
|
+
],
|
|
1849
1862
|
new iam3.PolicyStatement({
|
|
1850
1863
|
sid: "AssumeCdkBootstrapRoles",
|
|
1851
1864
|
actions: ["sts:AssumeRole"],
|
|
@@ -1927,7 +1940,8 @@ function deployStatements(stack, names, targets, instance) {
|
|
|
1927
1940
|
"bedrock-agentcore:GetAgentRuntime",
|
|
1928
1941
|
"bedrock-agentcore:ListAgentRuntimeVersions",
|
|
1929
1942
|
"bedrock-agentcore:GetAgentRuntimeEndpoint",
|
|
1930
|
-
"bedrock-agentcore:UpdateAgentRuntimeEndpoint"
|
|
1943
|
+
"bedrock-agentcore:UpdateAgentRuntimeEndpoint",
|
|
1944
|
+
"bedrock-agentcore:CreateAgentRuntimeEndpoint"
|
|
1931
1945
|
],
|
|
1932
1946
|
resources: [
|
|
1933
1947
|
`arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:runtime/${names.runtimeName}*`,
|
|
@@ -1970,7 +1984,7 @@ class FoundationCi extends cdk3.Stack {
|
|
|
1970
1984
|
}),
|
|
1971
1985
|
inlinePolicies: {
|
|
1972
1986
|
FoundationDeploy: new iam4.PolicyDocument({
|
|
1973
|
-
statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey }, props.instance)
|
|
1987
|
+
statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) }, props.instance)
|
|
1974
1988
|
})
|
|
1975
1989
|
}
|
|
1976
1990
|
});
|
|
@@ -3453,7 +3467,7 @@ class FoundationPipeline extends cdk8.Stack {
|
|
|
3453
3467
|
assumedBy: new iam9.ServicePrincipal("codebuild.amazonaws.com"),
|
|
3454
3468
|
inlinePolicies: {
|
|
3455
3469
|
FoundationDeploy: new iam9.PolicyDocument({
|
|
3456
|
-
statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey }, props.instance)
|
|
3470
|
+
statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) }, props.instance)
|
|
3457
3471
|
})
|
|
3458
3472
|
}
|
|
3459
3473
|
});
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_RELEASE_BUCKET,
|
|
3
|
+
configuredKeyArn
|
|
4
|
+
} from "./chunk-dz91avrb.js";
|
|
5
|
+
|
|
1
6
|
// ../core/src/instance.ts
|
|
2
7
|
import { parse as parseYaml } from "yaml";
|
|
3
8
|
import { z as z2 } from "zod";
|
|
@@ -1816,6 +1821,14 @@ function releaseSource(scope) {
|
|
|
1816
1821
|
throw new Error(`-c releaseBucket=<name> is required when deploying a release (or set ${RELEASE_BUCKET_ENV}); it is the bucket holding ${manifest.lambda.gateway?.key ?? "the release artifacts"}`);
|
|
1817
1822
|
return { version: manifest.version, bucket, manifest };
|
|
1818
1823
|
}
|
|
1824
|
+
function releaseAccess(scope, env = process.env) {
|
|
1825
|
+
const context = scope.node.tryGetContext("releaseBucket");
|
|
1826
|
+
const fromContext = typeof context === "string" && context !== "" ? context : undefined;
|
|
1827
|
+
const fromEnv = env[RELEASE_BUCKET_ENV];
|
|
1828
|
+
const bucket = fromContext ?? (fromEnv !== undefined && fromEnv !== "" ? fromEnv : DEFAULT_RELEASE_BUCKET);
|
|
1829
|
+
const signingKeyArn = configuredKeyArn(env);
|
|
1830
|
+
return { bucket, ...signingKeyArn === undefined ? {} : { signingKeyArn } };
|
|
1831
|
+
}
|
|
1819
1832
|
function releaseBucket(scope, source) {
|
|
1820
1833
|
const stack = cdk.Stack.of(scope);
|
|
1821
1834
|
const existing = stack.node.tryFindChild("FoundationReleaseBucket");
|
|
@@ -1899,4 +1912,4 @@ function ecrRepositoryArn(repositoryUri) {
|
|
|
1899
1912
|
return `arn:aws:ecr:${region}:${account}:repository/${name}`;
|
|
1900
1913
|
}
|
|
1901
1914
|
|
|
1902
|
-
export { parseSchedule, BASE_SLACK_EVENTS, CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, encodeProactiveChannels, CUSTOMIZATION_ARTIFACT_NAME, newsletterManifestFor, FOUNDATION_VERSION_RE, versionedConfigKey, instanceNames, slackCommandPrefix, slackCommandPrefixes, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, capabilityScheduledJobs, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, loadInstanceFile, configPathFor, capabilityEnabled, requiresAuthenticatedQueue, enabledIntegrations, provisionsIntegration, adminsFor, WEB_SEARCH_TARGET, WEB_SEARCH_CONNECTOR_VERSION, LIVE_ENDPOINT_NAME, RELEASE_VERSION_RE, parseManifest, manifestKey, lambdaKey, skillsKey, buildManifest, manifestSigningPayload, verifyManifest, lambdaBundleContext, BUN_VERSION, LAMBDA_ENTRY_POINTS, skipBundle, RELEASE_BUCKET_ENV, RELEASE_DIR_ENV, releaseCacheDir, releaseSource, lambdaCode, agentImage, agentImageTagRequired, releaseImageRepositoryArn, ecrRepositoryArn };
|
|
1915
|
+
export { parseSchedule, BASE_SLACK_EVENTS, CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, encodeProactiveChannels, CUSTOMIZATION_ARTIFACT_NAME, newsletterManifestFor, FOUNDATION_VERSION_RE, versionedConfigKey, instanceNames, slackCommandPrefix, slackCommandPrefixes, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, capabilityScheduledJobs, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, loadInstanceFile, configPathFor, capabilityEnabled, requiresAuthenticatedQueue, enabledIntegrations, provisionsIntegration, adminsFor, WEB_SEARCH_TARGET, WEB_SEARCH_CONNECTOR_VERSION, LIVE_ENDPOINT_NAME, RELEASE_VERSION_RE, parseManifest, manifestKey, lambdaKey, skillsKey, buildManifest, manifestSigningPayload, verifyManifest, lambdaBundleContext, BUN_VERSION, LAMBDA_ENTRY_POINTS, skipBundle, RELEASE_BUCKET_ENV, RELEASE_DIR_ENV, releaseCacheDir, releaseSource, releaseAccess, lambdaCode, agentImage, agentImageTagRequired, releaseImageRepositoryArn, ecrRepositoryArn };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/release/trust.ts
|
|
2
|
+
var DEFAULT_RELEASE_BUCKET = "foundry41-foundation-releases";
|
|
3
|
+
var RELEASE_KMS_KEY_ARN_ENV = "FOUNDATION_RELEASE_KMS_KEY_ARN";
|
|
4
|
+
var RELEASE_KMS_KEY_ARN = "arn:aws:kms:us-east-1:793593623536:key/fba7d9f3-e9b6-49ae-b4a5-c8c5f1429231";
|
|
5
|
+
function configuredKeyArn(env = process.env) {
|
|
6
|
+
const configured = env[RELEASE_KMS_KEY_ARN_ENV] ?? RELEASE_KMS_KEY_ARN;
|
|
7
|
+
return configured === undefined || configured === "" ? undefined : configured;
|
|
8
|
+
}
|
|
9
|
+
function expectedKeyArn(env = process.env) {
|
|
10
|
+
const configured = configuredKeyArn(env);
|
|
11
|
+
if (configured === undefined)
|
|
12
|
+
throw new Error(`no Foundation release signing key is configured; set ${RELEASE_KMS_KEY_ARN_ENV} to the key ARN published with the release`);
|
|
13
|
+
return configured;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { DEFAULT_RELEASE_BUCKET, configuredKeyArn, expectedKeyArn };
|
package/dist/src/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
FoundationPipeline,
|
|
11
11
|
NewsletterStack,
|
|
12
12
|
deployStatements
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-6v0ewr5b.js";
|
|
14
14
|
import {
|
|
15
15
|
BUN_VERSION,
|
|
16
16
|
LAMBDA_ENTRY_POINTS,
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
manifestSigningPayload,
|
|
37
37
|
parseManifest,
|
|
38
38
|
provisionsIntegration,
|
|
39
|
+
releaseAccess,
|
|
39
40
|
releaseCacheDir,
|
|
40
41
|
releaseImageRepositoryArn,
|
|
41
42
|
releaseSource,
|
|
@@ -43,7 +44,8 @@ import {
|
|
|
43
44
|
skillsKey,
|
|
44
45
|
skipBundle,
|
|
45
46
|
verifyManifest
|
|
46
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-7rd8c1th.js";
|
|
48
|
+
import"../chunk-dz91avrb.js";
|
|
47
49
|
export {
|
|
48
50
|
BUILD_TIMEOUT,
|
|
49
51
|
BUN_VERSION,
|
|
@@ -80,6 +82,7 @@ export {
|
|
|
80
82
|
instanceNames as namesFor,
|
|
81
83
|
parseManifest,
|
|
82
84
|
provisionsIntegration,
|
|
85
|
+
releaseAccess,
|
|
83
86
|
releaseCacheDir,
|
|
84
87
|
releaseImageRepositoryArn,
|
|
85
88
|
releaseSource,
|
package/package.json
CHANGED
package/src/artifacts.ts
CHANGED
|
@@ -41,6 +41,7 @@ import * as s3 from "aws-cdk-lib/aws-s3";
|
|
|
41
41
|
import type { Construct } from "constructs";
|
|
42
42
|
import { lambdaBundleContext } from "./lambda-bundle-context.ts";
|
|
43
43
|
import { type ReleaseManifest, parseManifest } from "./release/manifest.ts";
|
|
44
|
+
import { DEFAULT_RELEASE_BUCKET, configuredKeyArn } from "./release/trust.ts";
|
|
44
45
|
|
|
45
46
|
/** Pinned Bun used to bundle every Lambda (and the docker fallback image tag). */
|
|
46
47
|
export const BUN_VERSION = "1.4.0";
|
|
@@ -174,6 +175,38 @@ export function releaseSource(scope: Construct): ReleaseSource | undefined {
|
|
|
174
175
|
return { version: manifest.version, bucket, manifest };
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
/**
|
|
179
|
+
* What a deployer of this app reads from Foundation's release account: the
|
|
180
|
+
* bucket the tool fetches `releases/*` from and the key whose signature it
|
|
181
|
+
* checks. The deployer roles (`deploy-permissions.ts`) are granted exactly
|
|
182
|
+
* these, cross-account, so a CodePipeline or GitHub Actions deploy can verify
|
|
183
|
+
* a release the way an admin's laptop can.
|
|
184
|
+
*
|
|
185
|
+
* Resolved the way `foundation-deploy` resolves them — `-c releaseBucket`
|
|
186
|
+
* (which the tool passes on a release deploy) or `FOUNDATION_RELEASE_BUCKET`,
|
|
187
|
+
* else the default bucket; `FOUNDATION_RELEASE_KMS_KEY_ARN`, else the pinned
|
|
188
|
+
* key — and resolved on EVERY synth, release or local build, so the grant does
|
|
189
|
+
* not come and go with how the last deploy was run.
|
|
190
|
+
*/
|
|
191
|
+
export interface ReleaseAccess {
|
|
192
|
+
bucket: string;
|
|
193
|
+
/** Absent only when no signing key is configured at all; nothing is verified then. */
|
|
194
|
+
signingKeyArn?: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function releaseAccess(
|
|
198
|
+
scope: Construct,
|
|
199
|
+
env: Record<string, string | undefined> = process.env,
|
|
200
|
+
): ReleaseAccess {
|
|
201
|
+
const context: unknown = scope.node.tryGetContext("releaseBucket");
|
|
202
|
+
const fromContext = typeof context === "string" && context !== "" ? context : undefined;
|
|
203
|
+
const fromEnv = env[RELEASE_BUCKET_ENV];
|
|
204
|
+
const bucket =
|
|
205
|
+
fromContext ?? (fromEnv !== undefined && fromEnv !== "" ? fromEnv : DEFAULT_RELEASE_BUCKET);
|
|
206
|
+
const signingKeyArn = configuredKeyArn(env);
|
|
207
|
+
return { bucket, ...(signingKeyArn === undefined ? {} : { signingKeyArn }) };
|
|
208
|
+
}
|
|
209
|
+
|
|
177
210
|
/**
|
|
178
211
|
* The release bucket as one imported construct per stack. Every Lambda in a
|
|
179
212
|
* stack references the same bucket, and importing it once keeps the template's
|
package/src/deploy/release.ts
CHANGED
|
@@ -29,34 +29,10 @@ import {
|
|
|
29
29
|
manifestKey,
|
|
30
30
|
verifyManifest,
|
|
31
31
|
} from "../release/manifest.ts";
|
|
32
|
+
import { DEFAULT_RELEASE_BUCKET, expectedKeyArn } from "../release/trust.ts";
|
|
32
33
|
import { type AwsCallContext, aws } from "./aws.ts";
|
|
33
34
|
import { FOUNDATION_ROOT, INFRA_ROOT } from "./paths.ts";
|
|
34
35
|
|
|
35
|
-
/**
|
|
36
|
-
* The bucket a release is fetched from when nothing says otherwise.
|
|
37
|
-
*
|
|
38
|
-
* One name, owned by Foundry 41, created by `release-account-stack.ts`. A
|
|
39
|
-
* mirror is `--release-bucket` or `FOUNDATION_RELEASE_BUCKET`, and the
|
|
40
|
-
* signature is what makes a mirror safe to use.
|
|
41
|
-
*/
|
|
42
|
-
export const DEFAULT_RELEASE_BUCKET = "foundry41-foundation-releases";
|
|
43
|
-
|
|
44
|
-
/** The env var that names the key a deployer will accept a manifest from. */
|
|
45
|
-
export const RELEASE_KMS_KEY_ARN_ENV = "FOUNDATION_RELEASE_KMS_KEY_ARN";
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* The Foundation release signing key, in the Foundry 41 release account
|
|
49
|
-
* (793593623536), created by `ReleaseAccountStack` on 2026-09-20.
|
|
50
|
-
*
|
|
51
|
-
* This constant is what makes signature verification mean anything: a
|
|
52
|
-
* deployer refuses a manifest signed by any other key. Changing it rotates
|
|
53
|
-
* the root of trust for every customer, so it changes only with a key
|
|
54
|
-
* rotation and a release that says so. {@link RELEASE_KMS_KEY_ARN_ENV}
|
|
55
|
-
* overrides it, for a private mirror or a test key.
|
|
56
|
-
*/
|
|
57
|
-
export const RELEASE_KMS_KEY_ARN: string | undefined =
|
|
58
|
-
"arn:aws:kms:us-east-1:793593623536:key/fba7d9f3-e9b6-49ae-b4a5-c8c5f1429231";
|
|
59
|
-
|
|
60
36
|
/** What the caller asked for, before anything has been fetched. */
|
|
61
37
|
export interface ReleaseRequest {
|
|
62
38
|
/** `vX.Y.Z`, when the caller named a version or one was implied. */
|
|
@@ -149,16 +125,6 @@ export function releaseRequest(
|
|
|
149
125
|
return { version, bucket };
|
|
150
126
|
}
|
|
151
127
|
|
|
152
|
-
/** The key ARN a deployer will accept a manifest from. */
|
|
153
|
-
export function expectedKeyArn(env: Record<string, string | undefined> = process.env): string {
|
|
154
|
-
const configured = env[RELEASE_KMS_KEY_ARN_ENV] ?? RELEASE_KMS_KEY_ARN;
|
|
155
|
-
if (configured === undefined || configured === "")
|
|
156
|
-
throw new Error(
|
|
157
|
-
`no Foundation release signing key is configured; set ${RELEASE_KMS_KEY_ARN_ENV} to the key ARN published with the release`,
|
|
158
|
-
);
|
|
159
|
-
return configured;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
128
|
export interface ResolveReleaseDeps {
|
|
163
129
|
/** Defaults to the SDK-backed verifier for the expected key. */
|
|
164
130
|
kms?: KmsVerifier;
|
|
@@ -17,6 +17,7 @@ import type * as cdk from "aws-cdk-lib";
|
|
|
17
17
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
18
18
|
import type * as kms from "aws-cdk-lib/aws-kms";
|
|
19
19
|
import type * as s3 from "aws-cdk-lib/aws-s3";
|
|
20
|
+
import type { ReleaseAccess } from "./artifacts.ts";
|
|
20
21
|
import { type Instance, type InstanceNames, provisionsIntegration } from "./names.ts";
|
|
21
22
|
|
|
22
23
|
export interface DeployTargets {
|
|
@@ -24,6 +25,13 @@ export interface DeployTargets {
|
|
|
24
25
|
bucket: s3.IBucket;
|
|
25
26
|
/** The CMK that bucket is encrypted with. */
|
|
26
27
|
dataKey: kms.IKey;
|
|
28
|
+
/**
|
|
29
|
+
* Foundation's release bucket and signing key, in the release account
|
|
30
|
+
* (`releaseAccess()` in `artifacts.ts`). Cross-account access needs BOTH
|
|
31
|
+
* sides to allow it: the release account's bucket and key policies name the
|
|
32
|
+
* customer account, and the statements below are this account's half.
|
|
33
|
+
*/
|
|
34
|
+
release: ReleaseAccess;
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
/**
|
|
@@ -49,6 +57,30 @@ export function deployStatements(
|
|
|
49
57
|
const knockOauthClient = secretArn(names.secretKnockOauthClient);
|
|
50
58
|
|
|
51
59
|
return [
|
|
60
|
+
// A release deploy, before anything else: `aws s3 cp` of
|
|
61
|
+
// `releases/<v>/manifest.json`, then of every Lambda zip and
|
|
62
|
+
// `skills.tar.gz` the manifest names (the digest check in `verifyRelease`),
|
|
63
|
+
// and of `skills.tar.gz` again for the config sync (`unpackReleaseSkills`).
|
|
64
|
+
// HeadObject is authorized by s3:GetObject. The bucket is SSE-S3, so there
|
|
65
|
+
// is no key to grant for it.
|
|
66
|
+
new iam.PolicyStatement({
|
|
67
|
+
sid: "ReadFoundationRelease",
|
|
68
|
+
actions: ["s3:GetObject"],
|
|
69
|
+
resources: [`arn:${stack.partition}:s3:::${targets.release.bucket}/releases/*`],
|
|
70
|
+
}),
|
|
71
|
+
// ...and the manifest's signature, checked with one KMS `Verify` against
|
|
72
|
+
// the pinned key (`src/release/kms.ts`); nothing calls GetPublicKey or
|
|
73
|
+
// DescribeKey. By key ARN, not alias: an identity policy cannot name
|
|
74
|
+
// another account's key by its alias.
|
|
75
|
+
...(targets.release.signingKeyArn === undefined
|
|
76
|
+
? []
|
|
77
|
+
: [
|
|
78
|
+
new iam.PolicyStatement({
|
|
79
|
+
sid: "VerifyReleaseSignature",
|
|
80
|
+
actions: ["kms:Verify"],
|
|
81
|
+
resources: [targets.release.signingKeyArn],
|
|
82
|
+
}),
|
|
83
|
+
]),
|
|
52
84
|
// How `cdk deploy` does everything else: it assumes the bootstrap roles for
|
|
53
85
|
// lookup, asset publishing and the CloudFormation call.
|
|
54
86
|
new iam.PolicyStatement({
|
|
@@ -140,8 +172,10 @@ export function deployStatements(
|
|
|
140
172
|
actions: ["kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey*"],
|
|
141
173
|
resources: [targets.dataKey.keyArn],
|
|
142
174
|
}),
|
|
143
|
-
// `foundation-deploy post-deploy
|
|
144
|
-
// `_fs_probe`) against DEFAULT, then promotion
|
|
175
|
+
// `foundation-deploy post-deploy` (and `deploy`'s inline promotion): the
|
|
176
|
+
// smoke test (`_smoke_test`, `_fs_probe`) against DEFAULT, then promotion
|
|
177
|
+
// of the new version onto `live`, which CREATES `live` when an instance
|
|
178
|
+
// brought up by `deploy` alone has none yet (`promoteEndpoint`).
|
|
145
179
|
new iam.PolicyStatement({
|
|
146
180
|
sid: "SmokeTestRuntime",
|
|
147
181
|
actions: [
|
|
@@ -150,6 +184,7 @@ export function deployStatements(
|
|
|
150
184
|
"bedrock-agentcore:ListAgentRuntimeVersions",
|
|
151
185
|
"bedrock-agentcore:GetAgentRuntimeEndpoint",
|
|
152
186
|
"bedrock-agentcore:UpdateAgentRuntimeEndpoint",
|
|
187
|
+
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
|
|
153
188
|
],
|
|
154
189
|
resources: [
|
|
155
190
|
`arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:runtime/${names.runtimeName}*`,
|
package/src/index.ts
CHANGED
|
@@ -4,11 +4,13 @@ export {
|
|
|
4
4
|
RELEASE_BUCKET_ENV,
|
|
5
5
|
RELEASE_DIR_ENV,
|
|
6
6
|
type LambdaArtifactId,
|
|
7
|
+
type ReleaseAccess,
|
|
7
8
|
type ReleaseSource,
|
|
8
9
|
agentImage,
|
|
9
10
|
agentImageTagRequired,
|
|
10
11
|
ecrRepositoryArn,
|
|
11
12
|
lambdaCode,
|
|
13
|
+
releaseAccess,
|
|
12
14
|
releaseCacheDir,
|
|
13
15
|
releaseImageRepositoryArn,
|
|
14
16
|
releaseSource,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a deployer gets a release from, and whose signature it accepts.
|
|
3
|
+
*
|
|
4
|
+
* Pure, like `manifest.ts`: the deploy tool reads these to fetch and verify a
|
|
5
|
+
* release, and the CDK app reads them to give the deployer roles
|
|
6
|
+
* (`deploy-permissions.ts`) the release-account permissions that fetch needs.
|
|
7
|
+
* Both must resolve to the same bucket and the same key, so the resolution
|
|
8
|
+
* lives here once rather than in each.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The bucket a release is fetched from when nothing says otherwise.
|
|
13
|
+
*
|
|
14
|
+
* One name, owned by Foundry 41, created by `release-account-stack.ts`. A
|
|
15
|
+
* mirror is `--release-bucket` or `FOUNDATION_RELEASE_BUCKET`, and the
|
|
16
|
+
* signature is what makes a mirror safe to use.
|
|
17
|
+
*/
|
|
18
|
+
export const DEFAULT_RELEASE_BUCKET = "foundry41-foundation-releases";
|
|
19
|
+
|
|
20
|
+
/** The env var that names the key a deployer will accept a manifest from. */
|
|
21
|
+
export const RELEASE_KMS_KEY_ARN_ENV = "FOUNDATION_RELEASE_KMS_KEY_ARN";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The Foundation release signing key, in the Foundry 41 release account
|
|
25
|
+
* (793593623536), created by `ReleaseAccountStack` on 2026-09-20.
|
|
26
|
+
*
|
|
27
|
+
* This constant is what makes signature verification mean anything: a
|
|
28
|
+
* deployer refuses a manifest signed by any other key. Changing it rotates
|
|
29
|
+
* the root of trust for every customer, so it changes only with a key
|
|
30
|
+
* rotation and a release that says so. {@link RELEASE_KMS_KEY_ARN_ENV}
|
|
31
|
+
* overrides it, for a private mirror or a test key.
|
|
32
|
+
*/
|
|
33
|
+
export const RELEASE_KMS_KEY_ARN: string | undefined =
|
|
34
|
+
"arn:aws:kms:us-east-1:793593623536:key/fba7d9f3-e9b6-49ae-b4a5-c8c5f1429231";
|
|
35
|
+
|
|
36
|
+
/** The key ARN a deployer will accept a manifest from, or `undefined` when none is configured. */
|
|
37
|
+
export function configuredKeyArn(
|
|
38
|
+
env: Record<string, string | undefined> = process.env,
|
|
39
|
+
): string | undefined {
|
|
40
|
+
const configured = env[RELEASE_KMS_KEY_ARN_ENV] ?? RELEASE_KMS_KEY_ARN;
|
|
41
|
+
return configured === undefined || configured === "" ? undefined : configured;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The key ARN a deployer will accept a manifest from. Throws when none is configured. */
|
|
45
|
+
export function expectedKeyArn(env: Record<string, string | undefined> = process.env): string {
|
|
46
|
+
const configured = configuredKeyArn(env);
|
|
47
|
+
if (configured === undefined)
|
|
48
|
+
throw new Error(
|
|
49
|
+
`no Foundation release signing key is configured; set ${RELEASE_KMS_KEY_ARN_ENV} to the key ARN published with the release`,
|
|
50
|
+
);
|
|
51
|
+
return configured;
|
|
52
|
+
}
|
package/src/stacks/ci-stack.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as iam from "aws-cdk-lib/aws-iam";
|
|
|
3
3
|
import type * as kms from "aws-cdk-lib/aws-kms";
|
|
4
4
|
import type * as s3 from "aws-cdk-lib/aws-s3";
|
|
5
5
|
import type { Construct } from "constructs";
|
|
6
|
+
import { releaseAccess } from "../artifacts.ts";
|
|
6
7
|
import { deployStatements } from "../deploy-permissions.ts";
|
|
7
8
|
import { type Instance, namesFor } from "../names.ts";
|
|
8
9
|
|
|
@@ -84,7 +85,7 @@ export class FoundationCi extends cdk.Stack {
|
|
|
84
85
|
statements: deployStatements(
|
|
85
86
|
this,
|
|
86
87
|
names,
|
|
87
|
-
{ bucket: props.bucket, dataKey: props.dataKey },
|
|
88
|
+
{ bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) },
|
|
88
89
|
props.instance,
|
|
89
90
|
),
|
|
90
91
|
}),
|
|
@@ -40,6 +40,7 @@ import type * as s3 from "aws-cdk-lib/aws-s3";
|
|
|
40
40
|
import * as sns from "aws-cdk-lib/aws-sns";
|
|
41
41
|
import * as subscriptions from "aws-cdk-lib/aws-sns-subscriptions";
|
|
42
42
|
import type { Construct } from "constructs";
|
|
43
|
+
import { releaseAccess } from "../artifacts.ts";
|
|
43
44
|
import { deployStatements } from "../deploy-permissions.ts";
|
|
44
45
|
import { type Instance, namesFor } from "../names.ts";
|
|
45
46
|
|
|
@@ -101,7 +102,7 @@ export class FoundationPipeline extends cdk.Stack {
|
|
|
101
102
|
statements: deployStatements(
|
|
102
103
|
this,
|
|
103
104
|
names,
|
|
104
|
-
{ bucket: props.bucket, dataKey: props.dataKey },
|
|
105
|
+
{ bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) },
|
|
105
106
|
props.instance,
|
|
106
107
|
),
|
|
107
108
|
}),
|