@jaypie/constructs 1.1.49 → 1.1.50

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 +15 -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 +95 -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 +589 -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 +15 -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 +95 -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 +582 -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,164 @@ 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 { accountId, oidcProviderArn = Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
1327
+ // Resolve repoRestriction from props or environment variables
1328
+ let repoRestriction = propsRepoRestriction;
1329
+ if (!repoRestriction) {
1330
+ const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
1331
+ if (!envRepo) {
1332
+ throw new ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
1333
+ }
1334
+ // Extract organization from owner/repo format and create org-wide restriction
1335
+ const organization = envRepo.split("/")[0];
1336
+ repoRestriction = `repo:${organization}/*:*`;
1337
+ }
1338
+ // Create the IAM role
1339
+ this._role = new Role(this, "GitHubActionsRole", {
1340
+ assumedBy: new FederatedPrincipal(oidcProviderArn, {
1341
+ StringLike: {
1342
+ "token.actions.githubusercontent.com:sub": repoRestriction,
1343
+ },
1344
+ }, "sts:AssumeRoleWithWebIdentity"),
1345
+ maxSessionDuration: Duration.hours(1),
1346
+ path: "/",
1347
+ });
1348
+ Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
1349
+ // Allow the role to access the GitHub OIDC provider
1350
+ this._role.addToPolicy(new PolicyStatement({
1351
+ actions: ["sts:AssumeRoleWithWebIdentity"],
1352
+ resources: [`arn:aws:iam::${accountId}:oidc-provider/*`],
1353
+ }));
1354
+ // Allow the role to deploy CDK apps
1355
+ this._role.addToPolicy(new PolicyStatement({
1356
+ actions: [
1357
+ "cloudformation:CreateStack",
1358
+ "cloudformation:DeleteStack",
1359
+ "cloudformation:DescribeStackEvents",
1360
+ "cloudformation:DescribeStackResource",
1361
+ "cloudformation:DescribeStackResources",
1362
+ "cloudformation:DescribeStacks",
1363
+ "cloudformation:GetTemplate",
1364
+ "cloudformation:SetStackPolicy",
1365
+ "cloudformation:UpdateStack",
1366
+ "cloudformation:ValidateTemplate",
1367
+ "iam:PassRole",
1368
+ "route53:ListHostedZones*",
1369
+ "s3:GetObject",
1370
+ "s3:ListBucket",
1371
+ ],
1372
+ effect: Effect.ALLOW,
1373
+ resources: ["*"],
1374
+ }));
1375
+ this._role.addToPolicy(new PolicyStatement({
1376
+ actions: ["iam:PassRole", "sts:AssumeRole"],
1377
+ effect: Effect.ALLOW,
1378
+ resources: [
1379
+ "arn:aws:iam::*:role/cdk-hnb659fds-deploy-role-*",
1380
+ "arn:aws:iam::*:role/cdk-hnb659fds-file-publishing-*",
1381
+ "arn:aws:iam::*:role/cdk-readOnlyRole",
1382
+ ],
1383
+ }));
1384
+ // Export the ARN of the role
1385
+ if (output !== false) {
1386
+ const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
1387
+ new CfnOutput(this, outputId, {
1388
+ value: this._role.roleArn,
1389
+ });
1390
+ }
1391
+ }
1392
+ get role() {
1393
+ return this._role;
1394
+ }
1395
+ get roleArn() {
1396
+ return this._role.roleArn;
1397
+ }
1398
+ get roleName() {
1399
+ return this._role.roleName;
1400
+ }
1401
+ }
1402
+
1217
1403
  class JaypieExpressLambda extends JaypieLambda {
1218
1404
  constructor(scope, id, props) {
1219
1405
  super(scope, id, {
@@ -1229,11 +1415,12 @@ const SERVICE = {
1229
1415
  };
1230
1416
  class JaypieHostedZone extends Construct {
1231
1417
  /**
1232
- * Create a new hosted zone with query logging
1418
+ * Create a new hosted zone with query logging and optional DNS records
1233
1419
  */
1234
1420
  constructor(scope, id, props) {
1235
1421
  super(scope, id);
1236
- const { destination, zoneName, project } = props;
1422
+ const { zoneName, project } = props;
1423
+ const destination = props.destination ?? true;
1237
1424
  const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
1238
1425
  // Create the log group
1239
1426
  this.logGroup = new LogGroup(this, "LogGroup", {
@@ -1250,10 +1437,13 @@ class JaypieHostedZone extends Construct {
1250
1437
  }
1251
1438
  // Grant Route 53 permissions to write to the log group
1252
1439
  this.logGroup.grantWrite(new ServicePrincipal(SERVICE.ROUTE53));
1253
- // Add destination if provided
1254
- if (destination) {
1440
+ // Add destination based on configuration
1441
+ if (destination !== false) {
1442
+ const lambdaDestination = destination === true
1443
+ ? resolveDatadogLoggingDestination(scope)
1444
+ : destination;
1255
1445
  this.logGroup.addSubscriptionFilter("DatadogLambdaDestination", {
1256
- destination,
1446
+ destination: lambdaDestination,
1257
1447
  filterPattern: FilterPattern.allEvents(),
1258
1448
  });
1259
1449
  }
@@ -1268,6 +1458,21 @@ class JaypieHostedZone extends Construct {
1268
1458
  if (project) {
1269
1459
  cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
1270
1460
  }
1461
+ // Create DNS records if provided
1462
+ this.dnsRecords = [];
1463
+ if (props.records) {
1464
+ props.records.forEach((recordConfig, index) => {
1465
+ const { id, ...recordProps } = recordConfig;
1466
+ // Generate a default ID if not provided
1467
+ const recordId = id ||
1468
+ `${recordProps.type}${recordProps.recordName ? `-${recordProps.recordName}` : ""}-${index}`;
1469
+ const dnsRecord = new JaypieDnsRecord(this, recordId, {
1470
+ ...recordProps,
1471
+ zone: this.hostedZone,
1472
+ });
1473
+ this.dnsRecords.push(dnsRecord);
1474
+ });
1475
+ }
1271
1476
  }
1272
1477
  }
1273
1478
 
@@ -1316,333 +1521,348 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
1316
1521
  }
1317
1522
 
1318
1523
  /**
1319
- * Permission set types with corresponding AWS managed policies
1524
+ * JaypieSsoPermissions Construct
1525
+ *
1526
+ * Creates and manages AWS IAM Identity Center (SSO) permission sets and assignments
1527
+ *
1528
+ * @example
1529
+ * const permissionSets = new JaypieSsoPermissions(this, "PermissionSets", {
1530
+ * iamIdentityCenterArn: "arn:aws:sso:::instance/...",
1531
+ * administratorGroupId: "b4c8b438-4031-7000-782d-5046945fb956",
1532
+ * analystGroupId: "2488f4e8-d061-708e-abe1-c315f0e30005",
1533
+ * developerGroupId: "b438a4f8-e0e1-707c-c6e8-21841daf9ad1",
1534
+ * administratorAccountAssignments: {
1535
+ * "211125635435": ["Administrator", "Analyst", "Developer"],
1536
+ * "381492033431": ["Administrator", "Analyst"],
1537
+ * },
1538
+ * analystAccountAssignments: {
1539
+ * "211125635435": ["Analyst", "Developer"],
1540
+ * "381492033431": [],
1541
+ * },
1542
+ * developerAccountAssignments: {
1543
+ * "211125635435": ["Analyst", "Developer"],
1544
+ * "381492033431": [],
1545
+ * },
1546
+ * });
1320
1547
  */
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 {
1548
+ class JaypieSsoPermissions extends Construct {
1333
1549
  constructor(scope, id, props) {
1334
1550
  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: [
1551
+ const { iamIdentityCenterArn, administratorGroupId, analystGroupId, developerGroupId, administratorAccountAssignments, analystAccountAssignments, developerAccountAssignments, } = props;
1552
+ if (!iamIdentityCenterArn) {
1553
+ // If no IAM Identity Center ARN provided, skip SSO setup
1554
+ return;
1555
+ }
1556
+ //
1557
+ // Permission Sets
1558
+ //
1559
+ this.administratorPermissionSet = new CfnPermissionSet(this, "AdministratorPermissionSet", {
1560
+ // Required
1561
+ instanceArn: iamIdentityCenterArn,
1562
+ name: "Administrator",
1563
+ // Optional
1564
+ description: "Unrestricted access",
1565
+ inlinePolicy: {
1566
+ Version: "2012-10-17",
1567
+ Statement: [
1568
+ {
1569
+ Effect: "Allow",
1570
+ Action: [
1571
+ "aws-portal:ViewUsage",
1572
+ "aws-portal:ViewBilling",
1573
+ "budgets:*",
1574
+ "cur:DescribeReportDefinitions",
1575
+ "cur:PutReportDefinition",
1576
+ "cur:DeleteReportDefinition",
1577
+ "cur:ModifyReportDefinition",
1578
+ ],
1579
+ Resource: "*",
1580
+ },
1581
+ ],
1582
+ },
1583
+ managedPolicies: [
1584
+ ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess")
1585
+ .managedPolicyArn,
1586
+ ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1587
+ ],
1588
+ sessionDuration: Duration.hours(1).toIsoString(),
1589
+ tags: [
1353
1590
  {
1354
- Effect: "Allow",
1355
- Action: [
1356
- "aws-portal:*",
1357
- "budgets:*",
1358
- "ce:*",
1359
- "cost-optimization-hub:*",
1360
- "cur:*",
1361
- ],
1362
- Resource: "*",
1591
+ key: CDK$2.TAG.SERVICE,
1592
+ value: CDK$2.SERVICE.SSO,
1363
1593
  },
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
1594
  {
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: "*",
1595
+ key: CDK$2.TAG.ROLE,
1596
+ value: CDK$2.ROLE.SECURITY,
1438
1597
  },
1439
1598
  ],
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(),
1599
+ });
1600
+ this.analystPermissionSet = new CfnPermissionSet(this, "AnalystPermissionSet", {
1601
+ // Required
1602
+ instanceArn: iamIdentityCenterArn,
1603
+ name: "Analyst",
1604
+ // Optional
1605
+ description: "Read-only access; may expand to limited write access",
1606
+ inlinePolicy: {
1607
+ Version: "2012-10-17",
1608
+ Statement: [
1609
+ {
1610
+ Effect: "Allow",
1611
+ Action: [
1612
+ "aws-portal:ViewUsage",
1613
+ "aws-portal:ViewBilling",
1614
+ "budgets:Describe*",
1615
+ "budgets:View*",
1616
+ "ce:Get*",
1617
+ "ce:List*",
1618
+ "cloudformation:Describe*",
1619
+ "cloudformation:Get*",
1620
+ "cloudformation:List*",
1621
+ "cloudwatch:BatchGet*",
1622
+ "cloudwatch:Get*",
1623
+ "cloudwatch:List*",
1624
+ "cost-optimization-hub:Get*",
1625
+ "cost-optimization-hub:List*",
1626
+ "ec2:Describe*",
1627
+ "ec2:Get*",
1628
+ "ec2:List*",
1629
+ "ec2:Search*",
1630
+ "iam:Get*",
1631
+ "iam:List*",
1632
+ "iam:PassRole",
1633
+ "lambda:Get*",
1634
+ "lambda:List*",
1635
+ "logs:Describe*",
1636
+ "logs:Get*",
1637
+ "logs:List*",
1638
+ "pipes:Describe*",
1639
+ "pipes:List*",
1640
+ "s3:Get*",
1641
+ "s3:List*",
1642
+ "secretsmanager:GetRandomPassword",
1643
+ "secretsmanager:GetResourcePolicy",
1644
+ "secretsmanager:List*",
1645
+ "securityhub:Describe*",
1646
+ "securityhub:Get*",
1647
+ "securityhub:List*",
1648
+ "servicecatalog:Describe*",
1649
+ "sns:Get*",
1650
+ "sns:List*",
1651
+ "sqs:Get*",
1652
+ "sqs:List*",
1653
+ "states:Describe*",
1654
+ "states:Get*",
1655
+ "states:List*",
1656
+ "tag:*",
1657
+ "xray:*",
1658
+ ],
1659
+ Resource: "*",
1660
+ },
1661
+ ],
1662
+ },
1448
1663
  managedPolicies: [
1449
1664
  ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
1450
1665
  .managedPolicyArn,
1666
+ ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1451
1667
  ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
1452
1668
  .managedPolicyArn,
1453
1669
  ],
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: [
1670
+ sessionDuration: Duration.hours(12).toIsoString(),
1671
+ tags: [
1467
1672
  {
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: "*",
1673
+ key: CDK$2.TAG.SERVICE,
1674
+ value: CDK$2.SERVICE.SSO,
1493
1675
  },
1494
1676
  {
1495
- Effect: "Deny",
1496
- Action: [
1497
- "iam:*User*",
1498
- "iam:*Role*",
1499
- "iam:*Policy*",
1500
- "organizations:*",
1501
- "account:*",
1502
- ],
1503
- Resource: "*",
1677
+ key: CDK$2.TAG.ROLE,
1678
+ value: CDK$2.ROLE.SECURITY,
1504
1679
  },
1505
1680
  ],
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(),
1681
+ });
1682
+ this.developerPermissionSet = new CfnPermissionSet(this, "DeveloperPermissionSet", {
1683
+ // Required
1684
+ instanceArn: iamIdentityCenterArn,
1685
+ name: "Developer",
1686
+ // Optional
1687
+ description: "Administrative access with limited restrictions",
1688
+ inlinePolicy: {
1689
+ Version: "2012-10-17",
1690
+ Statement: [
1691
+ {
1692
+ Effect: "Allow",
1693
+ Action: [
1694
+ "budgets:*",
1695
+ "ce:*",
1696
+ "cloudformation:*",
1697
+ "cloudwatch:*",
1698
+ "cost-optimization-hub:*",
1699
+ "ec2:*",
1700
+ "iam:Get*",
1701
+ "iam:List*",
1702
+ "iam:PassRole",
1703
+ "lambda:*",
1704
+ "logs:*",
1705
+ "pipes:*",
1706
+ "s3:*",
1707
+ "secretsmanager:*",
1708
+ "securityhub:*",
1709
+ "servicecatalog:*",
1710
+ "sns:*",
1711
+ "sqs:*",
1712
+ "states:*",
1713
+ "tag:*",
1714
+ "xray:*",
1715
+ ],
1716
+ Resource: "*",
1717
+ },
1718
+ ],
1719
+ },
1514
1720
  managedPolicies: [
1515
1721
  ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
1516
1722
  .managedPolicyArn,
1723
+ ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1517
1724
  ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
1518
1725
  .managedPolicyArn,
1519
1726
  ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
1520
1727
  ],
1521
- inlinePolicy: mergedPolicy,
1728
+ sessionDuration: Duration.hours(4).toIsoString(),
1729
+ tags: [
1730
+ {
1731
+ key: CDK$2.TAG.SERVICE,
1732
+ value: CDK$2.SERVICE.SSO,
1733
+ },
1734
+ {
1735
+ key: CDK$2.TAG.ROLE,
1736
+ value: CDK$2.ROLE.SECURITY,
1737
+ },
1738
+ ],
1522
1739
  });
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];
1740
+ // Map permission set names to their ARNs and labels
1741
+ const permissionSetMap = {
1742
+ Administrator: {
1743
+ arn: this.administratorPermissionSet.attrPermissionSetArn,
1744
+ label: "Administrator",
1745
+ },
1746
+ Analyst: {
1747
+ arn: this.analystPermissionSet.attrPermissionSetArn,
1748
+ label: "Analyst",
1749
+ },
1750
+ Developer: {
1751
+ arn: this.developerPermissionSet.attrPermissionSetArn,
1752
+ label: "Developer",
1753
+ },
1754
+ };
1755
+ //
1756
+ // Assignments
1757
+ //
1758
+ // Helper function to create assignments for a group
1759
+ const createAssignments = (groupId, accountAssignments) => {
1760
+ if (!groupId || !accountAssignments) {
1761
+ return; // Skip if group ID or assignments not provided
1762
+ }
1763
+ Object.keys(accountAssignments).forEach((accountId) => {
1764
+ const permissionSetNames = accountAssignments[accountId];
1765
+ permissionSetNames.forEach((permissionSetName) => {
1766
+ const permissionSet = permissionSetMap[permissionSetName];
1767
+ if (!permissionSet) {
1768
+ throw new ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
1769
+ }
1770
+ const accountAssignment = new CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
1771
+ // Required
1772
+ instanceArn: iamIdentityCenterArn,
1773
+ permissionSetArn: permissionSet.arn,
1774
+ principalId: groupId,
1775
+ principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
1776
+ targetId: accountId,
1777
+ targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
1778
+ });
1779
+ Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1780
+ Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
1781
+ });
1782
+ });
1783
+ };
1784
+ // Create assignments for each group
1785
+ createAssignments(administratorGroupId, administratorAccountAssignments);
1786
+ createAssignments(analystGroupId, analystAccountAssignments);
1787
+ createAssignments(developerGroupId, developerAccountAssignments);
1531
1788
  }
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;
1789
+ }
1790
+
1791
+ //
1792
+ //
1793
+ // Constants
1794
+ //
1795
+ const DEFAULT_APPLICATION_ID = "arn:aws:serverlessrepo:us-east-2:004480582608:applications/SSOSync";
1796
+ const DEFAULT_APPLICATION_VERSION = "2.3.3";
1797
+ const DEFAULT_GOOGLE_GROUP_MATCH = "name:AWS*";
1798
+ //
1799
+ //
1800
+ // Class
1801
+ //
1802
+ class JaypieSsoSyncApplication extends Construct {
1803
+ constructor(scope, id = "SSOSyncApplication", props = {}) {
1804
+ super(scope, id);
1805
+ 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;
1806
+ // Resolve all values from props or environment variables
1807
+ const resolvedGoogleAdminEmail = googleAdminEmail || process.env[googleAdminEmailEnvKey];
1808
+ const resolvedGoogleCredentials = googleCredentials || process.env[googleCredentialsEnvKey];
1809
+ const resolvedGoogleGroupMatch = googleGroupMatch ||
1810
+ process.env[googleGroupMatchEnvKey] ||
1811
+ DEFAULT_GOOGLE_GROUP_MATCH;
1812
+ const resolvedIdentityStoreId = identityStoreId || process.env[identityStoreIdEnvKey];
1813
+ const resolvedScimEndpointAccessToken = scimEndpointAccessToken || process.env[scimEndpointAccessTokenEnvKey];
1814
+ const resolvedScimEndpointUrl = scimEndpointUrl || process.env[scimEndpointUrlEnvKey];
1815
+ const resolvedSemanticVersion = semanticVersion ||
1816
+ process.env[semanticVersionEnvKey] ||
1817
+ DEFAULT_APPLICATION_VERSION;
1818
+ // Validate required parameters
1819
+ const missingParams = [];
1820
+ if (!resolvedGoogleAdminEmail) {
1821
+ missingParams.push(`googleAdminEmail or ${googleAdminEmailEnvKey} environment variable`);
1542
1822
  }
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");
1823
+ if (!resolvedGoogleCredentials) {
1824
+ missingParams.push(`googleCredentials or ${googleCredentialsEnvKey} environment variable`);
1825
+ }
1826
+ if (!resolvedIdentityStoreId) {
1827
+ missingParams.push(`identityStoreId or ${identityStoreIdEnvKey} environment variable`);
1828
+ }
1829
+ if (!resolvedScimEndpointAccessToken) {
1830
+ missingParams.push(`scimEndpointAccessToken or ${scimEndpointAccessTokenEnvKey} environment variable`);
1831
+ }
1832
+ if (!resolvedScimEndpointUrl) {
1833
+ missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
1834
+ }
1835
+ if (missingParams.length > 0) {
1836
+ throw new ConfigurationError$1(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
1837
+ }
1838
+ // Create the SSO Sync Application
1839
+ // Type assertion is safe because we validated all required values above
1840
+ this._application = new CfnApplication(this, "Application", {
1841
+ location: {
1842
+ applicationId: ssoSyncApplicationId,
1843
+ semanticVersion: resolvedSemanticVersion,
1844
+ },
1845
+ parameters: {
1846
+ GoogleAdminEmail: resolvedGoogleAdminEmail,
1847
+ GoogleCredentials: resolvedGoogleCredentials,
1848
+ GoogleGroupMatch: resolvedGoogleGroupMatch,
1849
+ IdentityStoreID: resolvedIdentityStoreId,
1850
+ Region: Stack.of(this).region,
1851
+ SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
1852
+ SCIMEndpointUrl: resolvedScimEndpointUrl,
1853
+ },
1592
1854
  });
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");
1855
+ // Add tags
1856
+ const defaultTags = {
1857
+ [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
1858
+ };
1859
+ const allTags = { ...defaultTags, ...tags };
1860
+ Object.entries(allTags).forEach(([key, value]) => {
1861
+ Tags.of(this._application).add(key, value);
1619
1862
  });
1620
1863
  }
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
- });
1864
+ get application() {
1865
+ return this._application;
1646
1866
  }
1647
1867
  }
1648
1868
 
@@ -1904,7 +2124,13 @@ class JaypieWebDeploymentBucket extends Construct {
1904
2124
  applyRemovalPolicy(policy) {
1905
2125
  this.bucket.applyRemovalPolicy(policy);
1906
2126
  }
2127
+ get bucketRef() {
2128
+ return {
2129
+ bucketArn: this.bucket.bucketArn,
2130
+ bucketName: this.bucket.bucketName,
2131
+ };
2132
+ }
1907
2133
  }
1908
2134
 
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 };
2135
+ 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
2136
  //# sourceMappingURL=index.js.map