Skip to content
Merged

Seed #2546

Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
e3900e3
seed setup and seed secrets
Jan 22, 2025
8260a2e
skeleton of sandbox seed command
Jan 22, 2025
8935019
merge main
Jan 28, 2025
31c6da1
seed policy generation
Jan 30, 2025
bf92de4
grants for storage
Jan 30, 2025
6db8fbc
changed seed policy name to amplifySeedPolicy
Jan 31, 2025
889e903
test for secrets
Jan 31, 2025
1dd9c54
removed managed seed policy
Feb 5, 2025
ec798d4
set secret and seed command
Feb 5, 2025
0747b04
generate seed policy command
Feb 5, 2025
a996f26
auth APIs - incomplete
Feb 6, 2025
e1a2bc7
created folders, getting data from generateConfig
Feb 7, 2025
0f1bd95
removed static, changed name of AuthOutputsReader to ConfigReader
Feb 7, 2025
5dd262d
fixed seed commands, figured out callback for TOTP
Feb 10, 2025
f548f3f
conditional typing
Feb 10, 2025
b5d18f5
mfa updates and seed command test
Feb 13, 2025
47123a9
adjustments and tests added
Feb 17, 2025
c55f39e
update to user group type
Feb 17, 2025
e9991c4
merge main into branch
Feb 17, 2025
51f47c7
changeset
Feb 24, 2025
8ce4880
update api
Feb 24, 2025
b34532e
unit tests
Feb 27, 2025
c395733
adjust MFA type
Feb 27, 2025
8b3dc92
Merge branch 'main' into seed-feat
Feb 27, 2025
c447235
remove edit to construct container
Feb 27, 2025
892628b
removed unnecessary changeset
Feb 27, 2025
5485bd6
fixed some tests
Feb 27, 2025
7c2287c
fix dependencies tests
Feb 27, 2025
7828ae0
adjust seed command test
Feb 27, 2025
f98fee9
fix dep inconsistencies
Feb 27, 2025
ef27369
updated challenge response to use secretValue instead of input
Feb 27, 2025
8212b46
PR feedback
Feb 28, 2025
9ace2f2
more PR feedback
Feb 28, 2025
7f9915d
Merge branch 'main' into seed-feat
Feb 28, 2025
5852d7b
even more PR feedback
Feb 28, 2025
a189d11
testing something
Mar 1, 2025
446018b
reverted
Mar 1, 2025
0b5910c
e2e tests
Mar 3, 2025
7a7b2f9
e2e-tests
Mar 3, 2025
c7defa8
revert some package-lock changes
Mar 3, 2025
587586d
e2e tests
Mar 5, 2025
d89dd6c
removed user check from seed test
Mar 5, 2025
5949f2e
remove describeUserpoolCommand
Mar 5, 2025
c9755a7
update test
Mar 5, 2025
725ef86
sts package-lock update
Mar 5, 2025
9505ba1
e2e tests
Mar 5, 2025
909813c
fix package versions
Mar 5, 2025
32a77d9
fixing permissions
Mar 5, 2025
682b22a
import crypto
Mar 5, 2025
72bc973
appeasing json
Mar 5, 2025
9f8c5ab
fixing json
Mar 5, 2025
c05e494
cleaned up e2e file
Mar 5, 2025
b1dcca9
crpyto polyfill in e2e test seed script
Mar 5, 2025
10d4717
added context
Mar 12, 2025
2dd52a5
Merge branch 'main' into seed-feat
Mar 12, 2025
20cee9f
fix dependency issues
Mar 12, 2025
a0682e0
added spinner and success message to seed command
Mar 17, 2025
241d6e6
Merge branch 'main' into seed-feat
Mar 17, 2025
0f2ae13
updated success message
Mar 18, 2025
9949198
lint fixes from prettier update
Mar 18, 2025
bfea93a
updated some tests
Mar 19, 2025
e6de640
Merge branch 'main' into seed-feat
ShadowCat567 Mar 20, 2025
da3d96b
adjustments
ShadowCat567 Mar 20, 2025
47836d8
fixes after prettier sandbox merge
Mar 20, 2025
99d4ff2
Merge branch 'main' into seed-feat
Mar 20, 2025
3cf5bec
small api adjustment
Mar 20, 2025
31d458e
Merge branch 'main' into seed-feat
Mar 21, 2025
8488ca1
seed version bump changed to major
Mar 21, 2025
a40f891
reverted some package changes
Mar 21, 2025
5941f88
PR feedback
Mar 21, 2025
cfe281d
Merge branch 'main' into seed-feat
Mar 21, 2025
83a15e0
make codeQL happy
Mar 21, 2025
a1ee08d
Potential fix for code scanning alert no. 327: Insecure randomness
ShadowCat567 Mar 21, 2025
8ef43dc
Merge branch 'seed-feat' of github.com:aws-amplify/amplify-backend in…
Mar 21, 2025
252b150
e2e tests did not like readFile, using import
Mar 22, 2025
55a5253
fixed seed e2e tests
Mar 24, 2025
07adffb
Merge branch 'main' into seed-feat
Mar 24, 2025
dbde683
removed outputs import
Mar 24, 2025
a81efe3
Merge branch 'main' into seed-feat
Mar 24, 2025
b7decfa
Update packages/cli/src/commands/sandbox/sandbox-seed/sandbox_seed_co…
ShadowCat567 Mar 24, 2025
15b5277
fixed tests
Mar 25, 2025
2bf0970
Merge branch 'main' into seed-feat
Mar 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/little-kids-double.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'@aws-amplify/seed': minor
'@aws-amplify/seed': major
'@aws-amplify/backend-cli': minor
---

Expand Down
3,958 changes: 1,956 additions & 2,002 deletions package-lock.json

Large diffs are not rendered by default.

47 changes: 24 additions & 23 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,28 @@
},
"homepage": "https://github.com/aws-amplify/amplify-backend#readme",
"dependencies": {
"@aws-amplify/backend-deployer": "^1.1.17",
"@aws-amplify/backend-output-schemas": "^1.4.0",
"@aws-amplify/backend-secret": "^1.1.6",
"@aws-amplify/cli-core": "^1.2.4",
"@aws-amplify/client-config": "^1.5.7",
"@aws-amplify/deployed-backend-client": "^1.5.0",
"@aws-amplify/form-generator": "^1.0.4",
"@aws-amplify/model-generator": "^1.0.12",
"@aws-amplify/platform-core": "^1.6.3",
"@aws-amplify/plugin-types": "^1.8.0",
"@aws-amplify/sandbox": "^1.2.10",
"@aws-amplify/schema-generator": "^1.2.7",
"@aws-sdk/client-amplify": "^3.624.0",
"@aws-sdk/client-cloudformation": "^3.624.0",
"@aws-sdk/client-cognito-identity-provider": "^3.624.0",
"@aws-sdk/client-s3": "^3.624.0",
"@aws-sdk/credential-provider-ini": "^3.624.0",
"@aws-sdk/credential-providers": "^3.624.0",
"@aws-sdk/region-config-resolver": "^3.614.0",
"@smithy/node-config-provider": "^2.1.3",
"@smithy/shared-ini-file-loader": "^2.2.5",
"@aws-amplify/backend-deployer": "^1.1.20",
"@aws-amplify/backend-output-schemas": "^1.4.1",
"@aws-amplify/backend-secret": "^1.2.0",
"@aws-amplify/cli-core": "^1.4.1",
"@aws-amplify/client-config": "^1.5.8",
"@aws-amplify/deployed-backend-client": "^1.5.2",
"@aws-amplify/form-generator": "^1.0.5",
"@aws-amplify/model-generator": "^1.0.13",
"@aws-amplify/platform-core": "^1.6.5",
"@aws-amplify/plugin-types": "^1.8.1",
"@aws-amplify/sandbox": "^1.2.12",
"@aws-amplify/schema-generator": "^1.2.8",
"@aws-sdk/client-amplify": "^3.750.0",
"@aws-sdk/client-cloudformation": "^3.750.0",
"@aws-sdk/client-s3": "^3.750.0",
"@aws-sdk/client-sts": "^3.750.0",
"@aws-sdk/credential-provider-ini": "^3.750.0",
"@aws-sdk/credential-providers": "^3.750.0",
"@aws-sdk/region-config-resolver": "^3.734.0",
"@smithy/node-config-provider": "^4.0.1",
"@smithy/shared-ini-file-loader": "^4.0.1",
"@aws-cdk/toolkit-lib": "0.1.5",
"envinfo": "^7.11.0",
"execa": "^9.5.1",
"is-ci": "^4.1.0",
Expand All @@ -61,8 +62,8 @@
"zod": "^3.22.2"
},
"peerDependencies": {
"@aws-sdk/types": "^3.609.0",
"aws-cdk-lib": "^2.168.0"
"@aws-sdk/types": "^3.734.0",
"aws-cdk-lib": "^2.180.0"
},
"devDependencies": {
"@types/envinfo": "^7.8.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ void describe('sandbox seed command', () => {
const mockHandleProfile = mock.method(
commandMiddleware,
'ensureAwsCredentialAndRegion',
() => null
() => null,
);

const mockProfileResolver = mock.fn();

let amplifySeedDir: string;
let fullPath: string;

Expand All @@ -57,8 +59,9 @@ void describe('sandbox seed command', () => {
before(async () => {
const sandboxFactory = new SandboxSingletonFactory(
() => Promise.resolve(testBackendId),
mockProfileResolver,
printer,
format
format,
);

const sandboxSeedCommand = new SandboxSeedCommand(sandboxIdResolver, [
Expand All @@ -78,7 +81,7 @@ void describe('sandbox seed command', () => {
successfulDeployment: [clientConfigGenerationMock],
successfulDeletion: [clientConfigDeletionMock],
failedDeployment: [],
})
}),
);
const parser = yargs().command(sandboxCommand as unknown as CommandModule);
commandRunner = new TestCommandRunner(parser);
Expand All @@ -102,11 +105,16 @@ void describe('sandbox seed command', () => {
}
});

// this is deceptively hard to test because mocking open/read did not work on Linux/Mac
void it('runs seed if seed script is found', async () => {
const output = await commandRunner.runCommand('sandbox seed');

const cleanedOutput = output.trimEnd().trimStart();

assert.ok(output !== undefined);
assert.deepStrictEqual(
cleanedOutput,
'✔ seed has successfully completed',
);
assert.strictEqual(mockHandleProfile.mock.callCount(), 1);
});
});
Expand All @@ -130,15 +138,14 @@ void describe('sandbox seed command', () => {
await assert.rejects(
() => commandRunner.runCommand('sandbox seed'),
(err: TestCommandError) => {
// file differences between Unix and Windows makes it tricky to add the path
assert.match(err.output, /SeedScriptNotFoundError/);
assert.match(err.output, /There is no file that corresponds to/);
assert.match(
err.output,
/Please make a file that corresponds to (.*) and put your seed logic in it/
/Please make a file that corresponds to (.*) and put your seed logic in it/,
);
return true;
}
},
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { execa } from 'execa';
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js';
import { AmplifyUserError } from '@aws-amplify/platform-core';
import { SandboxCommandGlobalOptions } from '../option_types.js';
import { format, printer } from '@aws-amplify/cli-core';

/**
* Command that runs seed in sandbox environment
Expand All @@ -25,7 +26,7 @@ export class SandboxSeedCommand implements CommandModule<object> {
*/
constructor(
private readonly backendIDResolver: SandboxBackendIdResolver,
private readonly seedSubCommands: CommandModule[]
private readonly seedSubCommands: CommandModule[],
) {
this.command = 'seed';
this.describe = 'Seeds sandbox environment';
Expand All @@ -35,15 +36,22 @@ export class SandboxSeedCommand implements CommandModule<object> {
* @inheritDoc
*/
handler = async (): Promise<void> => {
printer.startSpinner('');
const backendID = await this.backendIDResolver.resolve();
const seedPath = path.join('amplify', 'seed', 'seed.ts');
await execa('tsx', [seedPath], {
cwd: process.cwd(),
stdio: 'inherit',
env: {
AMPLIFY_BACKEND_IDENTIFIER: JSON.stringify(backendID),
},
});
try {
await execa('tsx', [seedPath], {
cwd: process.cwd(),
stdio: 'inherit',
env: {
AMPLIFY_BACKEND_IDENTIFIER: JSON.stringify(backendID),
},
});
} finally {
printer.stopSpinner();
}
printer.printNewLine();
printer.print(`${format.success('✔')} seed has successfully completed`);
};

/**
Expand All @@ -53,7 +61,6 @@ export class SandboxSeedCommand implements CommandModule<object> {
return yargs.command(this.seedSubCommands).check(() => {
const seedPath = path.join(process.cwd(), 'amplify', 'seed', 'seed.ts');
if (!existsSync(seedPath)) {
// this only gets sent to outputs
throw new AmplifyUserError('SeedScriptNotFoundError', {
message: `There is no file that corresponds to ${seedPath}`,
resolution: `Please make a file that corresponds to ${seedPath} and put your seed logic in it`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class SandboxSeedGeneratePolicyCommand implements CommandModule<object> {
handler = async (): Promise<void> => {
const backendId = await this.backendIdResolver.resolve();
const policyDocument = await generateSeedPolicyTemplate(backendId);
printer.print(JSON.stringify(policyDocument.toJSON(), null, 1));
printer.print(JSON.stringify(policyDocument.toJSON(), null, 2));
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import {
import { S3Client } from '@aws-sdk/client-s3';
import { AmplifyClient } from '@aws-sdk/client-amplify';
import { CloudFormationClient } from '@aws-sdk/client-cloudformation';
import { NoticesRenderer } from '../../notices/notices_renderer.js';
import { SandboxSeedGeneratePolicyCommand } from './sandbox-seed/sandbox_seed_policy_command.js';
import { SDKProfileResolverProvider } from '../../sdk_profile_resolver_provider.js';

/**
* Creates wired sandbox command.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ import { beforeEach, describe, it, mock } from 'node:test';
import assert from 'assert';
import { BackendIdentifier } from '@aws-amplify/plugin-types';
import { AWSAmplifyBackendOutputs } from '../../../client-config/src/client-config-schema/client_config_v1.3.js';
import {
CognitoIdentityProviderClient,
DescribeUserPoolClientCommandInput,
DescribeUserPoolClientCommandOutput,
UserPoolType,
} from '@aws-sdk/client-cognito-identity-provider';
import { generateSeedPolicyTemplate } from './generate_seed_policy_template.js';
import { generateClientConfig } from '@aws-amplify/client-config';
import { AmplifyUserError } from '@aws-amplify/platform-core';
import { App, Stack } from 'aws-cdk-lib';
import { AccountPrincipal, Policy, Role } from 'aws-cdk-lib/aws-iam';
import { Template } from 'aws-cdk-lib/assertions';
import {
GetCallerIdentityCommandInput,
GetCallerIdentityCommandOutput,
STSClient,
} from '@aws-sdk/client-sts';

const testBackendId = 'testBackendId';
const testSandboxName = 'testSandboxName';
Expand All @@ -22,7 +21,7 @@ const testUserpoolId = 'us-east-1_userpoolTest';
const testUserpoolClient = 'userPoolClientId';
const testRegion = 'us-east-1';
const testArn =
'arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_1234567489';
'arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_userpoolTest';

const testBackendIdentifier: BackendIdentifier = {
namespace: testBackendId,
Expand All @@ -40,43 +39,42 @@ void describe('generate inline policy for seed', () => {
user_pool_id: testUserpoolId,
user_pool_client_id: testUserpoolClient,
},
} as AWSAmplifyBackendOutputs)
} as AWSAmplifyBackendOutputs),
);
const mockCognitoIdProviderClient = {

const mockStsClient = {
send: mock.fn<
(
input: DescribeUserPoolClientCommandInput
) => Promise<DescribeUserPoolClientCommandOutput>
input: GetCallerIdentityCommandInput,
) => Promise<GetCallerIdentityCommandOutput>
>(async () =>
Promise.resolve({
$metadata: {},
UserPool: {
UserPoolId: testUserpoolId,
Arn: testArn,
} as UserPoolType,
})
Account: '123456789012',
Arn: '',
UserId: '',
} as GetCallerIdentityCommandOutput),
),
};

const app = new App();
const stack = new Stack(app);

beforeEach(() => {
mockCognitoIdProviderClient.send.mock.resetCalls();
mockConfigGenerator.mock.resetCalls();
mockStsClient.send.mock.resetCalls();
});

void it('returns a policy with expected seed permissions', async () => {
const policyDoc = await generateSeedPolicyTemplate(
testBackendIdentifier,
mockConfigGenerator as unknown as typeof generateClientConfig,
mockCognitoIdProviderClient as unknown as CognitoIdentityProviderClient
mockStsClient as unknown as STSClient,
);

const policy = new Policy(stack, 'testSeedPolicy', { document: policyDoc });
// we have to attach the policy to a role, otherwise CDK erases the policy from the stack
policy.attachToRole(
new Role(stack, 'testRole', { assumedBy: new AccountPrincipal('1234') })
new Role(stack, 'testRole', { assumedBy: new AccountPrincipal('1234') }),
);

assert.ok(policy instanceof Policy);
Expand Down Expand Up @@ -113,7 +111,7 @@ void describe('generate inline policy for seed', () => {
aws_region: testRegion,
bucket_name: 'my-cool-bucket',
},
} as AWSAmplifyBackendOutputs)
} as AWSAmplifyBackendOutputs),
);

const expectedErr = new AmplifyUserError('MissingAuthError', {
Expand All @@ -126,14 +124,14 @@ void describe('generate inline policy for seed', () => {
async () =>
generateSeedPolicyTemplate(
testBackendIdentifier,
mockConfigGenerator as unknown as typeof generateClientConfig
mockConfigGenerator as unknown as typeof generateClientConfig,
),
(err: AmplifyUserError) => {
assert.strictEqual(err.name, expectedErr.name);
assert.strictEqual(err.message, expectedErr.message);
assert.strictEqual(err.resolution, expectedErr.resolution);
return true;
}
},
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ import { Effect, PolicyDocument, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { generateClientConfig } from '@aws-amplify/client-config';
import { BackendIdentifier } from '@aws-amplify/plugin-types';
import {
CognitoIdentityProviderClient,
DescribeUserPoolCommand,
} from '@aws-sdk/client-cognito-identity-provider';
import {
AmplifyFault,
AmplifyUserError,
ParameterPathConversions,
} from '@aws-amplify/platform-core';
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';

/**
* Generates policy template which allows seed to be run
Expand All @@ -19,7 +15,7 @@ import {
export const generateSeedPolicyTemplate = async (
backendId: BackendIdentifier,
generateClientConfiguration = generateClientConfig,
cognitoIdProvider = new CognitoIdentityProviderClient()
stsClient = new STSClient(),
): Promise<PolicyDocument> => {
const seedPolicy = new PolicyDocument();
const clientConfig = await generateClientConfiguration(backendId, '1.3');
Expand All @@ -31,30 +27,20 @@ export const generateSeedPolicyTemplate = async (
'Please add an auth resource to your sandbox and rerun this command',
});
}
const userPoolId = clientConfig.auth?.user_pool_id;

const userpoolOutput = await cognitoIdProvider.send(
new DescribeUserPoolCommand({ UserPoolId: userPoolId })
);
const userpoolArn = userpoolOutput.UserPool?.Arn;
// so long as there is an auth resource we should always be able to get an Arn for the userpool
if (!userpoolArn) {
throw new AmplifyFault('MissingUserpoolArnFault', {
message:
'Either the userpool is missing or the userpool exists but it is missing an arn',
resolution: 'Ensure your userpool exists and has an arn',
});
}
const stsResponse = await stsClient.send(new GetCallerIdentityCommand({}));
const arn = `arn:aws:cognito-idp:${clientConfig.auth.aws_region}:${stsResponse.Account}:userpool/${clientConfig.auth.user_pool_id}`;

const cognitoGrant = new PolicyStatement({
effect: Effect.ALLOW,
actions: ['cognito-idp:AdminCreateUser', 'cognito-idp:AdminAddUserToGroup'],
resources: [userpoolArn],
resources: [arn],
});

const backendParamPrefix =
ParameterPathConversions.toParameterPrefix(backendId);
const sharedParamPrefix = ParameterPathConversions.toParameterPrefix(
backendId.namespace
backendId.namespace,
);

const secretsGrant = new PolicyStatement({
Expand Down
1 change: 1 addition & 0 deletions packages/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@aws-amplify/deployed-backend-client": "^1.5.2",
"@aws-amplify/platform-core": "^1.6.5",
"@aws-amplify/plugin-types": "^1.8.1",
"@aws-amplify/seed": "^0.1.0",
"@aws-sdk/client-accessanalyzer": "^3.750.0",
"@aws-sdk/client-amplify": "^3.750.0",
"@aws-sdk/client-bedrock-runtime": "3.622.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { SeedTestProjectCreator } from '../../test-project-setup/seed_test_project.js';
import { defineSandboxTest } from './sandbox.test.template.js';

defineSandboxTest(new SeedTestProjectCreator());
Loading
Loading