create-aws-project 1.4.3 → 1.5.0
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/README.md +2 -0
- package/dist/aws/iam.d.ts +16 -3
- package/dist/aws/iam.js +61 -5
- package/dist/commands/initialize-github.js +25 -5
- package/dist/commands/setup-aws-envs.js +46 -11
- package/dist/utils/project-context.d.ts +1 -0
- package/package.json +1 -1
- package/templates/apps/api/cdk/static-stack.ts +0 -108
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Create a new AWS project from scratch including CloudFront, API Gateway, Lambdas
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/create-aws-project)
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## Quick Start
|
|
8
10
|
|
|
9
11
|
```bash
|
package/dist/aws/iam.d.ts
CHANGED
|
@@ -19,12 +19,17 @@ export declare function createIAMClient(region?: string): IAMClient;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare function createCrossAccountIAMClient(region: string, targetAccountId: string): IAMClient;
|
|
21
21
|
/**
|
|
22
|
-
* Creates an IAM deployment user with path /deployment
|
|
22
|
+
* Creates an IAM deployment user with path /deployment/, or adopts existing tagged user
|
|
23
23
|
* @param client - IAMClient instance
|
|
24
24
|
* @param userName - User name (format: {project}-{environment}-deploy)
|
|
25
|
-
* @throws Error if user
|
|
25
|
+
* @throws Error if user exists but was not created by this tool
|
|
26
26
|
*/
|
|
27
|
-
export declare function
|
|
27
|
+
export declare function createOrAdoptDeploymentUser(client: IAMClient, userName: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Legacy function for backward compatibility
|
|
30
|
+
* @deprecated Use createOrAdoptDeploymentUser instead
|
|
31
|
+
*/
|
|
32
|
+
export declare const createDeploymentUser: typeof createOrAdoptDeploymentUser;
|
|
28
33
|
/**
|
|
29
34
|
* Creates a customer managed policy with minimal CDK deployment permissions
|
|
30
35
|
* @param client - IAMClient instance
|
|
@@ -48,11 +53,19 @@ export interface AccessKeyCredentials {
|
|
|
48
53
|
accessKeyId: string;
|
|
49
54
|
secretAccessKey: string;
|
|
50
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Gets the count of access keys for an IAM user
|
|
58
|
+
* @param client - IAMClient instance
|
|
59
|
+
* @param userName - IAM user name
|
|
60
|
+
* @returns Number of access keys (active + inactive)
|
|
61
|
+
*/
|
|
62
|
+
export declare function getAccessKeyCount(client: IAMClient, userName: string): Promise<number>;
|
|
51
63
|
/**
|
|
52
64
|
* Creates access key credentials for an IAM user
|
|
53
65
|
* @param client - IAMClient instance
|
|
54
66
|
* @param userName - IAM user name
|
|
55
67
|
* @returns Access key ID and secret access key
|
|
68
|
+
* @throws Error if user already has 2 access keys (AWS maximum)
|
|
56
69
|
* @note The secret access key is ONLY available at creation time
|
|
57
70
|
*/
|
|
58
71
|
export declare function createAccessKey(client: IAMClient, userName: string): Promise<AccessKeyCredentials>;
|
package/dist/aws/iam.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IAMClient, CreateUserCommand, GetUserCommand, CreatePolicyCommand, GetPolicyCommand, AttachUserPolicyCommand, CreateAccessKeyCommand, NoSuchEntityException, } from '@aws-sdk/client-iam';
|
|
1
|
+
import { IAMClient, CreateUserCommand, GetUserCommand, CreatePolicyCommand, GetPolicyCommand, AttachUserPolicyCommand, CreateAccessKeyCommand, ListUserTagsCommand, ListAccessKeysCommand, NoSuchEntityException, } from '@aws-sdk/client-iam';
|
|
2
2
|
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
/**
|
|
@@ -73,16 +73,45 @@ async function policyExists(client, policyArn) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* Checks if an IAM user has a specific tag
|
|
77
|
+
* @param client - IAMClient instance
|
|
78
|
+
* @param userName - User name to check
|
|
79
|
+
* @param tagKey - Tag key to check for
|
|
80
|
+
* @param tagValue - Expected tag value
|
|
81
|
+
* @returns true if user has the tag with the specified value
|
|
82
|
+
*/
|
|
83
|
+
async function userHasTag(client, userName, tagKey, tagValue) {
|
|
84
|
+
try {
|
|
85
|
+
const command = new ListUserTagsCommand({ UserName: userName });
|
|
86
|
+
const response = await client.send(command);
|
|
87
|
+
return response.Tags?.some((tag) => tag.Key === tagKey && tag.Value === tagValue) ?? false;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Creates an IAM deployment user with path /deployment/, or adopts existing tagged user
|
|
77
95
|
* @param client - IAMClient instance
|
|
78
96
|
* @param userName - User name (format: {project}-{environment}-deploy)
|
|
79
|
-
* @throws Error if user
|
|
97
|
+
* @throws Error if user exists but was not created by this tool
|
|
80
98
|
*/
|
|
81
|
-
export async function
|
|
99
|
+
export async function createOrAdoptDeploymentUser(client, userName) {
|
|
82
100
|
// Check if user already exists
|
|
83
101
|
if (await userExists(client, userName)) {
|
|
84
|
-
|
|
102
|
+
// Check if user was created by this tool (has ManagedBy tag)
|
|
103
|
+
const isManagedByUs = await userHasTag(client, userName, 'ManagedBy', 'create-aws-starter-kit');
|
|
104
|
+
if (isManagedByUs) {
|
|
105
|
+
// Adopt existing user - this is idempotent re-run
|
|
106
|
+
console.log(pc.yellow(` Adopting existing deployment user: ${userName}`));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// User exists but not managed by us - error
|
|
111
|
+
throw new Error(`IAM user "${userName}" exists but was not created by this tool. Delete it or use a different project name.`);
|
|
112
|
+
}
|
|
85
113
|
}
|
|
114
|
+
// User doesn't exist - create it
|
|
86
115
|
const command = new CreateUserCommand({
|
|
87
116
|
UserName: userName,
|
|
88
117
|
Path: '/deployment/',
|
|
@@ -94,6 +123,11 @@ export async function createDeploymentUser(client, userName) {
|
|
|
94
123
|
await client.send(command);
|
|
95
124
|
console.log(pc.green(` Created IAM user: ${userName}`));
|
|
96
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Legacy function for backward compatibility
|
|
128
|
+
* @deprecated Use createOrAdoptDeploymentUser instead
|
|
129
|
+
*/
|
|
130
|
+
export const createDeploymentUser = createOrAdoptDeploymentUser;
|
|
97
131
|
/**
|
|
98
132
|
* CDK deployment policy document with minimal permissions
|
|
99
133
|
* @param accountId - AWS account ID for resource ARNs
|
|
@@ -226,14 +260,36 @@ export async function attachPolicyToUser(client, userName, policyArn) {
|
|
|
226
260
|
await client.send(command);
|
|
227
261
|
console.log(pc.green(` Attached policy to user ${userName}`));
|
|
228
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Gets the count of access keys for an IAM user
|
|
265
|
+
* @param client - IAMClient instance
|
|
266
|
+
* @param userName - IAM user name
|
|
267
|
+
* @returns Number of access keys (active + inactive)
|
|
268
|
+
*/
|
|
269
|
+
export async function getAccessKeyCount(client, userName) {
|
|
270
|
+
try {
|
|
271
|
+
const command = new ListAccessKeysCommand({ UserName: userName });
|
|
272
|
+
const response = await client.send(command);
|
|
273
|
+
return response.AccessKeyMetadata?.length ?? 0;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
229
279
|
/**
|
|
230
280
|
* Creates access key credentials for an IAM user
|
|
231
281
|
* @param client - IAMClient instance
|
|
232
282
|
* @param userName - IAM user name
|
|
233
283
|
* @returns Access key ID and secret access key
|
|
284
|
+
* @throws Error if user already has 2 access keys (AWS maximum)
|
|
234
285
|
* @note The secret access key is ONLY available at creation time
|
|
235
286
|
*/
|
|
236
287
|
export async function createAccessKey(client, userName) {
|
|
288
|
+
// Check access key limit before attempting creation
|
|
289
|
+
const keyCount = await getAccessKeyCount(client, userName);
|
|
290
|
+
if (keyCount >= 2) {
|
|
291
|
+
throw new Error(`IAM user ${userName} already has 2 access keys (AWS maximum). Delete an existing key in AWS Console > IAM > Users > ${userName} > Security credentials before retrying.`);
|
|
292
|
+
}
|
|
237
293
|
const command = new CreateAccessKeyCommand({
|
|
238
294
|
UserName: userName,
|
|
239
295
|
});
|
|
@@ -10,7 +10,7 @@ import prompts from 'prompts';
|
|
|
10
10
|
import pc from 'picocolors';
|
|
11
11
|
import { execSync } from 'node:child_process';
|
|
12
12
|
import { requireProjectContext } from '../utils/project-context.js';
|
|
13
|
-
import { createCrossAccountIAMClient, createDeploymentUserWithCredentials, } from '../aws/iam.js';
|
|
13
|
+
import { createCrossAccountIAMClient, createDeploymentUserWithCredentials, createAccessKey, } from '../aws/iam.js';
|
|
14
14
|
import { createGitHubClient, setEnvironmentCredentials, parseGitHubUrl, } from '../github/secrets.js';
|
|
15
15
|
const VALID_ENVIRONMENTS = ['dev', 'stage', 'prod'];
|
|
16
16
|
/**
|
|
@@ -283,9 +283,24 @@ export async function runInitializeGitHub(args) {
|
|
|
283
283
|
// Step 1: Create cross-account IAM client
|
|
284
284
|
spinner.text = `Assuming role in ${env} account (${accountId})...`;
|
|
285
285
|
const iamClient = createCrossAccountIAMClient(awsRegion, accountId);
|
|
286
|
-
// Step 2:
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
// Step 2: Get deployment user credentials
|
|
287
|
+
// If setup-aws-envs already created the user, just create an access key.
|
|
288
|
+
// Otherwise fall back to full user creation (backward compat with older projects).
|
|
289
|
+
const existingUserName = config.deploymentUsers?.[env];
|
|
290
|
+
let userName;
|
|
291
|
+
let credentials;
|
|
292
|
+
if (existingUserName) {
|
|
293
|
+
spinner.text = `Using existing deployment user: ${existingUserName}`;
|
|
294
|
+
userName = existingUserName;
|
|
295
|
+
credentials = await createAccessKey(iamClient, userName);
|
|
296
|
+
spinner.succeed(`Created access key for ${userName}`);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
spinner.text = `Creating IAM deployment user...`;
|
|
300
|
+
const fullCredentials = await createDeploymentUserWithCredentials(iamClient, projectName, env, accountId);
|
|
301
|
+
userName = fullCredentials.userName;
|
|
302
|
+
credentials = fullCredentials;
|
|
303
|
+
}
|
|
289
304
|
// Step 3: Configure GitHub
|
|
290
305
|
const githubEnvName = GITHUB_ENV_NAMES[env];
|
|
291
306
|
spinner.text = `Configuring GitHub Environment "${githubEnvName}"...`;
|
|
@@ -297,8 +312,13 @@ export async function runInitializeGitHub(args) {
|
|
|
297
312
|
console.log(pc.green(`${env} environment setup complete!`));
|
|
298
313
|
console.log('');
|
|
299
314
|
console.log('Resources created:');
|
|
300
|
-
console.log(`
|
|
315
|
+
console.log(` Deployment User: ${pc.cyan(userName)}`);
|
|
301
316
|
console.log(` GitHub Environment: ${pc.cyan(githubEnvName)}`);
|
|
317
|
+
// Add note if using existing user from setup-aws-envs
|
|
318
|
+
if (existingUserName) {
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log(pc.dim('Note: Deployment user was created by setup-aws-envs. Access key created for GitHub.'));
|
|
321
|
+
}
|
|
302
322
|
console.log('');
|
|
303
323
|
console.log('View secrets at:');
|
|
304
324
|
console.log(` ${pc.cyan(`https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/environments`)}`);
|
|
@@ -10,6 +10,7 @@ import pc from 'picocolors';
|
|
|
10
10
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { requireProjectContext } from '../utils/project-context.js';
|
|
12
12
|
import { createOrganizationsClient, checkExistingOrganization, createOrganization, createAccount, waitForAccountCreation, } from '../aws/organizations.js';
|
|
13
|
+
import { createCrossAccountIAMClient, createOrAdoptDeploymentUser, createCDKDeploymentPolicy, attachPolicyToUser, } from '../aws/iam.js';
|
|
13
14
|
/**
|
|
14
15
|
* Environment names for account creation
|
|
15
16
|
*/
|
|
@@ -101,14 +102,18 @@ async function collectEmails(projectName) {
|
|
|
101
102
|
};
|
|
102
103
|
}
|
|
103
104
|
/**
|
|
104
|
-
* Updates the config file with account IDs
|
|
105
|
+
* Updates the config file with account IDs and optionally deployment user names
|
|
105
106
|
* @param configPath Path to the config file
|
|
106
107
|
* @param accounts Record of environment to account ID
|
|
108
|
+
* @param deploymentUsers Optional record of environment to deployment user name
|
|
107
109
|
*/
|
|
108
|
-
function
|
|
110
|
+
function updateConfig(configPath, accounts, deploymentUsers) {
|
|
109
111
|
const content = readFileSync(configPath, 'utf-8');
|
|
110
112
|
const config = JSON.parse(content);
|
|
111
113
|
config.accounts = { ...config.accounts, ...accounts };
|
|
114
|
+
if (deploymentUsers) {
|
|
115
|
+
config.deploymentUsers = { ...config.deploymentUsers, ...deploymentUsers };
|
|
116
|
+
}
|
|
112
117
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
113
118
|
}
|
|
114
119
|
/**
|
|
@@ -174,14 +179,16 @@ export async function runSetupAwsEnvs(_args) {
|
|
|
174
179
|
const { config, configPath } = context;
|
|
175
180
|
// 2. Check if already configured (warn but don't abort - allows retry after partial failure)
|
|
176
181
|
const existingAccounts = config.accounts ?? {};
|
|
182
|
+
const existingUsers = config.deploymentUsers ?? {};
|
|
177
183
|
if (Object.keys(existingAccounts).length > 0) {
|
|
178
184
|
console.log('');
|
|
179
185
|
console.log(pc.yellow('Warning:') + ' AWS accounts already configured in this project:');
|
|
180
186
|
for (const [env, id] of Object.entries(existingAccounts)) {
|
|
181
|
-
|
|
187
|
+
const userInfo = existingUsers[env] ? ` (user: ${existingUsers[env]})` : '';
|
|
188
|
+
console.log(` ${env}: ${id}${userInfo}`);
|
|
182
189
|
}
|
|
183
190
|
console.log('');
|
|
184
|
-
console.log(pc.dim('Continuing will create
|
|
191
|
+
console.log(pc.dim('Continuing will skip existing accounts and create any missing ones...'));
|
|
185
192
|
}
|
|
186
193
|
// 3. Collect emails (all input before any AWS calls - per research pitfall #6)
|
|
187
194
|
const emails = await collectEmails(config.projectName);
|
|
@@ -202,8 +209,13 @@ export async function runSetupAwsEnvs(_args) {
|
|
|
202
209
|
spinner.succeed(`Using existing AWS Organization: ${orgId}`);
|
|
203
210
|
}
|
|
204
211
|
// Create accounts sequentially (per research - AWS rate limits require sequential)
|
|
205
|
-
const accounts = {};
|
|
212
|
+
const accounts = { ...existingAccounts };
|
|
206
213
|
for (const env of ENVIRONMENTS) {
|
|
214
|
+
// Skip if account already exists
|
|
215
|
+
if (existingAccounts[env]) {
|
|
216
|
+
spinner.succeed(`Using existing ${env} account: ${existingAccounts[env]}`);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
207
219
|
spinner.start(`Creating ${env} account (this may take several minutes)...`);
|
|
208
220
|
const accountName = `${config.projectName}-${env}`;
|
|
209
221
|
const { requestId } = await createAccount(client, emails[env], accountName);
|
|
@@ -211,19 +223,42 @@ export async function runSetupAwsEnvs(_args) {
|
|
|
211
223
|
const result = await waitForAccountCreation(client, requestId);
|
|
212
224
|
accounts[env] = result.accountId;
|
|
213
225
|
// Save after EACH successful account (per research pitfall #3 - handle partial success)
|
|
214
|
-
|
|
226
|
+
updateConfig(configPath, accounts);
|
|
215
227
|
spinner.succeed(`Created ${env} account: ${result.accountId}`);
|
|
216
228
|
}
|
|
217
|
-
//
|
|
229
|
+
// Create IAM deployment users in each account
|
|
230
|
+
const deploymentUsers = { ...existingUsers };
|
|
231
|
+
for (const env of ENVIRONMENTS) {
|
|
232
|
+
// Skip if user already exists in config
|
|
233
|
+
if (existingUsers[env]) {
|
|
234
|
+
spinner.succeed(`Using existing deployment user: ${existingUsers[env]}`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const accountId = accounts[env];
|
|
238
|
+
const userName = `${config.projectName}-${env}-deploy`;
|
|
239
|
+
const policyName = `${config.projectName}-${env}-cdk-deploy`;
|
|
240
|
+
spinner.start(`Creating deployment user in ${env} account...`);
|
|
241
|
+
const iamClient = createCrossAccountIAMClient(config.awsRegion, accountId);
|
|
242
|
+
await createOrAdoptDeploymentUser(iamClient, userName);
|
|
243
|
+
spinner.text = `Creating deployment policy for ${env}...`;
|
|
244
|
+
const policyArn = await createCDKDeploymentPolicy(iamClient, policyName, accountId);
|
|
245
|
+
await attachPolicyToUser(iamClient, userName, policyArn);
|
|
246
|
+
deploymentUsers[env] = userName;
|
|
247
|
+
// Save after EACH successful user creation
|
|
248
|
+
updateConfig(configPath, accounts, deploymentUsers);
|
|
249
|
+
spinner.succeed(`Created deployment user: ${userName}`);
|
|
250
|
+
}
|
|
251
|
+
// Final success with summary table
|
|
218
252
|
console.log('');
|
|
219
253
|
console.log(pc.green('AWS environment setup complete!'));
|
|
220
254
|
console.log('');
|
|
221
|
-
console.log('
|
|
222
|
-
|
|
223
|
-
|
|
255
|
+
console.log('Summary:');
|
|
256
|
+
console.log(` ${'Environment'.padEnd(14)}${'Account ID'.padEnd(16)}Deployment User`);
|
|
257
|
+
for (const env of ENVIRONMENTS) {
|
|
258
|
+
console.log(` ${env.padEnd(14)}${accounts[env].padEnd(16)}${deploymentUsers[env]}`);
|
|
224
259
|
}
|
|
225
260
|
console.log('');
|
|
226
|
-
console.log(
|
|
261
|
+
console.log('Deployment users created. Next: initialize-github to create access keys and push to GitHub.');
|
|
227
262
|
console.log(` ${pc.cyan('npx create-aws-project initialize-github dev')}`);
|
|
228
263
|
}
|
|
229
264
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -3,7 +3,6 @@ import * as s3 from 'aws-cdk-lib/aws-s3';
|
|
|
3
3
|
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
|
|
4
4
|
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
|
|
5
5
|
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
6
|
-
import * as opensearchserverless from 'aws-cdk-lib/aws-opensearchserverless';
|
|
7
6
|
import { Construct } from 'constructs';
|
|
8
7
|
|
|
9
8
|
export interface StaticStackProps extends cdk.StackProps {
|
|
@@ -24,7 +23,6 @@ export class StaticStack extends cdk.Stack {
|
|
|
24
23
|
public readonly bucket: s3.Bucket;
|
|
25
24
|
public readonly api: apigateway.RestApi;
|
|
26
25
|
public readonly distribution: cloudfront.Distribution;
|
|
27
|
-
public readonly openSearchCollection: opensearchserverless.CfnCollection;
|
|
28
26
|
|
|
29
27
|
constructor(scope: Construct, id: string, props: StaticStackProps) {
|
|
30
28
|
super(scope, id, props);
|
|
@@ -205,94 +203,6 @@ export class StaticStack extends cdk.Stack {
|
|
|
205
203
|
],
|
|
206
204
|
});
|
|
207
205
|
|
|
208
|
-
// OpenSearch Serverless Collection
|
|
209
|
-
const collectionName = `{{PROJECT_NAME}}-${environmentName}`;
|
|
210
|
-
|
|
211
|
-
// Encryption policy (required for all collections)
|
|
212
|
-
const encryptionPolicy = new opensearchserverless.CfnSecurityPolicy(this, 'OpenSearchEncryptionPolicy', {
|
|
213
|
-
name: `${collectionName}-encryption`,
|
|
214
|
-
type: 'encryption',
|
|
215
|
-
policy: JSON.stringify({
|
|
216
|
-
Rules: [
|
|
217
|
-
{
|
|
218
|
-
ResourceType: 'collection',
|
|
219
|
-
Resource: [`collection/${collectionName}`],
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
AWSOwnedKey: true,
|
|
223
|
-
}),
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Network policy - allows public access (adjust for production)
|
|
227
|
-
const networkPolicy = new opensearchserverless.CfnSecurityPolicy(this, 'OpenSearchNetworkPolicy', {
|
|
228
|
-
name: `${collectionName}-network`,
|
|
229
|
-
type: 'network',
|
|
230
|
-
policy: JSON.stringify([
|
|
231
|
-
{
|
|
232
|
-
Rules: [
|
|
233
|
-
{
|
|
234
|
-
ResourceType: 'collection',
|
|
235
|
-
Resource: [`collection/${collectionName}`],
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
ResourceType: 'dashboard',
|
|
239
|
-
Resource: [`collection/${collectionName}`],
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
AllowFromPublic: true,
|
|
243
|
-
},
|
|
244
|
-
]),
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Data access policy - grants access to the collection
|
|
248
|
-
const dataAccessPolicy = new opensearchserverless.CfnAccessPolicy(this, 'OpenSearchDataAccessPolicy', {
|
|
249
|
-
name: `${collectionName}-access`,
|
|
250
|
-
type: 'data',
|
|
251
|
-
policy: JSON.stringify([
|
|
252
|
-
{
|
|
253
|
-
Rules: [
|
|
254
|
-
{
|
|
255
|
-
ResourceType: 'collection',
|
|
256
|
-
Resource: [`collection/${collectionName}`],
|
|
257
|
-
Permission: [
|
|
258
|
-
'aoss:CreateCollectionItems',
|
|
259
|
-
'aoss:DeleteCollectionItems',
|
|
260
|
-
'aoss:UpdateCollectionItems',
|
|
261
|
-
'aoss:DescribeCollectionItems',
|
|
262
|
-
],
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
ResourceType: 'index',
|
|
266
|
-
Resource: [`index/${collectionName}/*`],
|
|
267
|
-
Permission: [
|
|
268
|
-
'aoss:CreateIndex',
|
|
269
|
-
'aoss:DeleteIndex',
|
|
270
|
-
'aoss:UpdateIndex',
|
|
271
|
-
'aoss:DescribeIndex',
|
|
272
|
-
'aoss:ReadDocument',
|
|
273
|
-
'aoss:WriteDocument',
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
Principal: [
|
|
278
|
-
`arn:aws:iam::${this.account}:root`,
|
|
279
|
-
],
|
|
280
|
-
},
|
|
281
|
-
]),
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// Create the OpenSearch Serverless collection
|
|
285
|
-
this.openSearchCollection = new opensearchserverless.CfnCollection(this, 'OpenSearchCollection', {
|
|
286
|
-
name: collectionName,
|
|
287
|
-
type: 'SEARCH', // SEARCH, TIMESERIES, or VECTORSEARCH
|
|
288
|
-
description: `OpenSearch Serverless collection for {{PROJECT_NAME_TITLE}} - ${environmentName}`,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Ensure policies are created before the collection
|
|
292
|
-
this.openSearchCollection.addDependency(encryptionPolicy);
|
|
293
|
-
this.openSearchCollection.addDependency(networkPolicy);
|
|
294
|
-
this.openSearchCollection.addDependency(dataAccessPolicy);
|
|
295
|
-
|
|
296
206
|
// Output important values
|
|
297
207
|
new cdk.CfnOutput(this, 'BucketName', {
|
|
298
208
|
value: this.bucket.bucketName,
|
|
@@ -339,23 +249,5 @@ export class StaticStack extends cdk.Stack {
|
|
|
339
249
|
value: `https://${this.distribution.distributionDomainName}/api`,
|
|
340
250
|
description: 'API URL via CloudFront',
|
|
341
251
|
});
|
|
342
|
-
|
|
343
|
-
new cdk.CfnOutput(this, 'OpenSearchCollectionEndpoint', {
|
|
344
|
-
value: this.openSearchCollection.attrCollectionEndpoint,
|
|
345
|
-
description: 'OpenSearch Serverless collection endpoint',
|
|
346
|
-
exportName: `${environmentName}-opensearch-endpoint`,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
new cdk.CfnOutput(this, 'OpenSearchDashboardEndpoint', {
|
|
350
|
-
value: this.openSearchCollection.attrDashboardEndpoint,
|
|
351
|
-
description: 'OpenSearch Serverless dashboard endpoint',
|
|
352
|
-
exportName: `${environmentName}-opensearch-dashboard`,
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
new cdk.CfnOutput(this, 'OpenSearchCollectionArn', {
|
|
356
|
-
value: this.openSearchCollection.attrArn,
|
|
357
|
-
description: 'OpenSearch Serverless collection ARN',
|
|
358
|
-
exportName: `${environmentName}-opensearch-arn`,
|
|
359
|
-
});
|
|
360
252
|
}
|
|
361
253
|
}
|