@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.
- package/generators.json +7 -0
- package/migrations.json +31 -1
- package/package.json +1 -1
- package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +51 -2
- package/src/agentcore-harness/__snapshots__/generator.spec.ts.snap +4 -5
- package/src/agentcore-harness/files/project/README.md.template +4 -5
- package/src/internal/test-matrix/generator.js +16 -0
- package/src/internal/test-matrix/generator.js.map +1 -1
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/__snapshots__/migration.spec.ts.snap +79 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/metadata.json +3 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.d.ts +6 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js +87 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js.map +1 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/__snapshots__/migration.spec.ts.snap +54 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/metadata.json +3 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.d.ts +6 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js +86 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js.map +1 -0
- package/src/migrations/latest/terraform-declare-used-providers/metadata.json +3 -0
- package/src/migrations/latest/terraform-declare-used-providers/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-declare-used-providers/migration.js +147 -0
- package/src/migrations/latest/terraform-declare-used-providers/migration.js.map +1 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/__snapshots__/migration.spec.ts.snap +521 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/metadata.json +3 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js +504 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js.map +1 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/metadata.json +3 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js +146 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +1 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/__snapshots__/migration.spec.ts.snap +286 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/metadata.json +3 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.d.ts +6 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js +238 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js.map +1 -0
- package/src/open-api/json-metadata/generator.d.ts +24 -0
- package/src/open-api/json-metadata/generator.js +37 -0
- package/src/open-api/json-metadata/generator.js.map +1 -0
- package/src/open-api/json-metadata/schema.d.js +6 -0
- package/src/open-api/json-metadata/schema.d.js.map +1 -0
- package/src/open-api/json-metadata/schema.d.ts +9 -0
- package/src/open-api/json-metadata/schema.json +20 -0
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +12 -0
- package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +1875 -623
- package/src/py/fast-api/generator.js +2 -1
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +8 -0
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/py/rdb/__snapshots__/generator.spec.ts.snap +31 -2
- package/src/sdk/open-api.d.ts +2 -0
- package/src/sdk/open-api.js +2 -1
- package/src/sdk/open-api.js.map +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1756 -457
- package/src/smithy/ts/api/generator.js +2 -1
- package/src/smithy/ts/api/generator.js.map +1 -1
- package/src/terraform/project/files/application/bootstrap/providers.tf.template +11 -0
- package/src/terraform/project/files/application/scripts/bootstrap-destroy.ts.template +111 -0
- package/src/terraform/project/files/application/src/providers.tf.template +6 -0
- package/src/terraform/project/files/checkov/checkov.yml.template +8 -0
- package/src/terraform/project/files/library/src/main.tf.template +9 -0
- package/src/terraform/project/generator.js +47 -4
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1848 -614
- package/src/trpc/backend/files-operations/scripts/generate-operations.ts.template +40 -0
- package/src/trpc/backend/generator.js +10 -0
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +8 -0
- package/src/ts/dcr-proxy/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +8 -0
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +62 -4
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +35 -8
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +4 -0
- package/src/utils/agent-core-constructs/files/cdk/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.ts.template +39 -1
- package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +8 -1
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +57 -2
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.tf.template +258 -24
- package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +4 -0
- package/src/utils/api-constructs/api-constructs.js +11 -0
- package/src/utils/api-constructs/api-constructs.js.map +1 -1
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +251 -3
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +437 -2
- package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +60 -7
- package/src/utils/api-constructs/open-api-metadata.d.ts +20 -3
- package/src/utils/api-constructs/open-api-metadata.js +64 -8
- package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
- package/src/utils/api-constructs/trpc-operations-metadata.d.ts +21 -0
- package/src/utils/api-constructs/trpc-operations-metadata.js +55 -0
- package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -0
- package/src/utils/dcr-proxy-constructs/files/terraform/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.tf.template +4 -0
- package/src/utils/files/terraform/src/metrics/providers.tf.template +12 -0
- package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +4 -0
- package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +33 -8
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +4 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +51 -2
- package/src/utils/rdb-constructs/files/cdk/core/rdb/aurora.ts.template +18 -0
- package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +15 -4
- package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +2 -2
- package/src/utils/rdb-constructs/rdb-constructs.d.ts +12 -0
- package/src/utils/rdb-constructs/rdb-constructs.js +24 -3
- package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +4 -0
package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { generateFiles, getProjects, joinPathFragments, OverwriteStrategy, updateProjectConfiguration } from "@nx/devkit";
|
|
5
|
+
import { TERRAFORM_PROJECT_GENERATOR_INFO } from "../../../terraform/project/generator.js";
|
|
6
|
+
import { applyGritQL, matchGritQL } from "../../../utils/ast.js";
|
|
7
|
+
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
8
|
+
import { kebabCase } from "../../../utils/names.js";
|
|
9
|
+
import { normalizeTargetKeyOrder } from "../../../utils/nx.js";
|
|
10
|
+
import { sortObjectKeys } from "../../../utils/object.js";
|
|
11
|
+
/**
|
|
12
|
+
* Terraform projects gained three fixes:
|
|
13
|
+
*
|
|
14
|
+
* - A `checkov` target carries the security scan, matching the name CDK
|
|
15
|
+
* infrastructure projects already use, so `nx run-many --target checkov` no
|
|
16
|
+
* longer skips Terraform projects. An existing `test` target is the user's,
|
|
17
|
+
* so it is left exactly as it is.
|
|
18
|
+
* - A `checkov.yml` gives Terraform users the same central place to configure
|
|
19
|
+
* skips that the CDK app has, wired in via `--config-file`.
|
|
20
|
+
* - `bootstrap-destroy` runs a script that resolves the region from the AWS SDK
|
|
21
|
+
* credential chain and passes it as `-var=aws_region`. The vended
|
|
22
|
+
* `aws_region` variable has no default, so the previous bare
|
|
23
|
+
* `terraform destroy` blocked forever on an input prompt in any non-TTY
|
|
24
|
+
* context.
|
|
25
|
+
* - Both vended `providers.tf` declare `required_version = ">= 1.0"`, matching
|
|
26
|
+
* every other `.tf` file the plugin vends, and the bootstrap one pins the AWS
|
|
27
|
+
* provider.
|
|
28
|
+
*
|
|
29
|
+
* How to write a migration:
|
|
30
|
+
* - https://nx.dev/docs/kb/migration-generators
|
|
31
|
+
* - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject
|
|
32
|
+
*/ const hcl = (pattern)=>`language hcl\n${pattern}`;
|
|
33
|
+
/** The scan command, as the pre-fix generator vended it on `test`. */ const CHECKOV_COMMAND_PATTERN = /uvx --from checkov==/;
|
|
34
|
+
const divergedTargetsStep = (projectName)=>`${projectName}: no 'test' target carrying the checkov scan was found, so no 'checkov' target was added - add one running checkov over the project so 'nx run-many --target checkov' includes this project.`;
|
|
35
|
+
const divergedProvidersStep = (filePath)=>`${filePath}: has diverged from the generated shape - left untouched. Add \`required_version = ">= 1.0"\` to its \`terraform\` block to pin the minimum Terraform version.`;
|
|
36
|
+
const divergedBootstrapDestroyStep = (projectName)=>`${projectName}: its 'bootstrap-destroy' target no longer matches the shape the generator produced - left untouched. Pass \`-var=aws_region=<region>\` to \`terraform destroy\`, otherwise it blocks on an input prompt when run without a TTY.`;
|
|
37
|
+
/**
|
|
38
|
+
* Add `required_version` to a `providers.tf` that omits it.
|
|
39
|
+
*
|
|
40
|
+
* The bootstrap copy has no `terraform` block at all, so it is created;
|
|
41
|
+
* the src copy has one holding `required_providers`, so it is added to.
|
|
42
|
+
*/ const migrateRequiredVersion = async (tree, filePath, nextSteps)=>{
|
|
43
|
+
if (!tree.exists(filePath)) return;
|
|
44
|
+
// Already declared - nothing to do, whatever constraint the user chose.
|
|
45
|
+
if (await matchGritQL(tree, filePath, hcl('`required_version = $_`'))) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const hasRequiredProviders = await matchGritQL(tree, filePath, hcl('`terraform { $body }` where { $body <: contains `required_providers` }'));
|
|
49
|
+
if (hasRequiredProviders) {
|
|
50
|
+
await applyGritQL(tree, filePath, hcl('`terraform { $body }` => `terraform {\n required_version = ">= 1.0"\n\n $body\n}`'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// No `terraform` block: prepend one ahead of the aws provider block.
|
|
54
|
+
const providerBlock = hcl('`provider "aws" { $body }`');
|
|
55
|
+
if (!await matchGritQL(tree, filePath, providerBlock)) {
|
|
56
|
+
nextSteps.push(divergedProvidersStep(filePath));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await applyGritQL(tree, filePath, hcl('`provider "aws" { $body }` => `terraform {\n required_version = ">= 1.0"\n}\n\nprovider "aws" {\n $body\n}`'));
|
|
60
|
+
};
|
|
61
|
+
export default async function migration(tree) {
|
|
62
|
+
const nextSteps = [];
|
|
63
|
+
for (const [projectName, project] of getProjects(tree)){
|
|
64
|
+
const generator = project.metadata?.generator;
|
|
65
|
+
if (generator !== TERRAFORM_PROJECT_GENERATOR_INFO.id) continue;
|
|
66
|
+
const targets = project.targets ?? {};
|
|
67
|
+
let changed = false;
|
|
68
|
+
// Add `checkov`, copying the scan from the `test` target the pre-fix
|
|
69
|
+
// generator put it on. `test` itself belongs to the user and is left as it
|
|
70
|
+
// is. Guarded on `checkov` being absent so a re-run is a no-op.
|
|
71
|
+
if (!targets.checkov) {
|
|
72
|
+
const command = targets.test?.options?.command;
|
|
73
|
+
if (typeof command !== 'string' || !CHECKOV_COMMAND_PATTERN.test(command)) {
|
|
74
|
+
nextSteps.push(divergedTargetsStep(projectName));
|
|
75
|
+
} else {
|
|
76
|
+
// The config file is vended below, so wire it in at the same time.
|
|
77
|
+
const withConfigFile = command.includes('--config-file') ? command : command.replace('checkov --directory', 'checkov --config-file ../checkov.yml --directory');
|
|
78
|
+
targets.checkov = normalizeTargetKeyOrder({
|
|
79
|
+
...targets.test,
|
|
80
|
+
options: {
|
|
81
|
+
...targets.test.options,
|
|
82
|
+
command: withConfigFile
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
changed = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Point `bootstrap-destroy` at the vended script, which resolves the
|
|
89
|
+
// region rather than leaving terraform to prompt for it.
|
|
90
|
+
const bootstrapDestroy = targets['bootstrap-destroy'];
|
|
91
|
+
if (bootstrapDestroy) {
|
|
92
|
+
const scriptCommand = `tsx {projectRoot}/scripts/bootstrap-destroy.ts {projectRoot}`;
|
|
93
|
+
const alreadyMigrated = bootstrapDestroy.options?.commands?.[0] === scriptCommand;
|
|
94
|
+
if (!alreadyMigrated) {
|
|
95
|
+
const command = bootstrapDestroy.options?.command;
|
|
96
|
+
// Only rewrite the exact shape the generator produced.
|
|
97
|
+
const isVendedShape = typeof command === 'string' && /^terraform destroy -state=\S+bootstrap\.tfstate$/.test(command.trim());
|
|
98
|
+
if (!isVendedShape) {
|
|
99
|
+
nextSteps.push(divergedBootstrapDestroyStep(projectName));
|
|
100
|
+
} else {
|
|
101
|
+
targets['bootstrap-destroy'] = normalizeTargetKeyOrder({
|
|
102
|
+
executor: bootstrapDestroy.executor,
|
|
103
|
+
options: {
|
|
104
|
+
forwardAllArgs: true,
|
|
105
|
+
commands: [
|
|
106
|
+
scriptCommand
|
|
107
|
+
],
|
|
108
|
+
cwd: '{workspaceRoot}'
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
changed = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (changed) {
|
|
116
|
+
updateProjectConfiguration(tree, projectName, {
|
|
117
|
+
...project,
|
|
118
|
+
targets: sortObjectKeys(targets)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Vend the checkov config and the bootstrap-destroy script. KeepExisting
|
|
122
|
+
// leaves a user's own copy alone and makes a re-run a no-op.
|
|
123
|
+
generateFiles(tree, joinPathFragments(import.meta.dirname, '../../../terraform/project/files/checkov'), project.root, {}, {
|
|
124
|
+
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
125
|
+
});
|
|
126
|
+
// Only application projects have a bootstrap dir to destroy. The scripts
|
|
127
|
+
// dir is shared with the generator's templates, so `stateKeyPrefix` is
|
|
128
|
+
// resolved the same way it computes it; KeepExisting leaves the scripts
|
|
129
|
+
// that already exist untouched regardless.
|
|
130
|
+
if (tree.exists(joinPathFragments(project.root, 'bootstrap'))) {
|
|
131
|
+
generateFiles(tree, joinPathFragments(import.meta.dirname, '../../../terraform/project/files/application/scripts'), joinPathFragments(project.root, 'scripts'), {
|
|
132
|
+
stateKeyPrefix: kebabCase(projectName)
|
|
133
|
+
}, {
|
|
134
|
+
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
await migrateRequiredVersion(tree, joinPathFragments(project.root, 'src/providers.tf'), nextSteps);
|
|
138
|
+
await migrateRequiredVersion(tree, joinPathFragments(project.root, 'bootstrap/providers.tf'), nextSteps);
|
|
139
|
+
}
|
|
140
|
+
await formatFilesInSubtree(tree);
|
|
141
|
+
return {
|
|
142
|
+
nextSteps
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.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 getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n OverwriteStrategy,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { TERRAFORM_PROJECT_GENERATOR_INFO } from '../../../terraform/project/generator.js';\nimport { applyGritQL, matchGritQL } from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport { kebabCase } from '../../../utils/names.js';\nimport { normalizeTargetKeyOrder } from '../../../utils/nx.js';\nimport { sortObjectKeys } from '../../../utils/object.js';\n\n/**\n * Terraform projects gained three fixes:\n *\n * - A `checkov` target carries the security scan, matching the name CDK\n * infrastructure projects already use, so `nx run-many --target checkov` no\n * longer skips Terraform projects. An existing `test` target is the user's,\n * so it is left exactly as it is.\n * - A `checkov.yml` gives Terraform users the same central place to configure\n * skips that the CDK app has, wired in via `--config-file`.\n * - `bootstrap-destroy` runs a script that resolves the region from the AWS SDK\n * credential chain and passes it as `-var=aws_region`. The vended\n * `aws_region` variable has no default, so the previous bare\n * `terraform destroy` blocked forever on an input prompt in any non-TTY\n * context.\n * - Both vended `providers.tf` declare `required_version = \">= 1.0\"`, matching\n * every other `.tf` file the plugin vends, and the bootstrap one pins the AWS\n * provider.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n */\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\n/** The scan command, as the pre-fix generator vended it on `test`. */\nconst CHECKOV_COMMAND_PATTERN = /uvx --from checkov==/;\n\nconst divergedTargetsStep = (projectName: string) =>\n `${projectName}: no 'test' target carrying the checkov scan was found, so no 'checkov' target was added - add one running checkov over the project so 'nx run-many --target checkov' includes this project.`;\n\nconst divergedProvidersStep = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. Add \\`required_version = \">= 1.0\"\\` to its \\`terraform\\` block to pin the minimum Terraform version.`;\n\nconst divergedBootstrapDestroyStep = (projectName: string) =>\n `${projectName}: its 'bootstrap-destroy' target no longer matches the shape the generator produced - left untouched. Pass \\`-var=aws_region=<region>\\` to \\`terraform destroy\\`, otherwise it blocks on an input prompt when run without a TTY.`;\n\n/**\n * Add `required_version` to a `providers.tf` that omits it.\n *\n * The bootstrap copy has no `terraform` block at all, so it is created;\n * the src copy has one holding `required_providers`, so it is added to.\n */\nconst migrateRequiredVersion = async (\n tree: Tree,\n filePath: string,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(filePath)) return;\n\n // Already declared - nothing to do, whatever constraint the user chose.\n if (await matchGritQL(tree, filePath, hcl('`required_version = $_`'))) {\n return;\n }\n\n const hasRequiredProviders = await matchGritQL(\n tree,\n filePath,\n hcl(\n '`terraform { $body }` where { $body <: contains `required_providers` }',\n ),\n );\n\n if (hasRequiredProviders) {\n await applyGritQL(\n tree,\n filePath,\n hcl(\n '`terraform { $body }` => `terraform {\\n required_version = \">= 1.0\"\\n\\n $body\\n}`',\n ),\n );\n return;\n }\n\n // No `terraform` block: prepend one ahead of the aws provider block.\n const providerBlock = hcl('`provider \"aws\" { $body }`');\n if (!(await matchGritQL(tree, filePath, providerBlock))) {\n nextSteps.push(divergedProvidersStep(filePath));\n return;\n }\n\n await applyGritQL(\n tree,\n filePath,\n hcl(\n '`provider \"aws\" { $body }` => `terraform {\\n required_version = \">= 1.0\"\\n}\\n\\nprovider \"aws\" {\\n $body\\n}`',\n ),\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const [projectName, project] of getProjects(tree)) {\n const generator = (project.metadata as { generator?: string } | undefined)\n ?.generator;\n if (generator !== TERRAFORM_PROJECT_GENERATOR_INFO.id) continue;\n\n const targets = project.targets ?? {};\n let changed = false;\n\n // Add `checkov`, copying the scan from the `test` target the pre-fix\n // generator put it on. `test` itself belongs to the user and is left as it\n // is. Guarded on `checkov` being absent so a re-run is a no-op.\n if (!targets.checkov) {\n const command = targets.test?.options?.command;\n\n if (\n typeof command !== 'string' ||\n !CHECKOV_COMMAND_PATTERN.test(command)\n ) {\n nextSteps.push(divergedTargetsStep(projectName));\n } else {\n // The config file is vended below, so wire it in at the same time.\n const withConfigFile = command.includes('--config-file')\n ? command\n : command.replace(\n 'checkov --directory',\n 'checkov --config-file ../checkov.yml --directory',\n );\n\n targets.checkov = normalizeTargetKeyOrder({\n ...targets.test,\n options: { ...targets.test.options, command: withConfigFile },\n });\n changed = true;\n }\n }\n\n // Point `bootstrap-destroy` at the vended script, which resolves the\n // region rather than leaving terraform to prompt for it.\n const bootstrapDestroy = targets['bootstrap-destroy'];\n if (bootstrapDestroy) {\n const scriptCommand = `tsx {projectRoot}/scripts/bootstrap-destroy.ts {projectRoot}`;\n const alreadyMigrated =\n bootstrapDestroy.options?.commands?.[0] === scriptCommand;\n\n if (!alreadyMigrated) {\n const command = bootstrapDestroy.options?.command;\n // Only rewrite the exact shape the generator produced.\n const isVendedShape =\n typeof command === 'string' &&\n /^terraform destroy -state=\\S+bootstrap\\.tfstate$/.test(\n command.trim(),\n );\n\n if (!isVendedShape) {\n nextSteps.push(divergedBootstrapDestroyStep(projectName));\n } else {\n targets['bootstrap-destroy'] = normalizeTargetKeyOrder({\n executor: bootstrapDestroy.executor,\n options: {\n forwardAllArgs: true,\n commands: [scriptCommand],\n cwd: '{workspaceRoot}',\n },\n });\n changed = true;\n }\n }\n }\n\n if (changed) {\n updateProjectConfiguration(tree, projectName, {\n ...project,\n targets: sortObjectKeys(targets),\n });\n }\n\n // Vend the checkov config and the bootstrap-destroy script. KeepExisting\n // leaves a user's own copy alone and makes a re-run a no-op.\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '../../../terraform/project/files/checkov',\n ),\n project.root,\n {},\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Only application projects have a bootstrap dir to destroy. The scripts\n // dir is shared with the generator's templates, so `stateKeyPrefix` is\n // resolved the same way it computes it; KeepExisting leaves the scripts\n // that already exist untouched regardless.\n if (tree.exists(joinPathFragments(project.root, 'bootstrap'))) {\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '../../../terraform/project/files/application/scripts',\n ),\n joinPathFragments(project.root, 'scripts'),\n { stateKeyPrefix: kebabCase(projectName) },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n }\n\n await migrateRequiredVersion(\n tree,\n joinPathFragments(project.root, 'src/providers.tf'),\n nextSteps,\n );\n await migrateRequiredVersion(\n tree,\n joinPathFragments(project.root, 'bootstrap/providers.tf'),\n nextSteps,\n );\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["generateFiles","getProjects","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","TERRAFORM_PROJECT_GENERATOR_INFO","applyGritQL","matchGritQL","formatFilesInSubtree","kebabCase","normalizeTargetKeyOrder","sortObjectKeys","hcl","pattern","CHECKOV_COMMAND_PATTERN","divergedTargetsStep","projectName","divergedProvidersStep","filePath","divergedBootstrapDestroyStep","migrateRequiredVersion","tree","nextSteps","exists","hasRequiredProviders","providerBlock","push","migration","project","generator","metadata","id","targets","changed","checkov","command","test","options","withConfigFile","includes","replace","bootstrapDestroy","scriptCommand","alreadyMigrated","commands","isVendedShape","trim","executor","forwardAllArgs","cwd","dirname","root","overwriteStrategy","KeepExisting","stateKeyPrefix"],"mappings":"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"}
|
package/src/migrations/latest/waf-log-group-cmk-encryption/__snapshots__/migration.spec.ts.snap
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`waf-log-group-cmk-encryption migration > should encrypt the cdk user identity WAF log group 1`] = `
|
|
4
|
+
"import { Effect, PolicyStatement, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
5
|
+
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
6
|
+
import { RemovalPolicy, Stack } from 'aws-cdk-lib';
|
|
7
|
+
import { CfnLoggingConfiguration, CfnWebACL } from 'aws-cdk-lib/aws-wafv2';
|
|
8
|
+
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
9
|
+
import { Construct } from 'constructs';
|
|
10
|
+
import { suppressRules } from './checkov.js';
|
|
11
|
+
|
|
12
|
+
export class UserIdentity extends Construct {
|
|
13
|
+
private createWebAcl = (id: string) => {
|
|
14
|
+
const webAcl = new CfnWebACL(this, 'WebAcl', {});
|
|
15
|
+
|
|
16
|
+
// Send WAF request logs to CloudWatch. The log group name must start with
|
|
17
|
+
// \`aws-waf-logs-\` to satisfy the WAFv2 logging destination requirement.
|
|
18
|
+
const stack = Stack.of(this);
|
|
19
|
+
const wafLogGroupName = \`aws-waf-logs-\${id}-\${this.node.addr.slice(-8)}\`;
|
|
20
|
+
|
|
21
|
+
// KMS key for encrypting WAF logs at rest, usable by CloudWatch Logs
|
|
22
|
+
const logsKey = new Key(this, 'WebAclLogsKey', {
|
|
23
|
+
enableKeyRotation: true,
|
|
24
|
+
});
|
|
25
|
+
logsKey.addToResourcePolicy(
|
|
26
|
+
new PolicyStatement({
|
|
27
|
+
effect: Effect.ALLOW,
|
|
28
|
+
principals: [
|
|
29
|
+
new ServicePrincipal(\`logs.\${stack.region}.amazonaws.com\`),
|
|
30
|
+
],
|
|
31
|
+
actions: [
|
|
32
|
+
'kms:Encrypt',
|
|
33
|
+
'kms:Decrypt',
|
|
34
|
+
'kms:ReEncrypt*',
|
|
35
|
+
'kms:GenerateDataKey*',
|
|
36
|
+
'kms:DescribeKey',
|
|
37
|
+
],
|
|
38
|
+
resources: ['*'],
|
|
39
|
+
conditions: {
|
|
40
|
+
ArnEquals: {
|
|
41
|
+
'kms:EncryptionContext:aws:logs:arn': \`arn:aws:logs:\${stack.region}:\${stack.account}:log-group:\${wafLogGroupName}\`,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const wafLogGroup = new LogGroup(this, 'WebAclLogs', {
|
|
48
|
+
logGroupName: wafLogGroupName,
|
|
49
|
+
encryptionKey: logsKey,
|
|
50
|
+
retention: RetentionDays.ONE_MONTH,
|
|
51
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
new CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
|
|
55
|
+
resourceArn: webAcl.attrArn,
|
|
56
|
+
logDestinationConfigs: [wafLogGroup.logGroupArn],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return webAcl;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
"
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
exports[`waf-log-group-cmk-encryption migration > should encrypt the terraform gateway WAF log group via the module locals 1`] = `
|
|
66
|
+
"data "aws_caller_identity" "current" {}
|
|
67
|
+
data "aws_region" "current" {}
|
|
68
|
+
|
|
69
|
+
locals {
|
|
70
|
+
aws_account_id = data.aws_caller_identity.current.account_id
|
|
71
|
+
aws_region = data.aws_region.current.id
|
|
72
|
+
gateway_waf_log_group_name = var.enable_waf ? "aws-waf-logs-my-gateway-\${random_id.unique_suffix.hex}" : null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
resource "random_id" "unique_suffix" {
|
|
76
|
+
byte_length = 4
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# WAF request logs. WAFv2 requires the \`aws-waf-logs-\` name prefix.
|
|
80
|
+
resource "aws_cloudwatch_log_group" "gateway_waf_logs" {
|
|
81
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one year which is sufficient for WAF logs
|
|
82
|
+
count = var.enable_waf ? 1 : 0
|
|
83
|
+
|
|
84
|
+
name = local.gateway_waf_log_group_name
|
|
85
|
+
kms_key_id = aws_kms_key.gateway_waf_logs[0].arn
|
|
86
|
+
retention_in_days = 365
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# KMS key for encrypting WAF request logs at rest
|
|
90
|
+
resource "aws_kms_key" "gateway_waf_logs" {
|
|
91
|
+
count = var.enable_waf ? 1 : 0
|
|
92
|
+
|
|
93
|
+
description = "my-gateway gateway WAF log encryption"
|
|
94
|
+
deletion_window_in_days = 7
|
|
95
|
+
enable_key_rotation = true
|
|
96
|
+
|
|
97
|
+
policy = jsonencode({
|
|
98
|
+
Version = "2012-10-17"
|
|
99
|
+
Statement = [
|
|
100
|
+
{
|
|
101
|
+
Sid = "Enable IAM User Permissions"
|
|
102
|
+
Effect = "Allow"
|
|
103
|
+
Principal = { AWS = "arn:aws:iam::\${local.aws_account_id}:root" }
|
|
104
|
+
Action = "kms:*"
|
|
105
|
+
Resource = "*"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
Sid = "Allow CloudWatch Logs"
|
|
109
|
+
Effect = "Allow"
|
|
110
|
+
Principal = { Service = "logs.\${local.aws_region}.amazonaws.com" }
|
|
111
|
+
Action = [
|
|
112
|
+
"kms:Encrypt",
|
|
113
|
+
"kms:Decrypt",
|
|
114
|
+
"kms:ReEncrypt*",
|
|
115
|
+
"kms:GenerateDataKey*",
|
|
116
|
+
"kms:DescribeKey"
|
|
117
|
+
]
|
|
118
|
+
Resource = "*"
|
|
119
|
+
Condition = {
|
|
120
|
+
ArnEquals = {
|
|
121
|
+
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${local.aws_region}:\${local.aws_account_id}:log-group:\${local.gateway_waf_log_group_name}"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
resource "aws_kms_alias" "gateway_waf_logs" {
|
|
130
|
+
count = var.enable_waf ? 1 : 0
|
|
131
|
+
|
|
132
|
+
name = "alias/my-gateway-\${random_id.unique_suffix.hex}-waf-logs"
|
|
133
|
+
target_key_id = aws_kms_key.gateway_waf_logs[0].key_id
|
|
134
|
+
}
|
|
135
|
+
"
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
exports[`waf-log-group-cmk-encryption migration > should encrypt the terraform rest api WAF log group 1`] = `
|
|
139
|
+
"data "aws_region" "current" {}
|
|
140
|
+
data "aws_caller_identity" "current" {}
|
|
141
|
+
|
|
142
|
+
locals {
|
|
143
|
+
existing = "untouched"
|
|
144
|
+
waf_log_group_name = var.enable_waf ? "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}" : null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
148
|
+
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
149
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
150
|
+
count = var.enable_waf ? 1 : 0
|
|
151
|
+
|
|
152
|
+
name = local.waf_log_group_name
|
|
153
|
+
kms_key_id = aws_kms_key.waf_logs[0].arn
|
|
154
|
+
retention_in_days = 30
|
|
155
|
+
tags = var.tags
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# KMS key for encrypting WAF request logs at rest
|
|
159
|
+
resource "aws_kms_key" "waf_logs" {
|
|
160
|
+
count = var.enable_waf ? 1 : 0
|
|
161
|
+
|
|
162
|
+
description = "\${var.api_name} API WAF log encryption"
|
|
163
|
+
deletion_window_in_days = 7
|
|
164
|
+
enable_key_rotation = true
|
|
165
|
+
|
|
166
|
+
policy = jsonencode({
|
|
167
|
+
Version = "2012-10-17"
|
|
168
|
+
Statement = [
|
|
169
|
+
{
|
|
170
|
+
Sid = "Enable IAM User Permissions"
|
|
171
|
+
Effect = "Allow"
|
|
172
|
+
Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
|
|
173
|
+
Action = "kms:*"
|
|
174
|
+
Resource = "*"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
Sid = "Allow CloudWatch Logs"
|
|
178
|
+
Effect = "Allow"
|
|
179
|
+
Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
|
|
180
|
+
Action = [
|
|
181
|
+
"kms:Encrypt",
|
|
182
|
+
"kms:Decrypt",
|
|
183
|
+
"kms:ReEncrypt*",
|
|
184
|
+
"kms:GenerateDataKey*",
|
|
185
|
+
"kms:DescribeKey"
|
|
186
|
+
]
|
|
187
|
+
Resource = "*"
|
|
188
|
+
Condition = {
|
|
189
|
+
ArnEquals = {
|
|
190
|
+
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:\${local.waf_log_group_name}"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
tags = var.tags
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
resource "aws_kms_alias" "waf_logs" {
|
|
201
|
+
count = var.enable_waf ? 1 : 0
|
|
202
|
+
|
|
203
|
+
name = "alias/\${var.api_name}-\${random_id.waf_log_suffix[0].hex}-waf-logs"
|
|
204
|
+
target_key_id = aws_kms_key.waf_logs[0].key_id
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
resource "random_id" "waf_log_suffix" {
|
|
208
|
+
count = var.enable_waf ? 1 : 0
|
|
209
|
+
byte_length = 4
|
|
210
|
+
}
|
|
211
|
+
"
|
|
212
|
+
`;
|
|
213
|
+
|
|
214
|
+
exports[`waf-log-group-cmk-encryption migration > should encrypt the terraform user identity WAF log group 1`] = `
|
|
215
|
+
"data "aws_caller_identity" "current" {}
|
|
216
|
+
data "aws_region" "current" {}
|
|
217
|
+
|
|
218
|
+
locals {
|
|
219
|
+
local_callback_urls = [
|
|
220
|
+
"http://localhost:4200"
|
|
221
|
+
]
|
|
222
|
+
user_pool_waf_log_group_name = var.enable_waf ? "aws-waf-logs-UserPool-\${random_id.unique_suffix.hex}" : null
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
resource "random_id" "unique_suffix" {
|
|
226
|
+
byte_length = 4
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
230
|
+
resource "aws_cloudwatch_log_group" "user_pool_waf_logs" {
|
|
231
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
232
|
+
count = var.enable_waf ? 1 : 0
|
|
233
|
+
|
|
234
|
+
name = local.user_pool_waf_log_group_name
|
|
235
|
+
kms_key_id = aws_kms_key.user_pool_waf_logs[0].arn
|
|
236
|
+
retention_in_days = 30
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# KMS key for encrypting WAF request logs at rest
|
|
240
|
+
resource "aws_kms_key" "user_pool_waf_logs" {
|
|
241
|
+
count = var.enable_waf ? 1 : 0
|
|
242
|
+
|
|
243
|
+
description = "User pool WAF log encryption"
|
|
244
|
+
deletion_window_in_days = 7
|
|
245
|
+
enable_key_rotation = true
|
|
246
|
+
|
|
247
|
+
policy = jsonencode({
|
|
248
|
+
Version = "2012-10-17"
|
|
249
|
+
Statement = [
|
|
250
|
+
{
|
|
251
|
+
Sid = "Enable IAM User Permissions"
|
|
252
|
+
Effect = "Allow"
|
|
253
|
+
Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
|
|
254
|
+
Action = "kms:*"
|
|
255
|
+
Resource = "*"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
Sid = "Allow CloudWatch Logs"
|
|
259
|
+
Effect = "Allow"
|
|
260
|
+
Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
|
|
261
|
+
Action = [
|
|
262
|
+
"kms:Encrypt",
|
|
263
|
+
"kms:Decrypt",
|
|
264
|
+
"kms:ReEncrypt*",
|
|
265
|
+
"kms:GenerateDataKey*",
|
|
266
|
+
"kms:DescribeKey"
|
|
267
|
+
]
|
|
268
|
+
Resource = "*"
|
|
269
|
+
Condition = {
|
|
270
|
+
ArnEquals = {
|
|
271
|
+
"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}"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
resource "aws_kms_alias" "user_pool_waf_logs" {
|
|
280
|
+
count = var.enable_waf ? 1 : 0
|
|
281
|
+
|
|
282
|
+
name = "alias/user-pool-\${random_id.unique_suffix.hex}-waf-logs"
|
|
283
|
+
target_key_id = aws_kms_key.user_pool_waf_logs[0].key_id
|
|
284
|
+
}
|
|
285
|
+
"
|
|
286
|
+
`;
|
|
@@ -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>;
|