@aws/nx-plugin 1.0.0-rc.83 → 1.0.0-rc.85

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 (104) hide show
  1. package/generators.json +7 -0
  2. package/migrations.json +31 -1
  3. package/package.json +1 -1
  4. package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +51 -2
  5. package/src/agentcore-harness/__snapshots__/generator.spec.ts.snap +4 -5
  6. package/src/agentcore-harness/files/project/README.md.template +4 -5
  7. package/src/internal/test-matrix/generator.js +16 -0
  8. package/src/internal/test-matrix/generator.js.map +1 -1
  9. package/src/migrations/latest/agentcore-harness-no-tools-by-default/__snapshots__/migration.spec.ts.snap +79 -0
  10. package/src/migrations/latest/agentcore-harness-no-tools-by-default/metadata.json +3 -0
  11. package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.d.ts +6 -0
  12. package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js +87 -0
  13. package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js.map +1 -0
  14. package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/__snapshots__/migration.spec.ts.snap +54 -0
  15. package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/metadata.json +3 -0
  16. package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.d.ts +6 -0
  17. package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js +86 -0
  18. package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js.map +1 -0
  19. package/src/migrations/latest/terraform-declare-used-providers/metadata.json +3 -0
  20. package/src/migrations/latest/terraform-declare-used-providers/migration.d.ts +6 -0
  21. package/src/migrations/latest/terraform-declare-used-providers/migration.js +147 -0
  22. package/src/migrations/latest/terraform-declare-used-providers/migration.js.map +1 -0
  23. package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/__snapshots__/migration.spec.ts.snap +521 -0
  24. package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/metadata.json +3 -0
  25. package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.d.ts +6 -0
  26. package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js +504 -0
  27. package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js.map +1 -0
  28. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/metadata.json +3 -0
  29. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.d.ts +6 -0
  30. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js +146 -0
  31. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +1 -0
  32. package/src/migrations/latest/waf-log-group-cmk-encryption/__snapshots__/migration.spec.ts.snap +286 -0
  33. package/src/migrations/latest/waf-log-group-cmk-encryption/metadata.json +3 -0
  34. package/src/migrations/latest/waf-log-group-cmk-encryption/migration.d.ts +6 -0
  35. package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js +238 -0
  36. package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js.map +1 -0
  37. package/src/open-api/json-metadata/generator.d.ts +24 -0
  38. package/src/open-api/json-metadata/generator.js +37 -0
  39. package/src/open-api/json-metadata/generator.js.map +1 -0
  40. package/src/open-api/json-metadata/schema.d.js +6 -0
  41. package/src/open-api/json-metadata/schema.d.js.map +1 -0
  42. package/src/open-api/json-metadata/schema.d.ts +9 -0
  43. package/src/open-api/json-metadata/schema.json +20 -0
  44. package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +12 -0
  45. package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +1875 -623
  46. package/src/py/fast-api/generator.js +2 -1
  47. package/src/py/fast-api/generator.js.map +1 -1
  48. package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +8 -0
  49. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +4 -0
  50. package/src/py/rdb/__snapshots__/generator.spec.ts.snap +31 -2
  51. package/src/sdk/open-api.d.ts +2 -0
  52. package/src/sdk/open-api.js +2 -1
  53. package/src/sdk/open-api.js.map +1 -1
  54. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1756 -457
  55. package/src/smithy/ts/api/generator.js +2 -1
  56. package/src/smithy/ts/api/generator.js.map +1 -1
  57. package/src/terraform/project/files/application/bootstrap/providers.tf.template +11 -0
  58. package/src/terraform/project/files/application/scripts/bootstrap-destroy.ts.template +111 -0
  59. package/src/terraform/project/files/application/src/providers.tf.template +6 -0
  60. package/src/terraform/project/files/checkov/checkov.yml.template +8 -0
  61. package/src/terraform/project/files/library/src/main.tf.template +9 -0
  62. package/src/terraform/project/generator.js +47 -4
  63. package/src/terraform/project/generator.js.map +1 -1
  64. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1848 -614
  65. package/src/trpc/backend/files-operations/scripts/generate-operations.ts.template +40 -0
  66. package/src/trpc/backend/generator.js +10 -0
  67. package/src/trpc/backend/generator.js.map +1 -1
  68. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +8 -0
  69. package/src/ts/dcr-proxy/__snapshots__/generator.spec.ts.snap +4 -0
  70. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +8 -0
  71. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +4 -0
  72. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +62 -4
  73. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +4 -0
  74. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +35 -8
  75. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +4 -0
  76. package/src/utils/agent-core-constructs/files/cdk/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.ts.template +39 -1
  77. package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +8 -1
  78. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +57 -2
  79. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.tf.template +258 -24
  80. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +4 -0
  81. package/src/utils/api-constructs/api-constructs.js +11 -0
  82. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  83. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +251 -3
  84. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +437 -2
  85. package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +60 -7
  86. package/src/utils/api-constructs/open-api-metadata.d.ts +20 -3
  87. package/src/utils/api-constructs/open-api-metadata.js +64 -8
  88. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  89. package/src/utils/api-constructs/trpc-operations-metadata.d.ts +21 -0
  90. package/src/utils/api-constructs/trpc-operations-metadata.js +55 -0
  91. package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -0
  92. package/src/utils/dcr-proxy-constructs/files/terraform/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.tf.template +4 -0
  93. package/src/utils/files/terraform/src/metrics/providers.tf.template +12 -0
  94. package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +4 -0
  95. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +33 -8
  96. package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +4 -0
  97. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +51 -2
  98. package/src/utils/rdb-constructs/files/cdk/core/rdb/aurora.ts.template +18 -0
  99. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +15 -4
  100. package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +2 -2
  101. package/src/utils/rdb-constructs/rdb-constructs.d.ts +12 -0
  102. package/src/utils/rdb-constructs/rdb-constructs.js +24 -3
  103. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  104. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +4 -0
@@ -9,9 +9,26 @@ export interface AddOpenApiMetadataGenerateTargetOptions {
9
9
  apiNameKebabCase: string;
10
10
  specPath: string;
11
11
  specBuildTargetName: string;
12
+ /**
13
+ * How integrations are mapped to operations. Terraform only needs the
14
+ * operations metadata for the `isolated` pattern, since the `shared` pattern
15
+ * routes every operation to one proxy Lambda.
16
+ */
17
+ integrationPattern?: 'isolated' | 'shared';
12
18
  }
13
19
  /**
14
- * Adds a generate:<api-name>-metadata target to shared constructs (CDK only)
15
- * This allows for API CDK constructs to have type-safety for integrations based on an OpenAPI Specification
20
+ * Adds a target which generates metadata about the API's operations from its
21
+ * OpenAPI specification, so that infrastructure can define an integration per
22
+ * operation.
23
+ *
24
+ * For CDK this generates TypeScript, giving the construct type-safe integrations.
25
+ * For Terraform this generates JSON, which the vended module reads to build one
26
+ * Lambda function and route per operation.
16
27
  */
17
- export declare const addSharedConstructsOpenApiMetadataGenerateTarget: (tree: Tree, { iac, apiNameKebabCase, specPath, specBuildTargetName, }: AddOpenApiMetadataGenerateTargetOptions) => void;
28
+ export declare const addSharedConstructsOpenApiMetadataGenerateTarget: (tree: Tree, options: AddOpenApiMetadataGenerateTargetOptions) => void;
29
+ /**
30
+ * Directory the operations metadata for an API is generated into, relative to
31
+ * the shared Terraform project's `src`. The vended module reads the file from
32
+ * here, so the module template and this target must agree on the location.
33
+ */
34
+ export declare const terraformOperationsMetadataDir: (apiNameKebabCase: string) => string;
@@ -4,16 +4,23 @@
4
4
  */ import { joinPathFragments, updateJson } from "@nx/devkit";
5
5
  import { updateGitIgnore } from "../git.js";
6
6
  import { addDependencyToTargetIfNotPresent } from "../nx.js";
7
- import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR } from "../shared-constructs-constants.js";
7
+ import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
8
8
  /**
9
- * Adds a generate:<api-name>-metadata target to shared constructs (CDK only)
10
- * This allows for API CDK constructs to have type-safety for integrations based on an OpenAPI Specification
11
- */ export const addSharedConstructsOpenApiMetadataGenerateTarget = (tree, { iac, apiNameKebabCase, specPath, specBuildTargetName })=>{
12
- if (iac !== 'cdk') {
13
- // For Terraform, we do not support type-safe integration builders, rather only the single lambda
14
- // router pattern, and therefore do not need to add depenencies on metadata generation.
15
- return;
9
+ * Adds a target which generates metadata about the API's operations from its
10
+ * OpenAPI specification, so that infrastructure can define an integration per
11
+ * operation.
12
+ *
13
+ * For CDK this generates TypeScript, giving the construct type-safe integrations.
14
+ * For Terraform this generates JSON, which the vended module reads to build one
15
+ * Lambda function and route per operation.
16
+ */ export const addSharedConstructsOpenApiMetadataGenerateTarget = (tree, options)=>{
17
+ if (options.iac === 'cdk') {
18
+ addCdkOpenApiMetadataGenerateTarget(tree, options);
19
+ } else if (options.iac === 'terraform') {
20
+ addTerraformOpenApiOperationsGenerateTarget(tree, options);
16
21
  }
22
+ };
23
+ const addCdkOpenApiMetadataGenerateTarget = (tree, { apiNameKebabCase, specPath, specBuildTargetName })=>{
17
24
  const generatedMetadataDir = joinPathFragments('generated', apiNameKebabCase);
18
25
  const generatedMetadataDirFromRoot = joinPathFragments(joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR), 'src', generatedMetadataDir);
19
26
  updateJson(tree, joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'project.json'), (config)=>{
@@ -58,5 +65,54 @@ import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR } from "../shared-constructs-consta
58
65
  joinPathFragments('src', generatedMetadataDir)
59
66
  ]);
60
67
  };
68
+ /**
69
+ * Directory the operations metadata for an API is generated into, relative to
70
+ * the shared Terraform project's `src`. The vended module reads the file from
71
+ * here, so the module template and this target must agree on the location.
72
+ */ export const terraformOperationsMetadataDir = (apiNameKebabCase)=>joinPathFragments('generated', apiNameKebabCase);
73
+ const addTerraformOpenApiOperationsGenerateTarget = (tree, { apiNameKebabCase, specPath, specBuildTargetName, integrationPattern })=>{
74
+ // The `shared` pattern routes every operation to a single proxy Lambda, so the
75
+ // module has no need for the operations metadata.
76
+ if (integrationPattern !== 'isolated') {
77
+ return;
78
+ }
79
+ const generatedDirFromRoot = joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', terraformOperationsMetadataDir(apiNameKebabCase));
80
+ updateJson(tree, joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'), (config)=>{
81
+ config.targets ??= {};
82
+ // Terraform reads the operations metadata with `file()`, so it must exist
83
+ // before plan; the target is wired into `build` below.
84
+ const operationsTargetName = `generate:${apiNameKebabCase}-operations`;
85
+ if (!config.targets[operationsTargetName]) {
86
+ config.targets[operationsTargetName] = {
87
+ cache: true,
88
+ executor: 'nx:run-commands',
89
+ inputs: [
90
+ {
91
+ dependentTasksOutputFiles: '**/*.json'
92
+ }
93
+ ],
94
+ outputs: [
95
+ joinPathFragments('{workspaceRoot}', generatedDirFromRoot)
96
+ ],
97
+ options: {
98
+ commands: [
99
+ `nx g @aws/nx-plugin:open-api#json-metadata --openApiSpecPath="${specPath}" --outputPath="${generatedDirFromRoot}" --no-interactive`
100
+ ]
101
+ },
102
+ dependsOn: [
103
+ specBuildTargetName
104
+ ]
105
+ };
106
+ }
107
+ addDependencyToTargetIfNotPresent(config, 'build', operationsTargetName);
108
+ return config;
109
+ });
110
+ // Ignore the generated metadata by default
111
+ // Users can safely remove the entry from the .gitignore if they prefer to check it in
112
+ updateGitIgnore(tree, joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR), (patterns)=>[
113
+ ...patterns,
114
+ joinPathFragments('src', terraformOperationsMetadataDir(apiNameKebabCase))
115
+ ]);
116
+ };
61
117
 
62
118
  //# sourceMappingURL=open-api-metadata.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/api-constructs/open-api-metadata.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { updateGitIgnore } from '../git.js';\nimport type { Iac } from '../iac.js';\nimport { addDependencyToTargetIfNotPresent } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n} from '../shared-constructs-constants.js';\n\nexport interface AddOpenApiMetadataGenerateTargetOptions {\n iac: Iac;\n apiNameKebabCase: string;\n specPath: string;\n specBuildTargetName: string;\n}\n\n/**\n * Adds a generate:<api-name>-metadata target to shared constructs (CDK only)\n * This allows for API CDK constructs to have type-safety for integrations based on an OpenAPI Specification\n */\nexport const addSharedConstructsOpenApiMetadataGenerateTarget = (\n tree: Tree,\n {\n iac,\n apiNameKebabCase,\n specPath,\n specBuildTargetName,\n }: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n if (iac !== 'cdk') {\n // For Terraform, we do not support type-safe integration builders, rather only the single lambda\n // router pattern, and therefore do not need to add depenencies on metadata generation.\n return;\n }\n\n const generatedMetadataDir = joinPathFragments('generated', apiNameKebabCase);\n const generatedMetadataDirFromRoot = joinPathFragments(\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n 'src',\n generatedMetadataDir,\n );\n\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n if (!config.targets) {\n config.targets = {};\n }\n if (!config.targets.build) {\n config.targets.build = {};\n }\n // If not already defined, add a target to generate metadata from the OpenAPI spec, used\n // for providing a type-safe CDK construct\n const metadataTargetName = `generate:${apiNameKebabCase}-metadata`;\n if (!config.targets[metadataTargetName]) {\n config.targets[metadataTargetName] = {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [\n joinPathFragments('{workspaceRoot}', generatedMetadataDirFromRoot),\n ],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#ts-metadata --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedMetadataDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n };\n }\n addDependencyToTargetIfNotPresent(config, 'compile', metadataTargetName);\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n (patterns) => [...patterns, joinPathFragments('src', generatedMetadataDir)],\n );\n};\n"],"names":["joinPathFragments","updateJson","updateGitIgnore","addDependencyToTargetIfNotPresent","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","addSharedConstructsOpenApiMetadataGenerateTarget","tree","iac","apiNameKebabCase","specPath","specBuildTargetName","generatedMetadataDir","generatedMetadataDirFromRoot","config","targets","build","metadataTargetName","cache","executor","inputs","dependentTasksOutputFiles","outputs","options","commands","dependsOn","patterns"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,eAAe,QAAQ,YAAY;AAE5C,SAASC,iCAAiC,QAAQ,WAAW;AAC7D,SACEC,YAAY,EACZC,qBAAqB,QAChB,oCAAoC;AAS3C;;;CAGC,GACD,OAAO,MAAMC,mDAAmD,CAC9DC,MACA,EACEC,GAAG,EACHC,gBAAgB,EAChBC,QAAQ,EACRC,mBAAmB,EACqB;IAE1C,IAAIH,QAAQ,OAAO;QACjB,iGAAiG;QACjG,uFAAuF;QACvF;IACF;IAEA,MAAMI,uBAAuBZ,kBAAkB,aAAaS;IAC5D,MAAMI,+BAA+Bb,kBACnCA,kBAAkBI,cAAcC,wBAChC,OACAO;IAGFX,WACEM,MACAP,kBAAkBI,cAAcC,uBAAuB,iBACvD,CAACS;QACC,IAAI,CAACA,OAAOC,OAAO,EAAE;YACnBD,OAAOC,OAAO,GAAG,CAAC;QACpB;QACA,IAAI,CAACD,OAAOC,OAAO,CAACC,KAAK,EAAE;YACzBF,OAAOC,OAAO,CAACC,KAAK,GAAG,CAAC;QAC1B;QACA,wFAAwF;QACxF,0CAA0C;QAC1C,MAAMC,qBAAqB,CAAC,SAAS,EAAER,iBAAiB,SAAS,CAAC;QAClE,IAAI,CAACK,OAAOC,OAAO,CAACE,mBAAmB,EAAE;YACvCH,OAAOC,OAAO,CAACE,mBAAmB,GAAG;gBACnCC,OAAO;gBACPC,UAAU;gBACVC,QAAQ;oBACN;wBACEC,2BAA2B;oBAC7B;iBACD;gBACDC,SAAS;oBACPtB,kBAAkB,mBAAmBa;iBACtC;gBACDU,SAAS;oBACPC,UAAU;wBACR,CAAC,4DAA4D,EAAEd,SAAS,gBAAgB,EAAEG,6BAA6B,kBAAkB,CAAC;qBAC3I;gBACH;gBACAY,WAAW;oBAACd;iBAAoB;YAClC;QACF;QACAR,kCAAkCW,QAAQ,WAAWG;QACrD,OAAOH;IACT;IAGF,2CAA2C;IAC3C,sFAAsF;IACtFZ,gBACEK,MACAP,kBAAkBI,cAAcC,wBAChC,CAACqB,WAAa;eAAIA;YAAU1B,kBAAkB,OAAOY;SAAsB;AAE/E,EAAE"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/api-constructs/open-api-metadata.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { updateGitIgnore } from '../git.js';\nimport type { Iac } from '../iac.js';\nimport { addDependencyToTargetIfNotPresent } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\n\nexport interface AddOpenApiMetadataGenerateTargetOptions {\n iac: Iac;\n apiNameKebabCase: string;\n specPath: string;\n specBuildTargetName: string;\n /**\n * How integrations are mapped to operations. Terraform only needs the\n * operations metadata for the `isolated` pattern, since the `shared` pattern\n * routes every operation to one proxy Lambda.\n */\n integrationPattern?: 'isolated' | 'shared';\n}\n\n/**\n * Adds a target which generates metadata about the API's operations from its\n * OpenAPI specification, so that infrastructure can define an integration per\n * operation.\n *\n * For CDK this generates TypeScript, giving the construct type-safe integrations.\n * For Terraform this generates JSON, which the vended module reads to build one\n * Lambda function and route per operation.\n */\nexport const addSharedConstructsOpenApiMetadataGenerateTarget = (\n tree: Tree,\n options: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n if (options.iac === 'cdk') {\n addCdkOpenApiMetadataGenerateTarget(tree, options);\n } else if (options.iac === 'terraform') {\n addTerraformOpenApiOperationsGenerateTarget(tree, options);\n }\n};\n\nconst addCdkOpenApiMetadataGenerateTarget = (\n tree: Tree,\n {\n apiNameKebabCase,\n specPath,\n specBuildTargetName,\n }: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n const generatedMetadataDir = joinPathFragments('generated', apiNameKebabCase);\n const generatedMetadataDirFromRoot = joinPathFragments(\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n 'src',\n generatedMetadataDir,\n );\n\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n if (!config.targets) {\n config.targets = {};\n }\n if (!config.targets.build) {\n config.targets.build = {};\n }\n // If not already defined, add a target to generate metadata from the OpenAPI spec, used\n // for providing a type-safe CDK construct\n const metadataTargetName = `generate:${apiNameKebabCase}-metadata`;\n if (!config.targets[metadataTargetName]) {\n config.targets[metadataTargetName] = {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [\n joinPathFragments('{workspaceRoot}', generatedMetadataDirFromRoot),\n ],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#ts-metadata --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedMetadataDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n };\n }\n addDependencyToTargetIfNotPresent(config, 'compile', metadataTargetName);\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n (patterns) => [...patterns, joinPathFragments('src', generatedMetadataDir)],\n );\n};\n\n/**\n * Directory the operations metadata for an API is generated into, relative to\n * the shared Terraform project's `src`. The vended module reads the file from\n * here, so the module template and this target must agree on the location.\n */\nexport const terraformOperationsMetadataDir = (apiNameKebabCase: string) =>\n joinPathFragments('generated', apiNameKebabCase);\n\nconst addTerraformOpenApiOperationsGenerateTarget = (\n tree: Tree,\n {\n apiNameKebabCase,\n specPath,\n specBuildTargetName,\n integrationPattern,\n }: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n // The `shared` pattern routes every operation to a single proxy Lambda, so the\n // module has no need for the operations metadata.\n if (integrationPattern !== 'isolated') {\n return;\n }\n\n const generatedDirFromRoot = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n );\n\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n config.targets ??= {};\n // Terraform reads the operations metadata with `file()`, so it must exist\n // before plan; the target is wired into `build` below.\n const operationsTargetName = `generate:${apiNameKebabCase}-operations`;\n if (!config.targets[operationsTargetName]) {\n config.targets[operationsTargetName] = {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [joinPathFragments('{workspaceRoot}', generatedDirFromRoot)],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#json-metadata --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n };\n }\n addDependencyToTargetIfNotPresent(config, 'build', operationsTargetName);\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR),\n (patterns) => [\n ...patterns,\n joinPathFragments(\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n ),\n ],\n );\n};\n"],"names":["joinPathFragments","updateJson","updateGitIgnore","addDependencyToTargetIfNotPresent","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","addSharedConstructsOpenApiMetadataGenerateTarget","tree","options","iac","addCdkOpenApiMetadataGenerateTarget","addTerraformOpenApiOperationsGenerateTarget","apiNameKebabCase","specPath","specBuildTargetName","generatedMetadataDir","generatedMetadataDirFromRoot","config","targets","build","metadataTargetName","cache","executor","inputs","dependentTasksOutputFiles","outputs","commands","dependsOn","patterns","terraformOperationsMetadataDir","integrationPattern","generatedDirFromRoot","operationsTargetName"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type ProjectConfiguration, type Tree } from '@nx/devkit';
6
+ export interface AddTrpcOperationsMetadataTargetOptions {
7
+ apiNameKebabCase: string;
8
+ /** The tRPC backend project the operations are derived from. */
9
+ project: ProjectConfiguration;
10
+ /** Template variables for the vended script (module format). */
11
+ templateOptions: Record<string, unknown>;
12
+ }
13
+ /**
14
+ * Adds operations metadata generation for a tRPC API deployed with Terraform.
15
+ *
16
+ * tRPC has no OpenAPI specification, so the operations are derived by walking
17
+ * the router: a vended script writes the same JSON shape the OpenAPI-based
18
+ * generator produces, which the Terraform module reads to define one Lambda
19
+ * function and route per procedure.
20
+ */
21
+ export declare const addTrpcOperationsMetadataTarget: (tree: Tree, { apiNameKebabCase, project, templateOptions, }: AddTrpcOperationsMetadataTargetOptions) => void;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { generateFiles, joinPathFragments, OverwriteStrategy, updateJson } from "@nx/devkit";
5
+ import { OPERATIONS_METADATA_FILE_NAME } from "../../open-api/json-metadata/generator.js";
6
+ import { updateGitIgnore } from "../git.js";
7
+ import { addDependencyToTargetIfNotPresent } from "../nx.js";
8
+ import { PACKAGES_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
9
+ import { terraformOperationsMetadataDir } from "./open-api-metadata.js";
10
+ /**
11
+ * Adds operations metadata generation for a tRPC API deployed with Terraform.
12
+ *
13
+ * tRPC has no OpenAPI specification, so the operations are derived by walking
14
+ * the router: a vended script writes the same JSON shape the OpenAPI-based
15
+ * generator produces, which the Terraform module reads to define one Lambda
16
+ * function and route per procedure.
17
+ */ export const addTrpcOperationsMetadataTarget = (tree, { apiNameKebabCase, project, templateOptions })=>{
18
+ generateFiles(tree, joinPathFragments(import.meta.dirname, '..', '..', 'trpc', 'backend', 'files-operations'), project.root, templateOptions, {
19
+ overwriteStrategy: OverwriteStrategy.Overwrite
20
+ });
21
+ const generatedDirFromRoot = joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', terraformOperationsMetadataDir(apiNameKebabCase));
22
+ const operationsFile = joinPathFragments(generatedDirFromRoot, OPERATIONS_METADATA_FILE_NAME);
23
+ project.targets ??= {};
24
+ project.targets.operations ??= {
25
+ cache: true,
26
+ executor: 'nx:run-commands',
27
+ outputs: [
28
+ joinPathFragments('{workspaceRoot}', generatedDirFromRoot)
29
+ ],
30
+ options: {
31
+ commands: [
32
+ `tsx {projectRoot}/scripts/generate-operations.ts ${operationsFile}`
33
+ ],
34
+ cwd: '{workspaceRoot}'
35
+ },
36
+ dependsOn: [
37
+ 'compile'
38
+ ]
39
+ };
40
+ addDependencyToTargetIfNotPresent(project, 'build', 'operations');
41
+ // The shared Terraform project reads the generated file with `file()`, so it
42
+ // must be written before that project is planned.
43
+ updateJson(tree, joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'), (config)=>{
44
+ addDependencyToTargetIfNotPresent(config, 'build', `${project.name}:operations`);
45
+ return config;
46
+ });
47
+ // Ignore the generated metadata by default
48
+ // Users can safely remove the entry from the .gitignore if they prefer to check it in
49
+ updateGitIgnore(tree, joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR), (patterns)=>[
50
+ ...patterns,
51
+ joinPathFragments('src', terraformOperationsMetadataDir(apiNameKebabCase))
52
+ ]);
53
+ };
54
+
55
+ //# sourceMappingURL=trpc-operations-metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/api-constructs/trpc-operations-metadata.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { OPERATIONS_METADATA_FILE_NAME } from '../../open-api/json-metadata/generator.js';\nimport { updateGitIgnore } from '../git.js';\nimport { addDependencyToTargetIfNotPresent } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\nimport { terraformOperationsMetadataDir } from './open-api-metadata.js';\n\nexport interface AddTrpcOperationsMetadataTargetOptions {\n apiNameKebabCase: string;\n /** The tRPC backend project the operations are derived from. */\n project: ProjectConfiguration;\n /** Template variables for the vended script (module format). */\n templateOptions: Record<string, unknown>;\n}\n\n/**\n * Adds operations metadata generation for a tRPC API deployed with Terraform.\n *\n * tRPC has no OpenAPI specification, so the operations are derived by walking\n * the router: a vended script writes the same JSON shape the OpenAPI-based\n * generator produces, which the Terraform module reads to define one Lambda\n * function and route per procedure.\n */\nexport const addTrpcOperationsMetadataTarget = (\n tree: Tree,\n {\n apiNameKebabCase,\n project,\n templateOptions,\n }: AddTrpcOperationsMetadataTargetOptions,\n) => {\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '..',\n '..',\n 'trpc',\n 'backend',\n 'files-operations',\n ),\n project.root,\n templateOptions,\n {\n overwriteStrategy: OverwriteStrategy.Overwrite,\n },\n );\n\n const generatedDirFromRoot = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n );\n const operationsFile = joinPathFragments(\n generatedDirFromRoot,\n OPERATIONS_METADATA_FILE_NAME,\n );\n\n project.targets ??= {};\n project.targets.operations ??= {\n cache: true,\n executor: 'nx:run-commands',\n outputs: [joinPathFragments('{workspaceRoot}', generatedDirFromRoot)],\n options: {\n commands: [\n `tsx {projectRoot}/scripts/generate-operations.ts ${operationsFile}`,\n ],\n cwd: '{workspaceRoot}',\n },\n dependsOn: ['compile'],\n };\n addDependencyToTargetIfNotPresent(project, 'build', 'operations');\n\n // The shared Terraform project reads the generated file with `file()`, so it\n // must be written before that project is planned.\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n addDependencyToTargetIfNotPresent(\n config,\n 'build',\n `${project.name}:operations`,\n );\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR),\n (patterns) => [\n ...patterns,\n joinPathFragments(\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n ),\n ],\n );\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateJson","OPERATIONS_METADATA_FILE_NAME","updateGitIgnore","addDependencyToTargetIfNotPresent","PACKAGES_DIR","SHARED_TERRAFORM_DIR","terraformOperationsMetadataDir","addTrpcOperationsMetadataTarget","tree","apiNameKebabCase","project","templateOptions","dirname","root","overwriteStrategy","Overwrite","generatedDirFromRoot","operationsFile","targets","operations","cache","executor","outputs","options","commands","cwd","dependsOn","config","name","patterns"],"mappings":"AAAA;;;CAGC,GACD,SACEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,6BAA6B,QAAQ,4CAA4C;AAC1F,SAASC,eAAe,QAAQ,YAAY;AAC5C,SAASC,iCAAiC,QAAQ,WAAW;AAC7D,SACEC,YAAY,EACZC,oBAAoB,QACf,oCAAoC;AAC3C,SAASC,8BAA8B,QAAQ,yBAAyB;AAUxE;;;;;;;CAOC,GACD,OAAO,MAAMC,kCAAkC,CAC7CC,MACA,EACEC,gBAAgB,EAChBC,OAAO,EACPC,eAAe,EACwB;IAEzCd,cACEW,MACAV,kBACE,YAAYc,OAAO,EACnB,MACA,MACA,QACA,WACA,qBAEFF,QAAQG,IAAI,EACZF,iBACA;QACEG,mBAAmBf,kBAAkBgB,SAAS;IAChD;IAGF,MAAMC,uBAAuBlB,kBAC3BM,cACAC,sBACA,OACAC,+BAA+BG;IAEjC,MAAMQ,iBAAiBnB,kBACrBkB,sBACAf;IAGFS,QAAQQ,OAAO,KAAK,CAAC;IACrBR,QAAQQ,OAAO,CAACC,UAAU,KAAK;QAC7BC,OAAO;QACPC,UAAU;QACVC,SAAS;YAACxB,kBAAkB,mBAAmBkB;SAAsB;QACrEO,SAAS;YACPC,UAAU;gBACR,CAAC,iDAAiD,EAAEP,gBAAgB;aACrE;YACDQ,KAAK;QACP;QACAC,WAAW;YAAC;SAAU;IACxB;IACAvB,kCAAkCO,SAAS,SAAS;IAEpD,6EAA6E;IAC7E,kDAAkD;IAClDV,WACEQ,MACAV,kBAAkBM,cAAcC,sBAAsB,iBACtD,CAACsB;QACCxB,kCACEwB,QACA,SACA,GAAGjB,QAAQkB,IAAI,CAAC,WAAW,CAAC;QAE9B,OAAOD;IACT;IAGF,2CAA2C;IAC3C,sFAAsF;IACtFzB,gBACEM,MACAV,kBAAkBM,cAAcC,uBAChC,CAACwB,WAAa;eACTA;YACH/B,kBACE,OACAQ,+BAA+BG;SAElC;AAEL,EAAE"}
@@ -15,6 +15,10 @@ terraform {
15
15
  source = "hashicorp/archive"
16
16
  version = "<%- archiveProviderVersion %>"
17
17
  }
18
+ random = {
19
+ source = "hashicorp/random"
20
+ version = "<%- randomProviderVersion %>"
21
+ }
18
22
  }
19
23
  }
20
24
 
@@ -0,0 +1,12 @@
1
+ terraform {
2
+ required_providers {
3
+ aws = {
4
+ source = "hashicorp/aws"
5
+ version = "<%- awsProviderVersion %>"
6
+ }
7
+ random = {
8
+ source = "hashicorp/random"
9
+ version = "<%- randomProviderVersion %>"
10
+ }
11
+ }
12
+ }
@@ -8,6 +8,10 @@ terraform {
8
8
  source = "hashicorp/archive"
9
9
  version = "<%- archiveProviderVersion %>"
10
10
  }
11
+ random = {
12
+ source = "hashicorp/random"
13
+ version = "<%- randomProviderVersion %>"
14
+ }
11
15
  }
12
16
  }
13
17
 
@@ -29,6 +29,8 @@ import {
29
29
  CfnWebACLAssociation,
30
30
  } from 'aws-cdk-lib/aws-wafv2';
31
31
  import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
32
+ import { Key } from 'aws-cdk-lib/aws-kms';
33
+ import { Effect, PolicyStatement, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
32
34
  import { Construct } from 'constructs';
33
35
  import { RuntimeConfig } from './runtime-config<% if (esm) { %>.js<% } %>';
34
36
  import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
@@ -265,18 +267,41 @@ export class UserIdentity extends Construct {
265
267
  webAclArn: webAcl.attrArn,
266
268
  });
267
269
 
268
- // Send WAF request logs to CloudWatch. The log group name must start with
269
- // `aws-waf-logs-` to satisfy the WAFv2 logging destination requirement.
270
+ // The log group name must start with `aws-waf-logs-` to satisfy the WAFv2
271
+ // logging destination requirement.
272
+ const stack = Stack.of(this);
273
+ const wafLogGroupName = `aws-waf-logs-${id}-${this.node.addr.slice(-8)}`;
274
+
275
+ // KMS key for encrypting WAF logs at rest, usable by CloudWatch Logs
276
+ const logsKey = new Key(this, 'WebAclLogsKey', {
277
+ enableKeyRotation: true,
278
+ });
279
+ logsKey.addToResourcePolicy(
280
+ new PolicyStatement({
281
+ effect: Effect.ALLOW,
282
+ principals: [new ServicePrincipal(`logs.${stack.region}.amazonaws.com`)],
283
+ actions: [
284
+ 'kms:Encrypt',
285
+ 'kms:Decrypt',
286
+ 'kms:ReEncrypt*',
287
+ 'kms:GenerateDataKey*',
288
+ 'kms:DescribeKey',
289
+ ],
290
+ resources: ['*'],
291
+ conditions: {
292
+ ArnEquals: {
293
+ 'kms:EncryptionContext:aws:logs:arn': `arn:aws:logs:${stack.region}:${stack.account}:log-group:${wafLogGroupName}`,
294
+ },
295
+ },
296
+ }),
297
+ );
298
+
270
299
  const wafLogGroup = new LogGroup(this, 'WebAclLogs', {
271
- logGroupName: `aws-waf-logs-${id}-${this.node.addr.slice(-8)}`,
300
+ logGroupName: wafLogGroupName,
272
301
  retention: RetentionDays.ONE_MONTH,
302
+ encryptionKey: logsKey,
273
303
  removalPolicy: RemovalPolicy.DESTROY,
274
304
  });
275
- suppressRules(
276
- wafLogGroup,
277
- ['CKV_AWS_158'],
278
- 'Using default CloudWatch log encryption for WAF logs',
279
- );
280
305
 
281
306
  new CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
282
307
  resourceArn: webAcl.attrArn,
@@ -4,6 +4,10 @@ terraform {
4
4
  source = "hashicorp/aws"
5
5
  version = "<%- awsProviderVersion %>"
6
6
  }
7
+ null = {
8
+ source = "hashicorp/null"
9
+ version = "<%- nullProviderVersion %>"
10
+ }
7
11
  }
8
12
  }
9
13
 
@@ -73,6 +73,8 @@ locals {
73
73
  "http://localhost:4200",
74
74
  "http://localhost:4300"
75
75
  ]
76
+
77
+ user_pool_waf_log_group_name = var.enable_waf ? "aws-waf-logs-UserPool-${random_id.unique_suffix.hex}" : null
76
78
  }
77
79
 
78
80
  # Random suffix for resource names
@@ -368,14 +370,61 @@ resource "aws_wafv2_web_acl_association" "user_pool_waf_association" {
368
370
  web_acl_arn = aws_wafv2_web_acl.user_pool_waf[0].arn
369
371
  }
370
372
 
373
+ # KMS key for encrypting WAF request logs at rest
374
+ resource "aws_kms_key" "user_pool_waf_logs" {
375
+ count = var.enable_waf ? 1 : 0
376
+
377
+ description = "User pool WAF log encryption"
378
+ deletion_window_in_days = 7
379
+ enable_key_rotation = true
380
+
381
+ policy = jsonencode({
382
+ Version = "2012-10-17"
383
+ Statement = [
384
+ {
385
+ Sid = "Enable IAM User Permissions"
386
+ Effect = "Allow"
387
+ Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }
388
+ Action = "kms:*"
389
+ Resource = "*"
390
+ },
391
+ {
392
+ Sid = "Allow CloudWatch Logs"
393
+ Effect = "Allow"
394
+ Principal = { Service = "logs.${data.aws_region.current.region}.amazonaws.com" }
395
+ Action = [
396
+ "kms:Encrypt",
397
+ "kms:Decrypt",
398
+ "kms:ReEncrypt*",
399
+ "kms:GenerateDataKey*",
400
+ "kms:DescribeKey"
401
+ ]
402
+ Resource = "*"
403
+ Condition = {
404
+ ArnEquals = {
405
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:log-group:${local.user_pool_waf_log_group_name}"
406
+ }
407
+ }
408
+ }
409
+ ]
410
+ })
411
+ }
412
+
413
+ resource "aws_kms_alias" "user_pool_waf_logs" {
414
+ count = var.enable_waf ? 1 : 0
415
+
416
+ name = "alias/user-pool-${random_id.unique_suffix.hex}-waf-logs"
417
+ target_key_id = aws_kms_key.user_pool_waf_logs[0].key_id
418
+ }
419
+
371
420
  # CloudWatch Log Group for WAF request logs. Name must start with `aws-waf-logs-`.
372
421
  resource "aws_cloudwatch_log_group" "user_pool_waf_logs" {
373
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
374
422
  #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
375
423
  count = var.enable_waf ? 1 : 0
376
424
 
377
- name = "aws-waf-logs-UserPool-${random_id.unique_suffix.hex}"
425
+ name = local.user_pool_waf_log_group_name
378
426
  retention_in_days = 30
427
+ kms_key_id = aws_kms_key.user_pool_waf_logs[0].arn
379
428
  }
380
429
 
381
430
  resource "aws_wafv2_web_acl_logging_configuration" "user_pool_waf_logging" {
@@ -180,6 +180,20 @@ export interface AuroraDatabaseProps extends _AuroraDatabaseProps {
180
180
  * @default true
181
181
  */
182
182
  readonly enablePerformanceInsights?: boolean;
183
+
184
+ /**
185
+ * Minimum Aurora Serverless v2 ACUs.
186
+ *
187
+ * @default <%= serverlessMinCapacity %>
188
+ */
189
+ readonly serverlessV2MinCapacity?: number;
190
+
191
+ /**
192
+ * Maximum Aurora Serverless v2 ACUs.
193
+ *
194
+ * @default <%= serverlessMaxCapacity %>
195
+ */
196
+ readonly serverlessV2MaxCapacity?: number;
183
197
  }
184
198
 
185
199
  /**
@@ -209,6 +223,8 @@ export abstract class AuroraDatabase extends Construct {
209
223
  enableKeyRotation = true,
210
224
  enableCloudwatchLogs = true,
211
225
  enablePerformanceInsights = true,
226
+ serverlessV2MinCapacity = <%= serverlessMinCapacity %>,
227
+ serverlessV2MaxCapacity = <%= serverlessMaxCapacity %>,
212
228
  engine,
213
229
  engineVersion,
214
230
  ...clusterProps
@@ -224,6 +240,8 @@ export abstract class AuroraDatabase extends Construct {
224
240
  vpc,
225
241
  vpcSubnets,
226
242
  engine: engine.clusterEngine(engineVersion ?? engine.defaultVersion),
243
+ serverlessV2MinCapacity,
244
+ serverlessV2MaxCapacity,
227
245
  writer:
228
246
  writer ??
229
247
  ClusterInstance.serverlessV2("writer", {
@@ -10,6 +10,10 @@ terraform {
10
10
  source = "hashicorp/aws"
11
11
  version = "<%- awsProviderVersion %>"
12
12
  }
13
+ external = {
14
+ source = "hashicorp/external"
15
+ version = "<%- externalProviderVersion %>"
16
+ }
13
17
  null = {
14
18
  source = "hashicorp/null"
15
19
  version = "<%- nullProviderVersion %>"
@@ -51,13 +55,13 @@ variable "port" {
51
55
  variable "serverless_min_capacity" {
52
56
  description = "Minimum Aurora Serverless v2 ACUs."
53
57
  type = number
54
- default = 0.5
58
+ default = <%= serverlessMinCapacity %>
55
59
  }
56
60
 
57
61
  variable "serverless_max_capacity" {
58
62
  description = "Maximum Aurora Serverless v2 ACUs."
59
63
  type = number
60
- default = 4
64
+ default = <%= serverlessMaxCapacity %>
61
65
  }
62
66
 
63
67
  variable "instance_count" {
@@ -91,9 +95,9 @@ variable "enable_credential_rotation" {
91
95
  }
92
96
 
93
97
  variable "enable_cloudwatch_logs" {
94
- description = "Whether to export Aurora engine logs to CloudWatch. PostgreSQL also enables verbose statement logging."
98
+ description = "Whether to export Aurora engine logs to CloudWatch. PostgreSQL logs DDL statements only (log_statement=ddl); MySQL enables Advanced Auditing scoped to connections and DDL (server_audit_events=CONNECT,QUERY_DDL). Neither logs statement parameter values, to avoid leaking PII into log data."
95
99
  type = bool
96
- default = false
100
+ default = true
97
101
  }
98
102
 
99
103
  variable "enable_performance_insights" {
@@ -114,6 +118,12 @@ variable "enable_backup" {
114
118
  default = false
115
119
  }
116
120
 
121
+ variable "enable_key_rotation" {
122
+ description = "Whether to enable automatic key rotation on the KMS key used to encrypt the Aurora cluster and its credentials secret."
123
+ type = bool
124
+ default = true
125
+ }
126
+
117
127
  variable "tags" {
118
128
  description = "Tags to apply to all resources."
119
129
  type = map(string)
@@ -184,6 +194,7 @@ module "aurora" {
184
194
  enable_performance_insights = var.enable_performance_insights
185
195
  performance_insights_retention_period = var.performance_insights_retention_period
186
196
  enable_backup = var.enable_backup
197
+ enable_key_rotation = var.enable_key_rotation
187
198
  tags = var.tags
188
199
  }
189
200
 
@@ -84,13 +84,13 @@ variable "port" {
84
84
  variable "serverless_min_capacity" {
85
85
  description = "Minimum Aurora Serverless v2 ACUs."
86
86
  type = number
87
- default = 0.5
87
+ default = <%= serverlessMinCapacity %>
88
88
  }
89
89
 
90
90
  variable "serverless_max_capacity" {
91
91
  description = "Maximum Aurora Serverless v2 ACUs."
92
92
  type = number
93
- default = 4
93
+ default = <%= serverlessMaxCapacity %>
94
94
  }
95
95
 
96
96
  variable "instance_count" {
@@ -5,6 +5,18 @@
5
5
  import { type Tree } from '@nx/devkit';
6
6
  import type { Containers } from '../containers.js';
7
7
  import type { Iac } from '../iac.js';
8
+ /**
9
+ * Aurora Serverless v2 scaling bounds, in ACUs, that both IaC providers derive
10
+ * from so the same generator options vend the same ceiling regardless of `--iac`.
11
+ *
12
+ * Set explicitly on CDK rather than inheriting `DatabaseClusterProps`, whose
13
+ * default moves with `aws-cdk-lib`. Serverless v2 bills on ACUs consumed, not on
14
+ * the ceiling, so the maximum is headroom rather than a cost commitment.
15
+ */
16
+ export declare const AURORA_SERVERLESS_V2_CAPACITY: {
17
+ readonly min: 0.5;
18
+ readonly max: 4;
19
+ };
8
20
  export interface AddRdbConstructOptions {
9
21
  projectName: string;
10
22
  projectRoot: string;
@@ -7,6 +7,24 @@ import { esmVars } from "../module-format.js";
7
7
  import { addDependencyToTargetIfNotPresent } from "../nx.js";
8
8
  import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
9
9
  import { terraformProviderVersions } from "../versions.js";
10
+ /**
11
+ * Aurora Serverless v2 scaling bounds, in ACUs, that both IaC providers derive
12
+ * from so the same generator options vend the same ceiling regardless of `--iac`.
13
+ *
14
+ * Set explicitly on CDK rather than inheriting `DatabaseClusterProps`, whose
15
+ * default moves with `aws-cdk-lib`. Serverless v2 bills on ACUs consumed, not on
16
+ * the ceiling, so the maximum is headroom rather than a cost commitment.
17
+ */ export const AURORA_SERVERLESS_V2_CAPACITY = {
18
+ min: 0.5,
19
+ max: 4
20
+ };
21
+ /**
22
+ * Substitution variables exposing the Aurora Serverless v2 scaling bounds to
23
+ * generated CDK and Terraform templates.
24
+ */ const auroraServerlessV2Capacity = ()=>({
25
+ serverlessMinCapacity: AURORA_SERVERLESS_V2_CAPACITY.min,
26
+ serverlessMaxCapacity: AURORA_SERVERLESS_V2_CAPACITY.max
27
+ });
10
28
  export const addRdbInfra = async (tree, options)=>{
11
29
  if (options.iac === 'cdk') {
12
30
  await addRdbCdkConstructs(tree, options);
@@ -23,7 +41,8 @@ export const addRdbInfra = async (tree, options)=>{
23
41
  export const addRdbCdkConstructs = async (tree, options)=>{
24
42
  generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', 'cdk', 'core', 'rdb'), joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'src', 'core', 'rdb'), {
25
43
  ...options,
26
- ...esmVars(tree)
44
+ ...esmVars(tree),
45
+ ...auroraServerlessV2Capacity()
27
46
  }, {
28
47
  overwriteStrategy: OverwriteStrategy.KeepExisting
29
48
  });
@@ -39,13 +58,15 @@ export const addRdbCdkConstructs = async (tree, options)=>{
39
58
  };
40
59
  export const addRdbTerraformModules = (tree, options)=>{
41
60
  generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', 'terraform', 'core', 'rdb'), joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', 'core', 'rdb'), {
42
- ...terraformProviderVersions()
61
+ ...terraformProviderVersions(),
62
+ ...auroraServerlessV2Capacity()
43
63
  }, {
44
64
  overwriteStrategy: OverwriteStrategy.KeepExisting
45
65
  });
46
66
  generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', 'terraform', 'app', 'dbs'), joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', 'app', 'dbs'), {
47
67
  ...options,
48
- ...terraformProviderVersions()
68
+ ...terraformProviderVersions(),
69
+ ...auroraServerlessV2Capacity()
49
70
  }, {
50
71
  overwriteStrategy: OverwriteStrategy.KeepExisting
51
72
  });