@aws/nx-plugin 1.0.0-rc.76 → 1.0.0-rc.78

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 (30) hide show
  1. package/migrations.json +16 -1
  2. package/package.json +1 -1
  3. package/src/migrations/latest/dynamodb-configurable-encryption/__snapshots__/migration.spec.ts.snap +429 -0
  4. package/src/migrations/latest/dynamodb-configurable-encryption/metadata.json +3 -0
  5. package/src/migrations/latest/dynamodb-configurable-encryption/migration.d.ts +6 -0
  6. package/src/migrations/latest/dynamodb-configurable-encryption/migration.js +325 -0
  7. package/src/migrations/latest/dynamodb-configurable-encryption/migration.js.map +1 -0
  8. package/src/migrations/latest/static-website-configurable-waf-encryption/metadata.json +3 -0
  9. package/src/migrations/latest/static-website-configurable-waf-encryption/migration.d.ts +6 -0
  10. package/src/migrations/latest/static-website-configurable-waf-encryption/migration.js +418 -0
  11. package/src/migrations/latest/static-website-configurable-waf-encryption/migration.js.map +1 -0
  12. package/src/migrations/latest/user-identity-configurable-mfa/metadata.json +3 -0
  13. package/src/migrations/latest/user-identity-configurable-mfa/migration.d.ts +6 -0
  14. package/src/migrations/latest/user-identity-configurable-mfa/migration.js +362 -0
  15. package/src/migrations/latest/user-identity-configurable-mfa/migration.js.map +1 -0
  16. package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +90 -9
  17. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +90 -9
  18. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +259 -48
  19. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +37 -7
  20. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +26 -0
  21. package/src/utils/dynamodb-constructs/files/cdk/core/dynamodb.ts.template +23 -3
  22. package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +27 -1
  23. package/src/utils/dynamodb-constructs/files/terraform/core/dynamodb/dynamodb.tf.template +39 -5
  24. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +41 -7
  25. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +48 -13
  26. package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +26 -0
  27. package/src/utils/website-constructs/files/cdk/app/static-websites/__websiteNameKebabCase__.ts.template +11 -2
  28. package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +47 -10
  29. package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +55 -0
  30. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +57 -15
package/migrations.json CHANGED
@@ -22,6 +22,21 @@
22
22
  "description": "Extract the duplicated session-id Express middleware in ts#agent's ag-ui/a2a index.ts into a shared middleware/session-id-middleware.ts module",
23
23
  "implementation": "./src/migrations/latest/ts-agent-session-id-middleware-extraction/migration"
24
24
  },
25
+ "latest-dynamodb-configurable-encryption": {
26
+ "version": "1.0.0-rc.77",
27
+ "description": "Surface encryption and encryptionKey on the vended DynamoDBTable construct/module",
28
+ "implementation": "./src/migrations/latest/dynamodb-configurable-encryption/migration"
29
+ },
30
+ "latest-user-identity-configurable-mfa": {
31
+ "version": "1.0.0-rc.77",
32
+ "description": "Surface mfa and mfaSecondFactor on the vended UserIdentity construct/module",
33
+ "implementation": "./src/migrations/latest/user-identity-configurable-mfa/migration"
34
+ },
35
+ "latest-static-website-configurable-waf-encryption": {
36
+ "version": "1.0.0-rc.78",
37
+ "description": "Surface enableWaf, encryption, encryptionKey and enableKeyRotation on the vended StaticWebsite construct/module",
38
+ "implementation": "./src/migrations/latest/static-website-configurable-waf-encryption/migration"
39
+ },
25
40
  "v1.0.0-rc.50-0001-modernize-function-props-cast": {
26
41
  "version": "1.0.0-rc.50",
27
42
  "description": "Replace the legacy angle-bracket FunctionProps type assertion with the modern as syntax in generated API constructs",
@@ -143,7 +158,7 @@
143
158
  "implementation": "./src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration"
144
159
  },
145
160
  "sync-vended-versions": {
146
- "version": "1.0.0-rc.76",
161
+ "version": "1.0.0-rc.78",
147
162
  "description": "Sync vended dependency versions and the tracked plugin version to those vended by this release",
148
163
  "implementation": "./src/utils/version-upgrade-migration/migration"
149
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws/nx-plugin",
3
- "version": "1.0.0-rc.76",
3
+ "version": "1.0.0-rc.78",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/awslabs/nx-plugin-for-aws.git",
@@ -0,0 +1,429 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`dynamodb-configurable-encryption migration > surfaces encryption and encryptionKey on the CDK construct 1`] = `
4
+ "import { Construct } from 'constructs';
5
+ import {
6
+ AttributeType,
7
+ BillingMode,
8
+ Table,
9
+ TableEncryption,
10
+ TableProps,
11
+ } from 'aws-cdk-lib/aws-dynamodb';
12
+ import { RemovalPolicy } from 'aws-cdk-lib';
13
+ import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
14
+ import { Key, IKey } from 'aws-cdk-lib/aws-kms';
15
+ import { RuntimeConfig } from './runtime-config.js';
16
+
17
+ type _DynamoDBTableProps = Omit<
18
+ TableProps,
19
+ 'tableName' | 'partitionKey' | 'sortKey' | 'encryption' | 'encryptionKey'
20
+ >;
21
+
22
+ export interface DynamoDBTableProps extends _DynamoDBTableProps {
23
+ /**
24
+ * The DynamoDB table name. If omitted, CDK auto-generates a unique name
25
+ * from the stack name and construct path.
26
+ */
27
+ readonly tableName?: string;
28
+
29
+ /**
30
+ * RuntimeConfig key used under the \`dynamodb\` namespace.
31
+ */
32
+ readonly runtimeConfigKey: string;
33
+
34
+ /**
35
+ * Server-side encryption for the table.
36
+ *
37
+ * @default TableEncryption.CUSTOMER_MANAGED
38
+ */
39
+ readonly encryption?: TableEncryption;
40
+
41
+ /**
42
+ * KMS key used to encrypt the table. Only used when \`encryption\` is
43
+ * \`TableEncryption.CUSTOMER_MANAGED\`. When not provided, a new key is created.
44
+ */
45
+ readonly encryptionKey?: IKey;
46
+
47
+ /**
48
+ * Whether to enable automatic key rotation on the KMS key used to encrypt the table.
49
+ * Only applies when \`encryption\` is \`TableEncryption.CUSTOMER_MANAGED\` and no
50
+ * \`encryptionKey\` is supplied.
51
+ *
52
+ * @default true
53
+ */
54
+ readonly enableKeyRotation?: boolean;
55
+ }
56
+
57
+ export abstract class DynamoDBTable extends Construct {
58
+ public readonly table: Table;
59
+
60
+ constructor(
61
+ scope: Construct,
62
+ id: string,
63
+ {
64
+ tableName,
65
+ runtimeConfigKey,
66
+ billingMode = BillingMode.PAY_PER_REQUEST,
67
+ pointInTimeRecoverySpecification = { pointInTimeRecoveryEnabled: true },
68
+ deletionProtection = true,
69
+ removalPolicy = RemovalPolicy.RETAIN,
70
+ encryption = TableEncryption.CUSTOMER_MANAGED,
71
+ encryptionKey,
72
+ enableKeyRotation = true,
73
+ ...rest
74
+ }: DynamoDBTableProps,
75
+ ) {
76
+ super(scope, id);
77
+
78
+ const key: IKey | undefined =
79
+ encryption === TableEncryption.CUSTOMER_MANAGED
80
+ ? (encryptionKey ??
81
+ new Key(this, 'EncryptionKey', { enableKeyRotation }))
82
+ : undefined;
83
+
84
+ this.table = new Table(this, runtimeConfigKey, {
85
+ ...rest,
86
+ ...(tableName !== undefined && { tableName }),
87
+ partitionKey: { name: 'pk', type: AttributeType.STRING },
88
+ sortKey: { name: 'sk', type: AttributeType.STRING },
89
+ billingMode,
90
+ pointInTimeRecoverySpecification,
91
+ deletionProtection,
92
+ encryptionKey: key,
93
+ encryption,
94
+ removalPolicy,
95
+ });
96
+
97
+ const rc = RuntimeConfig.ensure(this);
98
+ rc.set('dynamodb', runtimeConfigKey, { tableName: this.table.tableName });
99
+ }
100
+
101
+ public grantReadData(grantee: IGrantable): Grant {
102
+ return this.table.grantReadData(grantee);
103
+ }
104
+
105
+ public grantWriteData(grantee: IGrantable): Grant {
106
+ return this.table.grantWriteData(grantee);
107
+ }
108
+
109
+ public grantReadWriteData(grantee: IGrantable): Grant {
110
+ return this.table.grantReadWriteData(grantee);
111
+ }
112
+
113
+ public grantFullAccess(grantee: IGrantable): Grant {
114
+ return this.table.grantFullAccess(grantee);
115
+ }
116
+
117
+ public grantStreamRead(grantee: IGrantable): Grant {
118
+ return this.table.grantStreamRead(grantee);
119
+ }
120
+
121
+ public grantTableListStreams(grantee: IGrantable): Grant {
122
+ return this.table.grantTableListStreams(grantee);
123
+ }
124
+
125
+ public grant(grantee: IGrantable, ...actions: string[]): Grant {
126
+ return this.table.grant(grantee, ...actions);
127
+ }
128
+
129
+ public grantStream(grantee: IGrantable, ...actions: string[]): Grant {
130
+ return this.table.grantStream(grantee, ...actions);
131
+ }
132
+ }
133
+ "
134
+ `;
135
+
136
+ exports[`dynamodb-configurable-encryption migration > surfaces encryption and kms_key_arn as pass-through variables on the terraform app module 1`] = `
137
+ "terraform {
138
+ required_providers {
139
+ aws = {
140
+ source = "hashicorp/aws"
141
+ version = "5.100.0"
142
+ }
143
+ random = {
144
+ source = "hashicorp/random"
145
+ version = "3.6.3"
146
+ }
147
+ }
148
+ }
149
+
150
+ variable "billing_mode" {
151
+ description = "Controls how you are charged for read and write throughput."
152
+ type = string
153
+ default = "PAY_PER_REQUEST"
154
+ }
155
+
156
+ variable "point_in_time_recovery_enabled" {
157
+ description = "Whether to enable point-in-time recovery for the table."
158
+ type = bool
159
+ default = true
160
+ }
161
+
162
+ variable "deletion_protection_enabled" {
163
+ description = "Whether to enable deletion protection on the table."
164
+ type = bool
165
+ default = true
166
+ }
167
+
168
+ variable "encryption" {
169
+ description = "Server-side encryption for the table. One of CUSTOMER_MANAGED, AWS_MANAGED or DEFAULT (the AWS owned key, at no cost and with no key to manage). Changing this on an already-deployed table away from CUSTOMER_MANAGED requires a manual step first - see the ts#dynamodb generator docs."
170
+ type = string
171
+ default = "CUSTOMER_MANAGED"
172
+
173
+ validation {
174
+ condition = contains(["CUSTOMER_MANAGED", "AWS_MANAGED", "DEFAULT"], var.encryption)
175
+ error_message = "encryption must be one of CUSTOMER_MANAGED, AWS_MANAGED or DEFAULT."
176
+ }
177
+ }
178
+
179
+ variable "kms_key_arn" {
180
+ description = "ARN of an existing KMS key used to encrypt the table when encryption is CUSTOMER_MANAGED. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the DynamoDB service the necessary permissions in its own key policy."
181
+ type = string
182
+ default = null
183
+ }
184
+
185
+ variable "create_kms_key" {
186
+ description = "Whether to create a KMS key for the table. Only applies when encryption is CUSTOMER_MANAGED. Set to false when supplying kms_key_arn."
187
+ type = bool
188
+ default = true
189
+ }
190
+
191
+ variable "enable_key_rotation" {
192
+ description = "Whether to enable automatic key rotation on the KMS key used to encrypt the table. Only applies when encryption is CUSTOMER_MANAGED and create_kms_key is true."
193
+ type = bool
194
+ default = true
195
+ }
196
+
197
+ resource "random_string" "suffix" {
198
+ length = 8
199
+ special = false
200
+ upper = false
201
+ }
202
+
203
+ locals {
204
+ dynamodb_config = jsondecode(file("\${path.module}/../../../../../../../apps/my-table/config.json"))
205
+ global_secondary_indexes = [for gsi in local.dynamodb_config.tableConfig.globalSecondaryIndexes : {
206
+ name = gsi.indexName
207
+ hash_key = gsi.partitionKey
208
+ range_key = try(gsi.sortKey, null)
209
+ projection_type = "ALL"
210
+ }]
211
+ }
212
+
213
+ module "dynamodb_table" {
214
+ source = "../../../core/dynamodb"
215
+ name = "my-table-\${random_string.suffix.result}"
216
+ billing_mode = var.billing_mode
217
+ point_in_time_recovery_enabled = var.point_in_time_recovery_enabled
218
+ deletion_protection_enabled = var.deletion_protection_enabled
219
+ encryption = var.encryption
220
+ kms_key_arn = var.kms_key_arn
221
+ create_kms_key = var.create_kms_key
222
+ enable_key_rotation = var.enable_key_rotation
223
+ global_secondary_indexes = local.global_secondary_indexes
224
+ }
225
+
226
+ module "add_to_runtime_config" {
227
+ source = "../../../core/runtime-config/entry"
228
+
229
+ namespace = "dynamodb"
230
+ key = local.dynamodb_config.runtimeConfigKey
231
+ value = {
232
+ tableName = module.dynamodb_table.table_name
233
+ }
234
+ }
235
+ "
236
+ `;
237
+
238
+ exports[`dynamodb-configurable-encryption migration > surfaces encryption and kms_key_arn on the terraform core module 1`] = `
239
+ "terraform {
240
+ required_version = ">= 1.0"
241
+
242
+ required_providers {
243
+ aws = {
244
+ source = "hashicorp/aws"
245
+ version = "5.100.0"
246
+ }
247
+ }
248
+ }
249
+
250
+ variable "name" {
251
+ description = "Name for the DynamoDB table."
252
+ type = string
253
+ }
254
+
255
+ variable "billing_mode" {
256
+ description = "Controls how you are charged for read and write throughput."
257
+ type = string
258
+ default = "PAY_PER_REQUEST"
259
+ }
260
+
261
+ variable "point_in_time_recovery_enabled" {
262
+ description = "Whether to enable point-in-time recovery for the table."
263
+ type = bool
264
+ default = true
265
+ }
266
+
267
+ variable "deletion_protection_enabled" {
268
+ description = "Whether to enable deletion protection on the table."
269
+ type = bool
270
+ default = true
271
+ }
272
+
273
+ variable "encryption" {
274
+ description = "Server-side encryption for the table. One of CUSTOMER_MANAGED, AWS_MANAGED or DEFAULT (the AWS owned key, at no cost and with no key to manage). Changing this on an already-deployed table away from CUSTOMER_MANAGED requires a manual step first - see the ts#dynamodb generator docs."
275
+ type = string
276
+ default = "CUSTOMER_MANAGED"
277
+
278
+ validation {
279
+ condition = contains(["CUSTOMER_MANAGED", "AWS_MANAGED", "DEFAULT"], var.encryption)
280
+ error_message = "encryption must be one of CUSTOMER_MANAGED, AWS_MANAGED or DEFAULT."
281
+ }
282
+ }
283
+
284
+ variable "kms_key_arn" {
285
+ description = "ARN of an existing KMS key used to encrypt the table when encryption is CUSTOMER_MANAGED. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the DynamoDB service the necessary permissions in its own key policy."
286
+ type = string
287
+ default = null
288
+ }
289
+
290
+ variable "create_kms_key" {
291
+ description = "Whether to create a KMS key for the table. Only applies when encryption is CUSTOMER_MANAGED. Set to false when supplying kms_key_arn."
292
+ type = bool
293
+ default = true
294
+ }
295
+
296
+ variable "enable_key_rotation" {
297
+ description = "Whether to enable automatic key rotation on the KMS key used to encrypt the table. Only applies when encryption is CUSTOMER_MANAGED and create_kms_key is true."
298
+ type = bool
299
+ default = true
300
+ }
301
+
302
+ variable "global_secondary_indexes" {
303
+ description = "Global secondary indexes to create on the table. All key attributes must be of type string."
304
+ type = list(object({
305
+ name = string
306
+ hash_key = string
307
+ range_key = string
308
+ projection_type = string
309
+ }))
310
+ default = []
311
+ }
312
+
313
+ locals {
314
+ gsi_attribute_names = distinct(flatten([
315
+ for gsi in var.global_secondary_indexes : [gsi.hash_key, gsi.range_key]
316
+ ]))
317
+
318
+ create_table_key = var.encryption == "CUSTOMER_MANAGED" && var.create_kms_key
319
+ table_kms_key_arn = (
320
+ var.encryption == "CUSTOMER_MANAGED" ? (local.create_table_key ? aws_kms_key.table[0].arn : var.kms_key_arn) :
321
+ var.encryption == "AWS_MANAGED" ? "arn:\${data.aws_partition.current.partition}:kms:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:alias/aws/dynamodb" :
322
+ null
323
+ )
324
+ }
325
+
326
+ data "aws_caller_identity" "current" {}
327
+ data "aws_partition" "current" {}
328
+ data "aws_region" "current" {}
329
+
330
+ resource "aws_kms_key" "table" {
331
+ count = local.create_table_key ? 1 : 0
332
+
333
+ description = "KMS key for DynamoDB table \${var.name}"
334
+ enable_key_rotation = var.enable_key_rotation
335
+
336
+ policy = jsonencode({
337
+ Version = "2012-10-17"
338
+ Statement = [
339
+ {
340
+ Sid = "EnableRootAccess"
341
+ Effect = "Allow"
342
+ Principal = {
343
+ AWS = "arn:\${data.aws_partition.current.partition}:iam::\${data.aws_caller_identity.current.account_id}:root"
344
+ }
345
+ Action = "kms:*"
346
+ Resource = "*"
347
+ },
348
+ {
349
+ Sid = "AllowDynamoDBService"
350
+ Effect = "Allow"
351
+ Principal = {
352
+ Service = "dynamodb.amazonaws.com"
353
+ }
354
+ Action = [
355
+ "kms:Encrypt",
356
+ "kms:Decrypt",
357
+ "kms:ReEncrypt*",
358
+ "kms:GenerateDataKey*",
359
+ "kms:DescribeKey",
360
+ "kms:CreateGrant"
361
+ ]
362
+ Resource = "*"
363
+ }
364
+ ]
365
+ })
366
+ }
367
+
368
+ resource "aws_dynamodb_table" "table" {
369
+ #checkov:skip=CKV_AWS_119:Encryption is configurable via var.encryption; checkov cannot resolve the conditional server_side_encryption.enabled expression
370
+ name = var.name
371
+ billing_mode = var.billing_mode
372
+ hash_key = "pk"
373
+ range_key = "sk"
374
+ deletion_protection_enabled = var.deletion_protection_enabled
375
+
376
+ attribute {
377
+ name = "pk"
378
+ type = "S"
379
+ }
380
+
381
+ attribute {
382
+ name = "sk"
383
+ type = "S"
384
+ }
385
+
386
+ dynamic "attribute" {
387
+ for_each = toset(local.gsi_attribute_names)
388
+ content {
389
+ name = attribute.value
390
+ type = "S"
391
+ }
392
+ }
393
+
394
+ dynamic "global_secondary_index" {
395
+ for_each = var.global_secondary_indexes
396
+ content {
397
+ name = global_secondary_index.value.name
398
+ hash_key = global_secondary_index.value.hash_key
399
+ range_key = global_secondary_index.value.range_key
400
+ projection_type = global_secondary_index.value.projection_type
401
+ }
402
+ }
403
+
404
+ point_in_time_recovery {
405
+ enabled = var.point_in_time_recovery_enabled
406
+ }
407
+
408
+ server_side_encryption {
409
+ enabled = var.encryption != "DEFAULT"
410
+ kms_key_arn = local.table_kms_key_arn
411
+ }
412
+ }
413
+
414
+ output "table_name" {
415
+ description = "Name of the DynamoDB table."
416
+ value = aws_dynamodb_table.table.name
417
+ }
418
+
419
+ output "table_arn" {
420
+ description = "ARN of the DynamoDB table."
421
+ value = aws_dynamodb_table.table.arn
422
+ }
423
+
424
+ output "kms_key_arn" {
425
+ description = "ARN of the KMS key used to encrypt the DynamoDB table, or null when using the AWS owned key (encryption = DEFAULT)."
426
+ value = local.table_kms_key_arn
427
+ }
428
+ "
429
+ `;
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Surface encryption and encryptionKey on the vended DynamoDBTable construct/module"
3
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type MigrationReturnObject, type Tree } from '@nx/devkit';
6
+ export default function migration(tree: Tree): Promise<MigrationReturnObject>;