@aws/nx-plugin 1.0.0-rc.87 → 1.0.0-rc.88
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/LICENSE-THIRD-PARTY +9977 -16000
- package/migrations.json +119 -76
- package/package.json +7 -7
- package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +7 -7
- package/src/migrations/latest/python-version-drop-exact-patch/metadata.json +3 -0
- package/src/migrations/latest/python-version-drop-exact-patch/migration.js +43 -0
- package/src/migrations/latest/python-version-drop-exact-patch/migration.js.map +1 -0
- package/src/migrations/latest/react-website-nx-react-express-override/metadata.json +3 -0
- package/src/migrations/latest/react-website-nx-react-express-override/migration.d.ts +31 -0
- package/src/migrations/latest/react-website-nx-react-express-override/migration.js +74 -0
- package/src/migrations/latest/react-website-nx-react-express-override/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.77/0001-dynamodb-configurable-encryption/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.77/0002-user-identity-configurable-mfa/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.79/0001-py-agent-ag-ui-hooks/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.82/0002-runtime-config-namespace-defaults/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.84/0003-terraform-declare-used-providers/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy/migration.d.ts +6 -0
- package/src/migrations/v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +2 -2
- package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +5 -5
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +4 -4
- package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +12 -12
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/py/rdb/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +7 -7
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +8 -8
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +12 -12
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/ts/dcr-proxy/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +59 -49
- package/src/ts/react-website/app/generator.d.ts +3 -0
- package/src/ts/react-website/app/generator.js +25 -1
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +2 -2
- package/src/utils/lambda-runtime-resolution.d.ts +0 -21
- package/src/utils/lambda-runtime-resolution.js +0 -14
- package/src/utils/lambda-runtime-resolution.js.map +1 -1
- package/src/utils/version-upgrade-migration/sync-python-language-version.d.ts +2 -2
- package/src/utils/version-upgrade-migration/sync-python-language-version.js +2 -2
- package/src/utils/version-upgrade-migration/sync-python-language-version.js.map +1 -1
- package/src/utils/versions.d.ts +58 -62
- package/src/utils/versions.js +57 -60
- package/src/utils/versions.js.map +1 -1
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js.map +0 -1
- package/src/migrations/latest/dynamodb-configurable-encryption/migration.js.map +0 -1
- package/src/migrations/latest/dynamodb-terraform-app-module-outputs/migration.js.map +0 -1
- package/src/migrations/latest/py-agent-ag-ui-hooks/migration.js.map +0 -1
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js.map +0 -1
- package/src/migrations/latest/runtime-config-namespace-defaults/migration.js.map +0 -1
- package/src/migrations/latest/static-website-configurable-waf-encryption/migration.js.map +0 -1
- package/src/migrations/latest/terraform-data-resource-destroy-protection/migration.js.map +0 -1
- package/src/migrations/latest/terraform-declare-used-providers/migration.js.map +0 -1
- package/src/migrations/latest/terraform-gateway-cedar-render-root-ejs/migration.js.map +0 -1
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js.map +0 -1
- package/src/migrations/latest/terraform-load-runtime-config-src-path/migration.js.map +0 -1
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +0 -1
- package/src/migrations/latest/user-identity-configurable-mfa/migration.js.map +0 -1
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js.map +0 -1
- /package/src/migrations/latest/{agentcore-harness-no-tools-by-default → python-version-drop-exact-patch}/migration.d.ts +0 -0
- /package/src/migrations/{latest/dynamodb-configurable-encryption → v1.0.0-rc.77/0001-dynamodb-configurable-encryption}/__snapshots__/migration.spec.ts.snap +0 -0
- /package/src/migrations/{latest/dynamodb-configurable-encryption → v1.0.0-rc.77/0001-dynamodb-configurable-encryption}/metadata.json +0 -0
- /package/src/migrations/{latest/dynamodb-configurable-encryption → v1.0.0-rc.77/0001-dynamodb-configurable-encryption}/migration.d.ts +0 -0
- /package/src/migrations/{latest/dynamodb-configurable-encryption → v1.0.0-rc.77/0001-dynamodb-configurable-encryption}/migration.js +0 -0
- /package/src/migrations/{latest/user-identity-configurable-mfa → v1.0.0-rc.77/0002-user-identity-configurable-mfa}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-data-resource-destroy-protection → v1.0.0-rc.77/0002-user-identity-configurable-mfa}/migration.d.ts +0 -0
- /package/src/migrations/{latest/user-identity-configurable-mfa → v1.0.0-rc.77/0002-user-identity-configurable-mfa}/migration.js +0 -0
- /package/src/migrations/{latest/static-website-configurable-waf-encryption → v1.0.0-rc.78/0001-static-website-configurable-waf-encryption}/metadata.json +0 -0
- /package/src/migrations/{latest/dynamodb-terraform-app-module-outputs → v1.0.0-rc.78/0001-static-website-configurable-waf-encryption}/migration.d.ts +0 -0
- /package/src/migrations/{latest/static-website-configurable-waf-encryption → v1.0.0-rc.78/0001-static-website-configurable-waf-encryption}/migration.js +0 -0
- /package/src/migrations/{latest/py-agent-ag-ui-hooks → v1.0.0-rc.79/0001-py-agent-ag-ui-hooks}/metadata.json +0 -0
- /package/src/migrations/{latest/py-agent-ag-ui-hooks → v1.0.0-rc.79/0001-py-agent-ag-ui-hooks}/migration.d.ts +0 -0
- /package/src/migrations/{latest/py-agent-ag-ui-hooks → v1.0.0-rc.79/0001-py-agent-ag-ui-hooks}/migration.js +0 -0
- /package/src/migrations/{latest/terraform-load-runtime-config-src-path → v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path}/metadata.json +0 -0
- /package/src/migrations/{latest/rdb-terraform-aurora-app-module-defaults → v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path}/migration.d.ts +0 -0
- /package/src/migrations/{latest/terraform-load-runtime-config-src-path → v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path}/migration.js +0 -0
- /package/src/migrations/{latest/runtime-config-namespace-defaults → v1.0.0-rc.82/0002-runtime-config-namespace-defaults}/metadata.json +0 -0
- /package/src/migrations/{latest/runtime-config-namespace-defaults → v1.0.0-rc.82/0002-runtime-config-namespace-defaults}/migration.d.ts +0 -0
- /package/src/migrations/{latest/runtime-config-namespace-defaults → v1.0.0-rc.82/0002-runtime-config-namespace-defaults}/migration.js +0 -0
- /package/src/migrations/{latest/dynamodb-terraform-app-module-outputs → v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs}/metadata.json +0 -0
- /package/src/migrations/{latest/static-website-configurable-waf-encryption → v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs}/migration.d.ts +0 -0
- /package/src/migrations/{latest/dynamodb-terraform-app-module-outputs → v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs}/migration.js +0 -0
- /package/src/migrations/{latest/terraform-data-resource-destroy-protection → v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection}/__snapshots__/migration.spec.ts.snap +0 -0
- /package/src/migrations/{latest/terraform-data-resource-destroy-protection → v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection}/metadata.json +0 -0
- /package/src/migrations/{latest/user-identity-configurable-mfa → v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection}/migration.d.ts +0 -0
- /package/src/migrations/{latest/terraform-data-resource-destroy-protection → v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection}/migration.js +0 -0
- /package/src/migrations/{latest/terraform-gateway-cedar-render-root-ejs → v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-declare-used-providers → v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs}/migration.d.ts +0 -0
- /package/src/migrations/{latest/terraform-gateway-cedar-render-root-ejs → v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs}/migration.js +0 -0
- /package/src/migrations/{latest/rdb-terraform-aurora-app-module-defaults → v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults}/__snapshots__/migration.spec.ts.snap +0 -0
- /package/src/migrations/{latest/rdb-terraform-aurora-app-module-defaults → v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-gateway-cedar-render-root-ejs → v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults}/migration.d.ts +0 -0
- /package/src/migrations/{latest/rdb-terraform-aurora-app-module-defaults → v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults}/migration.js +0 -0
- /package/src/migrations/{latest/agentcore-harness-no-tools-by-default → v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default}/__snapshots__/migration.spec.ts.snap +0 -0
- /package/src/migrations/{latest/agentcore-harness-no-tools-by-default → v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-harness-vpc-role-and-memory-grant → v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default}/migration.d.ts +0 -0
- /package/src/migrations/{latest/agentcore-harness-no-tools-by-default → v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default}/migration.js +0 -0
- /package/src/migrations/{latest/terraform-declare-used-providers → v1.0.0-rc.84/0003-terraform-declare-used-providers}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-load-runtime-config-src-path → v1.0.0-rc.84/0003-terraform-declare-used-providers}/migration.d.ts +0 -0
- /package/src/migrations/{latest/terraform-declare-used-providers → v1.0.0-rc.84/0003-terraform-declare-used-providers}/migration.js +0 -0
- /package/src/migrations/{latest/waf-log-group-cmk-encryption → v1.0.0-rc.84/0004-waf-log-group-cmk-encryption}/__snapshots__/migration.spec.ts.snap +0 -0
- /package/src/migrations/{latest/waf-log-group-cmk-encryption → v1.0.0-rc.84/0004-waf-log-group-cmk-encryption}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-project-checkov-target-and-bootstrap-destroy → v1.0.0-rc.84/0004-waf-log-group-cmk-encryption}/migration.d.ts +0 -0
- /package/src/migrations/{latest/waf-log-group-cmk-encryption → v1.0.0-rc.84/0004-waf-log-group-cmk-encryption}/migration.js +0 -0
- /package/src/migrations/{latest/terraform-harness-vpc-role-and-memory-grant → v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant}/__snapshots__/migration.spec.ts.snap +0 -0
- /package/src/migrations/{latest/terraform-harness-vpc-role-and-memory-grant → v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant}/metadata.json +0 -0
- /package/src/migrations/{latest/waf-log-group-cmk-encryption → v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant}/migration.d.ts +0 -0
- /package/src/migrations/{latest/terraform-harness-vpc-role-and-memory-grant → v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant}/migration.js +0 -0
- /package/src/migrations/{latest/terraform-project-checkov-target-and-bootstrap-destroy → v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy}/metadata.json +0 -0
- /package/src/migrations/{latest/terraform-project-checkov-target-and-bootstrap-destroy → v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy}/migration.js +0 -0
package/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/migration.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 MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Bring existing workspaces up to the generators' current StaticWebsite\n * configurability:\n *\n * - The vended `StaticWebsite` CDK construct gains `enableWaf`, `encryption`,\n * `encryptionKey` and `enableKeyRotation` props. The WAF Web ACL is now\n * optional, the KMS key used to encrypt the website and distribution log\n * buckets is now overridable (or skippable in favour of another\n * `BucketEncryption`), and the auto-created key's rotation is configurable.\n * - Each vended per-website app construct (`app/static-websites/*.ts`) gains\n * an optional `props` constructor parameter so these can be configured per\n * app, rather than only by hand-editing the vended construct.\n * - The vended Terraform static-website core module gains the equivalent\n * `enable_waf`, `encryption`, `kms_key_arn`, `create_kms_key` and\n * `enable_key_rotation` variables. The CloudFront distribution's\n * `lifecycle.replace_triggered_by` on the WAF ACL is also dropped - it\n * can't resolve once the ACL has a count of 0 and no prior state to\n * reference, which broke `terraform plan` on any greenfield deployment\n * with `enable_waf = false`.\n * - Each vended per-website Terraform app module (`app/static-websites/<name>/<name>.tf`)\n * gains pass-through `custom_domain_names`, `acm_certificate_arn`,\n * `enable_waf`, `encryption`, `kms_key_arn`, `create_kms_key` and\n * `enable_key_rotation` variables forwarded to the core module call,\n * matching the pass-through convention every other app-wraps-core\n * Terraform module in this plugin already follows (`rdb`, `agent-core`,\n * `dcr-proxies`, the REST/HTTP API).\n *\n * These files are generated with `KeepExisting`, so without this an upgraded\n * workspace has generators that support this configuration but vended files\n * that don't. Diverged files are left untouched and reported via `nextSteps`.\n */\n\nconst CDK_STATIC_WEBSITE_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/static-website.ts`;\nconst CDK_STATIC_WEBSITES_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/static-websites`;\nconst TERRAFORM_STATIC_WEBSITE_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/static-website/static-website.tf`;\nconst TERRAFORM_STATIC_WEBSITES_APP_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/static-websites`;\n\nconst CDK_DIVERGED_MESSAGE = `${CDK_STATIC_WEBSITE_FILE}: has diverged from the generated shape - left untouched. To pick up the enableWaf, encryption, encryptionKey and enableKeyRotation props, manually port them from the vended core/static-website.ts template (see the ts#react-website generator's static-website construct).`;\n\nconst TERRAFORM_DIVERGED_MESSAGE = `${TERRAFORM_STATIC_WEBSITE_FILE}: has diverged from the generated shape - left untouched. To pick up the enable_waf, encryption, kms_key_arn, create_kms_key and enable_key_rotation variables, manually port them from the vended static-website.tf template (see the ts#react-website generator's static-website module).`;\n\nconst terraformAppDivergedMessage = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To make custom_domain_names, acm_certificate_arn, enable_waf, encryption, kms_key_arn, create_kms_key and enable_key_rotation configurable from your root Terraform configuration, add pass-through variables here and forward them to the static_website module call (see the ts#react-website generator's static-websites app template).`;\n\n// The doc comments below carry backticked markdown, which must stay out of\n// the GritQL pattern that inserts this text (see insertViaGritQL). It's routed\n// in as plain text via the placeholder instead.\nconst STATIC_WEBSITE_PROPS_TEXT = `/**\n * Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\n *\n * @default true\n */\n readonly enableWaf?: boolean;\n /**\n * Server-side encryption for the website and distribution log buckets.\n *\n * @default BucketEncryption.KMS\n */\n readonly encryption?: BucketEncryption;\n /**\n * KMS key used to encrypt the website and distribution log buckets. Only used when \\`encryption\\` is\n * \\`BucketEncryption.KMS\\`. When not provided, a new key is created. Note that a key imported via\n * \\`Key.fromKeyArn\\` must already grant the CloudWatch Logs, S3 and CloudFront service principals the\n * necessary permissions in its own key policy - \\`addToResourcePolicy\\` is a no-op on an imported key,\n * so this construct cannot grant them on your behalf.\n */\n readonly encryptionKey?: IKey;\n /**\n * Whether the automatically created KMS key has rotation enabled. Only applies when \\`encryption\\` is\n * \\`BucketEncryption.KMS\\` and no \\`encryptionKey\\` is supplied.\n *\n * @default true\n */\n readonly enableKeyRotation?: boolean`;\n\n/**\n * Surface enableWaf/encryption/encryptionKey/enableKeyRotation on the vended\n * StaticWebsite CDK construct.\n */\nconst migrateCdkConstruct = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_STATIC_WEBSITE_FILE)) {\n return; // This workspace has no CDK static website construct.\n }\n\n if (\n await matchGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n '`readonly enableWaf?: boolean`',\n )\n ) {\n return; // Already migrated.\n }\n\n const CERTIFICATE_FIELD = '`readonly certificate?: ICertificate`';\n const DESTRUCTURE_OLD =\n '`{ websiteFilePath, websiteName, domainNames, certificate }: StaticWebsiteProps`';\n const KEY_CREATION_OLD =\n \"`const websiteKey = new Key(this, 'WebsiteKey', { enableKeyRotation: true })`\";\n const ADD_TO_RESOURCE_POLICY_OLD = '`websiteKey.addToResourcePolicy($args)`';\n const WEBSITE_BUCKET_ENCRYPTION_OLD =\n \"`encryption: BucketEncryption.KMS` as $prop where { $prop <: within `new Bucket($_, 'WebsiteBucket', $_)` }\";\n const DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD =\n \"`encryption: BucketEncryption.KMS` as $prop where { $prop <: within `new Bucket($_, 'DistributionLogBucket', $_)` }\";\n const WAF_STACK_OLD = \"`const wafStack = new CloudfrontWebAcl(this, 'waf')`\";\n const WEB_ACL_ID_OLD = '`webAclId: wafStack.wafArn`';\n\n const anchors = [\n CERTIFICATE_FIELD,\n DESTRUCTURE_OLD,\n KEY_CREATION_OLD,\n ADD_TO_RESOURCE_POLICY_OLD,\n WEBSITE_BUCKET_ENCRYPTION_OLD,\n DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD,\n WAF_STACK_OLD,\n WEB_ACL_ID_OLD,\n ];\n\n const allPresent = (\n await Promise.all(\n anchors.map((pattern) =>\n matchGritQL(tree, CDK_STATIC_WEBSITE_FILE, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!allPresent) {\n nextSteps.push(CDK_DIVERGED_MESSAGE);\n return;\n }\n\n await addDestructuredImport(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n ['IKey'],\n 'aws-cdk-lib/aws-kms',\n );\n\n await insertViaGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${CERTIFICATE_FIELD} as $field => \\`$field;\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n STATIC_WEBSITE_PROPS_TEXT,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${DESTRUCTURE_OLD} => \\`{\n websiteFilePath,\n websiteName,\n domainNames,\n certificate,\n enableWaf = true,\n encryption = BucketEncryption.KMS,\n encryptionKey,\n enableKeyRotation = true,\n }: StaticWebsiteProps\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${KEY_CREATION_OLD} => \\`const websiteKey: IKey | undefined =\n encryption === BucketEncryption.KMS\n ? (encryptionKey ?? new Key(this, 'WebsiteKey', { enableKeyRotation }))\n : undefined;\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${ADD_TO_RESOURCE_POLICY_OLD} => \\`websiteKey?.addToResourcePolicy($args)\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WEBSITE_BUCKET_ENCRYPTION_OLD} => \\`encryption\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD} => \\`encryption\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WAF_STACK_OLD} => \\`const wafStack = enableWaf ? new CloudfrontWebAcl(this, 'waf') : undefined;\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WEB_ACL_ID_OLD} => \\`webAclId: wafStack?.wafArn\\``,\n );\n};\n\n/**\n * Surface the same props on each vended per-website app construct, so they\n * can be configured per app rather than only by editing the shared construct.\n */\nconst migrateCdkAppConstructs = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_STATIC_WEBSITES_APP_DIR)) {\n return;\n }\n\n for (const fileName of tree.children(CDK_STATIC_WEBSITES_APP_DIR)) {\n if (fileName === 'index.ts' || !fileName.endsWith('.ts')) {\n continue;\n }\n const filePath = joinPathFragments(CDK_STATIC_WEBSITES_APP_DIR, fileName);\n\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (contents.includes('StaticWebsiteProps')) {\n continue; // Already migrated.\n }\n\n const name = await captureGritQLVariable(\n tree,\n filePath,\n '`export class $name extends StaticWebsite { $_ }`',\n 'name',\n );\n if (!name) {\n continue; // Not a StaticWebsite subclass, not ours to touch.\n }\n\n const importPattern = '`import { StaticWebsite } from $path`';\n const ctorPattern = '`constructor(scope: Construct, id: string) { $body }`';\n // Captures the whole property list rather than anchoring on websiteName\n // being followed by exactly one more property ($rest binds a single\n // trailing node, not a variadic list). This must still match when a\n // workspace already customised the super() call with e.g. domainNames\n // and certificate (the pre-existing documented customization point).\n const superPattern = `\\`super(scope, id, { $props })\\` as $call where {\n $props <: contains \\`websiteName: '${name}'\\`\n}`;\n\n const ready = (\n await Promise.all(\n [importPattern, ctorPattern, superPattern].map((pattern) =>\n matchGritQL(tree, filePath, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!ready) {\n nextSteps.push(\n `${filePath}: has diverged from the generated shape - left untouched. To make enableWaf, encryption, encryptionKey and enableKeyRotation configurable here, add an optional \\`props?: ${name}Props\\` constructor parameter (\\`Omit<StaticWebsiteProps, 'websiteName' | 'websiteFilePath'>\\`) and spread it into the super() call (see the ts#react-website generator's static-websites app template).`,\n );\n continue;\n }\n\n await applyGritQL(\n tree,\n filePath,\n `${importPattern} => \\`import { StaticWebsite, StaticWebsiteProps } from $path\\``,\n );\n\n await insertViaGritQL(\n tree,\n filePath,\n `\\`export class ${name} extends StaticWebsite { $body }\\` as $cls => \\`${GRIT_INSERT_PLACEHOLDER}\n\n$cls\\``,\n `export type ${name}Props = Omit<\\n StaticWebsiteProps,\\n 'websiteName' | 'websiteFilePath'\\n>;`,\n );\n\n await applyGritQL(\n tree,\n filePath,\n `${ctorPattern} => \\`constructor(scope: Construct, id: string, props?: ${name}Props) { $body }\\``,\n );\n\n await applyGritQL(\n tree,\n filePath,\n `${superPattern} => \\`super(scope, id, { ...props, $props })\\``,\n );\n }\n};\n\n// Terraform (HCL) patterns are built from plain strings rather than JS\n// template literals, since the HCL text itself is full of `${...}`\n// interpolations that would otherwise be parsed as JS interpolation.\nconst hcl = (pattern: string) => 'language hcl\\n' + pattern;\n\nconst withinResource = (\n linePattern: string,\n resourceType: string,\n resourceLabel: string,\n) =>\n hcl(\n '`' +\n linePattern +\n '` as $line where {\\n' +\n ' $line <: within `resource \"' +\n resourceType +\n '\" \"' +\n resourceLabel +\n '\" { $_ }`\\n' +\n '}',\n );\n\nconst NEW_VARIABLES_TEXT = [\n 'variable \"enable_waf\" {',\n ' description = \"Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"encryption\" {',\n ' description = \"Server-side encryption for the website and distribution log buckets. One of KMS or S3_MANAGED.\"',\n ' type = string',\n ' default = \"KMS\"',\n '',\n ' validation {',\n ' condition = contains([\"KMS\", \"S3_MANAGED\"], var.encryption)',\n ' error_message = \"encryption must be one of KMS or S3_MANAGED.\"',\n ' }',\n '}',\n '',\n 'variable \"kms_key_arn\" {',\n ' description = \"ARN of an existing KMS key used to encrypt the website and distribution log buckets when encryption is KMS. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the CloudWatch Logs, S3 and CloudFront service principals the necessary permissions in its own key policy.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"create_kms_key\" {',\n ' description = \"Whether to create a KMS key for the website. Only applies when encryption is KMS. Set to false when supplying kms_key_arn.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether the automatically created KMS key has rotation enabled. Only applies when encryption is KMS and create_kms_key is true.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst NEW_LOCALS_TEXT = [\n '',\n ' create_website_key = var.encryption == \"KMS\" && var.create_kms_key',\n ' website_kms_key_arn = (',\n ' var.encryption != \"KMS\" ? null :',\n ' local.create_website_key ? aws_kms_key.website_key[0].arn :',\n ' var.kms_key_arn',\n ' )',\n].join('\\n');\n\n/**\n * Surface the same configuration on the vended Terraform static-website\n * module.\n */\nconst migrateTerraformModule = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(TERRAFORM_STATIC_WEBSITE_FILE)) {\n return; // This workspace has no Terraform static website module.\n }\n\n if (\n await matchGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl('`variable \"enable_waf\" { $_ }`'),\n )\n ) {\n return; // Already migrated.\n }\n\n const ACM_CERT_VARIABLE = hcl('`variable \"acm_certificate_arn\" { $_ }`');\n const ACCESS_LOGS_LOCAL = hcl(\n '`access_logs_name_prefix = substr(lower(var.website_name), 0, 16)`',\n );\n const KMS_KEY_BLOCK = hcl('`resource \"aws_kms_key\" \"website_key\" { $_ }`');\n const KMS_KEY_ROTATION_LINE = withinResource(\n 'enable_key_rotation = true',\n 'aws_kms_key',\n 'website_key',\n );\n const KMS_ALIAS_BLOCK = hcl(\n '`resource \"aws_kms_alias\" \"website_key_alias\" { $_ }`',\n );\n const KMS_ALIAS_TARGET_LINE = hcl(\n '`target_key_id = aws_kms_key.website_key.key_id`',\n );\n const LOG_GROUP_KMS_LINE = hcl('`kms_key_id = aws_kms_key.website_key.arn`');\n const WEBSITE_ENCRYPTION_KEY_LINE = withinResource(\n 'kms_master_key_id = aws_kms_key.website_key.arn',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'website_encryption',\n );\n const WEBSITE_ENCRYPTION_ALGO_LINE = withinResource(\n 'sse_algorithm = \"aws:kms\"',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'website_encryption',\n );\n const DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE = withinResource(\n 'kms_master_key_id = aws_kms_key.website_key.arn',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'distribution_logs_encryption',\n );\n const DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE = withinResource(\n 'sse_algorithm = \"aws:kms\"',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'distribution_logs_encryption',\n );\n const WAF_BLOCK = hcl(\n '`resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" { $_ }`',\n );\n const WEB_ACL_ID_LINE = hcl(\n '`web_acl_id = aws_wafv2_web_acl.cloudfront_waf.arn`',\n );\n const WAF_OUTPUT_VALUE_LINE = hcl(\n '`value = aws_wafv2_web_acl.cloudfront_waf.arn` as $line where {\\n' +\n ' $line <: within `output \"waf_web_acl_arn\" { $_ }`\\n' +\n '}',\n );\n // Forces distribution replacement whenever the WAF ACL changes. Once the\n // ACL gains `count`, this can't be resolved on a greenfield apply where\n // count is 0 and there's no prior state to reference - see\n // https://github.com/awslabs/nx-plugin-for-aws/pull/1107. It's also\n // redundant: web_acl_id already references the ACL ARN directly, so a\n // replaced ACL still drives a plain distribution update.\n const LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK = hcl(\n '`lifecycle {\\n replace_triggered_by = [\\n aws_wafv2_web_acl.cloudfront_waf\\n ]\\n }`',\n );\n\n const anchors = [\n ACM_CERT_VARIABLE,\n ACCESS_LOGS_LOCAL,\n KMS_KEY_BLOCK,\n KMS_KEY_ROTATION_LINE,\n KMS_ALIAS_BLOCK,\n KMS_ALIAS_TARGET_LINE,\n LOG_GROUP_KMS_LINE,\n WEBSITE_ENCRYPTION_KEY_LINE,\n WEBSITE_ENCRYPTION_ALGO_LINE,\n DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE,\n DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE,\n WAF_BLOCK,\n WEB_ACL_ID_LINE,\n WAF_OUTPUT_VALUE_LINE,\n LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK,\n ];\n\n const allPresent = (\n await Promise.all(\n anchors.map((pattern) =>\n matchGritQL(tree, TERRAFORM_STATIC_WEBSITE_FILE, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!allPresent) {\n nextSteps.push(TERRAFORM_DIVERGED_MESSAGE);\n return;\n }\n\n // 1. New variables, after acm_certificate_arn. Routed through the\n // placeholder since the variable descriptions contain double-quoted\n // HCL string values.\n await insertViaGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`variable \"acm_certificate_arn\" { $body }` as $var => `$var\\n\\n' +\n GRIT_INSERT_PLACEHOLDER +\n '`',\n ),\n NEW_VARIABLES_TEXT,\n );\n\n // 2. New locals, after access_logs_name_prefix.\n await insertViaGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`access_logs_name_prefix = substr(lower(var.website_name), 0, 16)` as $line => `$line\\n' +\n GRIT_INSERT_PLACEHOLDER +\n '`',\n ),\n NEW_LOCALS_TEXT,\n );\n\n // 3. Make the KMS key and alias conditional on encryption/kms_key_arn.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_kms_key\" \"website_key\" { $body }` => `resource \"aws_kms_key\" \"website_key\" {\\n' +\n ' count = local.create_website_key ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n KMS_KEY_ROTATION_LINE +\n ' => `enable_key_rotation = var.enable_key_rotation`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_kms_alias\" \"website_key_alias\" { $body }` => `resource \"aws_kms_alias\" \"website_key_alias\" {\\n' +\n ' count = local.create_website_key ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n KMS_ALIAS_TARGET_LINE +\n ' => `target_key_id = aws_kms_key.website_key[0].key_id`',\n );\n\n // 4. Resolve the key ARN through the new local everywhere it's consumed.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n LOG_GROUP_KMS_LINE + ' => `kms_key_id = local.website_kms_key_arn`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEBSITE_ENCRYPTION_KEY_LINE +\n ' => `kms_master_key_id = var.encryption == \"KMS\" ? local.website_kms_key_arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEBSITE_ENCRYPTION_ALGO_LINE +\n ' => `sse_algorithm = var.encryption == \"KMS\" ? \"aws:kms\" : \"AES256\"`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE +\n ' => `kms_master_key_id = var.encryption == \"KMS\" ? local.website_kms_key_arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE +\n ' => `sse_algorithm = var.encryption == \"KMS\" ? \"aws:kms\" : \"AES256\"`',\n );\n\n // 5. Make the WAF Web ACL conditional on enable_waf.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" { $body }` => `resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" {\\n' +\n ' count = var.enable_waf ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEB_ACL_ID_LINE +\n ' => `web_acl_id = var.enable_waf ? aws_wafv2_web_acl.cloudfront_waf[0].arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WAF_OUTPUT_VALUE_LINE +\n ' => `value = var.enable_waf ? aws_wafv2_web_acl.cloudfront_waf[0].arn : null`',\n );\n\n // 6. Drop the lifecycle block that can no longer resolve once the WAF ACL\n // has a count of 0 and no prior state to reference.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK + ' => ``',\n );\n};\n\nconst NEW_APP_MODULE_VARIABLES_TEXT = [\n 'variable \"custom_domain_names\" {',\n ' description = \"Custom domain names (aliases) for the CloudFront distribution. Requires acm_certificate_arn.\"',\n ' type = list(string)',\n ' default = []',\n '}',\n '',\n 'variable \"acm_certificate_arn\" {',\n ' description = \"ARN of an ACM certificate (in us-east-1) for the custom domain names. When set, viewers are required to use TLS 1.2 or later.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"enable_waf\" {',\n ' description = \"Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"encryption\" {',\n ' description = \"Server-side encryption for the website and distribution log buckets. One of KMS or S3_MANAGED.\"',\n ' type = string',\n ' default = \"KMS\"',\n '',\n ' validation {',\n ' condition = contains([\"KMS\", \"S3_MANAGED\"], var.encryption)',\n ' error_message = \"encryption must be one of KMS or S3_MANAGED.\"',\n ' }',\n '}',\n '',\n 'variable \"kms_key_arn\" {',\n ' description = \"ARN of an existing KMS key used to encrypt the website and distribution log buckets when encryption is KMS. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the CloudWatch Logs, S3 and CloudFront service principals the necessary permissions in its own key policy.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"create_kms_key\" {',\n ' description = \"Whether to create a KMS key for the website. Only applies when encryption is KMS. Set to false when supplying kms_key_arn.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether the automatically created KMS key has rotation enabled. Only applies when encryption is KMS and create_kms_key is true.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst NEW_APP_MODULE_FORWARD_TEXT = [\n 'custom_domain_names = var.custom_domain_names',\n 'acm_certificate_arn = var.acm_certificate_arn',\n 'enable_waf = var.enable_waf',\n 'encryption = var.encryption',\n 'kms_key_arn = var.kms_key_arn',\n 'create_kms_key = var.create_kms_key',\n 'enable_key_rotation = var.enable_key_rotation',\n].join('\\n ');\n\n/**\n * Surface the same configuration as pass-through variables on each vended\n * per-website Terraform app module, matching the pass-through convention\n * every other app-wraps-core Terraform module already follows.\n */\nconst migrateTerraformAppModules = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(TERRAFORM_STATIC_WEBSITES_APP_DIR)) {\n return;\n }\n\n for (const dirName of tree.children(TERRAFORM_STATIC_WEBSITES_APP_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_STATIC_WEBSITES_APP_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a website app module directory.\n }\n\n if (\n await matchGritQL(tree, filePath, hcl('`variable \"enable_waf\" { $_ }`'))\n ) {\n continue; // Already migrated.\n }\n\n const TERRAFORM_BLOCK = hcl('`terraform { $_ }`');\n const MODULE_BLOCK = hcl('`module \"static_website\" { $_ }`');\n const FILE_PATH_LINE = hcl(\n '`website_file_path = $val` as $line where {\\n' +\n ' $line <: within `module \"static_website\" { $_ }`\\n' +\n '}',\n );\n // custom_domain_names/acm_certificate_arn predate this migration as a\n // documented customization point (hand-editing the module block). If\n // either is already present as a literal argument, blindly forwarding\n // our own `= var.x` line would produce a duplicate argument, which is\n // invalid HCL. Treat that as diverged instead of silently corrupting the file.\n const hasExistingCustomDomainArg = async (name: string) =>\n matchGritQL(\n tree,\n filePath,\n hcl(\n `\\`${name} = $_\\` as $line where {\\n` +\n ' $line <: within `module \"static_website\" { $_ }`\\n' +\n '}',\n ),\n );\n\n const ready =\n (\n await Promise.all(\n [TERRAFORM_BLOCK, MODULE_BLOCK, FILE_PATH_LINE].map((pattern) =>\n matchGritQL(tree, filePath, pattern),\n ),\n )\n ).every(Boolean) &&\n !(await hasExistingCustomDomainArg('custom_domain_names')) &&\n !(await hasExistingCustomDomainArg('acm_certificate_arn'));\n\n if (!ready) {\n nextSteps.push(terraformAppDivergedMessage(filePath));\n continue;\n }\n\n // Inserted right after the terraform/required_providers block (and\n // before the \"Static website module\" comment), matching where the\n // vended template puts these variables.\n await insertViaGritQL(\n tree,\n filePath,\n hcl('`terraform { $body }` as $tf') +\n ` => \\`$tf\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n NEW_APP_MODULE_VARIABLES_TEXT,\n );\n\n await insertViaGritQL(\n tree,\n filePath,\n FILE_PATH_LINE + ` => \\`$line\\n\\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n NEW_APP_MODULE_FORWARD_TEXT,\n );\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n await migrateCdkConstruct(tree, nextSteps);\n await migrateCdkAppConstructs(tree, nextSteps);\n await migrateTerraformModule(tree, nextSteps);\n await migrateTerraformAppModules(tree, nextSteps);\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","addDestructuredImport","applyGritQL","captureGritQLVariable","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_STATIC_WEBSITE_FILE","CDK_STATIC_WEBSITES_APP_DIR","TERRAFORM_STATIC_WEBSITE_FILE","TERRAFORM_STATIC_WEBSITES_APP_DIR","CDK_DIVERGED_MESSAGE","TERRAFORM_DIVERGED_MESSAGE","terraformAppDivergedMessage","filePath","STATIC_WEBSITE_PROPS_TEXT","migrateCdkConstruct","tree","nextSteps","exists","CERTIFICATE_FIELD","DESTRUCTURE_OLD","KEY_CREATION_OLD","ADD_TO_RESOURCE_POLICY_OLD","WEBSITE_BUCKET_ENCRYPTION_OLD","DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD","WAF_STACK_OLD","WEB_ACL_ID_OLD","anchors","allPresent","Promise","all","map","pattern","every","Boolean","push","migrateCdkAppConstructs","fileName","children","endsWith","contents","read","includes","name","importPattern","ctorPattern","superPattern","ready","hcl","withinResource","linePattern","resourceType","resourceLabel","NEW_VARIABLES_TEXT","join","NEW_LOCALS_TEXT","migrateTerraformModule","ACM_CERT_VARIABLE","ACCESS_LOGS_LOCAL","KMS_KEY_BLOCK","KMS_KEY_ROTATION_LINE","KMS_ALIAS_BLOCK","KMS_ALIAS_TARGET_LINE","LOG_GROUP_KMS_LINE","WEBSITE_ENCRYPTION_KEY_LINE","WEBSITE_ENCRYPTION_ALGO_LINE","DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE","DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE","WAF_BLOCK","WEB_ACL_ID_LINE","WAF_OUTPUT_VALUE_LINE","LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK","NEW_APP_MODULE_VARIABLES_TEXT","NEW_APP_MODULE_FORWARD_TEXT","migrateTerraformAppModules","dirName","TERRAFORM_BLOCK","MODULE_BLOCK","FILE_PATH_LINE","hasExistingCustomDomainArg","migration"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.79/0001-py-agent-ag-ui-hooks/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { PY_AGENT_GENERATOR_INFO } from '../../../py/agent/generator.js';\nimport {\n addPythonDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport type { ComponentMetadata } from '../../../utils/nx.js';\n\n/**\n * Forward a Strands py#agent's hooks to the AG-UI adapter.\n *\n * `StrandsAgent` builds a fresh `Agent` per `thread_id` by copying the template\n * Agent's kwargs, but deliberately skips `hooks` - Strands keeps only the built\n * `HookRegistry`, so the providers can't be read back off it. An AG-UI agent's\n * `log_model_errors` / `log_tool_errors` therefore never fired for served\n * requests, and model/tool failures were reported as successful runs.\n *\n * The fix hoists the hooks list out of the `Agent(...)` call into an\n * `AGENT_HOOKS` module constant in `agent.py` - whatever the user has put in it,\n * so custom hooks come along - and passes that to `StrandsAgent(...)` in\n * `main.py` as well. `agent.py` is hoisted for every protocol so the constant\n * matches what the generator vends today; only AG-UI needs the `main.py` half.\n */\n\n// `Agent`'s `hooks` list is invariant, so the hoisted constant has to declare\n// the parameter's own type - an inline literal was inferred from it instead.\nconst HOOKS_TYPE = 'list[HookProvider | HookCallback]';\nconst HOOKS_MODULE = 'strands.hooks';\n\n// Anchored on the `get_agent` context manager - the one shape shared by every\n// generated protocol and connection combination - so the constant lands\n// directly above its only use, where the generator vends it.\nconst HOIST_HOOKS_PATTERN = `language python\n\\`@contextmanager\ndef get_agent($params):\n $body\\` => raw\\`AGENT_HOOKS: ${HOOKS_TYPE} = ${GRIT_INSERT_PLACEHOLDER}\n\n\n@contextmanager\ndef get_agent($params):\n $body\\` where { $program <: not contains \\`AGENT_HOOKS\\` }`;\n\n// Scoped to the Agent constructor so a hooks list the user passes elsewhere in\n// agent.py is left alone.\nconst HOOKS_LIST_CAPTURE_PATTERN =\n 'language python\\n`hooks=[$hooks]` where { $hooks <: within `Agent($_)` }';\nconst HOOKS_LIST_REWRITE_PATTERN =\n 'language python\\n`hooks=[$_]` as $kwarg where { $kwarg <: within `Agent($_)`, $kwarg => `hooks=AGENT_HOOKS` }';\n\nconst AGUI_CONSTRUCTOR_MATCH_PATTERN =\n 'language python\\n`StrandsAgent($args)` where { $args <: contains `agent=$_` }';\nconst AGUI_HOOKS_WIRED_PATTERN =\n 'language python\\n`StrandsAgent($args)` where { $args <: contains `agent=$_`, $args <: contains `hooks=$_` }';\n// Appends after the last argument rather than re-emitting the argument list,\n// both to land `hooks=` where the template puts it and to leave the other\n// arguments' formatting and comments untouched.\nconst AGUI_HOOKS_APPEND_PATTERN =\n 'language python\\n`StrandsAgent($args)` where { $args <: contains `agent=$_`, $args <: not contains `hooks=$_`, $args <: [$..., $last], $last += `, hooks=AGENT_HOOKS` }';\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) => component.generator === PY_AGENT_GENERATOR_INFO.id,\n );\n\n/**\n * Hoists `agent.py`'s `hooks=[...]` list into an `AGENT_HOOKS` module constant,\n * returning whether the file ends up exporting one.\n */\nconst hoistAgentHooks = async (\n tree: Tree,\n agentPath: string,\n): Promise<boolean> => {\n if ((tree.read(agentPath, 'utf-8') ?? '').includes('AGENT_HOOKS')) {\n return true;\n }\n\n // Nothing to hoist unless the Agent is constructed with an inline hooks list.\n const hooks = await captureGritQLVariable(\n tree,\n agentPath,\n HOOKS_LIST_CAPTURE_PATTERN,\n 'hooks',\n );\n if (!hooks) return false;\n\n if (\n !(await insertViaGritQL(tree, agentPath, HOIST_HOOKS_PATTERN, `[${hooks}]`))\n ) {\n return false;\n }\n await applyGritQL(tree, agentPath, HOOKS_LIST_REWRITE_PATTERN);\n await addPythonDestructuredImport(\n tree,\n agentPath,\n ['HookCallback', 'HookProvider'],\n HOOKS_MODULE,\n );\n return true;\n};\n\nconst migrateAgent = async (\n tree: Tree,\n agentDir: string,\n component: ComponentMetadata,\n nextSteps: string[],\n): Promise<void> => {\n // LangChain agents pass no hooks - their AG-UI adapter is handed the graph\n // itself rather than rebuilding one, so nothing is dropped.\n if (component.framework === 'langchain') return;\n\n const agentPath = joinPathFragments(agentDir, 'agent.py');\n const mainPath = joinPathFragments(agentDir, 'main.py');\n const isAgUi = component.protocol === 'ag-ui';\n\n if (!tree.exists(agentPath)) return;\n\n if (!(await hoistAgentHooks(tree, agentPath))) {\n if (isAgUi) {\n nextSteps.push(\n `${agentPath}: the hooks passed to \\`Agent(...)\\` have diverged from the generated shape - left untouched. Hoist them into an \\`AGENT_HOOKS\\` module constant and pass it to \\`StrandsAgent(...)\\` in ${mainPath} too, or AG-UI will not register them (see the py#agent generator's template).`,\n );\n }\n return;\n }\n\n // Only AG-UI rebuilds the Agent, so only its main.py needs the hooks.\n if (!isAgUi || !tree.exists(mainPath)) return;\n\n // Already passing hooks, whether from a previous run or the user's own wiring.\n if (await matchGritQL(tree, mainPath, AGUI_HOOKS_WIRED_PATTERN)) return;\n\n if (!(await matchGritQL(tree, mainPath, AGUI_CONSTRUCTOR_MATCH_PATTERN))) {\n nextSteps.push(\n `${mainPath}: the \\`StrandsAgent(...)\\` constructor has diverged from the generated shape - left untouched. Pass \\`hooks=AGENT_HOOKS\\` to it, importing \\`AGENT_HOOKS\\` from \\`.agent\\`, or AG-UI will not register the agent's hooks (see the py#agent generator's template).`,\n );\n return;\n }\n\n await applyGritQL(tree, mainPath, AGUI_HOOKS_APPEND_PATTERN);\n await addPythonDestructuredImport(tree, mainPath, ['AGENT_HOOKS'], '.agent');\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n // Legacy component paths point at agent.py rather than its directory.\n const componentDir = component.path.endsWith('/agent.py')\n ? component.path.slice(0, -'/agent.py'.length)\n : component.path;\n\n await migrateAgent(\n tree,\n joinPathFragments(project.root, componentDir),\n component,\n nextSteps,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["getProjects","joinPathFragments","PY_AGENT_GENERATOR_INFO","addPythonDestructuredImport","applyGritQL","captureGritQLVariable","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","HOOKS_TYPE","HOOKS_MODULE","HOIST_HOOKS_PATTERN","HOOKS_LIST_CAPTURE_PATTERN","HOOKS_LIST_REWRITE_PATTERN","AGUI_CONSTRUCTOR_MATCH_PATTERN","AGUI_HOOKS_WIRED_PATTERN","AGUI_HOOKS_APPEND_PATTERN","findAgentComponents","components","filter","component","generator","id","hoistAgentHooks","tree","agentPath","read","includes","hooks","migrateAgent","agentDir","nextSteps","framework","mainPath","isAgUi","protocol","exists","push","migration","project","values","metadata","path","componentDir","endsWith","slice","length","root"],"mappings":"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"}
|
package/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport { TERRAFORM_WEBSITE_RUNTIME_CONFIG_FILE } from '../../../utils/shared-constructs-constants.js';\n\n/**\n * The Terraform `load-runtime-config` target copied\n * `dist/packages/common/terraform/runtime-config.json`, which nothing writes:\n * the vended runtime-config modules aggregate per namespace into the\n * `runtime-config` *directory*, so the website's config lands at\n * `runtime-config/connection.json`. Copying the directory path always failed\n * with ENOENT, so point it at the file the aggregation actually writes.\n *\n * Both target names are rewritten — `load-runtime-config` and the\n * `load:runtime-config` it was cloned from, which is left in place for\n * workspaces that still reference it.\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 TARGETS = ['load-runtime-config', 'load:runtime-config'];\n\n/** The path the target used to name, which never existed on disk. */\nconst BROKEN_SRC_FILE = 'dist/packages/common/terraform/runtime-config.json';\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n for (const [projectName, project] of getProjects(tree)) {\n let updated = false;\n\n for (const targetName of TARGETS) {\n const env = project.targets?.[targetName]?.options?.env;\n // Only the broken path is rewritten, so a workspace that pointed the\n // target somewhere of its own keeps it.\n if (env?.SRC_FILE !== BROKEN_SRC_FILE) {\n continue;\n }\n env.SRC_FILE = TERRAFORM_WEBSITE_RUNTIME_CONFIG_FILE;\n updated = true;\n }\n\n if (updated) {\n updateProjectConfiguration(tree, projectName, project);\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return {};\n}\n"],"names":["getProjects","updateProjectConfiguration","formatFilesInSubtree","TERRAFORM_WEBSITE_RUNTIME_CONFIG_FILE","TARGETS","BROKEN_SRC_FILE","migration","tree","projectName","project","updated","targetName","env","targets","options","SRC_FILE"],"mappings":"AAAA;;;CAGC,GACD,SACEA,WAAW,EAGXC,0BAA0B,QACrB,aAAa;AACpB,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SAASC,qCAAqC,QAAQ,gDAAgD;AAEtG;;;;;;;;;;;;;;;CAeC,GAED,MAAMC,UAAU;IAAC;IAAuB;CAAsB;AAE9D,mEAAmE,GACnE,MAAMC,kBAAkB;AAExB,eAAe,eAAeC,UAC5BC,IAAU;IAEV,KAAK,MAAM,CAACC,aAAaC,QAAQ,IAAIT,YAAYO,MAAO;QACtD,IAAIG,UAAU;QAEd,KAAK,MAAMC,cAAcP,QAAS;YAChC,MAAMQ,MAAMH,QAAQI,OAAO,EAAE,CAACF,WAAW,EAAEG,SAASF;YACpD,qEAAqE;YACrE,wCAAwC;YACxC,IAAIA,KAAKG,aAAaV,iBAAiB;gBACrC;YACF;YACAO,IAAIG,QAAQ,GAAGZ;YACfO,UAAU;QACZ;QAEA,IAAIA,SAAS;YACXT,2BAA2BM,MAAMC,aAAaC;QAChD;IACF;IAEA,MAAMP,qBAAqBK;IAE3B,OAAO,CAAC;AACV"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.82/0002-runtime-config-namespace-defaults/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type MigrationReturnObject,\n type Tree,\n visitNotIgnoredFiles,\n} from '@nx/devkit';\nimport { applyGritQL, matchGritQL } from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Provision AppConfig configuration profiles for every namespace the vended terraform modules write to\n *\n * `core/runtime-config/appconfig` creates one configuration profile per entry in\n * its `namespaces` list, and `appconfig-deployment` aggregates and deploys the\n * same list. A module contributing an entry to a namespace outside that list\n * writes its entry file to disk and nothing more — the value never reaches\n * AppConfig, so a consumer reading it back fails at runtime. The vended defaults\n * now cover `dynamodb` and `database` alongside `connection` and `agentcore`.\n *\n * Root modules that pass `namespaces` explicitly are updated too, since an\n * explicit list overrides the vended default.\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 * Guardrails:\n * - Transform source with GritQL (`applyGritQL`, `matchGritQL`), not regexes or\n * string replacements: it matches the AST, so it holds up against however the\n * user's copy has been formatted. Use `updateJson` for JSON.\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, or consider a\n * hybrid migration, rather than clobbering the user's changes.\n * - `nextSteps` is for work left for the user to do by hand, not a log of the\n * edits you made: an edit that applied cleanly needs no entry.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\n/** Namespaces the vended modules write to that the old defaults omitted. */\nconst ADDED_NAMESPACES = ['database', 'dynamodb'];\n\nconst RUNTIME_CONFIG_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/runtime-config`;\nconst APPCONFIG_FILE = `${RUNTIME_CONFIG_DIR}/appconfig/appconfig.tf`;\nconst APPCONFIG_DEPLOYMENT_FILE = `${RUNTIME_CONFIG_DIR}/appconfig-deployment/appconfig-deployment.tf`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\n/**\n * Where a `namespaces` list lives, as the block that encloses it and the\n * attribute holding the list. `clauses` narrows the block further, keeping an\n * unrelated list elsewhere in the file out of scope; `$body` is the block's body\n * in every case, so the clauses below can all be written against it.\n */\ninterface NamespaceList {\n readonly block: string;\n readonly attribute: string;\n readonly clauses?: string;\n}\n\n/** The `namespaces` variable declaration in a vended appconfig module. */\nconst VENDED_DEFAULT: NamespaceList = {\n block: 'variable \"namespaces\" { $body }',\n attribute: 'default',\n};\n\n/**\n * A call to the vended appconfig module that passes `namespaces` explicitly.\n * `$src` binds the quoted string, so the regex ends at the closing quote — that\n * anchor is what keeps the sibling `appconfig-deployment` call, which takes its\n * namespaces from this module's output, out of scope.\n */\nconst ROOT_MODULE_ARGUMENT: NamespaceList = {\n block: 'module $name { $body }',\n attribute: 'namespaces',\n clauses:\n '$body <: contains `source = $src`, $src <: r\".*runtime-config/appconfig\\\\\"\", $body <: contains `namespaces = $_`',\n};\n\n/** A GritQL pattern matching the enclosing block, plus any extra clauses. */\nconst scopePattern = (list: NamespaceList, extraClauses?: string) => {\n const clauses = [list.clauses, extraClauses].filter(Boolean).join(', ');\n return hcl(`\\`${list.block}\\`${clauses ? ` where { ${clauses} }` : ''}`);\n};\n\n/** Whether a namespace already appears in the list. */\nconst listsNamespace = (\n tree: Tree,\n filePath: string,\n list: NamespaceList,\n namespace: string,\n) =>\n matchGritQL(\n tree,\n filePath,\n scopePattern(list, `$body <: contains \\`\"${namespace}\"\\``),\n );\n\n/**\n * Append a namespace to the list. Appended within the list rather than\n * re-emitting it, so no trailing hole is introduced.\n */\nconst appendNamespace = (\n tree: Tree,\n filePath: string,\n list: NamespaceList,\n namespace: string,\n) =>\n applyGritQL(\n tree,\n filePath,\n scopePattern(\n list,\n `$body <: contains bubble \\`${list.attribute} = [$items]\\` where { $items <: [$..., $last], $last += \\`, \"${namespace}\"\\` }`,\n ),\n );\n\n/**\n * Add every missing namespace to a list, returning false when one could not be\n * added. Already-listed namespaces are skipped, which is what makes a re-run a\n * no-op.\n */\nconst addMissingNamespaces = async (\n tree: Tree,\n filePath: string,\n list: NamespaceList,\n): Promise<boolean> => {\n for (const namespace of ADDED_NAMESPACES) {\n if (await listsNamespace(tree, filePath, list, namespace)) {\n continue;\n }\n if (!(await appendNamespace(tree, filePath, list, namespace))) {\n return false;\n }\n }\n return true;\n};\n\n/** The vended module's own description, updated to document the new defaults. */\nconst DESCRIPTION_REWRITE = scopePattern(\n VENDED_DEFAULT,\n '$body <: contains bubble `description = $desc` where { $desc <: r\".*one Configuration Profile is created per namespace.*\", $desc => `\"List of runtime-config namespaces this AppConfig application should expose (one Configuration Profile is created per namespace). The default covers every namespace the generated modules write to, and a namespace with no contributions deploys an empty profile — when adding namespaces of your own, keep the defaults alongside them.\"` }',\n);\n\nconst missing = ADDED_NAMESPACES.map((n) => `\"${n}\"`).join(' and ');\n\nconst divergedNextStep = (filePath: string) =>\n `${filePath}: the \\`namespaces\\` variable has diverged from the generated shape - left untouched. Add ${missing} to its \\`default\\` list, otherwise no configuration profile is created for them and the entries the vended dynamodb/database modules contribute are never deployed.`;\n\nconst rootModuleNextStep = (filePath: string) =>\n `${filePath}: passes \\`namespaces\\` to the runtime-config appconfig module in a shape this migration could not update - left untouched. Add ${missing} to that list, or drop the argument to take the module's defaults, otherwise the entries the vended dynamodb/database modules contribute are never deployed.`;\n\n/** Bring one vended appconfig module's `namespaces` default up to date. */\nconst migrateVendedModule = async (\n tree: Tree,\n filePath: string,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(filePath)) {\n return; // This workspace has no Terraform runtime config.\n }\n\n if (!(await addMissingNamespaces(tree, filePath, VENDED_DEFAULT))) {\n nextSteps.push(divergedNextStep(filePath));\n }\n};\n\n/**\n * Root modules that pass `namespaces` explicitly override the vended default, so\n * they need the same additions.\n */\nconst migrateRootModules = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n const terraformFiles: string[] = [];\n visitNotIgnoredFiles(tree, '', (filePath) => {\n if (filePath.endsWith('.tf') && !filePath.startsWith(RUNTIME_CONFIG_DIR)) {\n terraformFiles.push(filePath);\n }\n });\n\n for (const filePath of terraformFiles) {\n if (\n !(await matchGritQL(tree, filePath, scopePattern(ROOT_MODULE_ARGUMENT)))\n ) {\n continue; // Takes the module's defaults, so nothing to update.\n }\n\n if (!(await addMissingNamespaces(tree, filePath, ROOT_MODULE_ARGUMENT))) {\n nextSteps.push(rootModuleNextStep(filePath));\n }\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n await migrateVendedModule(tree, APPCONFIG_FILE, nextSteps);\n await migrateVendedModule(tree, APPCONFIG_DEPLOYMENT_FILE, nextSteps);\n\n if (tree.exists(APPCONFIG_FILE)) {\n await applyGritQL(tree, APPCONFIG_FILE, DESCRIPTION_REWRITE);\n }\n\n await migrateRootModules(tree, nextSteps);\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["visitNotIgnoredFiles","applyGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","ADDED_NAMESPACES","RUNTIME_CONFIG_DIR","APPCONFIG_FILE","APPCONFIG_DEPLOYMENT_FILE","hcl","pattern","VENDED_DEFAULT","block","attribute","ROOT_MODULE_ARGUMENT","clauses","scopePattern","list","extraClauses","filter","Boolean","join","listsNamespace","tree","filePath","namespace","appendNamespace","addMissingNamespaces","DESCRIPTION_REWRITE","missing","map","n","divergedNextStep","rootModuleNextStep","migrateVendedModule","nextSteps","exists","push","migrateRootModules","terraformFiles","endsWith","startsWith","migration"],"mappings":"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"}
|
package/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/migration.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 MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport {\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Re-export the core dynamodb module's `table_name`, `table_arn` and\n * `kms_key_arn` from each vended per-table Terraform app module\n * (`app/dynamodb/<name>/<name>.tf`).\n *\n * The app module is what a root configuration instantiates, so without these\n * outputs a root module cannot reference the table or its encryption key at\n * all — for example to grant a consumer access to it.\n *\n * These files are generated with `KeepExisting`, so an upgraded workspace keeps\n * a module with no outputs until this runs. Diverged files are left untouched\n * and reported via `nextSteps`.\n */\n\nconst TERRAFORM_DYNAMODB_APP_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/dynamodb`;\n\nconst divergedMessage = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To reference the table from your root Terraform configuration, add table_name, table_arn and kms_key_arn outputs here forwarding the equivalent module.dynamodb_table attributes (see the ts#dynamodb generator's dynamodb app template).`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\nconst OUTPUTS_TEXT = [\n 'output \"table_name\" {',\n ' description = \"Name of the DynamoDB table\"',\n ' value = module.dynamodb_table.table_name',\n '}',\n '',\n 'output \"table_arn\" {',\n ' description = \"ARN of the DynamoDB table, for use in IAM policies granting access to the table. Suffix with /index/* to cover its global secondary indexes.\"',\n ' value = module.dynamodb_table.table_arn',\n '}',\n '',\n 'output \"kms_key_arn\" {',\n ' description = \"ARN of the KMS key used to encrypt the DynamoDB table, for use in IAM policies granting access to the table. Null when using the AWS owned key (encryption = DEFAULT).\"',\n ' value = module.dynamodb_table.kms_key_arn',\n '}',\n].join('\\n');\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(TERRAFORM_DYNAMODB_APP_DIR)) {\n return { nextSteps }; // This workspace has no Terraform dynamodb modules.\n }\n\n for (const dirName of tree.children(TERRAFORM_DYNAMODB_APP_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_DYNAMODB_APP_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a dynamodb app module directory.\n }\n\n if (await matchGritQL(tree, filePath, hcl('`output \"table_arn\" { $_ }`'))) {\n continue; // Already migrated.\n }\n\n const RUNTIME_CONFIG_MODULE_BLOCK = hcl(\n '`module \"add_to_runtime_config\" { $_ }`',\n );\n if (!(await matchGritQL(tree, filePath, RUNTIME_CONFIG_MODULE_BLOCK))) {\n nextSteps.push(divergedMessage(filePath));\n continue;\n }\n\n // Appended after the runtime config module call, which the template ends\n // with, matching where the generator now writes the outputs.\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`module \"add_to_runtime_config\" { $body }\\` => \\`module \"add_to_runtime_config\" {\\n $body\\n}\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n OUTPUTS_TEXT,\n );\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TERRAFORM_DYNAMODB_APP_DIR","divergedMessage","filePath","hcl","pattern","OUTPUTS_TEXT","join","migration","tree","nextSteps","exists","dirName","children","RUNTIME_CONFIG_MODULE_BLOCK","push"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SACEC,uBAAuB,EACvBC,eAAe,EACfC,WAAW,QACN,wBAAwB;AAC/B,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,oBAAoB,QACf,gDAAgD;AAEvD;;;;;;;;;;;;CAYC,GAED,MAAMC,6BAA6B,GAAGF,aAAa,CAAC,EAAEC,qBAAqB,iBAAiB,CAAC;AAE7F,MAAME,kBAAkB,CAACC,WACvB,GAAGA,SAAS,mSAAmS,CAAC;AAElT,MAAMC,MAAM,CAACC,UAAoB,CAAC,cAAc,EAAEA,SAAS;AAE3D,MAAMC,eAAe;IACnB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAACC,IAAI,CAAC;AAEP,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,IAAI,CAACD,KAAKE,MAAM,CAACV,6BAA6B;QAC5C,OAAO;YAAES;QAAU,GAAG,oDAAoD;IAC5E;IAEA,KAAK,MAAME,WAAWH,KAAKI,QAAQ,CAACZ,4BAA6B;QAC/D,MAAME,WAAWT,kBACfO,4BACAW,SACA,GAAGA,QAAQ,GAAG,CAAC;QAEjB,IAAI,CAACH,KAAKE,MAAM,CAACR,WAAW;YAC1B,UAAU,uCAAuC;QACnD;QAEA,IAAI,MAAMN,YAAYY,MAAMN,UAAUC,IAAI,iCAAiC;YACzE,UAAU,oBAAoB;QAChC;QAEA,MAAMU,8BAA8BV,IAClC;QAEF,IAAI,CAAE,MAAMP,YAAYY,MAAMN,UAAUW,8BAA+B;YACrEJ,UAAUK,IAAI,CAACb,gBAAgBC;YAC/B;QACF;QAEA,yEAAyE;QACzE,6DAA6D;QAC7D,MAAMP,gBACJa,MACAN,UACAC,IACE,CAAC,kGAAkG,EAAET,wBAAwB,EAAE,CAAC,GAElIW;IAEJ;IAEA,MAAMR,qBAAqBW;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
|
package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MigrationReturnObject, Tree } from '@nx/devkit';\nimport { applyGritQL, matchGritQL } from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Bring existing workspaces up to the destroy protection the generators now\n * vend on the data-bearing Terraform resources:\n *\n * - The DynamoDB table and the Aurora cluster gain\n * `lifecycle { prevent_destroy = true }`. This is a second, independent layer\n * on top of the service-side `deletion_protection_enabled` /\n * `deletion_protection` flags, matching the two guards the CDK constructs\n * already have (`deletionProtection` plus `RemovalPolicy.RETAIN`). Without it,\n * clearing the service-side flag destroys the data in the same apply.\n * `prevent_destroy` cannot reference a variable, so it is vended as a literal\n * and removed to tear down.\n * - Their KMS keys pin `deletion_window_in_days = 30`, the provider maximum and\n * the CDK default. Restoring either resource from a point-in-time backup or\n * snapshot requires its key, so the window in which a scheduled key deletion\n * can still be cancelled matters as much as the backup itself.\n *\n * These files are generated with `KeepExisting`, so without this an upgraded\n * workspace keeps the weaker single-layer protection. Diverged files are left\n * untouched and reported via `nextSteps`.\n */\n\nconst TERRAFORM_DYNAMODB_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/dynamodb/dynamodb.tf`;\nconst TERRAFORM_AURORA_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/rdb/aurora/aurora.tf`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\nconst divergedMessage = (filePath: string, resource: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To protect the ${resource} from being destroyed when the service-side deletion protection flag is cleared, add \\`lifecycle { prevent_destroy = true }\\` to it, and set \\`deletion_window_in_days = 30\\` on its KMS key.`;\n\n/**\n * Add the maximum KMS pending window to a key that omits it, so a scheduled\n * deletion stays cancellable for as long as the provider allows.\n */\nconst migrateKmsKey = async (\n tree: Tree,\n filePath: string,\n keyLabel: string,\n): Promise<void> => {\n const keyBlock = hcl(`\\`resource \"aws_kms_key\" \"${keyLabel}\" { $props }\\``);\n\n // Already pinned - nothing to do.\n if (\n await matchGritQL(\n tree,\n filePath,\n hcl(\n `\\`resource \"aws_kms_key\" \"${keyLabel}\" { $props }\\` where { $props <: contains \\`deletion_window_in_days = $_\\` }`,\n ),\n )\n ) {\n return;\n }\n\n if (!(await matchGritQL(tree, filePath, keyBlock))) {\n return; // Reported by the caller, which matches the data resource too.\n }\n\n await applyGritQL(\n tree,\n filePath,\n hcl(\n `\\`resource \"aws_kms_key\" \"${keyLabel}\" { $props }\\` where { $props <: contains \\`enable_key_rotation = $rotation\\` => \\`enable_key_rotation = $rotation\\n\\n deletion_window_in_days = 30\\` }`,\n ),\n );\n};\n\n/**\n * Add `lifecycle { prevent_destroy = true }` to a data-bearing resource.\n */\nconst migrateDataResource = async (\n tree: Tree,\n filePath: string,\n resourceType: string,\n resourceLabel: string,\n nextSteps: string[],\n description: string,\n): Promise<boolean> => {\n const resourceBlock = hcl(\n `\\`resource \"${resourceType}\" \"${resourceLabel}\" { $props }\\``,\n );\n\n if (!(await matchGritQL(tree, filePath, resourceBlock))) {\n nextSteps.push(divergedMessage(filePath, description));\n return false;\n }\n\n // Already guarded - keep the user's block, whatever it is set to.\n if (\n await matchGritQL(\n tree,\n filePath,\n hcl(\n `\\`resource \"${resourceType}\" \"${resourceLabel}\" { $props }\\` where { $props <: contains \\`prevent_destroy = $_\\` }`,\n ),\n )\n ) {\n return true;\n }\n\n await applyGritQL(\n tree,\n filePath,\n hcl(\n `\\`resource \"${resourceType}\" \"${resourceLabel}\" { $props }\\` => \\`resource \"${resourceType}\" \"${resourceLabel}\" {\\n $props\\n\\n lifecycle {\\n prevent_destroy = true\\n }\\n}\\``,\n ),\n );\n\n return true;\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (tree.exists(TERRAFORM_DYNAMODB_FILE)) {\n if (\n await migrateDataResource(\n tree,\n TERRAFORM_DYNAMODB_FILE,\n 'aws_dynamodb_table',\n 'table',\n nextSteps,\n 'DynamoDB table',\n )\n ) {\n await migrateKmsKey(tree, TERRAFORM_DYNAMODB_FILE, 'table');\n }\n }\n\n if (tree.exists(TERRAFORM_AURORA_FILE)) {\n if (\n await migrateDataResource(\n tree,\n TERRAFORM_AURORA_FILE,\n 'aws_rds_cluster',\n 'database',\n nextSteps,\n 'Aurora cluster',\n )\n ) {\n await migrateKmsKey(tree, TERRAFORM_AURORA_FILE, 'database');\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["applyGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TERRAFORM_DYNAMODB_FILE","TERRAFORM_AURORA_FILE","hcl","pattern","divergedMessage","filePath","resource","migrateKmsKey","tree","keyLabel","keyBlock","migrateDataResource","resourceType","resourceLabel","nextSteps","description","resourceBlock","push","migration","exists"],"mappings":"AAAA;;;CAGC,GAED,SAASA,WAAW,EAAEC,WAAW,QAAQ,wBAAwB;AACjE,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,oBAAoB,QACf,gDAAgD;AAEvD;;;;;;;;;;;;;;;;;;;;CAoBC,GAED,MAAMC,0BAA0B,GAAGF,aAAa,CAAC,EAAEC,qBAAqB,8BAA8B,CAAC;AACvG,MAAME,wBAAwB,GAAGH,aAAa,CAAC,EAAEC,qBAAqB,8BAA8B,CAAC;AAErG,MAAMG,MAAM,CAACC,UAAoB,CAAC,cAAc,EAAEA,SAAS;AAE3D,MAAMC,kBAAkB,CAACC,UAAkBC,WACzC,GAAGD,SAAS,yEAAyE,EAAEC,SAAS,6LAA6L,CAAC;AAEhS;;;CAGC,GACD,MAAMC,gBAAgB,OACpBC,MACAH,UACAI;IAEA,MAAMC,WAAWR,IAAI,CAAC,0BAA0B,EAAEO,SAAS,cAAc,CAAC;IAE1E,kCAAkC;IAClC,IACE,MAAMb,YACJY,MACAH,UACAH,IACE,CAAC,0BAA0B,EAAEO,SAAS,4EAA4E,CAAC,IAGvH;QACA;IACF;IAEA,IAAI,CAAE,MAAMb,YAAYY,MAAMH,UAAUK,WAAY;QAClD,QAAQ,+DAA+D;IACzE;IAEA,MAAMf,YACJa,MACAH,UACAH,IACE,CAAC,0BAA0B,EAAEO,SAAS,wJAAwJ,CAAC;AAGrM;AAEA;;CAEC,GACD,MAAME,sBAAsB,OAC1BH,MACAH,UACAO,cACAC,eACAC,WACAC;IAEA,MAAMC,gBAAgBd,IACpB,CAAC,YAAY,EAAEU,aAAa,GAAG,EAAEC,cAAc,cAAc,CAAC;IAGhE,IAAI,CAAE,MAAMjB,YAAYY,MAAMH,UAAUW,gBAAiB;QACvDF,UAAUG,IAAI,CAACb,gBAAgBC,UAAUU;QACzC,OAAO;IACT;IAEA,kEAAkE;IAClE,IACE,MAAMnB,YACJY,MACAH,UACAH,IACE,CAAC,YAAY,EAAEU,aAAa,GAAG,EAAEC,cAAc,oEAAoE,CAAC,IAGxH;QACA,OAAO;IACT;IAEA,MAAMlB,YACJa,MACAH,UACAH,IACE,CAAC,YAAY,EAAEU,aAAa,GAAG,EAAEC,cAAc,8BAA8B,EAAED,aAAa,GAAG,EAAEC,cAAc,oEAAoE,CAAC;IAIxL,OAAO;AACT;AAEA,eAAe,eAAeK,UAC5BV,IAAU;IAEV,MAAMM,YAAsB,EAAE;IAE9B,IAAIN,KAAKW,MAAM,CAACnB,0BAA0B;QACxC,IACE,MAAMW,oBACJH,MACAR,yBACA,sBACA,SACAc,WACA,mBAEF;YACA,MAAMP,cAAcC,MAAMR,yBAAyB;QACrD;IACF;IAEA,IAAIQ,KAAKW,MAAM,CAAClB,wBAAwB;QACtC,IACE,MAAMU,oBACJH,MACAP,uBACA,mBACA,YACAa,WACA,mBAEF;YACA,MAAMP,cAAcC,MAAMP,uBAAuB;QACnD;IACF;IAEA,MAAMJ,qBAAqBW;IAE3B,OAAO;QAAEM;IAAU;AACrB"}
|
package/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { AGENTCORE_GATEWAY_GENERATOR_INFO } from '../../../agentcore-gateway/generator.js';\nimport { addDependenciesToPackageJson } from '../../../utils/dependencies.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\nimport { TS_VERSIONS } from '../../../utils/versions.js';\n\n/**\n * Declare `ejs` at the workspace root for Terraform gateways with Cedar\n * policies, which is where `render-cedar.cjs` resolves it from: the script runs\n * in the shared terraform project, which has no `package.json` of its own.\n */\n\nconst RENDER_SCRIPT = 'render-cedar.cjs';\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const gatewaysDir = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'app',\n 'gateways',\n );\n let needsRootEjs = false;\n\n for (const project of getProjects(tree).values()) {\n const metadata = project.metadata as\n | { generator?: string; name?: string }\n | undefined;\n if (metadata?.generator !== AGENTCORE_GATEWAY_GENERATOR_INFO.id) {\n continue;\n }\n\n // Only a Terraform gateway with Cedar policies vends the render script; a\n // CDK one resolves ejs from the shared constructs project instead.\n if (\n tree.exists(\n joinPathFragments(gatewaysDir, metadata.name ?? '', RENDER_SCRIPT),\n )\n ) {\n needsRootEjs = true;\n }\n }\n\n if (needsRootEjs) {\n addDependenciesToPackageJson(tree, {}, { ejs: TS_VERSIONS.ejs });\n }\n\n await formatFilesInSubtree(tree);\n\n return {};\n}\n"],"names":["getProjects","joinPathFragments","AGENTCORE_GATEWAY_GENERATOR_INFO","addDependenciesToPackageJson","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TS_VERSIONS","RENDER_SCRIPT","migration","tree","gatewaysDir","needsRootEjs","project","values","metadata","generator","id","exists","name","ejs"],"mappings":"AAAA;;;CAGC,GACD,SACEA,WAAW,EACXC,iBAAiB,QAGZ,aAAa;AACpB,SAASC,gCAAgC,QAAQ,0CAA0C;AAC3F,SAASC,4BAA4B,QAAQ,iCAAiC;AAC9E,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,oBAAoB,QACf,gDAAgD;AACvD,SAASC,WAAW,QAAQ,6BAA6B;AAEzD;;;;CAIC,GAED,MAAMC,gBAAgB;AAEtB,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,cAAcV,kBAClBI,cACAC,sBACA,OACA,OACA;IAEF,IAAIM,eAAe;IAEnB,KAAK,MAAMC,WAAWb,YAAYU,MAAMI,MAAM,GAAI;QAChD,MAAMC,WAAWF,QAAQE,QAAQ;QAGjC,IAAIA,UAAUC,cAAcd,iCAAiCe,EAAE,EAAE;YAC/D;QACF;QAEA,0EAA0E;QAC1E,mEAAmE;QACnE,IACEP,KAAKQ,MAAM,CACTjB,kBAAkBU,aAAaI,SAASI,IAAI,IAAI,IAAIX,iBAEtD;YACAI,eAAe;QACjB;IACF;IAEA,IAAIA,cAAc;QAChBT,6BAA6BO,MAAM,CAAC,GAAG;YAAEU,KAAKb,YAAYa,GAAG;QAAC;IAChE;IAEA,MAAMhB,qBAAqBM;IAE3B,OAAO,CAAC;AACV"}
|
package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/migration.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 MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport {\n applyGritQL,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Align each vended per-database Terraform app module\n * (`app/dbs/<name>/<name>.tf`) with the core Aurora module it wraps.\n *\n * The app module is what a root configuration instantiates and it forwards its\n * own value down, so its default is the one that takes effect:\n *\n * - `enable_cloudwatch_logs` defaulted to `false` against the core module's\n * `true`, so Aurora exported no engine logs and the cluster parameter group\n * that configures them (`log_statement=ddl` on PostgreSQL,\n * `server_audit_events=CONNECT,QUERY_DDL` on MySQL) was never created —\n * the CDK construct enabled both.\n * - `enable_key_rotation` was never declared, leaving the core module's\n * variable unreachable from a root configuration.\n *\n * These files are generated with `KeepExisting`, so an upgraded workspace keeps\n * the diverged defaults until this runs. Diverged files are left untouched and\n * reported via `nextSteps`.\n */\n\nconst TERRAFORM_DBS_APP_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/dbs`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\nconst KEY_ROTATION_VARIABLE = [\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether to enable automatic key rotation on the KMS key used to encrypt the Aurora cluster and its credentials secret.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst divergedMessage = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To export Aurora engine logs to CloudWatch as the CDK construct and the core Terraform module do, default \\`enable_cloudwatch_logs\\` to true here, and declare an \\`enable_key_rotation\\` variable (bool, default true) forwarded to the aurora module (see the ts#rdb generator's dbs app template).`;\n\nconst OLD_LOGS_DESCRIPTION =\n 'Whether to export Aurora engine logs to CloudWatch. PostgreSQL also enables verbose statement logging.';\nconst NEW_LOGS_DESCRIPTION =\n '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.';\n\n/**\n * Flip the app module's `enable_cloudwatch_logs` default to match the core\n * module, and replace the description that documented the old behaviour.\n *\n * Keyed on that description, which is the only thing distinguishing the vended\n * `default = false` from a user who deliberately turned log export off.\n */\nconst migrateCloudwatchLogsDefault = (tree: Tree, filePath: string) =>\n applyGritQL(\n tree,\n filePath,\n hcl(\n `\\`variable \"enable_cloudwatch_logs\" { $props }\\` where {` +\n ` $props <: contains \\`description = \"${OLD_LOGS_DESCRIPTION}\"\\` => \\`description = \"${NEW_LOGS_DESCRIPTION}\"\\`,` +\n ` $props <: contains \\`default = false\\` => \\`default = true\\`` +\n ` }`,\n ),\n );\n\n/**\n * Declare `enable_key_rotation` and forward it to the core aurora module, so a\n * root configuration can reach the variable the core module already exposes.\n */\nconst migrateKeyRotationVariable = async (\n tree: Tree,\n filePath: string,\n): Promise<void> => {\n if (\n await matchGritQL(\n tree,\n filePath,\n hcl('`variable \"enable_key_rotation\" { $_ }`'),\n )\n ) {\n return; // Already declared.\n }\n\n const declared = await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`variable \"enable_backup\" { $props }\\` => \\`variable \"enable_backup\" {\\n $props\\n}\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n KEY_ROTATION_VARIABLE,\n );\n if (!declared) return;\n\n // Appended after the existing arguments so their alignment is preserved;\n // `terraform fmt` re-aligns the block.\n await applyGritQL(\n tree,\n filePath,\n hcl(\n `\\`module \"aurora\" { $props }\\` => \\`module \"aurora\" {\\n $props\\n\\n enable_key_rotation = var.enable_key_rotation\\n}\\``,\n ),\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(TERRAFORM_DBS_APP_DIR)) {\n return { nextSteps }; // This workspace has no Terraform database modules.\n }\n\n for (const dirName of tree.children(TERRAFORM_DBS_APP_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_DBS_APP_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a database app module directory.\n }\n\n // Both edits hang off the shape the generator produces: the two variables\n // and the module call that forwards them.\n const recognised =\n (await matchGritQL(\n tree,\n filePath,\n hcl('`variable \"enable_cloudwatch_logs\" { $_ }`'),\n )) &&\n (await matchGritQL(\n tree,\n filePath,\n hcl('`variable \"enable_backup\" { $_ }`'),\n )) &&\n (await matchGritQL(\n tree,\n filePath,\n hcl(\n '`module \"aurora\" { $props }` where { $props <: contains `enable_cloudwatch_logs = var.enable_cloudwatch_logs` }',\n ),\n ));\n\n if (!recognised) {\n nextSteps.push(divergedMessage(filePath));\n continue;\n }\n\n await migrateCloudwatchLogsDefault(tree, filePath);\n await migrateKeyRotationVariable(tree, filePath);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","applyGritQL","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TERRAFORM_DBS_APP_DIR","hcl","pattern","KEY_ROTATION_VARIABLE","join","divergedMessage","filePath","OLD_LOGS_DESCRIPTION","NEW_LOGS_DESCRIPTION","migrateCloudwatchLogsDefault","tree","migrateKeyRotationVariable","declared","migration","nextSteps","exists","dirName","children","recognised","push"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SACEC,WAAW,EACXC,uBAAuB,EACvBC,eAAe,EACfC,WAAW,QACN,wBAAwB;AAC/B,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,oBAAoB,QACf,gDAAgD;AAEvD;;;;;;;;;;;;;;;;;;CAkBC,GAED,MAAMC,wBAAwB,GAAGF,aAAa,CAAC,EAAEC,qBAAqB,YAAY,CAAC;AAEnF,MAAME,MAAM,CAACC,UAAoB,CAAC,cAAc,EAAEA,SAAS;AAE3D,MAAMC,wBAAwB;IAC5B;IACA;IACA;IACA;IACA;CACD,CAACC,IAAI,CAAC;AAEP,MAAMC,kBAAkB,CAACC,WACvB,GAAGA,SAAS,+VAA+V,CAAC;AAE9W,MAAMC,uBACJ;AACF,MAAMC,uBACJ;AAEF;;;;;;CAMC,GACD,MAAMC,+BAA+B,CAACC,MAAYJ,WAChDb,YACEiB,MACAJ,UACAL,IACE,CAAC,wDAAwD,CAAC,GACxD,CAAC,qCAAqC,EAAEM,qBAAqB,wBAAwB,EAAEC,qBAAqB,IAAI,CAAC,GACjH,CAAC,6DAA6D,CAAC,GAC/D,CAAC,EAAE,CAAC;AAIZ;;;CAGC,GACD,MAAMG,6BAA6B,OACjCD,MACAJ;IAEA,IACE,MAAMV,YACJc,MACAJ,UACAL,IAAI,6CAEN;QACA,QAAQ,oBAAoB;IAC9B;IAEA,MAAMW,WAAW,MAAMjB,gBACrBe,MACAJ,UACAL,IACE,CAAC,wFAAwF,EAAEP,wBAAwB,EAAE,CAAC,GAExHS;IAEF,IAAI,CAACS,UAAU;IAEf,yEAAyE;IACzE,uCAAuC;IACvC,MAAMnB,YACJiB,MACAJ,UACAL,IACE,CAAC,uHAAuH,CAAC;AAG/H;AAEA,eAAe,eAAeY,UAC5BH,IAAU;IAEV,MAAMI,YAAsB,EAAE;IAE9B,IAAI,CAACJ,KAAKK,MAAM,CAACf,wBAAwB;QACvC,OAAO;YAAEc;QAAU,GAAG,oDAAoD;IAC5E;IAEA,KAAK,MAAME,WAAWN,KAAKO,QAAQ,CAACjB,uBAAwB;QAC1D,MAAMM,WAAWd,kBACfQ,uBACAgB,SACA,GAAGA,QAAQ,GAAG,CAAC;QAEjB,IAAI,CAACN,KAAKK,MAAM,CAACT,WAAW;YAC1B,UAAU,uCAAuC;QACnD;QAEA,0EAA0E;QAC1E,0CAA0C;QAC1C,MAAMY,aACJ,AAAC,MAAMtB,YACLc,MACAJ,UACAL,IAAI,kDAEL,MAAML,YACLc,MACAJ,UACAL,IAAI,yCAEL,MAAML,YACLc,MACAJ,UACAL,IACE;QAIN,IAAI,CAACiB,YAAY;YACfJ,UAAUK,IAAI,CAACd,gBAAgBC;YAC/B;QACF;QAEA,MAAMG,6BAA6BC,MAAMJ;QACzC,MAAMK,2BAA2BD,MAAMJ;IACzC;IAEA,MAAMT,qBAAqBa;IAE3B,OAAO;QAAEI;IAAU;AACrB"}
|
package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/migration.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/migration.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 MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport {\n applyGritQL,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Pin each vended Harness to no tools by default.\n *\n * AgentCore treats an absent `allowedTools` as every tool, so a Harness that\n * never set it deployed with `allowedTools: [\"*\"]` — shell execution and file\n * read/write included. Both providers now send the field explicitly, defaulting\n * to none.\n *\n * These files are generated with `KeepExisting`, so an upgraded workspace keeps\n * the implicit every-tool default until this runs. Diverged files are left\n * untouched and reported via `nextSteps`.\n */\n\nconst CDK_HARNESSES_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/harnesses`;\nconst TERRAFORM_HARNESSES_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/harnesses`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\nconst ALLOWED_TOOLS_VARIABLE = [\n 'variable \"allowed_tools\" {',\n ' description = \"Tools the Harness may use. Defaults to none; set [\\\\\"@builtin\\\\\"] for every built-in tool, or name individual tools. Always sent explicitly, since the service treats an absent value as every tool.\"',\n ' type = list(string)',\n ' default = []',\n '}',\n].join('\\n');\n\nconst divergedMessage = (filePath: string, remedy: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. AgentCore treats an absent allowedTools as every tool, so this Harness deploys with all built-in tools (including shell and file access). ${remedy}`;\n\n/**\n * Default the construct's `allowedTools` to none and pass it to the resource\n * after the props spread, so an undefined caller value cannot reinstate the\n * service's every-tool default.\n */\nconst migrateCdkConstruct = async (\n tree: Tree,\n filePath: string,\n): Promise<boolean> => {\n const destructured = await insertViaGritQL(\n tree,\n filePath,\n `\\`const { $fields } = props ?? {};\\` => \\`const {\\n ${GRIT_INSERT_PLACEHOLDER}\\n $fields\\n } = props ?? {};\\``,\n [\n '// The service treats an absent allowedTools as every tool, so this',\n '// defaults to none and is always passed explicitly.',\n 'allowedTools = [],',\n ].join('\\n '),\n );\n if (!destructured) return false;\n\n return await applyGritQL(\n tree,\n filePath,\n '`new agentcore.CfnHarness($scope, $id, { $props })` where {' +\n ' $props <: contains spread_element() as $spread,' +\n ' $spread => `$spread,\\n allowedTools`' +\n ' }',\n );\n};\n\n/**\n * Declare `allowed_tools` and assign it to the Harness resource, so the module\n * always sends the field.\n */\nconst migrateTerraformModule = async (\n tree: Tree,\n filePath: string,\n): Promise<boolean> => {\n const declared = await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`variable \"model_resource_arns\" { $props }\\` => \\`${GRIT_INSERT_PLACEHOLDER}\\n\\nvariable \"model_resource_arns\" {\\n $props\\n}\\``,\n ),\n ALLOWED_TOOLS_VARIABLE,\n );\n if (!declared) return false;\n\n // Appended after the existing arguments so their alignment is preserved;\n // `terraform fmt` re-aligns the block.\n return await applyGritQL(\n tree,\n filePath,\n hcl(\n '`execution_role_arn = aws_iam_role.execution_role.arn` => `execution_role_arn = aws_iam_role.execution_role.arn\\n allowed_tools = var.allowed_tools`',\n ),\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const dirName of tree.exists(CDK_HARNESSES_DIR)\n ? tree.children(CDK_HARNESSES_DIR)\n : []) {\n const filePath = joinPathFragments(\n CDK_HARNESSES_DIR,\n dirName,\n `${dirName}.ts`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a Harness construct directory.\n }\n\n // Already pinned, by this migration or by hand.\n if (tree.read(filePath, 'utf-8')?.includes('allowedTools = [],')) {\n continue;\n }\n\n if (!(await migrateCdkConstruct(tree, filePath))) {\n nextSteps.push(\n divergedMessage(\n filePath,\n 'Default `allowedTools` to `[]` when destructuring props, and pass `allowedTools` to the CfnHarness after the `...harnessProps` spread.',\n ),\n );\n }\n }\n\n for (const dirName of tree.exists(TERRAFORM_HARNESSES_DIR)\n ? tree.children(TERRAFORM_HARNESSES_DIR)\n : []) {\n const filePath = joinPathFragments(\n TERRAFORM_HARNESSES_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a Harness module directory.\n }\n\n // Already assigned, by this migration or by hand.\n if (await matchGritQL(tree, filePath, hcl('`allowed_tools = $_`'))) {\n continue;\n }\n\n if (!(await migrateTerraformModule(tree, filePath))) {\n nextSteps.push(\n divergedMessage(\n filePath,\n 'Declare an `allowed_tools` variable (list(string), default []) and assign `allowed_tools = var.allowed_tools` on the `aws_bedrockagentcore_harness` resource.',\n ),\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","applyGritQL","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_HARNESSES_DIR","TERRAFORM_HARNESSES_DIR","hcl","pattern","ALLOWED_TOOLS_VARIABLE","join","divergedMessage","filePath","remedy","migrateCdkConstruct","tree","destructured","migrateTerraformModule","declared","migration","nextSteps","dirName","exists","children","read","includes","push"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SACEC,WAAW,EACXC,uBAAuB,EACvBC,eAAe,EACfC,WAAW,QACN,wBAAwB;AAC/B,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,qBAAqB,EACrBC,oBAAoB,QACf,gDAAgD;AAEvD;;;;;;;;;;;CAWC,GAED,MAAMC,oBAAoB,GAAGH,aAAa,CAAC,EAAEC,sBAAsB,kBAAkB,CAAC;AACtF,MAAMG,0BAA0B,GAAGJ,aAAa,CAAC,EAAEE,qBAAqB,kBAAkB,CAAC;AAE3F,MAAMG,MAAM,CAACC,UAAoB,CAAC,cAAc,EAAEA,SAAS;AAE3D,MAAMC,yBAAyB;IAC7B;IACA;IACA;IACA;IACA;CACD,CAACC,IAAI,CAAC;AAEP,MAAMC,kBAAkB,CAACC,UAAkBC,SACzC,GAAGD,SAAS,oMAAoM,EAAEC,QAAQ;AAE5N;;;;CAIC,GACD,MAAMC,sBAAsB,OAC1BC,MACAH;IAEA,MAAMI,eAAe,MAAMjB,gBACzBgB,MACAH,UACA,CAAC,yDAAyD,EAAEd,wBAAwB,uCAAuC,CAAC,EAC5H;QACE;QACA;QACA;KACD,CAACY,IAAI,CAAC;IAET,IAAI,CAACM,cAAc,OAAO;IAE1B,OAAO,MAAMnB,YACXkB,MACAH,UACA,gEACE,qDACA,+CACA;AAEN;AAEA;;;CAGC,GACD,MAAMK,yBAAyB,OAC7BF,MACAH;IAEA,MAAMM,WAAW,MAAMnB,gBACrBgB,MACAH,UACAL,IACE,CAAC,mDAAmD,EAAET,wBAAwB,mDAAmD,CAAC,GAEpIW;IAEF,IAAI,CAACS,UAAU,OAAO;IAEtB,yEAAyE;IACzE,uCAAuC;IACvC,OAAO,MAAMrB,YACXkB,MACAH,UACAL,IACE;AAGN;AAEA,eAAe,eAAeY,UAC5BJ,IAAU;IAEV,MAAMK,YAAsB,EAAE;IAE9B,KAAK,MAAMC,WAAWN,KAAKO,MAAM,CAACjB,qBAC9BU,KAAKQ,QAAQ,CAAClB,qBACd,EAAE,CAAE;QACN,MAAMO,WAAWhB,kBACfS,mBACAgB,SACA,GAAGA,QAAQ,GAAG,CAAC;QAEjB,IAAI,CAACN,KAAKO,MAAM,CAACV,WAAW;YAC1B,UAAU,qCAAqC;QACjD;QAEA,gDAAgD;QAChD,IAAIG,KAAKS,IAAI,CAACZ,UAAU,UAAUa,SAAS,uBAAuB;YAChE;QACF;QAEA,IAAI,CAAE,MAAMX,oBAAoBC,MAAMH,WAAY;YAChDQ,UAAUM,IAAI,CACZf,gBACEC,UACA;QAGN;IACF;IAEA,KAAK,MAAMS,WAAWN,KAAKO,MAAM,CAAChB,2BAC9BS,KAAKQ,QAAQ,CAACjB,2BACd,EAAE,CAAE;QACN,MAAMM,WAAWhB,kBACfU,yBACAe,SACA,GAAGA,QAAQ,GAAG,CAAC;QAEjB,IAAI,CAACN,KAAKO,MAAM,CAACV,WAAW;YAC1B,UAAU,kCAAkC;QAC9C;QAEA,kDAAkD;QAClD,IAAI,MAAMZ,YAAYe,MAAMH,UAAUL,IAAI,0BAA0B;YAClE;QACF;QAEA,IAAI,CAAE,MAAMU,uBAAuBF,MAAMH,WAAY;YACnDQ,UAAUM,IAAI,CACZf,gBACEC,UACA;QAGN;IACF;IAEA,MAAMX,qBAAqBc;IAE3B,OAAO;QAAEK;IAAU;AACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.84/0003-terraform-declare-used-providers/migration.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 MigrationReturnObject,\n type Tree,\n visitNotIgnoredFiles,\n} from '@nx/devkit';\nimport {\n captureAllGritQL,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\nimport { TERRAFORM_VERSIONS } from '../../../utils/versions.js';\n\n/**\n * Declare every provider each vended Terraform module uses in its own\n * `required_providers`.\n *\n * A provider a module uses but never declares is resolved by implicit\n * inheritance from the root module, unpinned — so `terraform init` on that\n * module takes whatever the registry serves, bypassing the version pinning the\n * generators vend. Several vended modules referenced `random`, `archive`,\n * `null`, `external` and `aws` without declaring them.\n *\n * These files are generated with `KeepExisting`, so an upgraded workspace keeps\n * the unpinned shape until this runs. Each missing provider is added to the\n * module's existing `required_providers`; a module with no `terraform` block at\n * all is reported via `nextSteps` rather than having one inserted, since where\n * it belongs depends on how the user has arranged the file.\n */\n\nconst TERRAFORM_SRC = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n);\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\n/** Providers built in to Terraform, which have no entry and no version. */\nconst BUILTIN_TYPES = new Set(['terraform_data', 'terraform_remote_state']);\n\n/**\n * The provider a resource or data source type resolves to. Terraform derives\n * this from the type's first underscore-delimited word; a type it can't\n * attribute to a provider the plugin vends is not this migration's to touch.\n */\nconst providerOf = (type: string): string | undefined => {\n // `null_resource` and `external` are the whole type, not a prefixed one.\n if (type === 'null_resource') {\n return 'null';\n }\n if (type === 'external') {\n return 'external';\n }\n const [prefix] = type.split('_');\n return prefix in TERRAFORM_VERSIONS ? prefix : undefined;\n};\n\n/**\n * The providers a module requires, read from its `resource` and `data` blocks.\n *\n * Only declarations are read, because Terraform resolves a resource reference\n * within the module that declares it — so a provider a module references is one\n * it also declares a block for, and the blocks alone are complete. Matching the\n * syntax tree also means a type named in a comment, in string prose or inside a\n * `local-exec` heredoc's embedded Python is not a use, since none of those parse\n * as a block.\n */\nconst usedProviders = async (\n tree: Tree,\n filePath: string,\n): Promise<Set<string>> => {\n const used = new Set<string>();\n for (const kind of ['resource', 'data']) {\n for (const block of await captureAllGritQL(\n tree,\n filePath,\n hcl(`\\`${kind} $type $name { $_ }\\``),\n )) {\n // The block's own type, e.g. `resource \"random_string\" \"suffix\" {`.\n const type = /\"([A-Za-z0-9_]+)\"/.exec(block)?.[1];\n if (!type || BUILTIN_TYPES.has(type)) {\n continue;\n }\n const provider = providerOf(type);\n if (provider) {\n used.add(provider);\n }\n }\n }\n return used;\n};\n\n/** Every `.tf` file in the workspace's shared terraform library, by directory. */\nconst terraformModules = (tree: Tree): Map<string, string[]> => {\n const modules = new Map<string, string[]>();\n if (!tree.exists(TERRAFORM_SRC)) {\n return modules;\n }\n visitNotIgnoredFiles(tree, TERRAFORM_SRC, (filePath) => {\n if (!filePath.endsWith('.tf')) {\n return;\n }\n const dir = filePath.slice(0, filePath.lastIndexOf('/'));\n modules.set(dir, [...(modules.get(dir) ?? []), filePath]);\n });\n return modules;\n};\n\n/** The provider entry to vend, at the version this release pins. */\nconst providerEntry = (provider: string) =>\n [\n `${provider} = {`,\n ` source = \"hashicorp/${provider}\"`,\n ` version = \"${TERRAFORM_VERSIONS[provider as keyof typeof TERRAFORM_VERSIONS]}\"`,\n '}',\n ].join('\\n');\n\n/**\n * Add a provider to a file's `required_providers`. Appended after the block's\n * existing body, so an entry the user has customised is left as it is.\n */\nconst declareProvider = (tree: Tree, filePath: string, provider: string) =>\n insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`required_providers { $body }\\` => \\`required_providers {\\n $body\\n\\n ${GRIT_INSERT_PLACEHOLDER}\\n}\\`` +\n ` where { $body <: not contains \\`${provider} = { $_ }\\` }`,\n ),\n providerEntry(provider),\n );\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const [dir, filePaths] of terraformModules(tree)) {\n const used = new Set<string>();\n // The file holding the module's `required_providers`, which is where the\n // missing declarations are added — Terraform merges every `.tf` in a\n // directory, so one block covers the module.\n let blockFile: string | undefined;\n const declared = new Set<string>();\n\n for (const filePath of filePaths) {\n for (const provider of await usedProviders(tree, filePath)) {\n used.add(provider);\n }\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (!contents.includes('required_providers')) {\n continue;\n }\n blockFile ??= filePath;\n for (const provider of Object.keys(TERRAFORM_VERSIONS)) {\n if (\n await matchGritQL(\n tree,\n filePath,\n hcl(\n `\\`required_providers { $body }\\` where { $body <: contains \\`${provider} = { $_ }\\` }`,\n ),\n )\n ) {\n declared.add(provider);\n }\n }\n }\n\n const missing = [...used].filter((p) => !declared.has(p)).sort();\n if (missing.length === 0) {\n continue;\n }\n\n if (!blockFile) {\n nextSteps.push(\n `${dir}: has no terraform required_providers block - left untouched. Add one declaring ${missing.join(', ')}, so the module pins the providers it uses rather than inheriting them unpinned from the root module.`,\n );\n continue;\n }\n\n for (const provider of missing) {\n if (!(await declareProvider(tree, blockFile, provider))) {\n nextSteps.push(\n `${blockFile}: its required_providers block has diverged from the generated shape - left untouched. Add a ${provider} entry to it, sourced from hashicorp/${provider}.`,\n );\n }\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","visitNotIgnoredFiles","captureAllGritQL","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TERRAFORM_VERSIONS","TERRAFORM_SRC","hcl","pattern","BUILTIN_TYPES","Set","providerOf","type","prefix","split","undefined","usedProviders","tree","filePath","used","kind","block","exec","has","provider","add","terraformModules","modules","Map","exists","endsWith","dir","slice","lastIndexOf","set","get","providerEntry","join","declareProvider","migration","nextSteps","filePaths","blockFile","declared","contents","read","includes","Object","keys","missing","filter","p","sort","length","push"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/migration.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 MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport {\n addDestructuredImport,\n applyGritQL,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * WAF request logs carry full request metadata - URIs, headers and client IPs -\n * so the vended WAF log groups are encrypted with a customer-managed KMS key,\n * matching how the vended access log groups beside them already are. Three were\n * plaintext: the Terraform REST API and user-pool WAF log groups, the Terraform\n * AgentCore gateway WAF log group, and the CDK user-pool WAF log group. Each now\n * gets a key whose policy grants CloudWatch Logs use of it, scoped by\n * `kms:EncryptionContext:aws:logs:arn` to that log group alone.\n *\n * CloudWatch Logs cannot encrypt a log group that already exists in place, so\n * this only rewrites the vended templates. On the next deploy Terraform sets\n * `kms_key_id` on the existing group (an in-place update) and CloudFormation\n * replaces the CDK one, which the vended `RemovalPolicy.DESTROY` already allows.\n * Events written before then stay unencrypted; the `nextSteps` note says so.\n */\n\nconst CONSTRUCTS_SRC = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src`;\nconst TERRAFORM_SRC = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src`;\n\nconst CDK_USER_IDENTITY_FILE = `${CONSTRUCTS_SRC}/core/user-identity.ts`;\nconst TERRAFORM_REST_API_FILE = `${TERRAFORM_SRC}/core/api/rest-api/rest-api.tf`;\nconst TERRAFORM_USER_IDENTITY_FILE = `${TERRAFORM_SRC}/core/user-identity/identity/identity.tf`;\nconst TERRAFORM_GATEWAYS_DIR = `${TERRAFORM_SRC}/app/gateways`;\n\nconst divergedMessage = (file: string) =>\n `${file}: the WAF log group has diverged from the generated shape - left untouched. Its request logs stay unencrypted. To encrypt them, add a customer-managed KMS key granting the \\`logs.<region>.amazonaws.com\\` service principal kms:Encrypt/Decrypt/ReEncrypt*/GenerateDataKey*/DescribeKey, conditioned on \\`kms:EncryptionContext:aws:logs:arn\\` matching the log group, and point the log group at it (see the vended WAF log group template).`;\n\nconst ALREADY_ENCRYPTED_TERRAFORM = 'aws_kms_key.';\n\n// Terraform (HCL) patterns are built from plain strings rather than JS template\n// literals, since the HCL text itself is full of `${...}` interpolations that\n// would otherwise be parsed as JS interpolation.\nconst hcl = (pattern: string) => 'language hcl\\n' + pattern;\n\n/** A vended Terraform WAF log group and the key this migration gives it. */\ninterface TerraformWafLogGroup {\n /** The `aws_cloudwatch_log_group` resource label. */\n readonly logGroupLabel: string;\n /** The `aws_kms_key` / `aws_kms_alias` label to create. */\n readonly keyLabel: string;\n /** The `locals` entry the log group name moves into. */\n readonly nameLocal: string;\n /** The log group's name expression, which becomes the local's value. */\n readonly nameExpression: string;\n /** The KMS key description. */\n readonly keyDescription: string;\n /** The KMS alias name expression. */\n readonly aliasName: string;\n /** How the account id is referenced in this module. */\n readonly accountId: string;\n /** How the region is referenced in this module. */\n readonly region: string;\n /** Whether the module tags its resources. */\n readonly tagged: boolean;\n}\n\nconst REST_API_LOG_GROUP: TerraformWafLogGroup = {\n logGroupLabel: 'api_waf_logs',\n keyLabel: 'waf_logs',\n nameLocal: 'waf_log_group_name',\n nameExpression:\n 'var.enable_waf ? \"aws-waf-logs-${var.api_name}-${random_id.waf_log_suffix[0].hex}\" : null',\n keyDescription: '\"${var.api_name} API WAF log encryption\"',\n aliasName:\n '\"alias/${var.api_name}-${random_id.waf_log_suffix[0].hex}-waf-logs\"',\n accountId: 'data.aws_caller_identity.current.account_id',\n region: 'data.aws_region.current.region',\n tagged: true,\n};\n\nconst USER_IDENTITY_LOG_GROUP: TerraformWafLogGroup = {\n logGroupLabel: 'user_pool_waf_logs',\n keyLabel: 'user_pool_waf_logs',\n nameLocal: 'user_pool_waf_log_group_name',\n nameExpression:\n 'var.enable_waf ? \"aws-waf-logs-UserPool-${random_id.unique_suffix.hex}\" : null',\n keyDescription: '\"User pool WAF log encryption\"',\n aliasName: '\"alias/user-pool-${random_id.unique_suffix.hex}-waf-logs\"',\n accountId: 'data.aws_caller_identity.current.account_id',\n region: 'data.aws_region.current.region',\n tagged: false,\n};\n\nconst gatewayLogGroup = (nameKebabCase: string): TerraformWafLogGroup => ({\n logGroupLabel: 'gateway_waf_logs',\n keyLabel: 'gateway_waf_logs',\n nameLocal: 'gateway_waf_log_group_name',\n nameExpression: `var.enable_waf ? \"aws-waf-logs-${nameKebabCase}-\\${random_id.unique_suffix.hex}\" : null`,\n keyDescription: `\"${nameKebabCase} gateway WAF log encryption\"`,\n aliasName: `\"alias/${nameKebabCase}-\\${random_id.unique_suffix.hex}-waf-logs\"`,\n accountId: 'local.aws_account_id',\n region: 'local.aws_region',\n tagged: false,\n});\n\n/** The KMS key and alias resources, as the templates now vend them. */\nconst keyResourcesText = (group: TerraformWafLogGroup): string =>\n [\n '# KMS key for encrypting WAF request logs at rest',\n `resource \"aws_kms_key\" \"${group.keyLabel}\" {`,\n ' count = var.enable_waf ? 1 : 0',\n '',\n ` description = ${group.keyDescription}`,\n ' deletion_window_in_days = 7',\n ' enable_key_rotation = true',\n '',\n ' policy = jsonencode({',\n ' Version = \"2012-10-17\"',\n ' Statement = [',\n ' {',\n ' Sid = \"Enable IAM User Permissions\"',\n ' Effect = \"Allow\"',\n ` Principal = { AWS = \"arn:aws:iam::\\${${group.accountId}}:root\" }`,\n ' Action = \"kms:*\"',\n ' Resource = \"*\"',\n ' },',\n ' {',\n ' Sid = \"Allow CloudWatch Logs\"',\n ' Effect = \"Allow\"',\n ` Principal = { Service = \"logs.\\${${group.region}}.amazonaws.com\" }`,\n ' Action = [',\n ' \"kms:Encrypt\",',\n ' \"kms:Decrypt\",',\n ' \"kms:ReEncrypt*\",',\n ' \"kms:GenerateDataKey*\",',\n ' \"kms:DescribeKey\"',\n ' ]',\n ' Resource = \"*\"',\n ' Condition = {',\n ' ArnEquals = {',\n ` \"kms:EncryptionContext:aws:logs:arn\" = \"arn:aws:logs:\\${${group.region}}:\\${${group.accountId}}:log-group:\\${local.${group.nameLocal}}\"`,\n ' }',\n ' }',\n ' }',\n ' ]',\n ' })',\n ...(group.tagged ? ['', ' tags = var.tags'] : []),\n '}',\n '',\n `resource \"aws_kms_alias\" \"${group.keyLabel}\" {`,\n ' count = var.enable_waf ? 1 : 0',\n '',\n ` name = ${group.aliasName}`,\n ` target_key_id = aws_kms_key.${group.keyLabel}[0].key_id`,\n '}',\n ].join('\\n');\n\nconst migrateTerraformLogGroup = async (\n tree: Tree,\n file: string,\n group: TerraformWafLogGroup,\n nextSteps: string[],\n): Promise<void> => {\n const contents = tree.read(file, 'utf-8') ?? '';\n\n // The WAF log group is only vended when WAF is enabled, so it may be absent.\n if (\n !contents.includes(`\"aws_cloudwatch_log_group\" \"${group.logGroupLabel}\"`)\n ) {\n return;\n }\n\n const LOG_GROUP_BLOCK = hcl(\n `\\`resource \"aws_cloudwatch_log_group\" \"${group.logGroupLabel}\" { $body }\\``,\n );\n const NAME_LINE = hcl(\n `\\`name = ${group.nameExpression.replace(/^var\\.enable_waf \\? /, '').replace(/ : null$/, '')}\\` as $line where {\\n` +\n ` $line <: within \\`resource \"aws_cloudwatch_log_group\" \"${group.logGroupLabel}\" { $_ }\\`\\n` +\n '}',\n );\n const SUPPRESSION_LINE =\n '#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption';\n const LOCALS_BLOCK = hcl('`locals { $body }`');\n\n if (contents.includes(ALREADY_ENCRYPTED_TERRAFORM + group.keyLabel)) {\n return; // Already migrated.\n }\n\n const ready =\n (await matchGritQL(tree, file, LOG_GROUP_BLOCK)) &&\n (await matchGritQL(tree, file, NAME_LINE)) &&\n (await matchGritQL(tree, file, LOCALS_BLOCK)) &&\n contents.includes(SUPPRESSION_LINE);\n\n if (!ready) {\n nextSteps.push(divergedMessage(file));\n return;\n }\n\n // 1. Route the log group name through a local, so the key policy's encryption\n // context condition can name the same log group.\n await insertViaGritQL(\n tree,\n file,\n LOCALS_BLOCK +\n ` as $l => \\`locals {\\n $body\\n ${GRIT_INSERT_PLACEHOLDER}\\n}\\``,\n `${group.nameLocal} = ${group.nameExpression}`,\n );\n await applyGritQL(\n tree,\n file,\n NAME_LINE + ` => \\`name = local.${group.nameLocal}\\``,\n );\n\n // 2. Point the log group at a new key, added after it so the log group keeps\n // the comment above it.\n await insertViaGritQL(\n tree,\n file,\n hcl(\n `\\`name = local.${group.nameLocal}\\` as $line where {\\n` +\n ` $line <: within \\`resource \"aws_cloudwatch_log_group\" \"${group.logGroupLabel}\" { $_ }\\`\\n` +\n '}',\n ) + ` => \\`$line\\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n `kms_key_id = aws_kms_key.${group.keyLabel}[0].arn`,\n );\n await insertViaGritQL(\n tree,\n file,\n LOG_GROUP_BLOCK + ` as $lg => \\`$lg\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n keyResourcesText(group),\n );\n\n // 3. Drop the suppression the encryption makes redundant.\n const updated = (tree.read(file, 'utf-8') ?? '')\n .split('\\n')\n .filter((line) => line.trim() !== SUPPRESSION_LINE)\n .join('\\n');\n tree.write(file, updated);\n};\n\n/**\n * The vended CDK user-pool WAF log group gains an `encryptionKey`, matching the\n * eight sibling CDK WAF log groups which already pass one.\n */\nconst migrateCdkUserIdentity = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_USER_IDENTITY_FILE)) {\n return; // This workspace has no CDK user identity construct.\n }\n\n const contents = tree.read(CDK_USER_IDENTITY_FILE, 'utf-8') ?? '';\n\n // The WAF web ACL (and its log group) is optional, so it may be absent.\n if (!contents.includes(`new LogGroup(this, 'WebAclLogs'`)) {\n return;\n }\n\n if (contents.includes('WebAclLogsKey')) {\n return; // Already migrated.\n }\n\n // Already encrypted, whether by this migration or by the user's own key.\n if (\n await matchGritQL(\n tree,\n CDK_USER_IDENTITY_FILE,\n \"`new LogGroup(this, 'WebAclLogs', { $props })` where { $props <: contains `encryptionKey` }\",\n )\n ) {\n return;\n }\n\n const LOG_GROUP =\n \"`new LogGroup(this, 'WebAclLogs', { $props })` as $lg where { $props <: contains `logGroupName: $name` }\";\n const SUPPRESS_CALL =\n \"`suppressRules(wafLogGroup, ['CKV_AWS_158'], $reason)` as $call\";\n\n const ready =\n (await matchGritQL(tree, CDK_USER_IDENTITY_FILE, LOG_GROUP)) &&\n (await matchGritQL(tree, CDK_USER_IDENTITY_FILE, SUPPRESS_CALL));\n\n if (!ready) {\n nextSteps.push(divergedMessage(CDK_USER_IDENTITY_FILE));\n return;\n }\n\n await addDestructuredImport(\n tree,\n CDK_USER_IDENTITY_FILE,\n ['Key'],\n 'aws-cdk-lib/aws-kms',\n );\n await addDestructuredImport(\n tree,\n CDK_USER_IDENTITY_FILE,\n ['Effect', 'PolicyStatement', 'ServicePrincipal'],\n 'aws-cdk-lib/aws-iam',\n );\n\n // Hoist the log group name into a const so the key policy's encryption context\n // condition can name the same log group, then create the key above the group.\n await insertViaGritQL(\n tree,\n CDK_USER_IDENTITY_FILE,\n \"`const wafLogGroup = new LogGroup(this, 'WebAclLogs', { $props })` as $decl => \" +\n `\\`${GRIT_INSERT_PLACEHOLDER}\\n\\n$decl\\``,\n `const stack = Stack.of(this);\n const wafLogGroupName = \\`aws-waf-logs-\\${id}-\\${this.node.addr.slice(-8)}\\`;\n\n // KMS key for encrypting WAF logs at rest, usable by CloudWatch Logs\n const logsKey = new Key(this, 'WebAclLogsKey', {\n enableKeyRotation: true,\n });\n logsKey.addToResourcePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n principals: [new ServicePrincipal(\\`logs.\\${stack.region}.amazonaws.com\\`)],\n actions: [\n 'kms:Encrypt',\n 'kms:Decrypt',\n 'kms:ReEncrypt*',\n 'kms:GenerateDataKey*',\n 'kms:DescribeKey',\n ],\n resources: ['*'],\n conditions: {\n ArnEquals: {\n 'kms:EncryptionContext:aws:logs:arn': \\`arn:aws:logs:\\${stack.region}:\\${stack.account}:log-group:\\${wafLogGroupName}\\`,\n },\n },\n }),\n );`,\n );\n\n await applyGritQL(\n tree,\n CDK_USER_IDENTITY_FILE,\n \"`logGroupName: $name` as $prop where { $prop <: within `new LogGroup(this, 'WebAclLogs', $_)` } => `logGroupName: wafLogGroupName,\\n encryptionKey: logsKey`\",\n );\n\n // Drop the suppression the encryption makes redundant.\n await applyGritQL(tree, CDK_USER_IDENTITY_FILE, SUPPRESS_CALL + ' => ``');\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (tree.exists(TERRAFORM_REST_API_FILE)) {\n await migrateTerraformLogGroup(\n tree,\n TERRAFORM_REST_API_FILE,\n REST_API_LOG_GROUP,\n nextSteps,\n );\n }\n if (tree.exists(TERRAFORM_USER_IDENTITY_FILE)) {\n await migrateTerraformLogGroup(\n tree,\n TERRAFORM_USER_IDENTITY_FILE,\n USER_IDENTITY_LOG_GROUP,\n nextSteps,\n );\n }\n if (tree.exists(TERRAFORM_GATEWAYS_DIR)) {\n for (const dirName of tree.children(TERRAFORM_GATEWAYS_DIR)) {\n const file = joinPathFragments(\n TERRAFORM_GATEWAYS_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (tree.exists(file)) {\n await migrateTerraformLogGroup(\n tree,\n file,\n gatewayLogGroup(dirName),\n nextSteps,\n );\n }\n }\n }\n\n await migrateCdkUserIdentity(tree, nextSteps);\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","addDestructuredImport","applyGritQL","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CONSTRUCTS_SRC","TERRAFORM_SRC","CDK_USER_IDENTITY_FILE","TERRAFORM_REST_API_FILE","TERRAFORM_USER_IDENTITY_FILE","TERRAFORM_GATEWAYS_DIR","divergedMessage","file","ALREADY_ENCRYPTED_TERRAFORM","hcl","pattern","REST_API_LOG_GROUP","logGroupLabel","keyLabel","nameLocal","nameExpression","keyDescription","aliasName","accountId","region","tagged","USER_IDENTITY_LOG_GROUP","gatewayLogGroup","nameKebabCase","keyResourcesText","group","join","migrateTerraformLogGroup","tree","nextSteps","contents","read","includes","LOG_GROUP_BLOCK","NAME_LINE","replace","SUPPRESSION_LINE","LOCALS_BLOCK","ready","push","updated","split","filter","line","trim","write","migrateCdkUserIdentity","exists","LOG_GROUP","SUPPRESS_CALL","migration","dirName","children"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/migration.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 MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport {\n applyGritQL,\n captureAllGritQL,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Bring each vended Terraform AgentCore Harness app module\n * (`app/harnesses/<name>/<name>.tf`) up to the shape the generator now writes:\n *\n * - `enable_vpc` / `vpc_id` / `subnet_ids` with a security group and a 443-only\n * egress rule, so the Harness can reach private resources.\n * - `create_execution_role` / `execution_role_arn`, so an existing role can be\n * supplied. The generated role and its baseline policy are then not created.\n * - `allowed_tools`, `memory`, `environment_variables`, `max_iterations` and\n * `timeout_seconds` as variables rather than HCL edits.\n * - The managed-memory grant moved to its own policy, made conditional and\n * re-scoped from a name-prefix guess to the ARN the service assigns.\n *\n * These files are generated with `KeepExisting`, so an upgraded workspace keeps\n * the older module until this runs. Modules that have diverged from the\n * generated shape are left untouched and reported via `nextSteps`.\n */\n\nconst TERRAFORM_HARNESS_APP_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/harnesses`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\nconst divergedMessage = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To pick up VPC support, a supplied execution role, the allowed_tools/memory/environment_variables/max_iterations/timeout_seconds variables and the corrected managed-memory grant, compare it against the agentcore-harness generator's Terraform template and apply the parts you want.`;\n\n/**\n * Variables added ahead of the existing `model_resource_arns` variable.\n * `allowed_tools` is not among them: the earlier `agentcore-harness-no-tools-by-default`\n * migration already declares it, and runs first.\n */\nconst NEW_VARIABLES_TEXT = `variable \"memory\" {\n description = \"Memory configuration for the Harness. Null leaves the service to provision managed memory with default strategies. Set exactly one field: managed_memory_configuration to tune that managed memory, agentcore_memory_configuration to use a memory resource you own, or disabled to turn memory off.\"\n type = object({\n managed_memory_configuration = optional(object({\n strategies = optional(set(string))\n event_expiry_duration = optional(number)\n encryption_key_arn = optional(string)\n }))\n agentcore_memory_configuration = optional(object({\n arn = string\n actor_id = optional(string)\n messages_count = optional(number)\n }))\n disabled = optional(bool, false)\n })\n default = null\n\n validation {\n condition = var.memory == null || length([\n for configured in [\n var.memory.managed_memory_configuration != null,\n var.memory.agentcore_memory_configuration != null,\n var.memory.disabled,\n ] : configured if configured\n ]) == 1\n error_message = \"Set exactly one of memory.managed_memory_configuration, memory.agentcore_memory_configuration or memory.disabled.\"\n }\n}\n\nvariable \"environment_variables\" {\n description = \"Environment variables available to the Harness.\"\n type = map(string)\n default = null\n sensitive = true\n}\n\nvariable \"max_iterations\" {\n description = \"Maximum agent loop iterations per invocation. Uses the service default when null.\"\n type = number\n default = null\n}\n\nvariable \"timeout_seconds\" {\n description = \"Maximum seconds an invocation may run. Uses the service default when null.\"\n type = number\n default = null\n}\n\nvariable \"create_execution_role\" {\n description = \"Create the execution role with the baseline permissions below. Set false alongside execution_role_arn to use a role you already have, which is then used as-is.\"\n type = bool\n default = true\n\n validation {\n condition = var.create_execution_role || var.execution_role_arn != null\n error_message = \"execution_role_arn must be set when create_execution_role is false.\"\n }\n\n validation {\n condition = var.create_execution_role || (\n var.model_resource_arns == null && length(var.additional_execution_role_policy_statements) == 0\n )\n error_message = \"model_resource_arns and additional_execution_role_policy_statements configure the generated execution role, which is not created when create_execution_role is false. Grant those permissions on the supplied role instead.\"\n }\n}\n\nvariable \"execution_role_arn\" {\n description = \"ARN of an existing IAM role for the Harness to assume. Requires create_execution_role = false.\"\n type = string\n default = null\n}`;\n\n/** VPC and tag variables added after `additional_execution_role_policy_statements`. */\nconst VPC_VARIABLES_TEXT = `# VPC Configuration (optional)\nvariable \"enable_vpc\" {\n description = \"Run the Harness inside a VPC so it can reach private resources. Set alongside vpc_id/subnet_ids.\"\n type = bool\n default = false\n}\n\nvariable \"vpc_id\" {\n description = \"VPC ID to run the Harness in. Required when enable_vpc is true.\"\n type = string\n default = null\n\n validation {\n condition = !var.enable_vpc || (var.vpc_id != null && var.subnet_ids != null)\n error_message = \"vpc_id and subnet_ids must be set when enable_vpc is true.\"\n }\n}\n\nvariable \"subnet_ids\" {\n description = \"Subnet IDs to run the Harness in. Required when enable_vpc is true.\"\n type = list(string)\n default = null\n}\n\nvariable \"tags\" {\n description = \"Tags to apply to resources\"\n type = map(string)\n default = {}\n}`;\n\n/** Locals added to the module's existing `locals` block. */\nconst NEW_LOCALS_TEXT = ` execution_role_arn = var.create_execution_role ? aws_iam_role.execution_role[0].arn : var.execution_role_arn\n\n # The service provisions managed memory unless var.memory redirects it\n # elsewhere or turns it off.\n has_managed_memory = var.memory == null || var.memory.managed_memory_configuration != null`;\n\n/** Security group and egress rule, added before the Harness resource. */\nconst SECURITY_GROUP_TEXT = `# Security group for the Harness, used when running inside a VPC\nresource \"aws_security_group\" \"harness\" {\n count = var.enable_vpc ? 1 : 0\n\n #checkov:skip=CKV2_AWS_5:Attached to the Harness via its network configuration; Checkov cannot resolve this reference\n name_prefix = \"\\${local.harness_name_prefix}-harness-\"\n description = \"Security group for the \\${local.harness_name_prefix} Harness\"\n vpc_id = var.vpc_id\n tags = var.tags\n}\n\nresource \"aws_vpc_security_group_egress_rule\" \"harness_https\" {\n count = var.enable_vpc ? 1 : 0\n\n security_group_id = aws_security_group.harness[0].id\n cidr_ipv4 = \"0.0.0.0/0\"\n from_port = 443\n to_port = 443\n ip_protocol = \"tcp\"\n description = \"Allow outbound HTTPS to AWS service endpoints\"\n}`;\n\n/**\n * Native fields added to the Harness resource. `allowed_tools` is re-emitted\n * here, aligned with the rest, replacing the line the earlier\n * no-tools-by-default migration added.\n */\nconst HARNESS_FIELDS_TEXT = ` allowed_tools = var.allowed_tools\n environment_variables = var.environment_variables\n max_iterations = var.max_iterations\n timeout_seconds = var.timeout_seconds`;\n\nconst HARNESS_BLOCKS_TEXT = ` dynamic \"memory\" {\n for_each = var.memory != null ? [var.memory] : []\n content {\n dynamic \"managed_memory_configuration\" {\n for_each = memory.value.managed_memory_configuration != null ? [memory.value.managed_memory_configuration] : []\n content {\n strategies = managed_memory_configuration.value.strategies\n event_expiry_duration = managed_memory_configuration.value.event_expiry_duration\n encryption_key_arn = managed_memory_configuration.value.encryption_key_arn\n }\n }\n\n dynamic \"agentcore_memory_configuration\" {\n for_each = memory.value.agentcore_memory_configuration != null ? [memory.value.agentcore_memory_configuration] : []\n content {\n arn = agentcore_memory_configuration.value.arn\n actor_id = agentcore_memory_configuration.value.actor_id\n messages_count = agentcore_memory_configuration.value.messages_count\n }\n }\n\n dynamic \"disabled\" {\n for_each = memory.value.disabled ? [1] : []\n content {}\n }\n }\n }\n\n dynamic \"environment\" {\n for_each = var.enable_vpc ? [1] : []\n content {\n agentcore_runtime_environment {\n network_configuration {\n network_mode = \"VPC\"\n network_mode_config {\n security_groups = [aws_security_group.harness[0].id]\n subnets = var.subnet_ids\n }\n }\n }\n }\n }\n\n tags = var.tags`;\n\n/** The managed-memory grant, as its own policy scoped to the assigned ARN. */\nconst MANAGED_MEMORY_POLICY_TEXT = `# Scoped to the memory ARN the service assigns, which is only readable after the\n# Harness exists, so this is a policy of its own rather than a baseline\n# statement. Skipped when the Harness uses a role or memory the module did not\n# create.\nresource \"aws_iam_role_policy\" \"managed_memory\" {\n count = var.create_execution_role && local.has_managed_memory ? 1 : 0\n\n name = \"\\${local.harness_name_prefix}-HarnessManagedMemoryPolicy\"\n role = aws_iam_role.execution_role[0].id\n\n policy = jsonencode({\n Version = \"2012-10-17\"\n Statement = [{\n Sid = \"AgentCoreManagedMemory\"\n Effect = \"Allow\"\n Action = [\n \"bedrock-agentcore:CreateEvent\",\n \"bedrock-agentcore:DeleteEvent\",\n \"bedrock-agentcore:GetEvent\",\n \"bedrock-agentcore:ListEvents\",\n \"bedrock-agentcore:RetrieveMemoryRecords\",\n ]\n Resource = [\n aws_bedrockagentcore_harness.this.memory_actual[0].managed_memory_configuration[0].arn,\n ]\n }]\n })\n}`;\n\nconst SECURITY_GROUP_OUTPUT_TEXT = `output \"security_group_id\" {\n description = \"Security group ID of the Harness, for use in ingress rules on resources it must reach (e.g. a database). Null unless enable_vpc is true.\"\n value = var.enable_vpc ? aws_security_group.harness[0].id : null\n}`;\n\n/**\n * The baseline policy, replaced whole: the managed-memory statement leaves it\n * for a policy of its own, and the resource gains a `count`. Rewriting the\n * statement list in place would strand the comment and comma around the\n * statement being removed.\n */\nconst BASELINE_POLICY_TEXT = `resource \"aws_iam_role_policy\" \"execution_role\" {\n count = var.create_execution_role ? 1 : 0\n\n name = \"\\${local.harness_name_prefix}-HarnessPolicy\"\n role = aws_iam_role.execution_role[0].id\n\n policy = jsonencode({\n Version = \"2012-10-17\"\n Statement = concat([\n {\n Sid = \"BedrockModelInvocation\"\n Effect = \"Allow\"\n Action = [\n \"bedrock:InvokeModel\",\n \"bedrock:InvokeModelWithResponseStream\",\n ]\n Resource = local.model_resource_arns\n },\n {\n #checkov:skip=CKV_AWS_355:EcrPublicTokenAccess requires a wildcard resource; ecr-public:GetAuthorizationToken has no resource-level permission\n Sid = \"EcrPublicTokenAccess\"\n Effect = \"Allow\"\n Action = [\"ecr-public:GetAuthorizationToken\"]\n Resource = [\"*\"]\n },\n {\n #checkov:skip=CKV_AWS_355:StsForEcrPublicPull requires a wildcard resource; sts:GetServiceBearerToken has no resource-level permission\n Sid = \"StsForEcrPublicPull\"\n Effect = \"Allow\"\n Action = [\"sts:GetServiceBearerToken\"]\n Resource = [\"*\"]\n },\n {\n #checkov:skip=CKV_AWS_355:XRayTracingAccess requires a wildcard resource; the X-Ray segment and sampling APIs have no resource-level permission\n #checkov:skip=CKV_AWS_290:XRayTracingAccess requires a wildcard resource; the X-Ray segment and sampling APIs have no resource-level permission\n Sid = \"XRayTracingAccess\"\n Effect = \"Allow\"\n Action = [\n \"xray:PutTraceSegments\",\n \"xray:PutTelemetryRecords\",\n \"xray:GetSamplingRules\",\n \"xray:GetSamplingTargets\",\n ]\n Resource = [\"*\"]\n },\n {\n Sid = \"CloudWatchLogsGroup\"\n Effect = \"Allow\"\n Action = [\n \"logs:CreateLogGroup\",\n \"logs:DescribeLogStreams\",\n ]\n Resource = [\n \"arn:\\${local.partition}:logs:\\${local.region}:\\${local.account_id}:log-group:/aws/bedrock-agentcore/runtimes/*\",\n ]\n },\n {\n Sid = \"CloudWatchLogsDescribeGroups\"\n Effect = \"Allow\"\n Action = [\"logs:DescribeLogGroups\"]\n Resource = [\"arn:\\${local.partition}:logs:\\${local.region}:\\${local.account_id}:log-group:*\"]\n },\n {\n Sid = \"CloudWatchLogsStream\"\n Effect = \"Allow\"\n Action = [\n \"logs:CreateLogStream\",\n \"logs:PutLogEvents\",\n ]\n Resource = [\n \"arn:\\${local.partition}:logs:\\${local.region}:\\${local.account_id}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*\",\n ]\n },\n {\n #checkov:skip=CKV_AWS_355:CloudWatchMetricsPublish requires a wildcard resource; cloudwatch:PutMetricData is scoped by the namespace condition instead\n #checkov:skip=CKV_AWS_290:CloudWatchMetricsPublish requires a wildcard resource; cloudwatch:PutMetricData is scoped by the namespace condition instead\n Sid = \"CloudWatchMetricsPublish\"\n Effect = \"Allow\"\n Action = [\"cloudwatch:PutMetricData\"]\n Resource = [\"*\"]\n Condition = {\n StringEquals = {\n \"cloudwatch:namespace\" = \"bedrock-agentcore\"\n }\n }\n },\n {\n Sid = \"AgentCoreWorkloadIdentity\"\n Effect = \"Allow\"\n Action = [\n \"bedrock-agentcore:GetWorkloadAccessToken\",\n \"bedrock-agentcore:GetWorkloadAccessTokenForJWT\",\n ]\n Resource = [\n \"arn:\\${local.partition}:bedrock-agentcore:\\${local.region}:\\${local.account_id}:workload-identity-directory/default\",\n \"arn:\\${local.partition}:bedrock-agentcore:\\${local.region}:\\${local.account_id}:workload-identity-directory/default/workload-identity/harness_\\${local.harness_name}-*\",\n ]\n },\n {\n Sid = \"AgentCoreBrowserDefault\"\n Effect = \"Allow\"\n Action = [\n \"bedrock-agentcore:StartBrowserSession\",\n \"bedrock-agentcore:StopBrowserSession\",\n \"bedrock-agentcore:GetBrowserSession\",\n \"bedrock-agentcore:ListBrowserSessions\",\n \"bedrock-agentcore:UpdateBrowserStream\",\n \"bedrock-agentcore:ConnectBrowserAutomationStream\",\n \"bedrock-agentcore:ConnectBrowserLiveViewStream\",\n ]\n Resource = [\n \"arn:\\${local.partition}:bedrock-agentcore:\\${local.region}:aws:browser/*\",\n ]\n },\n {\n Sid = \"AgentCoreCodeInterpreterDefault\"\n Effect = \"Allow\"\n Action = [\n \"bedrock-agentcore:StartCodeInterpreterSession\",\n \"bedrock-agentcore:StopCodeInterpreterSession\",\n \"bedrock-agentcore:GetCodeInterpreterSession\",\n \"bedrock-agentcore:ListCodeInterpreterSessions\",\n \"bedrock-agentcore:InvokeCodeInterpreter\",\n ]\n Resource = [\n \"arn:\\${local.partition}:bedrock-agentcore:\\${local.region}:aws:code-interpreter/*\",\n ]\n },\n ],\n # In VPC mode the Harness pulls its managed container from a private ECR\n # repository in the region rather than ECR Public, so it needs pull\n # permissions on it. The repository is service-owned, hence the wildcard\n # account.\n var.enable_vpc ? [\n {\n Sid = \"EcrManagedImagePull\"\n Effect = \"Allow\"\n Action = [\n \"ecr:BatchGetImage\",\n \"ecr:GetDownloadUrlForLayer\",\n \"ecr:BatchCheckLayerAvailability\",\n ]\n Resource = [\"arn:\\${local.partition}:ecr:\\${local.region}:*:repository/harness-*\"]\n },\n {\n #checkov:skip=CKV_AWS_355:EcrManagedImageToken requires a wildcard resource; ecr:GetAuthorizationToken has no resource-level permission\n Sid = \"EcrManagedImageToken\"\n Effect = \"Allow\"\n Action = [\"ecr:GetAuthorizationToken\"]\n Resource = [\"*\"]\n },\n ] : [],\n [\n # Null Sid/Condition fields are dropped from the rendered policy.\n for statement in var.additional_execution_role_policy_statements :\n { for key, value in statement : key => value if value != null }\n ])\n })\n}`;\n\n/**\n * Every shape the migration rewrites, so a module missing any of them is\n * reported rather than half-migrated.\n */\nconst REQUIRED_SHAPES = [\n '`variable \"model_resource_arns\" { $_ }`',\n '`variable \"additional_execution_role_policy_statements\" { $_ }`',\n '`locals { $_ }`',\n '`resource \"aws_iam_role\" \"execution_role\" { $_ }`',\n '`resource \"aws_iam_role_policy\" \"execution_role\" { $_ }`',\n '`resource \"aws_bedrockagentcore_harness\" \"this\" { $_ }`',\n '`output \"execution_role_arn\" { $_ }`',\n // The no-tools-by-default migration runs first, so its assignment is present\n // and is what the native fields below are anchored on.\n '`allowed_tools = var.allowed_tools`',\n];\n\n/**\n * The baseline policy is replaced whole, so it is only safe to touch when it\n * still carries exactly the statements the generator wrote. Any addition,\n * removal or reordering means the user has edited it, and the replacement would\n * discard that.\n */\nconst BASELINE_SIDS = [\n 'BedrockModelInvocation',\n 'EcrPublicTokenAccess',\n 'StsForEcrPublicPull',\n 'XRayTracingAccess',\n 'CloudWatchLogsGroup',\n 'CloudWatchLogsDescribeGroups',\n 'CloudWatchLogsStream',\n 'CloudWatchMetricsPublish',\n 'AgentCoreWorkloadIdentity',\n 'AgentCoreManagedMemory',\n 'AgentCoreBrowserDefault',\n 'AgentCoreCodeInterpreterDefault',\n];\n\n/**\n * Whether the module's baseline policy still carries exactly the generated\n * statements, in order. Sids are read from the whole file: the only other `Sid`\n * is the `optional(string)` field of the additional-statements variable, which\n * carries no string literal and so is filtered out.\n */\nconst hasUneditedBaselinePolicy = async (\n tree: Tree,\n filePath: string,\n): Promise<boolean> => {\n const sids = (await captureAllGritQL(tree, filePath, hcl('`Sid = $sid`')))\n .map((text) => /=\\s*\"([^\"]+)\"/.exec(text)?.[1])\n .filter((sid): sid is string => sid !== undefined);\n return (\n sids.length === BASELINE_SIDS.length &&\n sids.every((sid, index) => sid === BASELINE_SIDS[index])\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(TERRAFORM_HARNESS_APP_DIR)) {\n return { nextSteps }; // This workspace has no Terraform harness modules.\n }\n\n for (const dirName of tree.children(TERRAFORM_HARNESS_APP_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_HARNESS_APP_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a harness app module directory.\n }\n\n if (\n await matchGritQL(tree, filePath, hcl('`variable \"enable_vpc\" { $_ }`'))\n ) {\n continue; // Already migrated.\n }\n\n const shapeChecks = await Promise.all(\n REQUIRED_SHAPES.map((shape) => matchGritQL(tree, filePath, hcl(shape))),\n );\n if (\n shapeChecks.includes(false) ||\n !(await hasUneditedBaselinePolicy(tree, filePath))\n ) {\n nextSteps.push(divergedMessage(filePath));\n continue;\n }\n\n // Variables, ahead of the model/role variables they sit beside in the\n // template.\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`variable \"model_resource_arns\" { $body }\\` => \\`${GRIT_INSERT_PLACEHOLDER}\\n\\nvariable \"model_resource_arns\" {\\n $body\\n}\\``,\n ),\n NEW_VARIABLES_TEXT,\n );\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`variable \"additional_execution_role_policy_statements\" { $body }\\` => \\`variable \"additional_execution_role_policy_statements\" {\\n $body\\n}\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n VPC_VARIABLES_TEXT,\n );\n\n // Locals resolving the role ARN and whether managed memory applies.\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`locals { $body }\\` => \\`locals {\\n $body\\n\\n${GRIT_INSERT_PLACEHOLDER}\\n}\\``,\n ),\n NEW_LOCALS_TEXT,\n );\n\n // The role becomes conditional on none being supplied, so it is indexed\n // wherever it is referenced, and gains the shared tags.\n await applyGritQL(\n tree,\n filePath,\n hcl(\n '`resource \"aws_iam_role\" \"execution_role\" { $body }` => `resource \"aws_iam_role\" \"execution_role\" {\\n count = var.create_execution_role ? 1 : 0\\n\\n $body\\n\\n tags = var.tags\\n}`',\n ),\n );\n\n // The baseline policy is replaced whole, dropping the managed-memory\n // statement, which becomes a policy of its own scoped to the assigned ARN.\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`resource \"aws_iam_role_policy\" \"execution_role\" { $_ }\\` => \\`${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n BASELINE_POLICY_TEXT,\n );\n\n // The role ARN follows whichever role is in use, and the `allowed_tools`\n // line the no-tools-by-default migration added is re-aligned with the\n // native fields that now sit beside it.\n await applyGritQL(\n tree,\n filePath,\n hcl('`allowed_tools = var.allowed_tools` => ``'),\n );\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`execution_role_arn = aws_iam_role.execution_role.arn\\` => \\`execution_role_arn = local.execution_role_arn\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n HARNESS_FIELDS_TEXT,\n );\n // Ahead of `depends_on`, which the resource ends with.\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`depends_on = [aws_iam_role_policy.execution_role]\\` => \\`${GRIT_INSERT_PLACEHOLDER}\\n\\n depends_on = [aws_iam_role_policy.execution_role]\\``,\n ),\n // The placeholder already sits at the block's indentation.\n HARNESS_BLOCKS_TEXT.replace(/^ {2}/, ''),\n );\n\n // Security group ahead of the Harness resource, and the managed-memory\n // policy after it, since it reads an attribute of the deployed Harness.\n // Anchored on the comment introducing the resource so it stays attached.\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`# Omitting authorizer_configuration uses IAM inbound authorization; add a\\` => \\`${GRIT_INSERT_PLACEHOLDER}\\n\\n# Omitting authorizer_configuration uses IAM inbound authorization; add a\\``,\n ),\n SECURITY_GROUP_TEXT,\n );\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`resource \"aws_bedrockagentcore_harness\" \"this\" { $body }\\` => \\`resource \"aws_bedrockagentcore_harness\" \"this\" {\\n $body\\n}\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n MANAGED_MEMORY_POLICY_TEXT,\n );\n\n // The role ARN output follows whichever role is in use, and the security\n // group is exposed for ingress rules on resources the Harness reaches.\n await applyGritQL(\n tree,\n filePath,\n hcl(\n '`output \"execution_role_arn\" { $_ }` => `output \"execution_role_arn\" {\\n description = \"ARN of the IAM role assumed by the Harness, whether generated or supplied via var.execution_role_arn\"\\n value = local.execution_role_arn\\n}`',\n ),\n );\n await insertViaGritQL(\n tree,\n filePath,\n hcl(\n `\\`output \"execution_role_arn\" { $body }\\` => \\`output \"execution_role_arn\" {\\n $body\\n}\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n ),\n SECURITY_GROUP_OUTPUT_TEXT,\n );\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","applyGritQL","captureAllGritQL","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TERRAFORM_HARNESS_APP_DIR","hcl","pattern","divergedMessage","filePath","NEW_VARIABLES_TEXT","VPC_VARIABLES_TEXT","NEW_LOCALS_TEXT","SECURITY_GROUP_TEXT","HARNESS_FIELDS_TEXT","HARNESS_BLOCKS_TEXT","MANAGED_MEMORY_POLICY_TEXT","SECURITY_GROUP_OUTPUT_TEXT","BASELINE_POLICY_TEXT","REQUIRED_SHAPES","BASELINE_SIDS","hasUneditedBaselinePolicy","tree","sids","map","text","exec","filter","sid","undefined","length","every","index","migration","nextSteps","exists","dirName","children","shapeChecks","Promise","all","shape","includes","push","replace"],"mappings":"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"}
|
|
@@ -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>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.85/0001-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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"}
|
|
@@ -414,9 +414,9 @@ onlyBuiltDependencies:
|
|
|
414
414
|
- sharp
|
|
415
415
|
catalogMode: strict
|
|
416
416
|
catalog:
|
|
417
|
-
nx: 23.1.
|
|
418
|
-
'@nx/js': 23.1.
|
|
419
|
-
'@nx/workspace': 23.1.
|
|
417
|
+
nx: 23.1.2
|
|
418
|
+
'@nx/js': 23.1.2
|
|
419
|
+
'@nx/workspace': 23.1.2
|
|
420
420
|
typescript: ~6.0.3
|
|
421
421
|
'@biomejs/biome': 2.5.10
|
|
422
422
|
'@aws/nx-plugin': ^0.0.0
|
|
@@ -766,10 +766,10 @@ dependencies = [
|
|
|
766
766
|
"aws-lambda-powertools==3.34.0",
|
|
767
767
|
"aws-opentelemetry-distro==0.19.0",
|
|
768
768
|
"bedrock-agentcore==1.22.0",
|
|
769
|
-
"boto3==1.43.
|
|
769
|
+
"boto3==1.43.82",
|
|
770
770
|
"fastapi==0.141.1",
|
|
771
771
|
"mcp==1.28.1",
|
|
772
|
-
"strands-agents==1.
|
|
772
|
+
"strands-agents==1.54.0",
|
|
773
773
|
"strands-agents-tools==0.8.6",
|
|
774
774
|
"uvicorn==0.52.4"
|
|
775
775
|
]
|
|
@@ -704,16 +704,16 @@ dependencies = [
|
|
|
704
704
|
"aws-lambda-powertools==3.34.0",
|
|
705
705
|
"aws-opentelemetry-distro==0.19.0",
|
|
706
706
|
"bedrock-agentcore==1.22.0",
|
|
707
|
-
"boto3==1.43.
|
|
707
|
+
"boto3==1.43.82",
|
|
708
708
|
"fastapi==0.141.1",
|
|
709
709
|
"mcp==1.28.1",
|
|
710
|
-
"langchain==1.3.
|
|
711
|
-
"langchain-aws==1.7.
|
|
710
|
+
"langchain==1.3.18",
|
|
711
|
+
"langchain-aws==1.7.4",
|
|
712
712
|
"langgraph==1.2.11",
|
|
713
713
|
"langgraph-checkpoint-sqlite==3.1.1",
|
|
714
714
|
"aiosqlite==0.22.1",
|
|
715
|
-
"ag-ui-protocol==0.1.
|
|
716
|
-
"ag-ui-langgraph==0.0.
|
|
715
|
+
"ag-ui-protocol==0.1.21",
|
|
716
|
+
"ag-ui-langgraph==0.0.44",
|
|
717
717
|
"uvicorn==0.52.4"
|
|
718
718
|
]
|
|
719
719
|
|
|
@@ -5,7 +5,7 @@ exports[`py#agent generator > should create DynamoDB checkpoint table and S3 off
|
|
|
5
5
|
required_providers {
|
|
6
6
|
aws = {
|
|
7
7
|
source = "hashicorp/aws"
|
|
8
|
-
version = "6.
|
|
8
|
+
version = "6.62.0"
|
|
9
9
|
}
|
|
10
10
|
random = {
|
|
11
11
|
source = "hashicorp/random"
|
|
@@ -369,7 +369,7 @@ exports[`py#agent generator > should match snapshot for Terraform generated file
|
|
|
369
369
|
required_providers {
|
|
370
370
|
aws = {
|
|
371
371
|
source = "hashicorp/aws"
|
|
372
|
-
version = "6.
|
|
372
|
+
version = "6.62.0"
|
|
373
373
|
}
|
|
374
374
|
random = {
|
|
375
375
|
source = "hashicorp/random"
|
|
@@ -680,7 +680,7 @@ exports[`py#agent generator > should match snapshot for Terraform generated file
|
|
|
680
680
|
required_providers {
|
|
681
681
|
aws = {
|
|
682
682
|
source = "hashicorp/aws"
|
|
683
|
-
version = "6.
|
|
683
|
+
version = "6.62.0"
|
|
684
684
|
}
|
|
685
685
|
external = {
|
|
686
686
|
source = "hashicorp/external"
|
|
@@ -1100,7 +1100,7 @@ resource "null_resource" "runtime_ready" {
|
|
|
1100
1100
|
|
|
1101
1101
|
provisioner "local-exec" {
|
|
1102
1102
|
command = <<-EOT
|
|
1103
|
-
uv run --with boto3==1.43.
|
|
1103
|
+
uv run --with boto3==1.43.82 python -c "
|
|
1104
1104
|
import boto3
|
|
1105
1105
|
import json
|
|
1106
1106
|
import os
|
|
@@ -35,7 +35,7 @@ exports[`py#dynamodb generator > should generate terraform modules when iac is t
|
|
|
35
35
|
required_providers {
|
|
36
36
|
aws = {
|
|
37
37
|
source = "hashicorp/aws"
|
|
38
|
-
version = "6.
|
|
38
|
+
version = "6.62.0"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -232,7 +232,7 @@ exports[`py#dynamodb generator > should generate terraform modules when iac is t
|
|
|
232
232
|
required_providers {
|
|
233
233
|
aws = {
|
|
234
234
|
source = "hashicorp/aws"
|
|
235
|
-
version = "6.
|
|
235
|
+
version = "6.62.0"
|
|
236
236
|
}
|
|
237
237
|
random = {
|
|
238
238
|
source = "hashicorp/random"
|