@jaypie/constructs 1.1.49 → 1.1.51

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.
Files changed (45) hide show
  1. package/dist/cjs/JaypieApiGateway.d.ts +1 -0
  2. package/dist/cjs/JaypieBucketQueuedLambda.d.ts +4 -19
  3. package/dist/cjs/JaypieDnsRecord.d.ts +45 -0
  4. package/dist/cjs/JaypieGitHubDeployRole.d.ts +14 -0
  5. package/dist/cjs/JaypieHostedZone.d.ts +26 -4
  6. package/dist/cjs/JaypieLambda.d.ts +1 -1
  7. package/dist/cjs/JaypieQueuedLambda.d.ts +1 -1
  8. package/dist/cjs/JaypieSsoPermissions.d.ts +96 -0
  9. package/dist/cjs/JaypieSsoSyncApplication.d.ts +27 -0
  10. package/dist/cjs/JaypieWebDeploymentBucket.d.ts +1 -0
  11. package/dist/cjs/__tests__/JaypieSsoSyncApplication.spec.d.ts +1 -0
  12. package/dist/cjs/helpers/__tests__/resolveDatadogForwarderFunction.spec.d.ts +1 -0
  13. package/dist/cjs/helpers/__tests__/resolveDatadogLoggingDestination.spec.d.ts +1 -0
  14. package/dist/cjs/helpers/index.d.ts +2 -0
  15. package/dist/cjs/helpers/resolveDatadogForwarderFunction.d.ts +7 -0
  16. package/dist/cjs/helpers/resolveDatadogLoggingDestination.d.ts +4 -0
  17. package/dist/cjs/index.cjs +592 -355
  18. package/dist/cjs/index.cjs.map +1 -1
  19. package/dist/cjs/index.d.ts +6 -2
  20. package/dist/esm/JaypieApiGateway.d.ts +1 -0
  21. package/dist/esm/JaypieBucketQueuedLambda.d.ts +4 -19
  22. package/dist/esm/JaypieDnsRecord.d.ts +45 -0
  23. package/dist/esm/JaypieGitHubDeployRole.d.ts +14 -0
  24. package/dist/esm/JaypieHostedZone.d.ts +26 -4
  25. package/dist/esm/JaypieLambda.d.ts +1 -1
  26. package/dist/esm/JaypieQueuedLambda.d.ts +1 -1
  27. package/dist/esm/JaypieSsoPermissions.d.ts +96 -0
  28. package/dist/esm/JaypieSsoSyncApplication.d.ts +27 -0
  29. package/dist/esm/JaypieWebDeploymentBucket.d.ts +1 -0
  30. package/dist/esm/__tests__/JaypieDnsRecord.spec.d.ts +1 -0
  31. package/dist/esm/__tests__/JaypieSsoPermissions.spec.d.ts +1 -0
  32. package/dist/esm/__tests__/JaypieSsoSyncApplication.spec.d.ts +1 -0
  33. package/dist/esm/helpers/__tests__/resolveDatadogForwarderFunction.spec.d.ts +1 -0
  34. package/dist/esm/helpers/__tests__/resolveDatadogLoggingDestination.spec.d.ts +1 -0
  35. package/dist/esm/helpers/index.d.ts +2 -0
  36. package/dist/esm/helpers/resolveDatadogForwarderFunction.d.ts +7 -0
  37. package/dist/esm/helpers/resolveDatadogLoggingDestination.d.ts +4 -0
  38. package/dist/esm/index.d.ts +6 -2
  39. package/dist/esm/index.js +585 -356
  40. package/dist/esm/index.js.map +1 -1
  41. package/package.json +3 -2
  42. package/dist/cjs/JaypieSsoGroups.d.ts +0 -121
  43. package/dist/esm/JaypieSsoGroups.d.ts +0 -121
  44. /package/dist/cjs/__tests__/{JaypieSsoGroups.spec.d.ts → JaypieDnsRecord.spec.d.ts} +0 -0
  45. /package/dist/{esm/__tests__/JaypieSsoGroups.spec.d.ts → cjs/__tests__/JaypieSsoPermissions.spec.d.ts} +0 -0
package/dist/esm/index.js CHANGED
@@ -1,22 +1,26 @@
1
+ import { CDK as CDK$2, ConfigurationError, mergeDomain, isValidSubdomain, isValidHostname } from '@jaypie/cdk';
2
+ export { CDK } from '@jaypie/cdk';
1
3
  import { Construct } from 'constructs';
2
4
  import * as cdk from 'aws-cdk-lib';
3
5
  import { Tags, Stack, Duration, RemovalPolicy, Fn, CfnOutput, SecretValue } from 'aws-cdk-lib';
4
6
  import * as acm from 'aws-cdk-lib/aws-certificatemanager';
5
7
  import * as apiGateway from 'aws-cdk-lib/aws-apigateway';
6
8
  import * as route53 from 'aws-cdk-lib/aws-route53';
7
- import { HostedZone } from 'aws-cdk-lib/aws-route53';
9
+ import { TxtRecord, NsRecord, MxRecord, CnameRecord, ARecord, RecordTarget, HostedZone } from 'aws-cdk-lib/aws-route53';
8
10
  import * as route53Targets from 'aws-cdk-lib/aws-route53-targets';
9
- import { CDK as CDK$2, ConfigurationError, mergeDomain, isValidSubdomain, isValidHostname } from '@jaypie/cdk';
10
11
  import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
11
12
  import { DatadogLambda } from 'datadog-cdk-constructs-v2';
12
13
  import * as lambda from 'aws-cdk-lib/aws-lambda';
14
+ import * as logDestinations from 'aws-cdk-lib/aws-logs-destinations';
13
15
  import * as s3 from 'aws-cdk-lib/aws-s3';
14
16
  import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
15
17
  import * as sqs from 'aws-cdk-lib/aws-sqs';
16
18
  import * as lambdaEventSources from 'aws-cdk-lib/aws-lambda-event-sources';
17
- import { ServicePrincipal, ManagedPolicy, Role, FederatedPrincipal, PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';
19
+ import { Role, FederatedPrincipal, PolicyStatement, Effect, ServicePrincipal, ManagedPolicy } from 'aws-cdk-lib/aws-iam';
18
20
  import { LogGroup, RetentionDays, FilterPattern } from 'aws-cdk-lib/aws-logs';
19
- import * as sso from 'aws-cdk-lib/aws-sso';
21
+ import { CfnPermissionSet, CfnAssignment } from 'aws-cdk-lib/aws-sso';
22
+ import { CfnApplication } from 'aws-cdk-lib/aws-sam';
23
+ import { ConfigurationError as ConfigurationError$1 } from '@jaypie/errors';
20
24
  import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
21
25
  import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
22
26
 
@@ -215,6 +219,33 @@ function jaypieLambdaEnv(options = {}) {
215
219
  return environment;
216
220
  }
217
221
 
222
+ const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
223
+ // Cache to store resolved functions
224
+ // Using nested structure to support multiple functions per scope with automatic GC
225
+ const functionCache = new WeakMap();
226
+ function resolveDatadogForwarderFunction(scope, options) {
227
+ const { import: importValue, name } = options || {};
228
+ const functionName = name || DEFAULT_FUNCTION_NAME$1;
229
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
230
+ // Create a cache key based on name and import
231
+ const cacheKey = `${functionName}:${importKey}`;
232
+ // Get or create scope cache
233
+ let scopeCache = functionCache.get(scope);
234
+ if (!scopeCache) {
235
+ scopeCache = new Map();
236
+ functionCache.set(scope, scopeCache);
237
+ }
238
+ // Return cached function if it exists
239
+ const cachedFunction = scopeCache.get(cacheKey);
240
+ if (cachedFunction) {
241
+ return cachedFunction;
242
+ }
243
+ // Create and cache the function
244
+ const func = lambda.Function.fromFunctionArn(scope, functionName, cdk.Fn.importValue(importKey));
245
+ scopeCache.set(cacheKey, func);
246
+ return func;
247
+ }
248
+
218
249
  function resolveDatadogLayers(scope, options = {}) {
219
250
  const { datadogApiKeyArn, uniqueId } = options;
220
251
  let resolvedRegion = Stack.of(scope).region || "us-east-1";
@@ -234,6 +265,35 @@ function resolveDatadogLayers(scope, options = {}) {
234
265
  return [datadogNodeLayer, datadogExtensionLayer];
235
266
  }
236
267
 
268
+ const DEFAULT_FUNCTION_NAME = "DatadogForwarderFunction";
269
+ // Cache to store resolved logging destinations
270
+ // Using nested structure to support multiple destinations per scope with automatic GC
271
+ const destinationCache = new WeakMap();
272
+ function resolveDatadogLoggingDestination(scope, options) {
273
+ const { import: importValue, name } = options || {};
274
+ // Create a cache key based on name and import (same as forwarder function)
275
+ const functionName = name || DEFAULT_FUNCTION_NAME;
276
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
277
+ const cacheKey = `${functionName}:${importKey}`;
278
+ // Get or create scope cache
279
+ let scopeCache = destinationCache.get(scope);
280
+ if (!scopeCache) {
281
+ scopeCache = new Map();
282
+ destinationCache.set(scope, scopeCache);
283
+ }
284
+ // Return cached destination if it exists
285
+ const cachedDestination = scopeCache.get(cacheKey);
286
+ if (cachedDestination) {
287
+ return cachedDestination;
288
+ }
289
+ // Resolve the Datadog forwarder function
290
+ const datadogForwarderFunction = resolveDatadogForwarderFunction(scope, options);
291
+ // Create and cache the logging destination
292
+ const datadogLoggingDestination = new logDestinations.LambdaDestination(datadogForwarderFunction);
293
+ scopeCache.set(cacheKey, datadogLoggingDestination);
294
+ return datadogLoggingDestination;
295
+ }
296
+
237
297
  function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
238
298
  if (!zone) {
239
299
  throw new ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
@@ -407,6 +467,11 @@ class JaypieApiGateway extends Construct {
407
467
  applyRemovalPolicy(policy) {
408
468
  this._api.applyRemovalPolicy(policy);
409
469
  }
470
+ get restApiRef() {
471
+ return {
472
+ restApiId: this._api.restApiId,
473
+ };
474
+ }
410
475
  }
411
476
 
412
477
  class JaypieStack extends Stack {
@@ -588,7 +653,10 @@ class JaypieLambda extends Construct {
588
653
  return this._reference.resourceArnsForGrantInvoke;
589
654
  }
590
655
  get functionRef() {
591
- return this._reference.functionRef;
656
+ return {
657
+ functionArn: this._reference.functionArn,
658
+ functionName: this._reference.functionName,
659
+ };
592
660
  }
593
661
  addEventSource(source) {
594
662
  this._reference.addEventSource(source);
@@ -950,7 +1018,7 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
950
1018
  get policy() {
951
1019
  return this._bucket.policy;
952
1020
  }
953
- addEventNotification(event, dest, filters) {
1021
+ addEventNotification(event, dest, ...filters) {
954
1022
  this._bucket.addEventNotification(event, dest, ...filters);
955
1023
  }
956
1024
  addObjectCreatedNotification(dest, ...filters) {
@@ -968,9 +1036,6 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
968
1036
  enableEventBridgeNotification() {
969
1037
  this._bucket.enableEventBridgeNotification();
970
1038
  }
971
- grant(grantee, ...actions) {
972
- return this._bucket.grant(grantee, ...actions);
973
- }
974
1039
  grantDelete(grantee, objectsKeyPattern) {
975
1040
  return this._bucket.grantDelete(grantee, objectsKeyPattern);
976
1041
  }
@@ -1013,54 +1078,17 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1013
1078
  virtualHostedUrlForObject(key, options) {
1014
1079
  return this._bucket.virtualHostedUrlForObject(key, options);
1015
1080
  }
1016
- // Bucket metrics
1017
- metricAllRequests(props) {
1018
- return this._bucket.metricAllRequests(props);
1019
- }
1020
- metricBucketSizeBytes(props) {
1021
- return this._bucket.metricBucketSizeBytes(props);
1022
- }
1023
- metricDeleteRequests(props) {
1024
- return this._bucket.metricDeleteRequests(props);
1025
- }
1026
- metricDownloadBytes(props) {
1027
- return this._bucket.metricDownloadBytes(props);
1028
- }
1029
- metricFirstByteLatency(props) {
1030
- return this._bucket.metricFirstByteLatency(props);
1031
- }
1032
- metricGetRequests(props) {
1033
- return this._bucket.metricGetRequests(props);
1034
- }
1035
- metricHeadRequests(props) {
1036
- return this._bucket.metricHeadRequests(props);
1037
- }
1038
- metricHttpRequests(props) {
1039
- return this._bucket.metricHttpRequests(props);
1040
- }
1041
- metricListRequests(props) {
1042
- return this._bucket.metricListRequests(props);
1043
- }
1044
- metricNumberOfObjects(props) {
1045
- return this._bucket.metricNumberOfObjects(props);
1046
- }
1047
- metricPostRequests(props) {
1048
- return this._bucket.metricPostRequests(props);
1049
- }
1050
- metricPutRequests(props) {
1051
- return this._bucket.metricPutRequests(props);
1052
- }
1053
- metricSelectRequests(props) {
1054
- return this._bucket.metricSelectRequests(props);
1055
- }
1056
- metricSelectScannedBytes(props) {
1057
- return this._bucket.metricSelectScannedBytes(props);
1081
+ grantReplicationPermission(identity, props) {
1082
+ return this._bucket.grantReplicationPermission(identity, props);
1058
1083
  }
1059
- metricUploadBytes(props) {
1060
- return this._bucket.metricUploadBytes(props);
1084
+ addReplicationPolicy(policy) {
1085
+ this._bucket.addReplicationPolicy(policy);
1061
1086
  }
1062
- metricSelectReturnedBytes(props) {
1063
- return this._bucket.metricSelectReturnedBytes(props);
1087
+ get bucketRef() {
1088
+ return {
1089
+ bucketArn: this._bucket.bucketArn,
1090
+ bucketName: this._bucket.bucketName,
1091
+ };
1064
1092
  }
1065
1093
  // Override applyRemovalPolicy to apply to all resources
1066
1094
  applyRemovalPolicy(policy) {
@@ -1214,6 +1242,166 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
1214
1242
  }
1215
1243
  }
1216
1244
 
1245
+ class JaypieDnsRecord extends Construct {
1246
+ constructor(scope, id, props) {
1247
+ super(scope, id);
1248
+ const { comment, recordName, type, values } = props;
1249
+ const ttl = props.ttl || cdk.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
1250
+ // Resolve the hosted zone (supports both string and IHostedZone)
1251
+ const zone = resolveHostedZone(scope, {
1252
+ name: `${id}HostedZone`,
1253
+ zone: props.zone,
1254
+ });
1255
+ // Common properties for all record types
1256
+ const baseProps = {
1257
+ comment,
1258
+ recordName,
1259
+ ttl,
1260
+ zone,
1261
+ };
1262
+ // Create the appropriate record based on type
1263
+ switch (type) {
1264
+ case CDK$2.DNS.RECORD.A: {
1265
+ if (!Array.isArray(values) || values.length === 0) {
1266
+ throw new ConfigurationError("A record requires at least one IP address");
1267
+ }
1268
+ this.record = new ARecord(this, "Record", {
1269
+ ...baseProps,
1270
+ target: RecordTarget.fromIpAddresses(...values),
1271
+ });
1272
+ break;
1273
+ }
1274
+ case CDK$2.DNS.RECORD.CNAME: {
1275
+ if (!Array.isArray(values) || values.length === 0) {
1276
+ throw new ConfigurationError("CNAME record requires a domain name");
1277
+ }
1278
+ this.record = new CnameRecord(this, "Record", {
1279
+ ...baseProps,
1280
+ domainName: values[0],
1281
+ });
1282
+ break;
1283
+ }
1284
+ case CDK$2.DNS.RECORD.MX: {
1285
+ if (!Array.isArray(values) || values.length === 0) {
1286
+ throw new ConfigurationError("MX record requires at least one mail server");
1287
+ }
1288
+ this.record = new MxRecord(this, "Record", {
1289
+ ...baseProps,
1290
+ values: values,
1291
+ });
1292
+ break;
1293
+ }
1294
+ case CDK$2.DNS.RECORD.NS: {
1295
+ if (!Array.isArray(values) || values.length === 0) {
1296
+ throw new ConfigurationError("NS record requires at least one name server");
1297
+ }
1298
+ this.record = new NsRecord(this, "Record", {
1299
+ ...baseProps,
1300
+ values: values,
1301
+ });
1302
+ break;
1303
+ }
1304
+ case CDK$2.DNS.RECORD.TXT: {
1305
+ if (!Array.isArray(values) || values.length === 0) {
1306
+ throw new ConfigurationError("TXT record requires at least one value");
1307
+ }
1308
+ this.record = new TxtRecord(this, "Record", {
1309
+ ...baseProps,
1310
+ values: values,
1311
+ });
1312
+ break;
1313
+ }
1314
+ default:
1315
+ throw new ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
1316
+ }
1317
+ // Add standard tags to the DNS record
1318
+ cdk.Tags.of(this.record).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.INFRASTRUCTURE);
1319
+ cdk.Tags.of(this.record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1320
+ }
1321
+ }
1322
+
1323
+ class JaypieGitHubDeployRole extends Construct {
1324
+ constructor(scope, id, props = {}) {
1325
+ super(scope, id);
1326
+ const { oidcProviderArn = Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
1327
+ // Extract account ID from the scope
1328
+ const accountId = Stack.of(this).account;
1329
+ // Resolve repoRestriction from props or environment variables
1330
+ let repoRestriction = propsRepoRestriction;
1331
+ if (!repoRestriction) {
1332
+ const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
1333
+ if (!envRepo) {
1334
+ throw new ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
1335
+ }
1336
+ // Extract organization from owner/repo format and create org-wide restriction
1337
+ const organization = envRepo.split("/")[0];
1338
+ repoRestriction = `repo:${organization}/*:*`;
1339
+ }
1340
+ // Create the IAM role
1341
+ this._role = new Role(this, "GitHubActionsRole", {
1342
+ assumedBy: new FederatedPrincipal(oidcProviderArn, {
1343
+ StringLike: {
1344
+ "token.actions.githubusercontent.com:sub": repoRestriction,
1345
+ },
1346
+ }, "sts:AssumeRoleWithWebIdentity"),
1347
+ maxSessionDuration: Duration.hours(1),
1348
+ path: "/",
1349
+ });
1350
+ Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
1351
+ // Allow the role to access the GitHub OIDC provider
1352
+ this._role.addToPolicy(new PolicyStatement({
1353
+ actions: ["sts:AssumeRoleWithWebIdentity"],
1354
+ resources: [`arn:aws:iam::${accountId}:oidc-provider/*`],
1355
+ }));
1356
+ // Allow the role to deploy CDK apps
1357
+ this._role.addToPolicy(new PolicyStatement({
1358
+ actions: [
1359
+ "cloudformation:CreateStack",
1360
+ "cloudformation:DeleteStack",
1361
+ "cloudformation:DescribeStackEvents",
1362
+ "cloudformation:DescribeStackResource",
1363
+ "cloudformation:DescribeStackResources",
1364
+ "cloudformation:DescribeStacks",
1365
+ "cloudformation:GetTemplate",
1366
+ "cloudformation:SetStackPolicy",
1367
+ "cloudformation:UpdateStack",
1368
+ "cloudformation:ValidateTemplate",
1369
+ "iam:PassRole",
1370
+ "route53:ListHostedZones*",
1371
+ "s3:GetObject",
1372
+ "s3:ListBucket",
1373
+ ],
1374
+ effect: Effect.ALLOW,
1375
+ resources: ["*"],
1376
+ }));
1377
+ this._role.addToPolicy(new PolicyStatement({
1378
+ actions: ["iam:PassRole", "sts:AssumeRole"],
1379
+ effect: Effect.ALLOW,
1380
+ resources: [
1381
+ "arn:aws:iam::*:role/cdk-hnb659fds-deploy-role-*",
1382
+ "arn:aws:iam::*:role/cdk-hnb659fds-file-publishing-*",
1383
+ "arn:aws:iam::*:role/cdk-readOnlyRole",
1384
+ ],
1385
+ }));
1386
+ // Export the ARN of the role
1387
+ if (output !== false) {
1388
+ const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
1389
+ new CfnOutput(this, outputId, {
1390
+ value: this._role.roleArn,
1391
+ });
1392
+ }
1393
+ }
1394
+ get role() {
1395
+ return this._role;
1396
+ }
1397
+ get roleArn() {
1398
+ return this._role.roleArn;
1399
+ }
1400
+ get roleName() {
1401
+ return this._role.roleName;
1402
+ }
1403
+ }
1404
+
1217
1405
  class JaypieExpressLambda extends JaypieLambda {
1218
1406
  constructor(scope, id, props) {
1219
1407
  super(scope, id, {
@@ -1229,11 +1417,12 @@ const SERVICE = {
1229
1417
  };
1230
1418
  class JaypieHostedZone extends Construct {
1231
1419
  /**
1232
- * Create a new hosted zone with query logging
1420
+ * Create a new hosted zone with query logging and optional DNS records
1233
1421
  */
1234
1422
  constructor(scope, id, props) {
1235
1423
  super(scope, id);
1236
- const { destination, zoneName, project } = props;
1424
+ const { zoneName, project } = props;
1425
+ const destination = props.destination ?? true;
1237
1426
  const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
1238
1427
  // Create the log group
1239
1428
  this.logGroup = new LogGroup(this, "LogGroup", {
@@ -1250,10 +1439,13 @@ class JaypieHostedZone extends Construct {
1250
1439
  }
1251
1440
  // Grant Route 53 permissions to write to the log group
1252
1441
  this.logGroup.grantWrite(new ServicePrincipal(SERVICE.ROUTE53));
1253
- // Add destination if provided
1254
- if (destination) {
1442
+ // Add destination based on configuration
1443
+ if (destination !== false) {
1444
+ const lambdaDestination = destination === true
1445
+ ? resolveDatadogLoggingDestination(scope)
1446
+ : destination;
1255
1447
  this.logGroup.addSubscriptionFilter("DatadogLambdaDestination", {
1256
- destination,
1448
+ destination: lambdaDestination,
1257
1449
  filterPattern: FilterPattern.allEvents(),
1258
1450
  });
1259
1451
  }
@@ -1268,6 +1460,21 @@ class JaypieHostedZone extends Construct {
1268
1460
  if (project) {
1269
1461
  cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
1270
1462
  }
1463
+ // Create DNS records if provided
1464
+ this.dnsRecords = [];
1465
+ if (props.records) {
1466
+ props.records.forEach((recordConfig, index) => {
1467
+ const { id, ...recordProps } = recordConfig;
1468
+ // Generate a default ID if not provided
1469
+ const recordId = id ||
1470
+ `${recordProps.type}${recordProps.recordName ? `-${recordProps.recordName}` : ""}-${index}`;
1471
+ const dnsRecord = new JaypieDnsRecord(this, recordId, {
1472
+ ...recordProps,
1473
+ zone: this.hostedZone,
1474
+ });
1475
+ this.dnsRecords.push(dnsRecord);
1476
+ });
1477
+ }
1271
1478
  }
1272
1479
  }
1273
1480
 
@@ -1316,333 +1523,349 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
1316
1523
  }
1317
1524
 
1318
1525
  /**
1319
- * Permission set types with corresponding AWS managed policies
1526
+ * JaypieSsoPermissions Construct
1527
+ *
1528
+ * Creates and manages AWS IAM Identity Center (SSO) permission sets and assignments
1529
+ *
1530
+ * @example
1531
+ * const permissionSets = new JaypieSsoPermissions(this, "PermissionSets", {
1532
+ * iamIdentityCenterArn: "arn:aws:sso:::instance/...",
1533
+ * administratorGroupId: "b4c8b438-4031-7000-782d-5046945fb956",
1534
+ * analystGroupId: "2488f4e8-d061-708e-abe1-c315f0e30005",
1535
+ * developerGroupId: "b438a4f8-e0e1-707c-c6e8-21841daf9ad1",
1536
+ * administratorAccountAssignments: {
1537
+ * "211125635435": ["Administrator", "Analyst", "Developer"],
1538
+ * "381492033431": ["Administrator", "Analyst"],
1539
+ * },
1540
+ * analystAccountAssignments: {
1541
+ * "211125635435": ["Analyst", "Developer"],
1542
+ * "381492033431": [],
1543
+ * },
1544
+ * developerAccountAssignments: {
1545
+ * "211125635435": ["Analyst", "Developer"],
1546
+ * "381492033431": [],
1547
+ * },
1548
+ * });
1320
1549
  */
1321
- var PermissionSetType;
1322
- (function (PermissionSetType) {
1323
- PermissionSetType["ADMINISTRATOR"] = "Administrator";
1324
- PermissionSetType["ANALYST"] = "Analyst";
1325
- PermissionSetType["DEVELOPER"] = "Developer";
1326
- })(PermissionSetType || (PermissionSetType = {}));
1327
- /**
1328
- * Construct to simplify AWS SSO group management.
1329
- * This construct encapsulates the complexity of creating permission sets
1330
- * and assigning them to groups across multiple AWS accounts.
1331
- */
1332
- class JaypieSsoGroups extends Construct {
1550
+ class JaypieSsoPermissions extends Construct {
1333
1551
  constructor(scope, id, props) {
1334
1552
  super(scope, id);
1335
- this.permissionSets = {};
1336
- this.instanceArn = props.instanceArn;
1337
- this.props = props;
1338
- // Create the permission sets
1339
- this.createAdministratorPermissionSet();
1340
- this.createAnalystPermissionSet();
1341
- this.createDeveloperPermissionSet();
1342
- // Create the assignments
1343
- this.createPermissionSetAssignments(props);
1344
- }
1345
- /**
1346
- * Creates the Administrator permission set with AdministratorAccess policy
1347
- * and billing access
1348
- */
1349
- createAdministratorPermissionSet() {
1350
- const defaultInlinePolicy = {
1351
- Version: "2012-10-17",
1352
- Statement: [
1553
+ const { iamIdentityCenterArn: iamIdentityCenterArnProp, administratorGroupId, analystGroupId, developerGroupId, administratorAccountAssignments, analystAccountAssignments, developerAccountAssignments, } = props;
1554
+ const iamIdentityCenterArn = iamIdentityCenterArnProp || process.env.CDK_ENV_IAM_IDENTITY_CENTER_ARN;
1555
+ if (!iamIdentityCenterArn) {
1556
+ // If no IAM Identity Center ARN provided, skip SSO setup
1557
+ return;
1558
+ }
1559
+ //
1560
+ // Permission Sets
1561
+ //
1562
+ this.administratorPermissionSet = new CfnPermissionSet(this, "AdministratorPermissionSet", {
1563
+ // Required
1564
+ instanceArn: iamIdentityCenterArn,
1565
+ name: "Administrator",
1566
+ // Optional
1567
+ description: "Unrestricted access",
1568
+ inlinePolicy: {
1569
+ Version: "2012-10-17",
1570
+ Statement: [
1571
+ {
1572
+ Effect: "Allow",
1573
+ Action: [
1574
+ "aws-portal:ViewUsage",
1575
+ "aws-portal:ViewBilling",
1576
+ "budgets:*",
1577
+ "cur:DescribeReportDefinitions",
1578
+ "cur:PutReportDefinition",
1579
+ "cur:DeleteReportDefinition",
1580
+ "cur:ModifyReportDefinition",
1581
+ ],
1582
+ Resource: "*",
1583
+ },
1584
+ ],
1585
+ },
1586
+ managedPolicies: [
1587
+ ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess")
1588
+ .managedPolicyArn,
1589
+ ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1590
+ ],
1591
+ sessionDuration: Duration.hours(1).toIsoString(),
1592
+ tags: [
1353
1593
  {
1354
- Effect: "Allow",
1355
- Action: [
1356
- "aws-portal:*",
1357
- "budgets:*",
1358
- "ce:*",
1359
- "cost-optimization-hub:*",
1360
- "cur:*",
1361
- ],
1362
- Resource: "*",
1594
+ key: CDK$2.TAG.SERVICE,
1595
+ value: CDK$2.SERVICE.SSO,
1363
1596
  },
1364
- ],
1365
- };
1366
- // Merge with any additional policy statements provided for administrators
1367
- const mergedPolicy = this.mergeInlinePolicies(defaultInlinePolicy, this.props?.inlinePolicyStatements?.administrators);
1368
- const permissionSet = new sso.CfnPermissionSet(this, "AdministratorPermissionSet", {
1369
- instanceArn: this.instanceArn,
1370
- name: PermissionSetType.ADMINISTRATOR,
1371
- description: "Full administrative access to all AWS services and resources",
1372
- sessionDuration: Duration.hours(8).toIsoString(),
1373
- managedPolicies: ["arn:aws:iam::aws:policy/AdministratorAccess"],
1374
- inlinePolicy: mergedPolicy,
1375
- });
1376
- Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1377
- this.permissionSets[PermissionSetType.ADMINISTRATOR] = permissionSet;
1378
- }
1379
- /**
1380
- * Creates the Analyst permission set with ReadOnlyAccess policy
1381
- * and limited write access
1382
- */
1383
- createAnalystPermissionSet() {
1384
- const defaultInlinePolicy = {
1385
- Version: "2012-10-17",
1386
- Statement: [
1387
1597
  {
1388
- Effect: "Allow",
1389
- Action: [
1390
- "aws-portal:ViewUsage",
1391
- "aws-portal:ViewBilling",
1392
- "budgets:Describe*",
1393
- "budgets:View*",
1394
- "ce:Get*",
1395
- "ce:List*",
1396
- "cloudformation:Describe*",
1397
- "cloudformation:Get*",
1398
- "cloudformation:List*",
1399
- "cloudwatch:BatchGet*",
1400
- "cloudwatch:Get*",
1401
- "cloudwatch:List*",
1402
- "cost-optimization-hub:Get*",
1403
- "cost-optimization-hub:List*",
1404
- "ec2:Describe*",
1405
- "ec2:Get*",
1406
- "ec2:List*",
1407
- "ec2:Search*",
1408
- "iam:Get*",
1409
- "iam:List*",
1410
- "iam:PassRole",
1411
- "lambda:Get*",
1412
- "lambda:List*",
1413
- "logs:Describe*",
1414
- "logs:Get*",
1415
- "logs:List*",
1416
- "pipes:Describe*",
1417
- "pipes:List*",
1418
- "s3:Get*",
1419
- "s3:List*",
1420
- "secretsmanager:GetRandomPassword",
1421
- "secretsmanager:GetResourcePolicy",
1422
- "secretsmanager:List*",
1423
- "securityhub:Describe*",
1424
- "securityhub:Get*",
1425
- "securityhub:List*",
1426
- "servicecatalog:Describe*",
1427
- "sns:Get*",
1428
- "sns:List*",
1429
- "sqs:Get*",
1430
- "sqs:List*",
1431
- "states:Describe*",
1432
- "states:Get*",
1433
- "states:List*",
1434
- "tag:*",
1435
- "xray:*",
1436
- ],
1437
- Resource: "*",
1598
+ key: CDK$2.TAG.ROLE,
1599
+ value: CDK$2.ROLE.SECURITY,
1438
1600
  },
1439
1601
  ],
1440
- };
1441
- // Merge with any additional policy statements provided for analysts
1442
- const mergedPolicy = this.mergeInlinePolicies(defaultInlinePolicy, this.props?.inlinePolicyStatements?.analysts);
1443
- const permissionSet = new sso.CfnPermissionSet(this, "AnalystPermissionSet", {
1444
- instanceArn: this.instanceArn,
1445
- name: PermissionSetType.ANALYST,
1446
- description: "Read-only access with billing visibility and limited write access",
1447
- sessionDuration: Duration.hours(4).toIsoString(),
1602
+ });
1603
+ this.analystPermissionSet = new CfnPermissionSet(this, "AnalystPermissionSet", {
1604
+ // Required
1605
+ instanceArn: iamIdentityCenterArn,
1606
+ name: "Analyst",
1607
+ // Optional
1608
+ description: "Read-only access; may expand to limited write access",
1609
+ inlinePolicy: {
1610
+ Version: "2012-10-17",
1611
+ Statement: [
1612
+ {
1613
+ Effect: "Allow",
1614
+ Action: [
1615
+ "aws-portal:ViewUsage",
1616
+ "aws-portal:ViewBilling",
1617
+ "budgets:Describe*",
1618
+ "budgets:View*",
1619
+ "ce:Get*",
1620
+ "ce:List*",
1621
+ "cloudformation:Describe*",
1622
+ "cloudformation:Get*",
1623
+ "cloudformation:List*",
1624
+ "cloudwatch:BatchGet*",
1625
+ "cloudwatch:Get*",
1626
+ "cloudwatch:List*",
1627
+ "cost-optimization-hub:Get*",
1628
+ "cost-optimization-hub:List*",
1629
+ "ec2:Describe*",
1630
+ "ec2:Get*",
1631
+ "ec2:List*",
1632
+ "ec2:Search*",
1633
+ "iam:Get*",
1634
+ "iam:List*",
1635
+ "iam:PassRole",
1636
+ "lambda:Get*",
1637
+ "lambda:List*",
1638
+ "logs:Describe*",
1639
+ "logs:Get*",
1640
+ "logs:List*",
1641
+ "pipes:Describe*",
1642
+ "pipes:List*",
1643
+ "s3:Get*",
1644
+ "s3:List*",
1645
+ "secretsmanager:GetRandomPassword",
1646
+ "secretsmanager:GetResourcePolicy",
1647
+ "secretsmanager:List*",
1648
+ "securityhub:Describe*",
1649
+ "securityhub:Get*",
1650
+ "securityhub:List*",
1651
+ "servicecatalog:Describe*",
1652
+ "sns:Get*",
1653
+ "sns:List*",
1654
+ "sqs:Get*",
1655
+ "sqs:List*",
1656
+ "states:Describe*",
1657
+ "states:Get*",
1658
+ "states:List*",
1659
+ "tag:*",
1660
+ "xray:*",
1661
+ ],
1662
+ Resource: "*",
1663
+ },
1664
+ ],
1665
+ },
1448
1666
  managedPolicies: [
1449
1667
  ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
1450
1668
  .managedPolicyArn,
1669
+ ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1451
1670
  ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
1452
1671
  .managedPolicyArn,
1453
1672
  ],
1454
- inlinePolicy: mergedPolicy,
1455
- });
1456
- Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1457
- this.permissionSets[PermissionSetType.ANALYST] = permissionSet;
1458
- }
1459
- /**
1460
- * Creates the Developer permission set with SystemAdministrator policy
1461
- * and expanded write access
1462
- */
1463
- createDeveloperPermissionSet() {
1464
- const defaultInlinePolicy = {
1465
- Version: "2012-10-17",
1466
- Statement: [
1673
+ sessionDuration: Duration.hours(12).toIsoString(),
1674
+ tags: [
1467
1675
  {
1468
- Effect: "Allow",
1469
- Action: [
1470
- "budgets:*",
1471
- "ce:*",
1472
- "cloudformation:*",
1473
- "cloudwatch:*",
1474
- "cost-optimization-hub:*",
1475
- "ec2:*",
1476
- "iam:Get*",
1477
- "iam:List*",
1478
- "iam:PassRole",
1479
- "lambda:*",
1480
- "logs:*",
1481
- "pipes:*",
1482
- "s3:*",
1483
- "secretsmanager:*",
1484
- "securityhub:*",
1485
- "servicecatalog:*",
1486
- "sns:*",
1487
- "sqs:*",
1488
- "states:*",
1489
- "tag:*",
1490
- "xray:*",
1491
- ],
1492
- Resource: "*",
1676
+ key: CDK$2.TAG.SERVICE,
1677
+ value: CDK$2.SERVICE.SSO,
1493
1678
  },
1494
1679
  {
1495
- Effect: "Deny",
1496
- Action: [
1497
- "iam:*User*",
1498
- "iam:*Role*",
1499
- "iam:*Policy*",
1500
- "organizations:*",
1501
- "account:*",
1502
- ],
1503
- Resource: "*",
1680
+ key: CDK$2.TAG.ROLE,
1681
+ value: CDK$2.ROLE.SECURITY,
1504
1682
  },
1505
1683
  ],
1506
- };
1507
- // Merge with any additional policy statements provided for developers
1508
- const mergedPolicy = this.mergeInlinePolicies(defaultInlinePolicy, this.props?.inlinePolicyStatements?.developers);
1509
- const permissionSet = new sso.CfnPermissionSet(this, "DeveloperPermissionSet", {
1510
- instanceArn: this.instanceArn,
1511
- name: PermissionSetType.DEVELOPER,
1512
- description: "System administrator access with expanded write permissions",
1513
- sessionDuration: Duration.hours(8).toIsoString(),
1684
+ });
1685
+ this.developerPermissionSet = new CfnPermissionSet(this, "DeveloperPermissionSet", {
1686
+ // Required
1687
+ instanceArn: iamIdentityCenterArn,
1688
+ name: "Developer",
1689
+ // Optional
1690
+ description: "Administrative access with limited restrictions",
1691
+ inlinePolicy: {
1692
+ Version: "2012-10-17",
1693
+ Statement: [
1694
+ {
1695
+ Effect: "Allow",
1696
+ Action: [
1697
+ "budgets:*",
1698
+ "ce:*",
1699
+ "cloudformation:*",
1700
+ "cloudwatch:*",
1701
+ "cost-optimization-hub:*",
1702
+ "ec2:*",
1703
+ "iam:Get*",
1704
+ "iam:List*",
1705
+ "iam:PassRole",
1706
+ "lambda:*",
1707
+ "logs:*",
1708
+ "pipes:*",
1709
+ "s3:*",
1710
+ "secretsmanager:*",
1711
+ "securityhub:*",
1712
+ "servicecatalog:*",
1713
+ "sns:*",
1714
+ "sqs:*",
1715
+ "states:*",
1716
+ "tag:*",
1717
+ "xray:*",
1718
+ ],
1719
+ Resource: "*",
1720
+ },
1721
+ ],
1722
+ },
1514
1723
  managedPolicies: [
1515
1724
  ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
1516
1725
  .managedPolicyArn,
1726
+ ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1517
1727
  ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
1518
1728
  .managedPolicyArn,
1519
1729
  ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
1520
1730
  ],
1521
- inlinePolicy: mergedPolicy,
1731
+ sessionDuration: Duration.hours(4).toIsoString(),
1732
+ tags: [
1733
+ {
1734
+ key: CDK$2.TAG.SERVICE,
1735
+ value: CDK$2.SERVICE.SSO,
1736
+ },
1737
+ {
1738
+ key: CDK$2.TAG.ROLE,
1739
+ value: CDK$2.ROLE.SECURITY,
1740
+ },
1741
+ ],
1522
1742
  });
1523
- Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1524
- this.permissionSets[PermissionSetType.DEVELOPER] = permissionSet;
1525
- }
1526
- /**
1527
- * Gets the permission set for the specified type
1528
- */
1529
- getPermissionSet(type) {
1530
- return this.permissionSets[type];
1743
+ // Map permission set names to their ARNs and labels
1744
+ const permissionSetMap = {
1745
+ Administrator: {
1746
+ arn: this.administratorPermissionSet.attrPermissionSetArn,
1747
+ label: "Administrator",
1748
+ },
1749
+ Analyst: {
1750
+ arn: this.analystPermissionSet.attrPermissionSetArn,
1751
+ label: "Analyst",
1752
+ },
1753
+ Developer: {
1754
+ arn: this.developerPermissionSet.attrPermissionSetArn,
1755
+ label: "Developer",
1756
+ },
1757
+ };
1758
+ //
1759
+ // Assignments
1760
+ //
1761
+ // Helper function to create assignments for a group
1762
+ const createAssignments = (groupId, accountAssignments) => {
1763
+ if (!groupId || !accountAssignments) {
1764
+ return; // Skip if group ID or assignments not provided
1765
+ }
1766
+ Object.keys(accountAssignments).forEach((accountId) => {
1767
+ const permissionSetNames = accountAssignments[accountId];
1768
+ permissionSetNames.forEach((permissionSetName) => {
1769
+ const permissionSet = permissionSetMap[permissionSetName];
1770
+ if (!permissionSet) {
1771
+ throw new ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
1772
+ }
1773
+ const accountAssignment = new CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
1774
+ // Required
1775
+ instanceArn: iamIdentityCenterArn,
1776
+ permissionSetArn: permissionSet.arn,
1777
+ principalId: groupId,
1778
+ principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
1779
+ targetId: accountId,
1780
+ targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
1781
+ });
1782
+ Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1783
+ Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
1784
+ });
1785
+ });
1786
+ };
1787
+ // Create assignments for each group
1788
+ createAssignments(administratorGroupId, administratorAccountAssignments);
1789
+ createAssignments(analystGroupId, analystAccountAssignments);
1790
+ createAssignments(developerGroupId, developerAccountAssignments);
1531
1791
  }
1532
- /**
1533
- * Merges default inline policies with additional user-provided policy statements
1534
- *
1535
- * @param defaultPolicy - The default policy object with Version and Statement properties
1536
- * @param additionalStatements - Optional additional policy statements to merge
1537
- * @returns The merged policy object
1538
- */
1539
- mergeInlinePolicies(defaultPolicy, additionalStatements) {
1540
- if (!additionalStatements || additionalStatements.length === 0) {
1541
- return defaultPolicy;
1792
+ }
1793
+
1794
+ //
1795
+ //
1796
+ // Constants
1797
+ //
1798
+ const DEFAULT_APPLICATION_ID = "arn:aws:serverlessrepo:us-east-2:004480582608:applications/SSOSync";
1799
+ const DEFAULT_APPLICATION_VERSION = "2.3.3";
1800
+ const DEFAULT_GOOGLE_GROUP_MATCH = "name:AWS*";
1801
+ //
1802
+ //
1803
+ // Class
1804
+ //
1805
+ class JaypieSsoSyncApplication extends Construct {
1806
+ constructor(scope, id = "SsoSyncApplication", props = {}) {
1807
+ super(scope, id);
1808
+ const { googleAdminEmail, googleAdminEmailEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_ADMIN_EMAIL", googleCredentials, googleCredentialsEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_CREDENTIALS", googleGroupMatch, googleGroupMatchEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_GROUP_MATCH", identityStoreId, identityStoreIdEnvKey = "CDK_ENV_SSOSYNC_IDENTITY_STORE_ID", scimEndpointAccessToken, scimEndpointAccessTokenEnvKey = "CDK_ENV_SCIM_ENDPOINT_ACCESS_TOKEN", scimEndpointUrl, scimEndpointUrlEnvKey = "CDK_ENV_SSOSYNC_SCIM_ENDPOINT_URL", semanticVersion, semanticVersionEnvKey = "CDK_ENV_SSOSYNC_SEMANTIC_VERSION", ssoSyncApplicationId = DEFAULT_APPLICATION_ID, tags, } = props;
1809
+ // Resolve all values from props or environment variables
1810
+ const resolvedGoogleAdminEmail = googleAdminEmail || process.env[googleAdminEmailEnvKey];
1811
+ const resolvedGoogleCredentials = googleCredentials || process.env[googleCredentialsEnvKey];
1812
+ const resolvedGoogleGroupMatch = googleGroupMatch ||
1813
+ process.env[googleGroupMatchEnvKey] ||
1814
+ DEFAULT_GOOGLE_GROUP_MATCH;
1815
+ const resolvedIdentityStoreId = identityStoreId || process.env[identityStoreIdEnvKey];
1816
+ const resolvedScimEndpointAccessToken = scimEndpointAccessToken || process.env[scimEndpointAccessTokenEnvKey];
1817
+ const resolvedScimEndpointUrl = scimEndpointUrl || process.env[scimEndpointUrlEnvKey];
1818
+ const resolvedSemanticVersion = semanticVersion ||
1819
+ process.env[semanticVersionEnvKey] ||
1820
+ DEFAULT_APPLICATION_VERSION;
1821
+ // Validate required parameters
1822
+ const missingParams = [];
1823
+ if (!resolvedGoogleAdminEmail) {
1824
+ missingParams.push(`googleAdminEmail or ${googleAdminEmailEnvKey} environment variable`);
1542
1825
  }
1543
- // Create a deep copy of the default policy to avoid modifying the original
1544
- const mergedPolicy = JSON.parse(JSON.stringify(defaultPolicy));
1545
- // Add the additional statements to the existing statements
1546
- mergedPolicy.Statement = [
1547
- ...mergedPolicy.Statement,
1548
- ...additionalStatements,
1549
- ];
1550
- return mergedPolicy;
1551
- }
1552
- /**
1553
- * Creates assignments between permission sets, groups, and accounts
1554
- * based on the provided configuration
1555
- */
1556
- createPermissionSetAssignments(props) {
1557
- // Administrator assignments
1558
- this.assignAdministratorPermissions(props);
1559
- // Analyst assignments
1560
- this.assignAnalystPermissions(props);
1561
- // Developer assignments
1562
- this.assignDeveloperPermissions(props);
1563
- }
1564
- /**
1565
- * Assigns Administrator permissions to appropriate accounts
1566
- */
1567
- assignAdministratorPermissions(props) {
1568
- const administratorGroup = props.groupMap.administrators;
1569
- const administratorPermissionSet = this.permissionSets[PermissionSetType.ADMINISTRATOR];
1570
- // Administrators get access to all accounts
1571
- const allAccounts = [
1572
- ...props.accountMap.development,
1573
- ...props.accountMap.management,
1574
- ...props.accountMap.operations,
1575
- ...props.accountMap.production,
1576
- ...props.accountMap.sandbox,
1577
- ...props.accountMap.security,
1578
- ...props.accountMap.stage,
1579
- ];
1580
- // Create assignments for each account
1581
- allAccounts.forEach((accountId, index) => {
1582
- const assignment = new sso.CfnAssignment(this, `AdministratorAssignment${index}`, {
1583
- instanceArn: this.instanceArn,
1584
- permissionSetArn: administratorPermissionSet.attrPermissionSetArn,
1585
- principalId: administratorGroup,
1586
- principalType: "GROUP",
1587
- targetId: accountId,
1588
- targetType: "AWS_ACCOUNT",
1589
- });
1590
- Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1591
- Tags.of(assignment).add("Group", "administrators");
1826
+ if (!resolvedGoogleCredentials) {
1827
+ missingParams.push(`googleCredentials or ${googleCredentialsEnvKey} environment variable`);
1828
+ }
1829
+ if (!resolvedIdentityStoreId) {
1830
+ missingParams.push(`identityStoreId or ${identityStoreIdEnvKey} environment variable`);
1831
+ }
1832
+ if (!resolvedScimEndpointAccessToken) {
1833
+ missingParams.push(`scimEndpointAccessToken or ${scimEndpointAccessTokenEnvKey} environment variable`);
1834
+ }
1835
+ if (!resolvedScimEndpointUrl) {
1836
+ missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
1837
+ }
1838
+ if (missingParams.length > 0) {
1839
+ throw new ConfigurationError$1(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
1840
+ }
1841
+ // Create the SSO Sync Application
1842
+ // Type assertion is safe because we validated all required values above
1843
+ this._application = new CfnApplication(this, "Application", {
1844
+ location: {
1845
+ applicationId: ssoSyncApplicationId,
1846
+ semanticVersion: resolvedSemanticVersion,
1847
+ },
1848
+ parameters: {
1849
+ GoogleAdminEmail: resolvedGoogleAdminEmail,
1850
+ GoogleCredentials: resolvedGoogleCredentials,
1851
+ GoogleGroupMatch: resolvedGoogleGroupMatch,
1852
+ IdentityStoreID: resolvedIdentityStoreId,
1853
+ Region: Stack.of(this).region,
1854
+ SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
1855
+ SCIMEndpointUrl: resolvedScimEndpointUrl,
1856
+ },
1592
1857
  });
1593
- }
1594
- /**
1595
- * Assigns Analyst permissions to appropriate accounts
1596
- */
1597
- assignAnalystPermissions(props) {
1598
- const analystGroup = props.groupMap.analysts;
1599
- const analystPermissionSet = this.permissionSets[PermissionSetType.ANALYST];
1600
- // Analysts get access to development, management, sandbox, and stage accounts
1601
- const analystAccounts = [
1602
- ...props.accountMap.development,
1603
- ...props.accountMap.management,
1604
- ...props.accountMap.sandbox,
1605
- ...props.accountMap.stage,
1606
- ];
1607
- // Create assignments for each account
1608
- analystAccounts.forEach((accountId, index) => {
1609
- const assignment = new sso.CfnAssignment(this, `AnalystAssignment${index}`, {
1610
- instanceArn: this.instanceArn,
1611
- permissionSetArn: analystPermissionSet.attrPermissionSetArn,
1612
- principalId: analystGroup,
1613
- principalType: "GROUP",
1614
- targetId: accountId,
1615
- targetType: "AWS_ACCOUNT",
1616
- });
1617
- Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1618
- Tags.of(assignment).add("Group", "analysts");
1858
+ // Add tags
1859
+ const defaultTags = {
1860
+ [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
1861
+ };
1862
+ const allTags = { ...defaultTags, ...tags };
1863
+ Object.entries(allTags).forEach(([key, value]) => {
1864
+ Tags.of(this._application).add(key, value);
1619
1865
  });
1620
1866
  }
1621
- /**
1622
- * Assigns Developer permissions to appropriate accounts
1623
- */
1624
- assignDeveloperPermissions(props) {
1625
- const developerGroup = props.groupMap.developers;
1626
- const developerPermissionSet = this.permissionSets[PermissionSetType.DEVELOPER];
1627
- // Developers get access to development, sandbox, and stage accounts
1628
- const developerAccounts = [
1629
- ...props.accountMap.development,
1630
- ...props.accountMap.sandbox,
1631
- ...props.accountMap.stage,
1632
- ];
1633
- // Create assignments for each account
1634
- developerAccounts.forEach((accountId, index) => {
1635
- const assignment = new sso.CfnAssignment(this, `DeveloperAssignment${index}`, {
1636
- instanceArn: this.instanceArn,
1637
- permissionSetArn: developerPermissionSet.attrPermissionSetArn,
1638
- principalId: developerGroup,
1639
- principalType: "GROUP",
1640
- targetId: accountId,
1641
- targetType: "AWS_ACCOUNT",
1642
- });
1643
- Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1644
- Tags.of(assignment).add("Group", "developers");
1645
- });
1867
+ get application() {
1868
+ return this._application;
1646
1869
  }
1647
1870
  }
1648
1871
 
@@ -1904,7 +2127,13 @@ class JaypieWebDeploymentBucket extends Construct {
1904
2127
  applyRemovalPolicy(policy) {
1905
2128
  this.bucket.applyRemovalPolicy(policy);
1906
2129
  }
2130
+ get bucketRef() {
2131
+ return {
2132
+ bucketArn: this.bucket.bucketArn,
2133
+ bucketName: this.bucket.bucketName,
2134
+ };
2135
+ }
1907
2136
  }
1908
2137
 
1909
- export { JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieDatadogSecret, JaypieEnvSecret, JaypieExpressLambda, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieOpenAiSecret, JaypieQueuedLambda, JaypieSsoGroups, JaypieStack, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, PermissionSetType, addDatadogLayers, constructEnvName, constructStackName, constructTagger, envHostname, isEnv, isProductionEnv, isSandboxEnv, jaypieLambdaEnv, resolveDatadogLayers, resolveHostedZone, resolveParamsAndSecrets };
2138
+ export { JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieDatadogSecret, JaypieDnsRecord, JaypieEnvSecret, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieOpenAiSecret, JaypieQueuedLambda, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, addDatadogLayers, constructEnvName, constructStackName, constructTagger, envHostname, isEnv, isProductionEnv, isSandboxEnv, jaypieLambdaEnv, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveHostedZone, resolveParamsAndSecrets };
1910
2139
  //# sourceMappingURL=index.js.map