From f918ec3b5f7f10f658c9fa589f62fc23cf3f1d34 Mon Sep 17 00:00:00 2001 From: Momo Kornher Date: Tue, 14 Jan 2025 17:13:10 +0000 Subject: [PATCH] this works --- packages/@aws-cdk/cli-lib-alpha/.npmignore | 7 +- .../build-tools/copy-cli-resources.sh | 7 +- .../@aws-cdk/tmp-toolkit-helpers/generate.sh | 16 +- .../tmp-toolkit-helpers/lib/api/index.ts | 1 + .../@aws-cdk/tmp-toolkit-helpers/package.json | 7 +- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 194 +++- .../recommended-feature-flags.json | 3 +- packages/aws-cdk/.eslintrc.js | 3 +- packages/aws-cdk/.gitignore | 1 + packages/aws-cdk/.npmignore | 9 +- packages/aws-cdk/generate.sh | 13 +- .../aws-cdk/lib/api/aws-auth/account-cache.ts | 109 -- .../lib/api/aws-auth/awscli-compatible.ts | 299 ------ packages/aws-cdk/lib/api/aws-auth/cached.ts | 22 - .../lib/api/aws-auth/credential-plugins.ts | 176 ---- packages/aws-cdk/lib/api/aws-auth/index.ts | 2 - .../lib/api/aws-auth/provider-caching.ts | 26 - .../aws-cdk/lib/api/aws-auth/sdk-logger.ts | 142 --- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 531 ---------- packages/aws-cdk/lib/api/aws-auth/sdk.ts | 987 ------------------ .../aws-cdk/lib/api/aws-auth/user-agent.ts | 17 - packages/aws-cdk/lib/api/aws-auth/util.ts | 19 - .../api/bootstrap/bootstrap-environment.ts | 378 ------- .../lib/api/bootstrap/bootstrap-props.ts | 141 --- .../lib/api/bootstrap/bootstrap-template.yaml | 692 ------------ .../lib/api/bootstrap/deploy-bootstrap.ts | 167 --- packages/aws-cdk/lib/api/bootstrap/index.ts | 2 - .../lib/api/bootstrap/legacy-template.ts | 79 -- .../aws-cdk/lib/api/cxapp/cloud-assembly.ts | 387 ------- .../aws-cdk/lib/api/cxapp/cloud-executable.ts | 126 --- .../aws-cdk/lib/api/cxapp/environments.ts | 69 -- packages/aws-cdk/lib/api/cxapp/exec.ts | 311 ------ packages/aws-cdk/lib/api/deploy-stack.ts | 876 ---------------- packages/aws-cdk/lib/api/deployments.ts | 797 -------------- .../aws-cdk/lib/api/environment-access.ts | 274 ----- .../aws-cdk/lib/api/environment-resources.ts | 240 ----- .../api/evaluate-cloudformation-template.ts | 578 ---------- .../garbage-collection/garbage-collector.ts | 772 -------------- .../garbage-collection/progress-printer.ts | 76 -- .../api/garbage-collection/stack-refresh.ts | 191 ---- .../aws-cdk/lib/api/hotswap-deployments.ts | 493 --------- .../api/hotswap/appsync-mapping-templates.ts | 189 ---- .../lib/api/hotswap/code-build-projects.ts | 79 -- packages/aws-cdk/lib/api/hotswap/common.ts | 263 ----- .../aws-cdk/lib/api/hotswap/ecs-services.ts | 179 ---- .../lib/api/hotswap/lambda-functions.ts | 403 ------- .../lib/api/hotswap/s3-bucket-deployments.ts | 135 --- .../hotswap/stepfunctions-state-machines.ts | 48 - packages/aws-cdk/lib/api/index.ts | 5 - .../lib/api/logs/find-cloudwatch-logs.ts | 145 --- packages/aws-cdk/lib/api/logs/logs-monitor.ts | 235 ----- .../aws-cdk/lib/api/nested-stack-helpers.ts | 157 --- .../lib/api/plugin/context-provider-plugin.ts | 7 - packages/aws-cdk/lib/api/plugin/index.ts | 2 - packages/aws-cdk/lib/api/plugin/mode.ts | 6 - packages/aws-cdk/lib/api/plugin/plugin.ts | 111 -- packages/aws-cdk/lib/api/toolkit-info.ts | 223 ---- packages/aws-cdk/lib/api/util/checks.ts | 81 -- .../aws-cdk/lib/api/util/cloudformation.ts | 742 ------------- .../cloudformation/stack-activity-monitor.ts | 799 -------------- .../util/cloudformation/stack-event-poller.ts | 190 ---- .../api/util/cloudformation/stack-status.ts | 98 -- packages/aws-cdk/lib/api/util/display.ts | 87 -- packages/aws-cdk/lib/api/util/placeholders.ts | 28 - packages/aws-cdk/lib/api/util/rwlock.ts | 201 ---- .../lib/api/util/string-manipulation.ts | 7 - .../lib/api/util/template-body-parameter.ts | 135 --- packages/aws-cdk/lib/assets.ts | 139 --- packages/aws-cdk/lib/cdk-toolkit.ts | 70 +- packages/aws-cdk/lib/cli.ts | 48 +- .../lib/{ => cli}/platform-warnings.ts | 2 +- .../lib/{util => cli}/yargs-helpers.ts | 0 packages/aws-cdk/lib/commands/context.ts | 6 +- packages/aws-cdk/lib/commands/docs.ts | 2 +- packages/aws-cdk/lib/commands/doctor.ts | 2 +- .../aws-cdk/lib/{ => commands}/init-hooks.ts | 6 +- packages/aws-cdk/lib/{ => commands}/init.ts | 10 +- packages/aws-cdk/lib/commands/migrate.ts | 10 +- packages/aws-cdk/lib/config.ts | 8 +- packages/aws-cdk/lib/context-providers/ami.ts | 53 - .../context-providers/availability-zones.ts | 27 - .../endpoint-service-availability-zones.ts | 31 - .../lib/context-providers/hosted-zones.ts | 71 -- .../aws-cdk/lib/context-providers/index.ts | 121 --- .../aws-cdk/lib/context-providers/keys.ts | 61 -- .../lib/context-providers/load-balancers.ts | 200 ---- .../lib/context-providers/security-groups.ts | 80 -- .../lib/context-providers/ssm-parameters.ts | 58 - .../aws-cdk/lib/context-providers/vpcs.ts | 388 ------- packages/aws-cdk/lib/convert-to-user-input.ts | 2 +- packages/aws-cdk/lib/diff.ts | 203 ---- packages/aws-cdk/lib/import.ts | 466 --------- packages/aws-cdk/lib/index.ts | 2 +- .../init-templates/.no-packagejson-validator | 0 packages/aws-cdk/lib/init-templates/LICENSE | 16 - .../app/csharp/.template.gitignore | 342 ------ .../lib/init-templates/app/csharp/README.md | 14 - .../app/csharp/cdk.template.json | 15 - .../src/%name.PascalCased%.template.sln | 18 - .../%name.PascalCased%.template.csproj | 20 - .../%name.PascalCased%Stack.template.cs | 13 - .../%name.PascalCased%/GlobalSuppressions.cs | 1 - .../%name.PascalCased%/Program.template.cs | 44 - .../app/fsharp/.template.gitignore | 342 ------ .../lib/init-templates/app/fsharp/README.md | 18 - .../app/fsharp/cdk.template.json | 14 - .../src/%name.PascalCased%.template.sln | 18 - .../%name.PascalCased%.template.fsproj | 25 - .../%name.PascalCased%Stack.template.fs | 8 - .../%name.PascalCased%/Program.template.fs | 11 - .../init-templates/app/go/%name%.template.go | 70 -- .../app/go/%name%_test.template.go | 26 - .../init-templates/app/go/.template.gitignore | 19 - .../lib/init-templates/app/go/README.md | 12 - .../init-templates/app/go/cdk.template.json | 13 - .../lib/init-templates/app/go/go.template.mod | 9 - .../aws-cdk/lib/init-templates/app/info.json | 4 - .../app/java/.template.gitignore | 13 - .../lib/init-templates/app/java/README.md | 18 - .../lib/init-templates/app/java/cdk.json | 13 - .../lib/init-templates/app/java/pom.xml | 60 -- .../myorg/%name.PascalCased%App.template.java | 42 - .../%name.PascalCased%Stack.template.java | 24 - .../%name.PascalCased%Test.template.java | 26 - .../app/javascript/.template.gitignore | 5 - .../app/javascript/.template.npmignore | 3 - .../init-templates/app/javascript/README.md | 12 - .../app/javascript/bin/%name%.template.js | 21 - .../app/javascript/cdk.template.json | 15 - .../app/javascript/jest.config.js | 3 - .../javascript/lib/%name%-stack.template.js | 23 - .../app/javascript/package.json | 20 - .../javascript/test/%name%.test.template.js | 17 - .../%name.PythonModule%_stack.template.py | 19 - .../python/%name.PythonModule%/__init__.py | 0 .../app/python/.template.gitignore | 10 - .../app/python/README.template.md | 58 - .../init-templates/app/python/app.template.py | 28 - .../app/python/cdk.template.json | 15 - .../app/python/requirements-dev.txt | 1 - .../app/python/requirements.txt | 2 - .../lib/init-templates/app/python/source.bat | 13 - .../app/python/tests/__init__.py | 0 .../app/python/tests/unit/__init__.py | 0 ...test_%name.PythonModule%_stack.template.py | 15 - .../app/typescript/.template.gitignore | 8 - .../app/typescript/.template.npmignore | 6 - .../init-templates/app/typescript/README.md | 14 - .../app/typescript/bin/%name%.template.ts | 20 - .../app/typescript/cdk.template.json | 17 - .../app/typescript/jest.config.js | 8 - .../typescript/lib/%name%-stack.template.ts | 16 - .../app/typescript/package.json | 26 - .../typescript/test/%name%.test.template.ts | 17 - .../app/typescript/tsconfig.json | 31 - .../aws-cdk/lib/init-templates/lib/info.json | 4 - .../lib/typescript/.template.gitignore | 8 - .../lib/typescript/.template.npmignore | 6 - .../lib/typescript/README.template.md | 12 - .../lib/typescript/jest.config.js | 8 - .../lib/typescript/lib/index.template.ts | 21 - .../lib/typescript/package.json | 24 - .../typescript/test/%name%.test.template.ts | 18 - .../lib/typescript/tsconfig.json | 31 - .../sample-app/csharp/.template.gitignore | 342 ------ .../sample-app/csharp/README.template.md | 19 - .../sample-app/csharp/cdk.template.json | 15 - .../src/%name.PascalCased%.template.sln | 18 - .../%name.PascalCased%.template.csproj | 20 - .../%name.PascalCased%Stack.template.cs | 24 - .../%name.PascalCased%/GlobalSuppressions.cs | 1 - .../%name.PascalCased%/Program.template.cs | 15 - .../sample-app/fsharp/.template.gitignore | 342 ------ .../sample-app/fsharp/README.template.md | 20 - .../sample-app/fsharp/cdk.template.json | 14 - .../src/%name.PascalCased%.template.sln | 18 - .../%name.PascalCased%.template.fsproj | 25 - .../%name.PascalCased%Stack.template.fs | 14 - .../%name.PascalCased%/Program.template.fs | 11 - .../sample-app/go/%name%.template.go | 73 -- .../sample-app/go/%name%_test.template.go | 25 - .../sample-app/go/.template.gitignore | 19 - .../init-templates/sample-app/go/README.md | 12 - .../sample-app/go/cdk.template.json | 13 - .../sample-app/go/go.template.mod | 9 - .../lib/init-templates/sample-app/info.json | 4 - .../sample-app/java/.template.gitignore | 13 - .../sample-app/java/README.template.md | 19 - .../init-templates/sample-app/java/cdk.json | 13 - .../init-templates/sample-app/java/pom.xml | 55 - .../myorg/%name.PascalCased%App.template.java | 13 - .../%name.PascalCased%Stack.template.java | 29 - .../%name.PascalCased%StackTest.template.java | 27 - .../sample-app/javascript/.template.gitignore | 5 - .../sample-app/javascript/.template.npmignore | 3 - .../sample-app/javascript/README.template.md | 13 - .../javascript/bin/%name%.template.js | 6 - .../sample-app/javascript/cdk.template.json | 15 - .../sample-app/javascript/jest.config.js | 3 - .../javascript/lib/%name%-stack.template.js | 25 - .../sample-app/javascript/package.json | 20 - .../javascript/test/%name%.test.template.js | 16 - .../sample-app/javascript/tsconfig.json | 34 - .../%name.PythonModule%_stack.template.py | 26 - .../python/%name.PythonModule%/__init__.py | 0 .../sample-app/python/.template.gitignore | 22 - .../sample-app/python/README.template.md | 65 -- .../sample-app/python/app.template.py | 11 - .../sample-app/python/cdk.template.json | 15 - .../sample-app/python/requirements-dev.txt | 1 - .../sample-app/python/requirements.txt | 2 - .../sample-app/python/source.bat | 13 - .../sample-app/python/tests/__init__.py | 0 .../sample-app/python/tests/unit/__init__.py | 0 ...test_%name.PythonModule%_stack.template.py | 21 - .../sample-app/typescript/.template.gitignore | 8 - .../sample-app/typescript/.template.npmignore | 6 - .../sample-app/typescript/README.template.md | 15 - .../typescript/bin/%name%.template.ts | 6 - .../sample-app/typescript/cdk.template.json | 17 - .../sample-app/typescript/jest.config.js | 8 - .../typescript/lib/%name%-stack.template.ts | 19 - .../sample-app/typescript/package.json | 26 - .../typescript/test/%name%.test.template.ts | 17 - .../sample-app/typescript/tsconfig.json | 31 - packages/aws-cdk/lib/list-stacks.ts | 98 -- packages/aws-cdk/lib/logging.ts | 280 ----- packages/aws-cdk/lib/notices.ts | 519 --------- packages/aws-cdk/lib/os.ts | 97 -- .../lib/parse-command-line-arguments.ts | 4 +- packages/aws-cdk/lib/serialize.ts | 35 - packages/aws-cdk/lib/settings.ts | 542 ---------- packages/aws-cdk/lib/toolkit/cli-io-host.ts | 200 ---- packages/aws-cdk/lib/toolkit/error.ts | 48 - packages/aws-cdk/lib/tree.ts | 58 - packages/aws-cdk/lib/user-input.ts | 2 +- packages/aws-cdk/lib/util/archive.ts | 93 -- packages/aws-cdk/lib/util/arrays.ts | 31 - .../lib/util/asset-manifest-builder.ts | 32 - packages/aws-cdk/lib/util/asset-publishing.ts | 242 ----- packages/aws-cdk/lib/util/bool.ts | 9 - .../aws-cdk/lib/util/console-formatters.ts | 44 - packages/aws-cdk/lib/util/content-hash.ts | 44 - packages/aws-cdk/lib/util/directories.ts | 63 -- packages/aws-cdk/lib/util/error.ts | 19 - packages/aws-cdk/lib/util/index.ts | 5 - packages/aws-cdk/lib/util/npm.ts | 20 - packages/aws-cdk/lib/util/objects.ts | 176 ---- packages/aws-cdk/lib/util/parallel.ts | 44 - packages/aws-cdk/lib/util/tables.ts | 7 - packages/aws-cdk/lib/util/tracing.ts | 53 - packages/aws-cdk/lib/util/type-brands.ts | 44 - packages/aws-cdk/lib/util/types.ts | 32 - .../lib/util/validate-notification-arn.ts | 6 - packages/aws-cdk/lib/util/version-range.ts | 38 - .../aws-cdk/lib/util/work-graph-builder.ts | 176 ---- packages/aws-cdk/lib/util/work-graph-types.ts | 56 - packages/aws-cdk/lib/util/work-graph.ts | 401 ------- packages/aws-cdk/lib/util/yaml-cfn.ts | 59 -- packages/aws-cdk/lib/version.ts | 12 +- packages/aws-cdk/package.json | 66 +- packages/aws-cdk/test/account-cache.test.ts | 2 +- .../aws-cdk/test/api/account-cache.test.ts | 2 +- .../aws-cdk/test/api/assembly-versions.ts | 2 +- .../api/aws-auth/awscli-compatible.test.ts | 2 +- .../test/api/aws-auth/sdk-logger.test.ts | 4 +- packages/aws-cdk/test/api/bootstrap.test.ts | 6 +- packages/aws-cdk/test/api/bootstrap2.test.ts | 4 +- .../aws-cdk/test/api/cloud-assembly.test.ts | 2 +- .../aws-cdk/test/api/cloud-executable.test.ts | 4 +- .../api/cloudformation-deployments.test.ts | 10 +- .../test/api/credential-plugins.test.ts | 6 +- .../aws-cdk/test/api/deploy-stack.test.ts | 10 +- .../test/api/environment-resources.test.ts | 14 +- .../evaluate-cloudformation-template.test.ts | 4 +- packages/aws-cdk/test/api/exec.test.ts | 35 +- .../test/api/fake-cloudformation-stack.ts | 6 +- packages/aws-cdk/test/api/fake-sts.ts | 2 +- .../test/api/garbage-collection.test.ts | 4 +- ...ping-templates-hotswap-deployments.test.ts | 2 +- ...build-projects-hotswap-deployments.test.ts | 2 +- .../ecs-services-hotswap-deployments.test.ts | 4 +- .../api/hotswap/hotswap-deployments.test.ts | 4 +- .../test/api/hotswap/hotswap-test-setup.ts | 8 +- .../iam-policy-hotswap-deployment.test.ts | 2 +- ...nctions-docker-hotswap-deployments.test.ts | 2 +- ...mbda-functions-hotswap-deployments.test.ts | 2 +- ...nctions-inline-hotswap-deployments.test.ts | 2 +- ...rsions-aliases-hotswap-deployments.test.ts | 2 +- .../api/hotswap/nested-stacks-hotswap.test.ts | 2 +- .../s3-bucket-hotswap-deployments.test.ts | 4 +- .../state-machine-hotswap-deployments.test.ts | 2 +- .../api/lazy-list-stack-resources.test.ts | 2 +- .../test/api/lazy-lookup-export.test.ts | 2 +- .../api/logs/find-cloudwatch-logs.test.ts | 4 +- .../aws-cdk/test/api/logs/logging.test.ts | 2 +- .../test/api/logs/logs-monitor.test.ts | 2 +- .../test/api/plugin/credential-plugin.test.ts | 8 +- .../test/api/plugin/plugin-host.test.ts | 2 +- .../aws-cdk/test/api/sdk-provider.test.ts | 12 +- .../test/api/stack-activity-monitor.test.ts | 2 +- packages/aws-cdk/test/api/util/checks.test.ts | 2 +- .../cloudformation/stack-event-poller.test.ts | 2 +- .../aws-cdk/test/api/util/display.test.ts | 2 +- packages/aws-cdk/test/api/util/error.test.ts | 2 +- packages/aws-cdk/test/assets.test.ts | 6 +- packages/aws-cdk/test/bootstrap.test.ts | 2 +- packages/aws-cdk/test/build.test.ts | 2 +- packages/aws-cdk/test/cdk-toolkit.test.ts | 33 +- packages/aws-cdk/test/cli.test.ts | 2 +- .../test/commands/context-command.test.ts | 2 +- .../test/context-providers/amis.test.ts | 4 +- .../context-providers/asymmetric-vpcs.test.ts | 2 +- .../availability-zones.test.ts | 4 +- ...ndpoint-service-availability-zones.test.ts | 6 +- .../test/context-providers/generic.test.ts | 6 +- .../context-providers/hosted-zones.test.ts | 2 +- .../test/context-providers/keys.test.ts | 2 +- .../context-providers/load-balancers.test.ts | 10 +- .../context-providers/security-groups.test.ts | 2 +- .../context-providers/ssm-parameters.test.ts | 4 +- .../test/context-providers/vpcs.test.ts | 2 +- packages/aws-cdk/test/context.test.ts | 2 +- packages/aws-cdk/test/diff.test.ts | 6 +- .../aws-cdk/{lib => test/helpers}/build.ts | 3 +- packages/aws-cdk/test/import.test.ts | 4 +- packages/aws-cdk/test/init.test.ts | 2 +- packages/aws-cdk/test/list-stacks.test.ts | 22 +- packages/aws-cdk/test/notices.test.ts | 45 +- .../aws-cdk/test/platform-warnings.test.ts | 2 +- packages/aws-cdk/test/rwlock.test.ts | 2 +- packages/aws-cdk/test/serialize.test.ts | 2 +- packages/aws-cdk/test/settings.test.ts | 4 +- packages/aws-cdk/test/toolkit-error.test.ts | 2 +- .../aws-cdk/test/toolkit/cli-io-host.test.ts | 2 +- packages/aws-cdk/test/tree.test.ts | 2 +- packages/aws-cdk/test/usersettings.test.ts | 2 +- packages/aws-cdk/test/util.ts | 6 +- .../aws-cdk/test/util/applydefaults.test.ts | 2 +- packages/aws-cdk/test/util/arrays.test.ts | 2 +- .../aws-cdk/test/util/cloudformation.test.ts | 2 +- .../test/util/console-formatters.test.ts | 2 +- packages/aws-cdk/test/util/mock-sdk.ts | 4 +- .../aws-cdk/test/util/mock-toolkitinfo.ts | 4 +- packages/aws-cdk/test/util/objects.test.ts | 2 +- packages/aws-cdk/test/util/parallel.test.ts | 2 +- packages/aws-cdk/test/util/silent.ts | 2 +- .../aws-cdk/test/util/stack-monitor.test.ts | 10 +- .../util/validate-notification-arn.test.ts | 2 +- .../aws-cdk/test/util/version-range.test.ts | 2 +- .../aws-cdk/test/util/yargs-helpers.test.ts | 2 +- packages/aws-cdk/test/version.test.ts | 4 +- .../aws-cdk/test/work-graph-builder.test.ts | 6 +- packages/aws-cdk/test/work-graph.test.ts | 4 +- packages/aws-cdk/test/yaml.test.ts | 2 +- packages/aws-cdk/tsconfig.json | 11 +- tools/@aws-cdk/pkglint/lib/rules.ts | 1 + .../lib/convert-to-user-input-gen.ts | 2 +- .../user-input-gen/lib/user-input-gen.ts | 2 +- yarn.lock | 160 +-- 360 files changed, 554 insertions(+), 23126 deletions(-) delete mode 100644 packages/aws-cdk/lib/api/aws-auth/account-cache.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/cached.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/index.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/provider-caching.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/sdk.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/user-agent.ts delete mode 100644 packages/aws-cdk/lib/api/aws-auth/util.ts delete mode 100644 packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts delete mode 100644 packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts delete mode 100644 packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml delete mode 100644 packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts delete mode 100644 packages/aws-cdk/lib/api/bootstrap/index.ts delete mode 100644 packages/aws-cdk/lib/api/bootstrap/legacy-template.ts delete mode 100644 packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts delete mode 100644 packages/aws-cdk/lib/api/cxapp/cloud-executable.ts delete mode 100644 packages/aws-cdk/lib/api/cxapp/environments.ts delete mode 100644 packages/aws-cdk/lib/api/cxapp/exec.ts delete mode 100644 packages/aws-cdk/lib/api/deploy-stack.ts delete mode 100644 packages/aws-cdk/lib/api/deployments.ts delete mode 100644 packages/aws-cdk/lib/api/environment-access.ts delete mode 100644 packages/aws-cdk/lib/api/environment-resources.ts delete mode 100644 packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts delete mode 100644 packages/aws-cdk/lib/api/garbage-collection/garbage-collector.ts delete mode 100644 packages/aws-cdk/lib/api/garbage-collection/progress-printer.ts delete mode 100644 packages/aws-cdk/lib/api/garbage-collection/stack-refresh.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap-deployments.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/appsync-mapping-templates.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/code-build-projects.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/common.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/ecs-services.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/lambda-functions.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/s3-bucket-deployments.ts delete mode 100644 packages/aws-cdk/lib/api/hotswap/stepfunctions-state-machines.ts delete mode 100644 packages/aws-cdk/lib/api/index.ts delete mode 100644 packages/aws-cdk/lib/api/logs/find-cloudwatch-logs.ts delete mode 100644 packages/aws-cdk/lib/api/logs/logs-monitor.ts delete mode 100644 packages/aws-cdk/lib/api/nested-stack-helpers.ts delete mode 100644 packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts delete mode 100644 packages/aws-cdk/lib/api/plugin/index.ts delete mode 100644 packages/aws-cdk/lib/api/plugin/mode.ts delete mode 100644 packages/aws-cdk/lib/api/plugin/plugin.ts delete mode 100644 packages/aws-cdk/lib/api/toolkit-info.ts delete mode 100644 packages/aws-cdk/lib/api/util/checks.ts delete mode 100644 packages/aws-cdk/lib/api/util/cloudformation.ts delete mode 100644 packages/aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.ts delete mode 100644 packages/aws-cdk/lib/api/util/cloudformation/stack-event-poller.ts delete mode 100644 packages/aws-cdk/lib/api/util/cloudformation/stack-status.ts delete mode 100644 packages/aws-cdk/lib/api/util/display.ts delete mode 100644 packages/aws-cdk/lib/api/util/placeholders.ts delete mode 100644 packages/aws-cdk/lib/api/util/rwlock.ts delete mode 100644 packages/aws-cdk/lib/api/util/string-manipulation.ts delete mode 100644 packages/aws-cdk/lib/api/util/template-body-parameter.ts delete mode 100644 packages/aws-cdk/lib/assets.ts rename packages/aws-cdk/lib/{ => cli}/platform-warnings.ts (94%) rename packages/aws-cdk/lib/{util => cli}/yargs-helpers.ts (100%) rename packages/aws-cdk/lib/{ => commands}/init-hooks.ts (93%) rename packages/aws-cdk/lib/{ => commands}/init.ts (97%) delete mode 100644 packages/aws-cdk/lib/context-providers/ami.ts delete mode 100644 packages/aws-cdk/lib/context-providers/availability-zones.ts delete mode 100644 packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts delete mode 100644 packages/aws-cdk/lib/context-providers/hosted-zones.ts delete mode 100644 packages/aws-cdk/lib/context-providers/index.ts delete mode 100644 packages/aws-cdk/lib/context-providers/keys.ts delete mode 100644 packages/aws-cdk/lib/context-providers/load-balancers.ts delete mode 100644 packages/aws-cdk/lib/context-providers/security-groups.ts delete mode 100644 packages/aws-cdk/lib/context-providers/ssm-parameters.ts delete mode 100644 packages/aws-cdk/lib/context-providers/vpcs.ts delete mode 100644 packages/aws-cdk/lib/diff.ts delete mode 100644 packages/aws-cdk/lib/import.ts delete mode 100644 packages/aws-cdk/lib/init-templates/.no-packagejson-validator delete mode 100644 packages/aws-cdk/lib/init-templates/LICENSE delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/src/%name.PascalCased%.template.sln delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/src/%name.PascalCased%/%name.PascalCased%.template.csproj delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.cs delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/src/%name.PascalCased%/GlobalSuppressions.cs delete mode 100644 packages/aws-cdk/lib/init-templates/app/csharp/src/%name.PascalCased%/Program.template.cs delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/src/%name.PascalCased%.template.sln delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/src/%name.PascalCased%/%name.PascalCased%.template.fsproj delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.fs delete mode 100644 packages/aws-cdk/lib/init-templates/app/fsharp/src/%name.PascalCased%/Program.template.fs delete mode 100644 packages/aws-cdk/lib/init-templates/app/go/%name%.template.go delete mode 100644 packages/aws-cdk/lib/init-templates/app/go/%name%_test.template.go delete mode 100644 packages/aws-cdk/lib/init-templates/app/go/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/go/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/go/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/go/go.template.mod delete mode 100644 packages/aws-cdk/lib/init-templates/app/info.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/cdk.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/pom.xml delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/src/main/java/com/myorg/%name.PascalCased%App.template.java delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/src/main/java/com/myorg/%name.PascalCased%Stack.template.java delete mode 100644 packages/aws-cdk/lib/init-templates/app/java/src/test/java/com/myorg/%name.PascalCased%Test.template.java delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/.template.npmignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/bin/%name%.template.js delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/jest.config.js delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/lib/%name%-stack.template.js delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/package.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/javascript/test/%name%.test.template.js delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/%name.PythonModule%/%name.PythonModule%_stack.template.py delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/%name.PythonModule%/__init__.py delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/app.template.py delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/requirements-dev.txt delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/requirements.txt delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/source.bat delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/tests/__init__.py delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/tests/unit/__init__.py delete mode 100644 packages/aws-cdk/lib/init-templates/app/python/tests/unit/test_%name.PythonModule%_stack.template.py delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/.template.npmignore delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/bin/%name%.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/jest.config.js delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/lib/%name%-stack.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/package.json delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/test/%name%.test.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/app/typescript/tsconfig.json delete mode 100644 packages/aws-cdk/lib/init-templates/lib/info.json delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/.template.npmignore delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/jest.config.js delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/lib/index.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/package.json delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/test/%name%.test.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/lib/typescript/tsconfig.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/src/%name.PascalCased%.template.sln delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/src/%name.PascalCased%/%name.PascalCased%.template.csproj delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.cs delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/src/%name.PascalCased%/GlobalSuppressions.cs delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/csharp/src/%name.PascalCased%/Program.template.cs delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%.template.sln delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/%name.PascalCased%.template.fsproj delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/%name.PascalCased%Stack.template.fs delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/fsharp/src/%name.PascalCased%/Program.template.fs delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/go/%name%.template.go delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/go/%name%_test.template.go delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/go/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/go/README.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/go/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/go/go.template.mod delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/info.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/cdk.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/pom.xml delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/src/main/java/com/myorg/%name.PascalCased%App.template.java delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/src/main/java/com/myorg/%name.PascalCased%Stack.template.java delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/java/src/test/java/com/myorg/%name.PascalCased%StackTest.template.java delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/.template.npmignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/bin/%name%.template.js delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/jest.config.js delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/lib/%name%-stack.template.js delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/package.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/test/%name%.test.template.js delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/javascript/tsconfig.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/%name.PythonModule%/%name.PythonModule%_stack.template.py delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/%name.PythonModule%/__init__.py delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/app.template.py delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/requirements-dev.txt delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/requirements.txt delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/source.bat delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/tests/__init__.py delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/tests/unit/__init__.py delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/python/tests/unit/test_%name.PythonModule%_stack.template.py delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/.template.gitignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/.template.npmignore delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/README.template.md delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/bin/%name%.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/cdk.template.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/jest.config.js delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/lib/%name%-stack.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/package.json delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/test/%name%.test.template.ts delete mode 100644 packages/aws-cdk/lib/init-templates/sample-app/typescript/tsconfig.json delete mode 100644 packages/aws-cdk/lib/list-stacks.ts delete mode 100644 packages/aws-cdk/lib/logging.ts delete mode 100644 packages/aws-cdk/lib/notices.ts delete mode 100644 packages/aws-cdk/lib/os.ts delete mode 100644 packages/aws-cdk/lib/serialize.ts delete mode 100644 packages/aws-cdk/lib/settings.ts delete mode 100644 packages/aws-cdk/lib/toolkit/cli-io-host.ts delete mode 100644 packages/aws-cdk/lib/toolkit/error.ts delete mode 100644 packages/aws-cdk/lib/tree.ts delete mode 100644 packages/aws-cdk/lib/util/archive.ts delete mode 100644 packages/aws-cdk/lib/util/arrays.ts delete mode 100644 packages/aws-cdk/lib/util/asset-manifest-builder.ts delete mode 100644 packages/aws-cdk/lib/util/asset-publishing.ts delete mode 100644 packages/aws-cdk/lib/util/bool.ts delete mode 100644 packages/aws-cdk/lib/util/console-formatters.ts delete mode 100644 packages/aws-cdk/lib/util/content-hash.ts delete mode 100644 packages/aws-cdk/lib/util/directories.ts delete mode 100644 packages/aws-cdk/lib/util/error.ts delete mode 100644 packages/aws-cdk/lib/util/index.ts delete mode 100644 packages/aws-cdk/lib/util/npm.ts delete mode 100644 packages/aws-cdk/lib/util/objects.ts delete mode 100644 packages/aws-cdk/lib/util/parallel.ts delete mode 100644 packages/aws-cdk/lib/util/tables.ts delete mode 100644 packages/aws-cdk/lib/util/tracing.ts delete mode 100644 packages/aws-cdk/lib/util/type-brands.ts delete mode 100644 packages/aws-cdk/lib/util/types.ts delete mode 100644 packages/aws-cdk/lib/util/validate-notification-arn.ts delete mode 100644 packages/aws-cdk/lib/util/version-range.ts delete mode 100644 packages/aws-cdk/lib/util/work-graph-builder.ts delete mode 100644 packages/aws-cdk/lib/util/work-graph-types.ts delete mode 100644 packages/aws-cdk/lib/util/work-graph.ts delete mode 100644 packages/aws-cdk/lib/util/yaml-cfn.ts rename packages/aws-cdk/{lib => test/helpers}/build.ts (80%) diff --git a/packages/@aws-cdk/cli-lib-alpha/.npmignore b/packages/@aws-cdk/cli-lib-alpha/.npmignore index a3afe6f7b23d6..618688cd6d5a9 100644 --- a/packages/@aws-cdk/cli-lib-alpha/.npmignore +++ b/packages/@aws-cdk/cli-lib-alpha/.npmignore @@ -17,22 +17,21 @@ tsconfig.json # Make the linter happy, but... !*.js -# ... we don't actualy want unbundled files +# ... we don't actually want unbundled files *.js *.ts # Explicitly allow all required files +!resources/** +!db.json.gz !lib/main.js !lib/bridge.js !lib/setup-sandbox.js -!lib/api/bootstrap/bootstrap-template.yaml !*.d.ts !*.lit.ts !rosetta/ !.jsii !.jsii.tabl.json !.warnings.jsii.js -!build-info.json !NOTICE !THIRD_PARTY_LICENSES -!db.json.gz diff --git a/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh b/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh index d04e7fe939da4..7b08172748b7d 100755 --- a/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh +++ b/packages/@aws-cdk/cli-lib-alpha/build-tools/copy-cli-resources.sh @@ -3,10 +3,7 @@ set -euo pipefail aws_cdk="$1" -# Copy all resources that aws_cdk/generate.sh produced, and some othersCall the generator for the -cp $aws_cdk/build-info.json ./ -cp -R $aws_cdk/lib/init-templates ./lib/ - -mkdir -p ./lib/api/bootstrap/ && cp $aws_cdk/lib/api/bootstrap/bootstrap-template.yaml ./lib/api/bootstrap/ +# Copy the various resource files from the CLI +cp -R $aws_cdk/resources ./resources cp $aws_cdk/lib/index_bg.wasm ./lib/ cp $aws_cdk/db.json.gz ./ diff --git a/packages/@aws-cdk/tmp-toolkit-helpers/generate.sh b/packages/@aws-cdk/tmp-toolkit-helpers/generate.sh index 3473b975f5eaf..efc7fea40f342 100755 --- a/packages/@aws-cdk/tmp-toolkit-helpers/generate.sh +++ b/packages/@aws-cdk/tmp-toolkit-helpers/generate.sh @@ -1,20 +1,6 @@ #!/bin/bash set -euo pipefail -commit=${CODEBUILD_RESOLVED_SOURCE_VERSION:-} -# CODEBUILD_RESOLVED_SOURCE_VERSION is not defined (i.e. local build or CodePipeline build), -# use the HEAD commit hash -if [ -z "${commit}" ]; then - commit="$(git rev-parse --verify HEAD)" -fi - -cat > resources/build-info.json < @@ -240,6 +240,7 @@ different environments). This means that the name of the synthesized template file will be based on the construct path and not on the defined `stackName` of the stack. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.16.0 | `false` | `true` | @@ -247,6 +248,7 @@ of the stack. **Compatibility with old behavior:** Pass stack identifiers to the CLI instead of stack names. + ### aws-cdk:enableDiffNoFail *Make `cdk diff` not fail when there are differences* (default) @@ -254,13 +256,14 @@ of the stack. Determines what status code `cdk diff` should return when the specified stack differs from the deployed stack or the local CloudFormation template: -- `aws-cdk:enableDiffNoFail=true` => status code == 0 -- `aws-cdk:enableDiffNoFail=false` => status code == 1 +* `aws-cdk:enableDiffNoFail=true` => status code == 0 +* `aws-cdk:enableDiffNoFail=false` => status code == 1 You can override this behavior with the --fail flag: -- `--fail` => status code == 1 -- `--no-fail` => status code == 0 +* `--fail` => status code == 1 +* `--no-fail` => status code == 0 + | Since | Default | Recommended | | ----- | ----- | ----- | @@ -269,6 +272,7 @@ You can override this behavior with the --fail flag: **Compatibility with old behavior:** Specify `--fail` to the CLI. + ### @aws-cdk/aws-ecr-assets:dockerIgnoreSupport *DockerImageAsset properly supports `.dockerignore` files by default* (default) @@ -280,6 +284,7 @@ is standard Docker ignore semantics. This is a feature flag as the old behavior was technically incorrect but users may have come to depend on it. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.73.0 | `false` | `true` | @@ -287,6 +292,7 @@ users may have come to depend on it. **Compatibility with old behavior:** Update your `.dockerignore` file to match standard Docker ignore rules, if necessary. + ### @aws-cdk/aws-secretsmanager:parseOwnedSecretName *Fix the referencing of SecretsManager names from ARNs* (default) @@ -297,6 +303,7 @@ rather than the default full resource name, which includes the SecretsManager su If this flag is not set, Secret.secretName will include the SecretsManager suffix, which cannot be directly used by SecretsManager.DescribeSecret, and must be parsed by the user first (e.g., Fn:Join, Fn:Select, Fn:Split). + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.77.0 | `false` | `true` | @@ -304,6 +311,7 @@ used by SecretsManager.DescribeSecret, and must be parsed by the user first (e.g **Compatibility with old behavior:** Use `parseArn(secret.secretName).resourceName` to emulate the incorrect old parsing. + ### @aws-cdk/aws-kms:defaultKeyPolicies *Tighten default KMS key policies* (default) @@ -320,6 +328,7 @@ true, the policy matches what happens when this feature flag is set. Additionally, if this flag is not set and the user supplies a custom key policy, this will be appended to the key's default policy (rather than replacing it). + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.78.0 | `false` | `true` | @@ -327,6 +336,7 @@ to the key's default policy (rather than replacing it). **Compatibility with old behavior:** Pass `trustAccountIdentities: false` to `Key` construct to restore the old behavior. + ### @aws-cdk/aws-s3:grantWriteWithoutAcl *Remove `PutObjectAcl` from Bucket.grantWrite* (default) @@ -337,6 +347,7 @@ which could be used to grant read/write object access to IAM principals in other Use a feature flag to make sure existing customers who might be relying on the overly-broad permissions are not broken. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.85.0 | `false` | `true` | @@ -344,6 +355,7 @@ on the overly-broad permissions are not broken. **Compatibility with old behavior:** Call `bucket.grantPutAcl()` in addition to `bucket.grantWrite()` to grant ACL permissions. + ### @aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount *Do not specify a default DesiredCount for ECS services* (default) @@ -358,6 +370,7 @@ If this flag is not set, the default behaviour for CfnService.desiredCount is to desiredCount of 1, if one is not provided. If true, a default will not be defined for CfnService.desiredCount and as such desiredCount will be undefined, if one is not provided. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.92.0 | `false` | `true` | @@ -365,12 +378,14 @@ CfnService.desiredCount and as such desiredCount will be undefined, if one is no **Compatibility with old behavior:** You can pass `desiredCount: 1` explicitly, but you should never need this. + ### @aws-cdk/aws-efs:defaultEncryptionAtRest *Enable this feature flag to have elastic file systems encrypted at rest by default.* (default) Encryption can also be configured explicitly using the `encrypted` property. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.98.0 | `false` | `true` | @@ -378,6 +393,7 @@ Encryption can also be configured explicitly using the `encrypted` property. **Compatibility with old behavior:** Pass the `encrypted: false` property to the `FileSystem` construct to disable encryption. + ### @aws-cdk/core:newStyleStackSynthesis *Switch to new stack synthesis method which enables CI/CD* (fix) @@ -385,11 +401,13 @@ Encryption can also be configured explicitly using the `encrypted` property. If this flag is specified, all `Stack`s will use the `DefaultStackSynthesizer` by default. If it is not set, they will use the `LegacyStackSynthesizer`. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.39.0 | `false` | `true` | | 2.0.0 | `true` | `true` | + ### @aws-cdk/core:stackRelativeExports *Name exports based on the construct paths relative to the stack, rather than the global construct path* (fix) @@ -399,11 +417,13 @@ ensure uniqueness, and makes the export names robust against refactoring the location of the stack in the construct tree (specifically, moving the Stack into a Stage). + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.58.0 | `false` | `true` | | 2.0.0 | `true` | `true` | + ### @aws-cdk/aws-rds:lowercaseDbIdentifier *Force lowercasing of RDS Cluster names in CDK* (fix) @@ -418,11 +438,13 @@ Must be behind a permanent flag because changing a name from mixed case to lower would lead CloudFormation to think the name was changed and would trigger a cluster replacement (losing data!). + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.97.0 | `false` | `true` | | 2.0.0 | `true` | `true` | + ### @aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId *Allow adding/removing multiple UsagePlanKeys independently* (fix) @@ -440,11 +462,13 @@ which again is disallowed. In effect, there is no way to get out of this mess in a backwards compatible way, while supporting existing stacks. This flag changes the logical id layout of UsagePlanKey to not be sensitive to order. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.98.0 | `false` | `true` | | 2.0.0 | `true` | `true` | + ### @aws-cdk/aws-lambda:recognizeVersionProps *Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.* (fix) @@ -454,22 +478,26 @@ not constitute creating a new Version. See 'currentVersion' section in the aws-lambda module's README for more details. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.106.0 | `false` | `true` | | 2.0.0 | `true` | `true` | + ### @aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021 *Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.* (fix) The security policy can also be configured explicitly using the `minimumProtocolVersion` property. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.117.0 | `false` | `true` | | 2.0.0 | `true` | `true` | + ### @aws-cdk/core:target-partitions *What regions to include in lookup tables of environment agnostic stacks* (config) @@ -479,11 +507,13 @@ of unnecessary regions included in stacks without a known region. The type of this value should be a list of strings. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.137.0 | `false` | `["aws","aws-cn"]` | | 2.4.0 | `false` | `["aws","aws-cn"]` | + ### @aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver *ECS extensions will automatically add an `awslogs` driver if no logging is specified* (default) @@ -493,6 +523,7 @@ Enable this feature flag to configure default logging behavior for the ECS Servi This is a feature flag as the new behavior provides a better default experience for the users. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.140.0 | `false` | `true` | @@ -500,6 +531,7 @@ This is a feature flag as the new behavior provides a better default experience **Compatibility with old behavior:** Specify a log driver explicitly. + ### @aws-cdk/aws-ec2:uniqueImdsv2TemplateName *Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names.* (fix) @@ -510,11 +542,13 @@ account and region, the deployments would always fail as the generated Launch Te The new implementation addresses this issue by generating the Launch Template name with the `Names.uniqueId` method. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.140.0 | `false` | `true` | | 2.8.0 | `false` | `true` | + ### @aws-cdk/aws-iam:minimizePolicies *Minimize IAM policies by combining Statements* (config) @@ -523,11 +557,13 @@ Minimize IAM policies by combining Principals, Actions and Resources of two Statements in the policies, as long as it doesn't change the meaning of the policy. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.150.0 | `false` | `true` | | 2.18.0 | `false` | `true` | + ### @aws-cdk/core:checkSecretUsage *Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations* (config) @@ -536,11 +572,13 @@ With this flag enabled, `SecretValue` instances can only be passed to constructs that accept `SecretValue`s; otherwise, `unsafeUnwrap()` must be called to use it as a regular string. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.153.0 | `false` | `true` | | 2.21.0 | `false` | `true` | + ### @aws-cdk/aws-lambda:recognizeLayerVersion *Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.* (fix) @@ -549,11 +587,13 @@ This flag correct incorporates Lambda Layer properties into the Lambda Function See 'currentVersion' section in the aws-lambda module's README for more details. + | Since | Default | Recommended | | ----- | ----- | ----- | | 1.159.0 | `false` | `true` | | 2.27.0 | `false` | `true` | + ### @aws-cdk/core:validateSnapshotRemovalPolicy *Error on snapshot removal policies on resources that do not support it.* (default) @@ -563,6 +603,7 @@ If supplied on an unsupported resource, CloudFormation ignores the policy altoge This flag will reduce confusion and unexpected loss of data when erroneously supplying the snapshot removal policy. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -570,6 +611,7 @@ the snapshot removal policy. **Compatibility with old behavior:** The old behavior was incorrect. Update your source to not specify SNAPSHOT policies on resources that do not support it. + ### @aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName *Generate key aliases that include the stack name* (fix) @@ -581,11 +623,13 @@ the KMS key alias name created for these pipelines may be the same due to how th This new implementation creates a stack safe resource name for the alias using the stack name instead of the stack ID. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.29.0 | `false` | `true` | + ### @aws-cdk/aws-s3:createDefaultLoggingPolicy *Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist.* (fix) @@ -599,13 +643,15 @@ and error indicating that a bucket policy already exists. In cases where we know what the required policy is we can go ahead and create the policy so we can remain in control of it. -@see +@see https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AWS-logs-and-resource-policy.html#AWS-logs-infrastructure-S3 + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.31.0 | `false` | `true` | + ### @aws-cdk/aws-sns-subscriptions:restrictSqsDescryption *Restrict KMS key policy for encrypted Queues a bit more* (fix) @@ -617,11 +663,13 @@ Previously the decryption was only restricted to the SNS service principal. To m secure, it is a good practice to restrict the decryption further and only allow the connected SNS topic to decryption the subscribed queue. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.32.0 | `false` | `true` | + ### @aws-cdk/aws-ecs:arnFormatIncludesClusterName *ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID.* (fix) @@ -631,31 +679,35 @@ If this flag is set, the new ARN format (with cluster name) for ECS is used. This is a feature flag as the old format is still valid for existing ECS clusters. -See +See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.35.0 | `false` | `true` | + ### @aws-cdk/aws-apigateway:disableCloudWatchRole *Make default CloudWatch Role behavior safe for multiple API Gateways in one environment* (fix) Enable this feature flag to change the default behavior for aws-apigateway.RestApi and aws-apigateway.SpecRestApi -to *not* create a CloudWatch role and Account. There is only a single ApiGateway account per AWS +to _not_ create a CloudWatch role and Account. There is only a single ApiGateway account per AWS environment which means that each time you create a RestApi in your account the ApiGateway account is overwritten. If at some point the newest RestApi is deleted, the ApiGateway Account and CloudWatch role will also be deleted, breaking any existing ApiGateways that were depending on them. When this flag is enabled you should either create the ApiGateway account and CloudWatch role -separately *or* only enable the cloudWatchRole on a single RestApi. +separately _or_ only enable the cloudWatchRole on a single RestApi. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.38.0 | `false` | `true` | + ### @aws-cdk/core:enablePartitionLiterals *Make ARNs concrete if AWS partition is known* (fix) @@ -684,11 +736,13 @@ Principal: The intrinsic function will still be used in Stacks where no region is defined or the region's partition is unknown. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.38.0 | `false` | `true` | + ### @aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker *Avoid setting the "ECS" deployment controller when adding a circuit breaker* (fix) @@ -699,11 +753,13 @@ This does not change any behaviour as the default deployment controller when it This is a feature flag as the new behavior provides a better default experience for the users. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.51.0 | `false` | `true` | + ### @aws-cdk/aws-events:eventsTargetQueueSameAccount *Event Rules may only push to encrypted SQS queues in the same account* (fix) @@ -712,11 +768,13 @@ This flag applies to SQS Queues that are used as the target of event Rules. When from the same account as the Rule can send messages. If a queue is unencrypted, this restriction will always apply, regardless of the value of this flag. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.51.0 | `false` | `true` | + ### @aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName *Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in.* (fix) @@ -727,11 +785,13 @@ of a role using the same default policy name. This new implementation creates default policy names based on the constructs node path in their stack. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.60.0 | `false` | `true` | + ### @aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy *Use S3 Bucket Policy instead of ACLs for Server Access Logging* (fix) @@ -743,13 +803,15 @@ enabled on the bucket. This flag uses a Bucket Policy statement to allow Server Access Log delivery, following best practices for S3. -@see +@see https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-server-access-logging.html + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.60.0 | `false` | `true` | + ### @aws-cdk/customresources:installLatestAwsSdkDefault *Whether to install the latest SDK by default in AwsCustomResource* (default) @@ -761,6 +823,7 @@ do not have internet access, or in environments where 'npmjs.com' is not availab The recommended setting is to disable the default installation behavior, and pass the flag on a resource-by-resource basis to enable it if necessary. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -768,6 +831,7 @@ flag on a resource-by-resource basis to enable it if necessary. **Compatibility with old behavior:** Set installLatestAwsSdk: true on all resources that need it. + ### @aws-cdk/aws-route53-patters:useCertificate *Use the official `Certificate` resource instead of `DnsValidatedCertificate`* (default) @@ -777,6 +841,7 @@ of the deprecated `DnsValidatedCertificate` construct. If this flag is enabled a the stack in a region other than us-east-1 then you must also set `crossRegionReferences=true` on the stack. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -784,6 +849,7 @@ stack. **Compatibility with old behavior:** Define a `DnsValidatedCertificate` explicitly and pass in the `certificate` property + ### @aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup *Remove CloudWatch alarms from deployment group* (fix) @@ -792,11 +858,13 @@ Enable this flag to be able to remove all CloudWatch alarms from a deployment gr the alarms from the construct. If this flag is not set, removing all alarms from the construct will still leave the alarms configured for the deployment group. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.65.0 | `false` | `true` | + ### @aws-cdk/aws-rds:databaseProxyUniqueResourceName *Use unique resource name for Database Proxy* (fix) @@ -809,11 +877,13 @@ If this flag is set, the default behavior is to use unique resource names for ea This is a feature flag as the old behavior was technically incorrect, but users may have come to depend on it. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.65.0 | `false` | `true` | + ### @aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId *Include authorizer configuration in the calculation of the API deployment logical ID.* (fix) @@ -823,11 +893,13 @@ the API configuration, including methods, and resources, etc. Enable this featur to also include the configuration of any authorizer attached to the API in the calculation, so any changes made to an authorizer will create a new deployment. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.66.0 | `false` | `true` | + ### @aws-cdk/aws-ec2:launchTemplateDefaultUserData *Define user data for a launch template by default when a machine image is provided.* (fix) @@ -836,11 +908,13 @@ The ec2.LaunchTemplate construct did not define user data when a machine image i provided despite the document. If this is set, a user data is automatically defined according to the OS of the machine image. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.67.0 | `false` | `true` | + ### @aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments *SecretTargetAttachments uses the ResourcePolicy of the attached Secret.* (fix) @@ -856,11 +930,13 @@ This won't be possible without intervention due to limitation outlined above. First remove all permissions granted to the Secret and deploy without the ResourcePolicies. Then you can re-add the permissions and deploy again. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.67.0 | `false` | `true` | + ### @aws-cdk/aws-redshift:columnId *Whether to use an ID to track Redshift column changes* (fix) @@ -877,11 +953,13 @@ than their `name`. This will prevent data loss when columns are renamed. initial deployment, the columns will be dropped and recreated, causing data loss. After the initial deployment of the `id`s, the `name`s of the columns can be changed without data loss. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.68.0 | `false` | `true` | + ### @aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2 *Enable AmazonEMRServicePolicy_v2 managed policies* (fix) @@ -895,11 +973,13 @@ managed policies. This is a feature flag as the old behavior will be deprecated, but some resources may require manual intervention since they might not have the appropriate tags propagated automatically. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.72.0 | `false` | `true` | + ### @aws-cdk/aws-apigateway:requestValidatorUniqueId *Generate a unique id for each RequestValidator added to a method* (fix) @@ -910,11 +990,13 @@ providing the `RequestValidatorOptions` in the `addMethod()` method. If the flag is not set then only a single RequestValidator can be added in this way. Any additional RequestValidators have to be created directly with `new RequestValidator`. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.78.0 | `false` | `true` | + ### @aws-cdk/aws-ec2:restrictDefaultSecurityGroup *Restrict access to the VPC default security group* (default) @@ -924,17 +1006,20 @@ VPC default security group. When a VPC is created, a default security group is created as well and this cannot be deleted. The default security group is created with ingress/egress rules that allow -*all* traffic. [AWS Security best practices recommend](https://docs.aws.amazon.com/securityhub/latest/userguide/ec2-controls.html#ec2-2) +_all_ traffic. [AWS Security best practices recommend](https://docs.aws.amazon.com/securityhub/latest/userguide/ec2-controls.html#ec2-2) removing these ingress/egress rules in order to restrict access to the default security group. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.78.0 | `false` | `true` | -**Compatibility with old behavior:** +**Compatibility with old behavior:** To allow all ingress/egress traffic to the VPC default security group you can set the `restrictDefaultSecurityGroup: false`. + + ### @aws-cdk/aws-kms:aliasNameRef @@ -946,11 +1031,13 @@ when referencing key.aliasName or key.keyArn. If the flag is not set then a raw string is passed as the Alias name and no implicit dependencies will be set. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.83.0 | `false` | `true` | + ### @aws-cdk/core:includePrefixInUniqueNameGeneration *Include the stack prefix in the stack name generation process* (fix) @@ -964,11 +1051,13 @@ If the flag is not set, then the prefix of the stack is prepended to the generat feature flag can lead to a change in stacks' name. Changing a stack name mean recreating the whole stack, which is not viable in some productive setups. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.84.0 | `false` | `true` | + ### @aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig *Generate a launch template when creating an AutoScalingGroup* (fix) @@ -981,14 +1070,17 @@ will now create an equivalent 'launchTemplate'. Alternatively, users can provide attempt to set user data according to the OS of the machine image if explicit user data is not provided. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.88.0 | `false` | `true` | -**Compatibility with old behavior:** +**Compatibility with old behavior:** If backwards compatibility needs to be maintained due to an existing autoscaling group using a launch config, set this flag to false. + + ### @aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby @@ -997,6 +1089,7 @@ provided. If this is set, an opensearch domain will automatically be created with multi-az with standby enabled. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1004,6 +1097,7 @@ multi-az with standby enabled. **Compatibility with old behavior:** Pass `capacity.multiAzWithStandbyEnabled: false` to `Domain` construct to restore the old behavior. + ### @aws-cdk/aws-efs:denyAnonymousAccess *EFS denies anonymous clients accesses* (default) @@ -1014,6 +1108,7 @@ access to `efs.FileSystem`. If this flag is not set, `efs.FileSystem` will allow all anonymous clients that can access over the network. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1021,6 +1116,7 @@ that can access over the network. **Compatibility with old behavior:** You can pass `allowAnonymousAccess: true` so allow anonymous clients access. + ### @aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId *When enabled, mount targets will have a stable logicalId that is linked to the associated subnet.* (fix) @@ -1032,11 +1128,13 @@ subnets changes. Set this flag to false for existing mount targets. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.93.0 | `false` | `true` | + ### @aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion *Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default* (default) @@ -1046,6 +1144,7 @@ functions will us the latest version of the runtime provided by the Lambda service. Do not use this if you your lambda function is reliant on dependencies shipped as part of the runtime environment. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1053,6 +1152,7 @@ shipped as part of the runtime environment. **Compatibility with old behavior:** Pass `runtime: lambda.Runtime.NODEJS_16_X` to `Function` construct to restore the previous behavior. + ### @aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier *When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id.* (fix) @@ -1061,11 +1161,13 @@ When this feature flag is enabled, we use the IGraphqlApi ARN rather than ID whe the GraphqlApi construct. Using the ARN allows the association to support an association with a source api or merged api in another account. Note that for existing source api associations created with this flag disabled, enabling the flag will lead to a resource replacement. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.97.0 | `false` | `true` | + ### @aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters *When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change.* (fix) @@ -1077,11 +1179,13 @@ from AuroraCluster. If the flag is set to false then it can only make one `AuroraClusterInstance` with each `InstanceParameterGroup` in the AuroraCluster. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.97.0 | `false` | `true` | + ### @aws-cdk/aws-rds:preventRenderingDeprecatedCredentials *When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials.* (fix) @@ -1099,11 +1203,13 @@ Set this flag to prevent rendering deprecated `credentials` and creating an extra database secret when only using `snapshotCredentials` to create an RDS database cluster from a snapshot. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.98.0 | `false` | `true` | + ### @aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource *When enabled, the CodeCommit source action is using the default branch name 'main'.* (fix) @@ -1112,11 +1218,13 @@ When setting up a CodeCommit source action for the source stage of a pipeline, p default branch is 'master'. However, with the activation of this feature flag, the default branch is updated to 'main'. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.103.1 | `false` | `true` | + ### @aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction *When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID.* (fix) @@ -1128,11 +1236,13 @@ can be created with `LambdaAction`. If the flag is set to false then it can only make one alarm for the Lambda with `LambdaAction`. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.124.0 | `false` | `true` | + ### @aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse *Enables Pipeline to set the default value for crossAccountKeys to false.* (default) @@ -1140,6 +1250,7 @@ If the flag is set to false then it can only make one alarm for the Lambda with When this feature flag is enabled, and the `crossAccountKeys` property is not provided in a `Pipeline` construct, the construct automatically defaults the value of this property to false. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1147,6 +1258,7 @@ construct, the construct automatically defaults the value of this property to fa **Compatibility with old behavior:** Pass `crossAccountKeys: true` to `Pipeline` construct to restore the previous behavior. + ### @aws-cdk/aws-codepipeline:defaultPipelineTypeToV2 *Enables Pipeline to set the default pipeline type to V2.* (default) @@ -1154,6 +1266,7 @@ construct, the construct automatically defaults the value of this property to fa When this feature flag is enabled, and the `pipelineType` property is not provided in a `Pipeline` construct, the construct automatically defaults the value of this property to `PipelineType.V2`. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1161,6 +1274,7 @@ construct, the construct automatically defaults the value of this property to `P **Compatibility with old behavior:** Pass `pipelineType: PipelineType.V1` to `Pipeline` construct to restore the previous behavior. + ### @aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope *When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only.* (fix) @@ -1168,11 +1282,13 @@ construct, the construct automatically defaults the value of this property to `P When this feature flag is enabled and calling KMS key grant method, the created IAM policy will reduce the resource scope from '*' to this specific granting KMS key. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.134.0 | `false` | `true` | + ### @aws-cdk/aws-eks:nodegroupNameAttribute *When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix.* (fix) @@ -1180,17 +1296,20 @@ When this feature flag is enabled and calling KMS key grant method, the created When this feature flag is enabled, the nodegroupName attribute will be exactly the name of the nodegroup without any prefix. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.139.0 | `false` | `true` | + ### @aws-cdk/aws-ec2:ebsDefaultGp3Volume *When enabled, the default volume type of the EBS volume will be GP3* (default) When this featuer flag is enabled, the default volume type of the EBS volume will be `EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3`. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1198,6 +1317,7 @@ When this featuer flag is enabled, the default volume type of the EBS volume wil **Compatibility with old behavior:** Pass `volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD` to `Volume` construct to restore the previous behavior. + ### @aws-cdk/pipelines:reduceAssetRoleTrustScope *Remove the root account principal from PipelineAssetsFileRole trust policy* (default) @@ -1205,6 +1325,7 @@ When this featuer flag is enabled, the default volume type of the EBS volume wil When this feature flag is enabled, the root account principal will not be added to the trust policy of asset role. When this feature flag is disabled, it will keep the root account principal in the trust policy. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1212,12 +1333,14 @@ When this feature flag is disabled, it will keep the root account principal in t **Compatibility with old behavior:** Disable the feature flag to add the root account principal back + ### @aws-cdk/aws-ecs:removeDefaultDeploymentAlarm *When enabled, remove default deployment alarm settings* (default) When this featuer flag is enabled, remove the default deployment alarm settings when creating a AWS ECS service. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1225,6 +1348,7 @@ When this featuer flag is enabled, remove the default deployment alarm settings **Compatibility with old behavior:** Set AWS::ECS::Service 'DeploymentAlarms' manually to restore the previous behavior. + ### @aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault *When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default* (fix) @@ -1232,17 +1356,19 @@ When this featuer flag is enabled, remove the default deployment alarm settings This results in 'logApiResponseData' being passed as true to the custom resource provider. This will cause the custom resource handler to receive an 'Update' event. If you don't have an SDK call configured for the 'Update' event and you're dependent on specific SDK call response data, you will see this error from CFN: -CustomResource attribute error: Vendor response doesn't contain attribute in object. See ) for more details. +CustomResource attribute error: Vendor response doesn't contain attribute in object. See https://github.com/aws/aws-cdk/issues/29949) for more details. Unlike most feature flags, we don't recommend setting this feature flag to true. However, if you're using the 'AwsCustomResource' construct with 'logApiResponseData' as true in the event object, then setting this feature flag will keep this behavior. Otherwise, setting this feature flag to false will trigger an 'Update' event by removing the 'logApiResponseData' property from the event object. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.145.0 | `false` | `false` | + ### @aws-cdk/aws-s3:keepNotificationInImportedBucket *When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack.* (fix) @@ -1252,11 +1378,13 @@ Currently, adding notifications to a bucket where it was created by ourselves wi When this feature flag is enabled, adding notifications to a bucket in the current stack will only update notification defined in this stack. Other notifications that are not managed by this stack will be kept. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.155.0 | `false` | `false` | + ### @aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask *When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.* (fix) @@ -1267,6 +1395,7 @@ of State Machine Task definition. When this feature flag is enabled, specify newly introduced props 's3InputUri' and 's3OutputUri' to populate S3 uri under input and output fields in state machine task definition for Bedrock invoke model. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1274,6 +1403,7 @@ When this feature flag is enabled, specify newly introduced props 's3InputUri' a **Compatibility with old behavior:** Disable the feature flag to use input and output path fields for s3 URI + ### @aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions *When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration* (fix) @@ -1283,6 +1413,7 @@ specified as logConfiguration and it will grant 'Resources': ['*'] to the task r When this feature flag is enabled, we will only grant the necessary permissions when users specify cloudwatch log group. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1290,6 +1421,7 @@ When this feature flag is enabled, we will only grant the necessary permissions **Compatibility with old behavior:** Disable the feature flag to continue grant permissions to log group when no log group is specified + ### @aws-cdk/aws-ec2:ec2SumTImeoutEnabled *When enabled, initOptions.timeout and resourceSignalTimeout values will be summed together.* (fix) @@ -1299,11 +1431,13 @@ only the value from 'resourceSignalTimeout' will be used. When this feature flag is enabled, if both initOptions.timeout and resourceSignalTimeout are specified, the values will to be summed together. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.160.0 | `false` | `true` | + ### @aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission *When enabled, a Lambda authorizer Permission created when using GraphqlApi will be properly scoped with a SourceArn.* (fix) @@ -1315,11 +1449,13 @@ it allows invocations from any source. When this feature flag is enabled, the AWS::Lambda::Permission will be properly scoped with the SourceArn corresponding to the specific AppSync GraphQL API. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.161.0 | `false` | `true` | + ### @aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages *When enabled, both `@aws-sdk` and `@smithy` packages will be excluded from the Lambda Node.js 18.x runtime to prevent version mismatches in bundled applications.* (fix) @@ -1330,11 +1466,13 @@ However, this can cause version mismatches between the '@aws-sdk/*' and '@smithy When this feature flag is enabled, both '@aws-sdk/*' and '@smithy/*' packages will be excluded during the bundling process. This ensures that no mismatches occur between these tightly coupled dependencies when using the AWS SDK v3 in Lambda functions. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.161.0 | `false` | `true` | + ### @aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId *When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn`* (fix) @@ -1343,6 +1481,7 @@ Currently, the value of the property 'instanceResourceId' in construct 'Database When this feature flag is enabled, the value of that property will be as expected set to 'DbiResourceId' attribute, and that will fix the grantConnect method. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1350,6 +1489,7 @@ When this feature flag is enabled, the value of that property will be as expecte **Compatibility with old behavior:** Disable the feature flag to use `DbInstanceArn` as value for property `instanceResourceId` + ### @aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics *When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values.* (fix) @@ -1358,11 +1498,13 @@ Without enabling this feature flag, `cfn-include` will silently drop resource up Enabling this feature flag will make `cfn-include` throw on these templates, unless you specify the logical ID of the resource in the 'unhydratedResources' property. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.161.0 | `false` | `true` | + ### @aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy *When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN.* (fix) @@ -1372,11 +1514,13 @@ The revision number at the end will be replaced with a wildcard which it shouldn When this feature flag is enabled, if the task definition is created in the stack, the 'Resource' section will 'Ref' the taskDefinition. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.163.0 | `false` | `true` | + ### @aws-cdk/aws-dynamodb:resourcePolicyPerReplica *When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas* (fix) @@ -1388,11 +1532,13 @@ This will prevent you from creating a new table which has an additional replica This is a feature flag as the old behavior was technically incorrect but users may have come to depend on it. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.164.0 | `false` | `true` | + ### @aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault *When enabled, the BastionHost construct will use the latest Amazon Linux 2023 AMI, instead of Amazon Linux 2.* (default) @@ -1404,6 +1550,7 @@ and secure option. When this feature flag is enabled, if you do not pass the machineImage property to the BastionHost construct, the latest Amazon Linux 2023 version will be used instead of Amazon Linux 2. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1411,6 +1558,7 @@ the latest Amazon Linux 2023 version will be used instead of Amazon Linux 2. **Compatibility with old behavior:** Disable the feature flag or explicitly pass an Amazon Linux 2 machine image to the BastionHost construct. + ### @aws-cdk/core:aspectStabilization *When enabled, a stabilization loop will be run when invoking Aspects during synthesis.* (config) @@ -1420,11 +1568,13 @@ This means that the Aspects that create other Aspects are not run and Aspects th When this feature flag is enabled, a stabilization loop is run to recurse the construct tree multiple times when invoking Aspects. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.172.0 | `true` | `true` | + ### @aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource *When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource.* (fix) @@ -1434,22 +1584,25 @@ creates a custom resource internally, but the new method doesn't need a custom r If the flag is set to false then a custom resource will be created when using `UserPoolDomainTarget`. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | | 2.174.0 | `false` | `true` | + ### @aws-cdk/aws-ecs:disableEcsImdsBlocking *When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)*** (temporary) In an ECS Cluster with `MachineImageType.AMAZON_LINUX_2`, the canContainersAccessInstanceRole=false option attempts to add commands to block containers from accessing IMDS. CDK cannot guarantee the correct execution of the feature in all platforms. Setting this feature flag -to true will ensure CDK does not attempt to implement IMDS blocking. By **end of 2025**, CDK will remove the +to true will ensure CDK does not attempt to implement IMDS blocking. By **end of 2025**, CDK will remove the IMDS blocking feature. See [Github discussion](https://github.com/aws/aws-cdk/discussions/32609) for more information. It is recommended to follow ECS documentation to block IMDS for your specific platform and cluster configuration. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1457,16 +1610,18 @@ It is recommended to follow ECS documentation to block IMDS for your specific pl **Compatibility with old behavior:** It is strongly recommended to set this flag to true. However, if necessary, set this flag to false to continue using the old implementation. + ### @aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature *When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)*** (temporary) In an ECS Cluster with `MachineImageType.AMAZON_LINUX_2`, the canContainersAccessInstanceRole=false option attempts to add commands to block containers from -accessing IMDS. Set this flag to true in order to use new and updated commands. Please note that this -feature alone with this feature flag will be deprecated by **end of 2025** as CDK cannot +accessing IMDS. Set this flag to true in order to use new and updated commands. Please note that this +feature alone with this feature flag will be deprecated by **end of 2025** as CDK cannot guarantee the correct execution of the feature in all platforms. See [Github discussion](https://github.com/aws/aws-cdk/discussions/32609) for more information. It is recommended to follow ECS documentation to block IMDS for your specific platform and cluster configuration. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1474,6 +1629,7 @@ It is recommended to follow ECS documentation to block IMDS for your specific pl **Compatibility with old behavior:** Set this flag to false in order to continue using old and outdated commands. However, it is **not** recommended. + ### @aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault *When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere* (fix) @@ -1484,6 +1640,7 @@ will allow IPv6 ingress from anywhere (::/0). Previously, the default security g Using a feature flag to make sure existing customers who might be relying on the overly restrictive permissions are not broken. + | Since | Default | Recommended | | ----- | ----- | ----- | | (not in v1) | | | @@ -1491,4 +1648,5 @@ on the overly restrictive permissions are not broken. **Compatibility with old behavior:** Disable the feature flag to only allow IPv4 ingress in the default security group rules. + diff --git a/packages/aws-cdk-lib/recommended-feature-flags.json b/packages/aws-cdk-lib/recommended-feature-flags.json index 71285e804b547..4bb94bf0c6921 100644 --- a/packages/aws-cdk-lib/recommended-feature-flags.json +++ b/packages/aws-cdk-lib/recommended-feature-flags.json @@ -61,5 +61,6 @@ "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, - "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true } \ No newline at end of file diff --git a/packages/aws-cdk/.eslintrc.js b/packages/aws-cdk/.eslintrc.js index 437c4f0c222e9..13fa8795c9c7f 100644 --- a/packages/aws-cdk/.eslintrc.js +++ b/packages/aws-cdk/.eslintrc.js @@ -1,5 +1,4 @@ const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); -baseConfig.ignorePatterns.push('lib/init-templates/**/typescript/**/*.ts'); -baseConfig.ignorePatterns.push('test/integ/cli/sam_cdk_integ_app/**/*.ts'); +baseConfig.ignorePatterns.push('resources/init-templates/**/typescript/**/*.ts'); baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; module.exports = baseConfig; diff --git a/packages/aws-cdk/.gitignore b/packages/aws-cdk/.gitignore index d0b5e0796af72..698b3acb5e3fc 100644 --- a/packages/aws-cdk/.gitignore +++ b/packages/aws-cdk/.gitignore @@ -9,6 +9,7 @@ node_modules dist # Generated by generate.sh +resources/ build-info.json .LAST_BUILD diff --git a/packages/aws-cdk/.npmignore b/packages/aws-cdk/.npmignore index a287b2ba535b9..9e8c6cd7bd983 100644 --- a/packages/aws-cdk/.npmignore +++ b/packages/aws-cdk/.npmignore @@ -17,10 +17,6 @@ jest.config.js tsconfig.json .eslintrc.js -# init templates include default tsconfig.json files which we need -!lib/init-templates/**/tsconfig.json -!lib/init-templates/**/jest.config.js - !test/integ/**/* .DS_Store @@ -30,3 +26,8 @@ tsconfig.json junit.xml generate.sh + +# init templates include default tsconfig.json files which we need +!resources/ +!resources/init-templates/**/tsconfig.json +!resources/init-templates/**/jest.config.js diff --git a/packages/aws-cdk/generate.sh b/packages/aws-cdk/generate.sh index 9a74b49240b88..c9b47f2d6a93c 100755 --- a/packages/aws-cdk/generate.sh +++ b/packages/aws-cdk/generate.sh @@ -15,11 +15,8 @@ cat > build-info.json < lib/init-templates/.init-version.json - -# Copy the recommended-feature-flags.json file out from aws-cdk-lib. -path=$(node -p 'require.resolve("aws-cdk-lib/recommended-feature-flags.json")') -cp $path lib/init-templates/.recommended-feature-flags.json \ No newline at end of file +# Copy the various resource files from the Toolkit Helpers +path=$(node -p 'require.resolve("@aws-cdk/tmp-toolkit-helpers/package.json")') +toolkit=$(dirname $path) +rm -rf ./resources +cp -R $toolkit/resources/ ./resources/ diff --git a/packages/aws-cdk/lib/api/aws-auth/account-cache.ts b/packages/aws-cdk/lib/api/aws-auth/account-cache.ts deleted file mode 100644 index aea859646751f..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/account-cache.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs-extra'; -import { Account } from './sdk-provider'; -import { debug } from '../../logging'; -import { cdkCacheDir } from '../../util/directories'; - -/** - * Disk cache which maps access key IDs to account IDs. - * Usage: - * cache.get(accessKey) => accountId | undefined - * cache.put(accessKey, accountId) - */ -export class AccountAccessKeyCache { - /** - * Max number of entries in the cache, after which the cache will be reset. - */ - public static readonly MAX_ENTRIES = 1000; - - private readonly cacheFile: string; - - /** - * @param filePath Path to the cache file - */ - constructor(filePath?: string) { - this.cacheFile = filePath || path.join(cdkCacheDir(), 'accounts_partitions.json'); - } - - /** - * Tries to fetch the account ID from cache. If it's not in the cache, invokes - * the resolver function which should retrieve the account ID and return it. - * Then, it will be stored into disk cache returned. - * - * Example: - * - * const accountId = cache.fetch(accessKey, async () => { - * return await fetchAccountIdFromSomewhere(accessKey); - * }); - * - * @param accessKeyId - * @param resolver - */ - public async fetch(accessKeyId: string, resolver: () => Promise) { - // try to get account ID based on this access key ID from disk. - const cached = await this.get(accessKeyId); - if (cached) { - debug(`Retrieved account ID ${cached.accountId} from disk cache`); - return cached; - } - - // if it's not in the cache, resolve and put in cache. - const account = await resolver(); - if (account) { - await this.put(accessKeyId, account); - } - - return account; - } - - /** Get the account ID from an access key or undefined if not in cache */ - public async get(accessKeyId: string): Promise { - const map = await this.loadMap(); - return map[accessKeyId]; - } - - /** Put a mapping between access key and account ID */ - public async put(accessKeyId: string, account: Account) { - let map = await this.loadMap(); - - // nuke cache if it's too big. - if (Object.keys(map).length >= AccountAccessKeyCache.MAX_ENTRIES) { - map = {}; - } - - map[accessKeyId] = account; - await this.saveMap(map); - } - - private async loadMap(): Promise<{ [accessKeyId: string]: Account }> { - try { - return await fs.readJson(this.cacheFile); - } catch (e: any) { - // File doesn't exist or is not readable. This is a cache, - // pretend we successfully loaded an empty map. - if (e.code === 'ENOENT' || e.code === 'EACCES') { - return {}; - } - // File is not JSON, could be corrupted because of concurrent writes. - // Again, an empty cache is fine. - if (e instanceof SyntaxError) { - return {}; - } - throw e; - } - } - - private async saveMap(map: { [accessKeyId: string]: Account }) { - try { - await fs.ensureFile(this.cacheFile); - await fs.writeJson(this.cacheFile, map, { spaces: 2 }); - } catch (e: any) { - // File doesn't exist or file/dir isn't writable. This is a cache, - // if we can't write it then too bad. - if (e.code === 'ENOENT' || e.code === 'EACCES' || e.code === 'EROFS') { - return; - } - throw e; - } - } -} diff --git a/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts b/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts deleted file mode 100644 index fd6f255d0523d..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { createCredentialChain, fromEnv, fromIni, fromNodeProviderChain } from '@aws-sdk/credential-providers'; -import { MetadataService } from '@aws-sdk/ec2-metadata-service'; -import type { NodeHttpHandlerOptions } from '@smithy/node-http-handler'; -import { loadSharedConfigFiles } from '@smithy/shared-ini-file-loader'; -import { AwsCredentialIdentityProvider, Logger } from '@smithy/types'; -import * as promptly from 'promptly'; -import { ProxyAgent } from 'proxy-agent'; -import { makeCachingProvider } from './provider-caching'; -import type { SdkHttpOptions } from './sdk-provider'; -import { readIfPossible } from './util'; -import { debug } from '../../logging'; -import { AuthenticationError } from '../../toolkit/error'; - -const DEFAULT_CONNECTION_TIMEOUT = 10000; -const DEFAULT_TIMEOUT = 300000; - -/** - * Behaviors to match AWS CLI - * - * See these links: - * - * https://docs.aws.amazon.com/cli/latest/topic/config-vars.html - * https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html - */ -export class AwsCliCompatible { - /** - * Build an AWS CLI-compatible credential chain provider - * - * The credential chain returned by this function is always caching. - */ - public static async credentialChainBuilder( - options: CredentialChainOptions = {}, - ): Promise { - const clientConfig = { - requestHandler: AwsCliCompatible.requestHandlerBuilder(options.httpOptions), - customUserAgent: 'aws-cdk', - logger: options.logger, - }; - - // Super hacky solution to https://github.com/aws/aws-cdk/issues/32510, proposed by the SDK team. - // - // Summary of the problem: we were reading the region from the config file and passing it to - // the credential providers. However, in the case of SSO, this makes the credential provider - // use that region to do the SSO flow, which is incorrect. The region that should be used for - // that is the one set in the sso_session section of the config file. - // - // The idea here: the "clientConfig" is for configuring the inner auth client directly, - // and has the highest priority, whereas "parentClientConfig" is the upper data client - // and has lower priority than the sso_region but still higher priority than STS global region. - const parentClientConfig = { - region: await this.region(options.profile), - }; - /** - * The previous implementation matched AWS CLI behavior: - * - * If a profile is explicitly set using `--profile`, - * we use that to the exclusion of everything else. - * - * Note: this does not apply to AWS_PROFILE, - * environment credentials still take precedence over AWS_PROFILE - */ - if (options.profile) { - return makeCachingProvider(fromIni({ - profile: options.profile, - ignoreCache: true, - mfaCodeProvider: tokenCodeFn, - clientConfig, - parentClientConfig, - logger: options.logger, - })); - } - - const envProfile = process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE; - - /** - * Env AWS - EnvironmentCredentials with string AWS - * Env Amazon - EnvironmentCredentials with string AMAZON - * Profile Credentials - PatchedSharedIniFileCredentials with implicit profile, credentials file, http options, and token fn - * SSO with implicit profile only - * SharedIniFileCredentials with implicit profile and preferStaticCredentials true (profile with source_profile) - * Shared Credential file that points to Environment Credentials with AWS prefix - * Shared Credential file that points to EC2 Metadata - * Shared Credential file that points to ECS Credentials - * SSO Credentials - SsoCredentials with implicit profile and http options - * ProcessCredentials with implicit profile - * ECS Credentials - ECSCredentials with no input OR Web Identity - TokenFileWebIdentityCredentials with no input OR EC2 Metadata - EC2MetadataCredentials with no input - * - * These translate to: - * fromEnv() - * fromSSO()/fromIni() - * fromProcess() - * fromContainerMetadata() - * fromTokenFile() - * fromInstanceMetadata() - * - * The NodeProviderChain is already cached. - */ - const nodeProviderChain = fromNodeProviderChain({ - profile: envProfile, - clientConfig, - parentClientConfig, - logger: options.logger, - mfaCodeProvider: tokenCodeFn, - ignoreCache: true, - }); - - return shouldPrioritizeEnv() - ? createCredentialChain(fromEnv(), nodeProviderChain).expireAfter(60 * 60_000) - : nodeProviderChain; - } - - public static requestHandlerBuilder(options: SdkHttpOptions = {}): NodeHttpHandlerOptions { - const agent = this.proxyAgent(options); - - return { - connectionTimeout: DEFAULT_CONNECTION_TIMEOUT, - requestTimeout: DEFAULT_TIMEOUT, - httpsAgent: agent, - httpAgent: agent, - }; - } - - public static proxyAgent(options: SdkHttpOptions) { - // Force it to use the proxy provided through the command line. - // Otherwise, let the ProxyAgent auto-detect the proxy using environment variables. - const getProxyForUrl = options.proxyAddress != null - ? () => Promise.resolve(options.proxyAddress!) - : undefined; - - return new ProxyAgent({ - ca: tryGetCACert(options.caBundlePath), - getProxyForUrl, - }); - } - - /** - * Attempts to get the region from a number of sources and falls back to us-east-1 if no region can be found, - * as is done in the AWS CLI. - * - * The order of priority is the following: - * - * 1. Environment variables specifying region, with both an AWS prefix and AMAZON prefix - * to maintain backwards compatibility, and without `DEFAULT` in the name because - * Lambda and CodeBuild set the $AWS_REGION variable. - * 2. Regions listed in the Shared Ini Files - First checking for the profile provided - * and then checking for the default profile. - * 3. IMDS instance identity region from the Metadata Service. - * 4. us-east-1 - */ - public static async region(maybeProfile?: string): Promise { - const defaultRegion = 'us-east-1'; - const profile = maybeProfile || process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default'; - - const region = - process.env.AWS_REGION || - process.env.AMAZON_REGION || - process.env.AWS_DEFAULT_REGION || - process.env.AMAZON_DEFAULT_REGION || - (await getRegionFromIni(profile)) || - (await regionFromMetadataService()); - - if (!region) { - const usedProfile = !profile ? '' : ` (profile: "${profile}")`; - debug( - `Unable to determine AWS region from environment or AWS configuration${usedProfile}, defaulting to '${defaultRegion}'`, - ); - return defaultRegion; - } - - return region; - } -} - -/** - * Looks up the region of the provided profile. If no region is present, - * it will attempt to lookup the default region. - * @param profile The profile to use to lookup the region - * @returns The region for the profile or default profile, if present. Otherwise returns undefined. - */ -async function getRegionFromIni(profile: string): Promise { - const sharedFiles = await loadSharedConfigFiles({ ignoreCache: true }); - - // Priority: - // - // credentials come before config because aws-cli v1 behaves like that. - // - // 1. profile-region-in-credentials - // 2. profile-region-in-config - // 3. default-region-in-credentials - // 4. default-region-in-config - - return getRegionFromIniFile(profile, sharedFiles.credentialsFile) - ?? getRegionFromIniFile(profile, sharedFiles.configFile) - ?? getRegionFromIniFile('default', sharedFiles.credentialsFile) - ?? getRegionFromIniFile('default', sharedFiles.configFile); - -} - -function getRegionFromIniFile(profile: string, data?: any) { - return data?.[profile]?.region; -} - -function tryGetCACert(bundlePath?: string) { - const path = bundlePath || caBundlePathFromEnvironment(); - if (path) { - debug('Using CA bundle path: %s', path); - return readIfPossible(path); - } - return undefined; -} - -/** - * Find and return a CA certificate bundle path to be passed into the SDK. - */ -function caBundlePathFromEnvironment(): string | undefined { - if (process.env.aws_ca_bundle) { - return process.env.aws_ca_bundle; - } - if (process.env.AWS_CA_BUNDLE) { - return process.env.AWS_CA_BUNDLE; - } - return undefined; -} - -/** - * We used to support both AWS and AMAZON prefixes for these environment variables. - * - * Adding this for backward compatibility. - */ -function shouldPrioritizeEnv() { - const id = process.env.AWS_ACCESS_KEY_ID || process.env.AMAZON_ACCESS_KEY_ID; - const key = process.env.AWS_SECRET_ACCESS_KEY || process.env.AMAZON_SECRET_ACCESS_KEY; - - if (!!id && !!key) { - process.env.AWS_ACCESS_KEY_ID = id; - process.env.AWS_SECRET_ACCESS_KEY = key; - - const sessionToken = process.env.AWS_SESSION_TOKEN ?? process.env.AMAZON_SESSION_TOKEN; - if (sessionToken) { - process.env.AWS_SESSION_TOKEN = sessionToken; - } - - return true; - } - - return false; -} - -/** - * The MetadataService class will attempt to fetch the instance identity document from - * IMDSv2 first, and then will attempt v1 as a fallback. - * - * If this fails, we will use us-east-1 as the region so no error should be thrown. - * @returns The region for the instance identity - */ -async function regionFromMetadataService() { - debug('Looking up AWS region in the EC2 Instance Metadata Service (IMDS).'); - try { - const metadataService = new MetadataService({ - httpOptions: { - timeout: 1000, - }, - }); - - await metadataService.fetchMetadataToken(); - const document = await metadataService.request('/latest/dynamic/instance-identity/document', {}); - return JSON.parse(document).region; - } catch (e) { - debug(`Unable to retrieve AWS region from IMDS: ${e}`); - } -} - -export interface CredentialChainOptions { - readonly profile?: string; - readonly httpOptions?: SdkHttpOptions; - readonly logger?: Logger; -} - -/** - * Ask user for MFA token for given serial - * - * Result is send to callback function for SDK to authorize the request - */ -async function tokenCodeFn(serialArn: string): Promise { - debug('Require MFA token for serial ARN', serialArn); - try { - const token: string = await promptly.prompt(`MFA token for ${serialArn}: `, { - trim: true, - default: '', - }); - debug('Successfully got MFA token from user'); - return token; - } catch (err: any) { - debug('Failed to get MFA token', err); - const e = new AuthenticationError(`Error fetching MFA token: ${err.message ?? err}`); - e.name = 'SharedIniFileCredentialsProviderFailure'; - throw e; - } -} diff --git a/packages/aws-cdk/lib/api/aws-auth/cached.ts b/packages/aws-cdk/lib/api/aws-auth/cached.ts deleted file mode 100644 index d1a9982aa233e..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/cached.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Cache the result of a function on an object - * - * We could have used @decorators to make this nicer but we don't use them anywhere yet, - * so let's keep it simple and readable. - */ -export function cached(obj: A, sym: symbol, fn: () => B): B { - if (!(sym in obj)) { - (obj as any)[sym] = fn(); - } - return (obj as any)[sym]; -} - -/** - * Like 'cached', but async - */ -export async function cachedAsync(obj: A, sym: symbol, fn: () => Promise): Promise { - if (!(sym in obj)) { - (obj as any)[sym] = await fn(); - } - return (obj as any)[sym]; -} diff --git a/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts b/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts deleted file mode 100644 index 84303ffb4c5ca..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/credential-plugins.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { inspect, format } from 'util'; -import type { CredentialProviderSource, ForReading, ForWriting, PluginProviderResult, SDKv2CompatibleCredentials, SDKv3CompatibleCredentialProvider, SDKv3CompatibleCredentials } from '@aws-cdk/cli-plugin-contract'; -import type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@smithy/types'; -import { credentialsAboutToExpire, makeCachingProvider } from './provider-caching'; -import { debug, warning, info } from '../../logging'; -import { AuthenticationError } from '../../toolkit/error'; -import { formatErrorMessage } from '../../util/error'; -import { Mode } from '../plugin/mode'; -import { PluginHost } from '../plugin/plugin'; - -/** - * Cache for credential providers. - * - * Given an account and an operating mode (read or write) will return an - * appropriate credential provider for credentials for the given account. The - * credential provider will be cached so that multiple AWS clients for the same - * environment will not make multiple network calls to obtain credentials. - * - * Will use default credentials if they are for the right account; otherwise, - * all loaded credential provider plugins will be tried to obtain credentials - * for the given account. - */ -export class CredentialPlugins { - private readonly cache: { [key: string]: PluginCredentialsFetchResult | undefined } = {}; - private readonly host: PluginHost; - - constructor(host?: PluginHost) { - this.host = host ?? PluginHost.instance; - } - - public async fetchCredentialsFor(awsAccountId: string, mode: Mode): Promise { - const key = `${awsAccountId}-${mode}`; - if (!(key in this.cache)) { - this.cache[key] = await this.lookupCredentials(awsAccountId, mode); - } - return this.cache[key]; - } - - public get availablePluginNames(): string[] { - return this.host.credentialProviderSources.map((s) => s.name); - } - - private async lookupCredentials(awsAccountId: string, mode: Mode): Promise { - const triedSources: CredentialProviderSource[] = []; - // Otherwise, inspect the various credential sources we have - for (const source of this.host.credentialProviderSources) { - let available: boolean; - try { - available = await source.isAvailable(); - } catch (e: any) { - // This shouldn't happen, but let's guard against it anyway - warning(`Uncaught exception in ${source.name}: ${formatErrorMessage(e)}`); - available = false; - } - - if (!available) { - debug('Credentials source %s is not available, ignoring it.', source.name); - continue; - } - triedSources.push(source); - let canProvide: boolean; - try { - canProvide = await source.canProvideCredentials(awsAccountId); - } catch (e: any) { - // This shouldn't happen, but let's guard against it anyway - warning(`Uncaught exception in ${source.name}: ${formatErrorMessage(e)}`); - canProvide = false; - } - if (!canProvide) { - continue; - } - debug(`Using ${source.name} credentials for account ${awsAccountId}`); - - return { - credentials: await v3ProviderFromPlugin(() => source.getProvider(awsAccountId, mode as ForReading | ForWriting, { - supportsV3Providers: true, - })), - pluginName: source.name, - }; - } - return undefined; - } -} - -/** - * Result from trying to fetch credentials from the Plugin host - */ -export interface PluginCredentialsFetchResult { - /** - * SDK-v3 compatible credential provider - */ - readonly credentials: AwsCredentialIdentityProvider; - - /** - * Name of plugin that successfully provided credentials - */ - readonly pluginName: string; -} - -/** - * Take a function that calls the plugin, and turn it into an SDKv3-compatible credential provider. - * - * What we will do is the following: - * - * - Query the plugin and see what kind of result it gives us. - * - If the result is self-refreshing or doesn't need refreshing, we turn it into an SDKv3 provider - * and return it directly. - * * If the underlying return value is a provider, we will make it a caching provider - * (because we can't know if it will cache by itself or not). - * * If the underlying return value is a static credential, caching isn't relevant. - * * If the underlying return value is V2 credentials, those have caching built-in. - * - If the result is a static credential that expires, we will wrap it in an SDKv3 provider - * that will query the plugin again when the credential expires. - */ -async function v3ProviderFromPlugin(producer: () => Promise): Promise { - const initial = await producer(); - - if (isV3Provider(initial)) { - // Already a provider, make caching - return makeCachingProvider(initial); - } else if (isV3Credentials(initial) && initial.expiration === undefined) { - // Static credentials that don't need refreshing nor caching - return () => Promise.resolve(initial); - } else if (isV3Credentials(initial) && initial.expiration !== undefined) { - // Static credentials that do need refreshing and caching - return refreshFromPluginProvider(initial, producer); - } else if (isV2Credentials(initial)) { - // V2 credentials that refresh and cache themselves - return v3ProviderFromV2Credentials(initial); - } else { - throw new AuthenticationError(`Plugin returned a value that doesn't resemble AWS credentials: ${inspect(initial)}`); - } -} - -/** - * Converts a V2 credential into a V3-compatible provider - */ -function v3ProviderFromV2Credentials(x: SDKv2CompatibleCredentials): AwsCredentialIdentityProvider { - return async () => { - // Get will fetch or refresh as necessary - await x.getPromise(); - - return { - accessKeyId: x.accessKeyId, - secretAccessKey: x.secretAccessKey, - sessionToken: x.sessionToken, - expiration: x.expireTime ?? undefined, - }; - }; -} - -function refreshFromPluginProvider(current: AwsCredentialIdentity, producer: () => Promise): AwsCredentialIdentityProvider { - return async () => { - info(format(current), Date.now()); - if (credentialsAboutToExpire(current)) { - const newCreds = await producer(); - if (!isV3Credentials(newCreds)) { - throw new AuthenticationError(`Plugin initially returned static V3 credentials but now returned something else: ${inspect(newCreds)}`); - } - current = newCreds; - } - return current; - }; -} - -function isV3Provider(x: PluginProviderResult): x is SDKv3CompatibleCredentialProvider { - return typeof x === 'function'; -} - -function isV2Credentials(x: PluginProviderResult): x is SDKv2CompatibleCredentials { - return !!(x && typeof x === 'object' && (x as SDKv2CompatibleCredentials).getPromise); -} - -function isV3Credentials(x: PluginProviderResult): x is SDKv3CompatibleCredentials { - return !!(x && typeof x === 'object' && x.accessKeyId && !isV2Credentials(x)); -} diff --git a/packages/aws-cdk/lib/api/aws-auth/index.ts b/packages/aws-cdk/lib/api/aws-auth/index.ts deleted file mode 100644 index 987c8b1b3da54..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './sdk'; -export * from './sdk-provider'; diff --git a/packages/aws-cdk/lib/api/aws-auth/provider-caching.ts b/packages/aws-cdk/lib/api/aws-auth/provider-caching.ts deleted file mode 100644 index 77b7630230c22..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/provider-caching.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { memoize } from '@smithy/property-provider'; -import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@smithy/types'; - -/** - * Wrap a credential provider in a cache - * - * Some credential providers in the SDKv3 are cached (the default Node - * chain, specifically) but most others are not. - * - * Since we want to avoid duplicate calls to `AssumeRole`, or duplicate - * MFA prompts or what have you, we are going to liberally wrap providers - * in caches which will return the cached value until it expires. - */ -export function makeCachingProvider(provider: AwsCredentialIdentityProvider): AwsCredentialIdentityProvider { - return memoize( - provider, - credentialsAboutToExpire, - (token) => !!token.expiration, - ); -} - -export function credentialsAboutToExpire(token: AwsCredentialIdentity) { - const expiryMarginSecs = 5; - // token.expiration is sometimes null - return !!token.expiration && token.expiration.getTime() - Date.now() < expiryMarginSecs * 1000; -} diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts b/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts deleted file mode 100644 index ce1b981bab0d1..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-logger.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { inspect } from 'util'; -import { Logger } from '@smithy/types'; -import { trace } from '../../logging'; - -export class SdkToCliLogger implements Logger { - public trace(..._content: any[]) { - // This is too much detail for our logs - // trace('[SDK trace] %s', fmtContent(content)); - } - - public debug(..._content: any[]) { - // This is too much detail for our logs - // trace('[SDK debug] %s', fmtContent(content)); - } - - /** - * Info is called mostly (exclusively?) for successful API calls - * - * Payload: - * - * (Note the input contains entire CFN templates, for example) - * - * ``` - * { - * clientName: 'S3Client', - * commandName: 'GetBucketLocationCommand', - * input: { - * Bucket: '.....', - * ExpectedBucketOwner: undefined - * }, - * output: { LocationConstraint: 'eu-central-1' }, - * metadata: { - * httpStatusCode: 200, - * requestId: '....', - * extendedRequestId: '...', - * cfId: undefined, - * attempts: 1, - * totalRetryDelay: 0 - * } - * } - * ``` - */ - public info(...content: any[]) { - trace('[sdk info] %s', formatSdkLoggerContent(content)); - } - - public warn(...content: any[]) { - trace('[sdk warn] %s', formatSdkLoggerContent(content)); - } - - /** - * Error is called mostly (exclusively?) for failing API calls - * - * Payload (input would be the entire API call arguments). - * - * ``` - * { - * clientName: 'STSClient', - * commandName: 'GetCallerIdentityCommand', - * input: {}, - * error: AggregateError [ECONNREFUSED]: - * at internalConnectMultiple (node:net:1121:18) - * at afterConnectMultiple (node:net:1688:7) { - * code: 'ECONNREFUSED', - * '$metadata': { attempts: 3, totalRetryDelay: 600 }, - * [errors]: [ [Error], [Error] ] - * }, - * metadata: { attempts: 3, totalRetryDelay: 600 } - * } - * ``` - */ - public error(...content: any[]) { - trace('[sdk error] %s', formatSdkLoggerContent(content)); - } -} - -/** - * This can be anything. - * - * For debug, it seems to be mostly strings. - * For info, it seems to be objects. - * - * Stringify and join without separator. - */ -export function formatSdkLoggerContent(content: any[]) { - if (content.length === 1) { - const apiFmt = formatApiCall(content[0]); - if (apiFmt) { - return apiFmt; - } - } - return content.map((x) => typeof x === 'string' ? x : inspect(x)).join(''); -} - -function formatApiCall(content: any): string | undefined { - if (!isSdkApiCallSuccess(content) && !isSdkApiCallError(content)) { - return undefined; - } - - const service = content.clientName.replace(/Client$/, ''); - const api = content.commandName.replace(/Command$/, ''); - - const parts = []; - if ((content.metadata?.attempts ?? 0) > 1) { - parts.push(`[${content.metadata?.attempts} attempts, ${content.metadata?.totalRetryDelay}ms retry]`); - } - - parts.push(`${service}.${api}(${JSON.stringify(content.input)})`); - - if (isSdkApiCallSuccess(content)) { - parts.push('-> OK'); - } else { - parts.push(`-> ${content.error}`); - } - - return parts.join(' '); -} - -interface SdkApiCallBase { - clientName: string; - commandName: string; - input: Record; - metadata?: { - httpStatusCode?: number; - requestId?: string; - extendedRequestId?: string; - cfId?: string; - attempts?: number; - totalRetryDelay?: number; - }; -} - -type SdkApiCallSuccess = SdkApiCallBase & { output: Record }; -type SdkApiCallError = SdkApiCallBase & { error: Error }; - -function isSdkApiCallSuccess(x: any): x is SdkApiCallSuccess { - return x && typeof x === 'object' && x.commandName && x.output; -} - -function isSdkApiCallError(x: any): x is SdkApiCallError { - return x && typeof x === 'object' && x.commandName && x.error; -} diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts deleted file mode 100644 index 551908e5b3e71..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ /dev/null @@ -1,531 +0,0 @@ -import * as os from 'os'; -import { ContextLookupRoleOptions } from '@aws-cdk/cloud-assembly-schema'; -import { Environment, EnvironmentUtils, UNKNOWN_ACCOUNT, UNKNOWN_REGION } from '@aws-cdk/cx-api'; -import { AssumeRoleCommandInput } from '@aws-sdk/client-sts'; -import { fromTemporaryCredentials } from '@aws-sdk/credential-providers'; -import type { NodeHttpHandlerOptions } from '@smithy/node-http-handler'; -import { AwsCredentialIdentityProvider, Logger } from '@smithy/types'; -import { AwsCliCompatible } from './awscli-compatible'; -import { cached } from './cached'; -import { CredentialPlugins } from './credential-plugins'; -import { makeCachingProvider } from './provider-caching'; -import { SDK } from './sdk'; -import { debug, warning } from '../../logging'; -import { AuthenticationError } from '../../toolkit/error'; -import { formatErrorMessage } from '../../util/error'; -import { traceMethods } from '../../util/tracing'; -import { Mode } from '../plugin/mode'; - -export type AssumeRoleAdditionalOptions = Partial>; - -/** - * Options for the default SDK provider - */ -export interface SdkProviderOptions { - /** - * Profile to read from ~/.aws - * - * @default - No profile - */ - readonly profile?: string; - - /** - * HTTP options for SDK - */ - readonly httpOptions?: SdkHttpOptions; - - /** - * The logger for sdk calls. - */ - readonly logger?: Logger; -} - -/** - * Options for individual SDKs - */ -export interface SdkHttpOptions { - /** - * Proxy address to use - * - * @default No proxy - */ - readonly proxyAddress?: string; - - /** - * A path to a certificate bundle that contains a cert to be trusted. - * - * @default No certificate bundle - */ - readonly caBundlePath?: string; -} - -const CACHED_ACCOUNT = Symbol('cached_account'); - -/** - * SDK configuration for a given environment - * 'forEnvironment' will attempt to assume a role and if it - * is not successful, then it will either: - * 1. Check to see if the default credentials (local credentials the CLI was executed with) - * are for the given environment. If they are then return those. - * 2. If the default credentials are not for the given environment then - * throw an error - * - * 'didAssumeRole' allows callers to whether they are receiving the assume role - * credentials or the default credentials. - */ -export interface SdkForEnvironment { - /** - * The SDK for the given environment - */ - readonly sdk: SDK; - - /** - * Whether or not the assume role was successful. - * If the assume role was not successful (false) - * then that means that the 'sdk' returned contains - * the default credentials (not the assume role credentials) - */ - readonly didAssumeRole: boolean; -} - -/** - * Creates instances of the AWS SDK appropriate for a given account/region. - * - * Behavior is as follows: - * - * - First, a set of "base" credentials are established - * - If a target environment is given and the default ("current") SDK credentials are for - * that account, return those; otherwise - * - If a target environment is given, scan all credential provider plugins - * for credentials, and return those if found; otherwise - * - Return default ("current") SDK credentials, noting that they might be wrong. - * - * - Second, a role may optionally need to be assumed. Use the base credentials - * established in the previous process to assume that role. - * - If assuming the role fails and the base credentials are for the correct - * account, return those. This is a fallback for people who are trying to interact - * with a Default Synthesized stack and already have right credentials setup. - * - * Typical cases we see in the wild: - * - Credential plugin setup that, although not recommended, works for them - * - Seeded terminal with `ReadOnly` credentials in order to do `cdk diff`--the `ReadOnly` - * role doesn't have `sts:AssumeRole` and will fail for no real good reason. - */ -@traceMethods -export class SdkProvider { - /** - * Create a new SdkProvider which gets its defaults in a way that behaves like the AWS CLI does - * - * The AWS SDK for JS behaves slightly differently from the AWS CLI in a number of ways; see the - * class `AwsCliCompatible` for the details. - */ - public static async withAwsCliCompatibleDefaults(options: SdkProviderOptions = {}) { - const credentialProvider = await AwsCliCompatible.credentialChainBuilder({ - profile: options.profile, - httpOptions: options.httpOptions, - logger: options.logger, - }); - - const region = await AwsCliCompatible.region(options.profile); - const requestHandler = AwsCliCompatible.requestHandlerBuilder(options.httpOptions); - return new SdkProvider(credentialProvider, region, requestHandler, options.logger); - } - - private readonly plugins = new CredentialPlugins(); - - public constructor( - private readonly defaultCredentialProvider: AwsCredentialIdentityProvider, - /** - * Default region - */ - public readonly defaultRegion: string, - private readonly requestHandler: NodeHttpHandlerOptions = {}, - private readonly logger?: Logger, - ) {} - - /** - * Return an SDK which can do operations in the given environment - * - * The `environment` parameter is resolved first (see `resolveEnvironment()`). - */ - public async forEnvironment( - environment: Environment, - mode: Mode, - options?: CredentialsOptions, - quiet = false, - ): Promise { - const env = await this.resolveEnvironment(environment); - - const baseCreds = await this.obtainBaseCredentials(env.account, mode); - - // At this point, we need at least SOME credentials - if (baseCreds.source === 'none') { - throw new AuthenticationError(fmtObtainCredentialsError(env.account, baseCreds)); - } - - // Simple case is if we don't need to "assumeRole" here. If so, we must now have credentials for the right - // account. - if (options?.assumeRoleArn === undefined) { - if (baseCreds.source === 'incorrectDefault') { - throw new AuthenticationError(fmtObtainCredentialsError(env.account, baseCreds)); - } - - // Our current credentials must be valid and not expired. Confirm that before we get into doing - // actual CloudFormation calls, which might take a long time to hang. - const sdk = new SDK(baseCreds.credentials, env.region, this.requestHandler, this.logger); - await sdk.validateCredentials(); - return { sdk, didAssumeRole: false }; - } - - try { - // We will proceed to AssumeRole using whatever we've been given. - const sdk = await this.withAssumedRole( - baseCreds, - options.assumeRoleArn, - options.assumeRoleExternalId, - options.assumeRoleAdditionalOptions, - env.region, - ); - - return { sdk, didAssumeRole: true }; - } catch (err: any) { - if (err.name === 'ExpiredToken') { - throw err; - } - - // AssumeRole failed. Proceed and warn *if and only if* the baseCredentials were already for the right account - // or returned from a plugin. This is to cover some current setups for people using plugins or preferring to - // feed the CLI credentials which are sufficient by themselves. Prefer to assume the correct role if we can, - // but if we can't then let's just try with available credentials anyway. - if (baseCreds.source === 'correctDefault' || baseCreds.source === 'plugin') { - debug(err.message); - const logger = quiet ? debug : warning; - logger( - `${fmtObtainedCredentials(baseCreds)} could not be used to assume '${options.assumeRoleArn}', but are for the right account. Proceeding anyway.`, - ); - return { - sdk: new SDK(baseCreds.credentials, env.region, this.requestHandler, this.logger), - didAssumeRole: false, - }; - } - - throw err; - } - } - - /** - * Return the partition that base credentials are for - * - * Returns `undefined` if there are no base credentials. - */ - public async baseCredentialsPartition(environment: Environment, mode: Mode): Promise { - const env = await this.resolveEnvironment(environment); - const baseCreds = await this.obtainBaseCredentials(env.account, mode); - if (baseCreds.source === 'none') { - return undefined; - } - return (await new SDK(baseCreds.credentials, env.region, this.requestHandler, this.logger).currentAccount()).partition; - } - - /** - * Resolve the environment for a stack - * - * Replaces the magic values `UNKNOWN_REGION` and `UNKNOWN_ACCOUNT` - * with the defaults for the current SDK configuration (`~/.aws/config` or - * otherwise). - * - * It is an error if `UNKNOWN_ACCOUNT` is used but the user hasn't configured - * any SDK credentials. - */ - public async resolveEnvironment(env: Environment): Promise { - const region = env.region !== UNKNOWN_REGION ? env.region : this.defaultRegion; - const account = env.account !== UNKNOWN_ACCOUNT ? env.account : (await this.defaultAccount())?.accountId; - - if (!account) { - throw new AuthenticationError( - 'Unable to resolve AWS account to use. It must be either configured when you define your CDK Stack, or through the environment', - ); - } - - return { - region, - account, - name: EnvironmentUtils.format(account, region), - }; - } - - /** - * The account we'd auth into if we used default credentials. - * - * Default credentials are the set of ambiently configured credentials using - * one of the environment variables, or ~/.aws/credentials, or the *one* - * profile that was passed into the CLI. - * - * Might return undefined if there are no default/ambient credentials - * available (in which case the user should better hope they have - * credential plugins configured). - * - * Uses a cache to avoid STS calls if we don't need 'em. - */ - public async defaultAccount(): Promise { - return cached(this, CACHED_ACCOUNT, async () => { - try { - return await new SDK(this.defaultCredentialProvider, this.defaultRegion, this.requestHandler, this.logger).currentAccount(); - } catch (e: any) { - // Treat 'ExpiredToken' specially. This is a common situation that people may find themselves in, and - // they are complaining about if we fail 'cdk synth' on them. We loudly complain in order to show that - // the current situation is probably undesirable, but we don't fail. - if (e.name === 'ExpiredToken') { - warning( - 'There are expired AWS credentials in your environment. The CDK app will synth without current account information.', - ); - return undefined; - } - - debug(`Unable to determine the default AWS account (${e.name}): ${formatErrorMessage(e)}`); - return undefined; - } - }); - } - - /** - * Get credentials for the given account ID in the given mode - * - * 1. Use the default credentials if the destination account matches the - * current credentials' account. - * 2. Otherwise try all credential plugins. - * 3. Fail if neither of these yield any credentials. - * 4. Return a failure if any of them returned credentials - */ - private async obtainBaseCredentials(accountId: string, mode: Mode): Promise { - // First try 'current' credentials - const defaultAccountId = (await this.defaultAccount())?.accountId; - if (defaultAccountId === accountId) { - return { - source: 'correctDefault', - credentials: await this.defaultCredentialProvider, - }; - } - - // Then try the plugins - const pluginCreds = await this.plugins.fetchCredentialsFor(accountId, mode); - if (pluginCreds) { - return { source: 'plugin', ...pluginCreds }; - } - - // Fall back to default credentials with a note that they're not the right ones yet - if (defaultAccountId !== undefined) { - return { - source: 'incorrectDefault', - accountId: defaultAccountId, - credentials: await this.defaultCredentialProvider, - unusedPlugins: this.plugins.availablePluginNames, - }; - } - - // Apparently we didn't find any at all - return { - source: 'none', - unusedPlugins: this.plugins.availablePluginNames, - }; - } - - /** - * Return an SDK which uses assumed role credentials - * - * The base credentials used to retrieve the assumed role credentials will be the - * same credentials returned by obtainCredentials if an environment and mode is passed, - * otherwise it will be the current credentials. - */ - private async withAssumedRole( - mainCredentials: Exclude, - roleArn: string, - externalId?: string, - additionalOptions?: AssumeRoleAdditionalOptions, - region?: string, - ): Promise { - debug(`Assuming role '${roleArn}'.`); - - region = region ?? this.defaultRegion; - - const sourceDescription = fmtObtainedCredentials(mainCredentials); - - try { - const credentials = await makeCachingProvider(fromTemporaryCredentials({ - masterCredentials: mainCredentials.credentials, - params: { - RoleArn: roleArn, - ExternalId: externalId, - RoleSessionName: `aws-cdk-${safeUsername()}`, - ...additionalOptions, - TransitiveTagKeys: additionalOptions?.Tags ? additionalOptions.Tags.map((t) => t.Key!) : undefined, - }, - clientConfig: { - region, - requestHandler: this.requestHandler, - customUserAgent: 'aws-cdk', - logger: this.logger, - }, - logger: this.logger, - })); - - // Call the provider at least once here, to catch an error if it occurs - await credentials(); - - return new SDK(credentials, region, this.requestHandler, this.logger); - } catch (err: any) { - if (err.name === 'ExpiredToken') { - throw err; - } - - debug(`Assuming role failed: ${err.message}`); - throw new AuthenticationError( - [ - 'Could not assume role in target account', - ...(sourceDescription ? [`using ${sourceDescription}`] : []), - err.message, - ". Please make sure that this role exists in the account. If it doesn't exist, (re)-bootstrap the environment " + - "with the right '--trust', using the latest version of the CDK CLI.", - ].join(' '), - ); - } - } -} - -/** - * An AWS account - * - * An AWS account always exists in only one partition. Usually we don't care about - * the partition, but when we need to form ARNs we do. - */ -export interface Account { - /** - * The account number - */ - readonly accountId: string; - - /** - * The partition ('aws' or 'aws-cn' or otherwise) - */ - readonly partition: string; -} - -/** - * Return the username with characters invalid for a RoleSessionName removed - * - * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters - */ -function safeUsername() { - try { - return os.userInfo().username.replace(/[^\w+=,.@-]/g, '@'); - } catch { - return 'noname'; - } -} - -/** - * Options for obtaining credentials for an environment - */ -export interface CredentialsOptions { - /** - * The ARN of the role that needs to be assumed, if any - */ - readonly assumeRoleArn?: string; - - /** - * External ID required to assume the given role. - */ - readonly assumeRoleExternalId?: string; - - /** - * Session tags required to assume the given role. - */ - readonly assumeRoleAdditionalOptions?: AssumeRoleAdditionalOptions; -} - -/** - * Result of obtaining base credentials - */ -type ObtainBaseCredentialsResult = - | { source: 'correctDefault'; credentials: AwsCredentialIdentityProvider } - | { source: 'plugin'; pluginName: string; credentials: AwsCredentialIdentityProvider } - | { - source: 'incorrectDefault'; - credentials: AwsCredentialIdentityProvider; - accountId: string; - unusedPlugins: string[]; - } - | { source: 'none'; unusedPlugins: string[] }; - -/** - * Isolating the code that translates calculation errors into human error messages - * - * We cover the following cases: - * - * - No credentials are available at all - * - Default credentials are for the wrong account - */ -function fmtObtainCredentialsError( - targetAccountId: string, - obtainResult: ObtainBaseCredentialsResult & { - source: 'none' | 'incorrectDefault'; - }, -): string { - const msg = [`Need to perform AWS calls for account ${targetAccountId}`]; - switch (obtainResult.source) { - case 'incorrectDefault': - msg.push(`but the current credentials are for ${obtainResult.accountId}`); - break; - case 'none': - msg.push('but no credentials have been configured'); - } - if (obtainResult.unusedPlugins.length > 0) { - msg.push(`and none of these plugins found any: ${obtainResult.unusedPlugins.join(', ')}`); - } - return msg.join(', '); -} - -/** - * Format a message indicating where we got base credentials for the assume role - * - * We cover the following cases: - * - * - Default credentials for the right account - * - Default credentials for the wrong account - * - Credentials returned from a plugin - */ -function fmtObtainedCredentials(obtainResult: Exclude): string { - switch (obtainResult.source) { - case 'correctDefault': - return 'current credentials'; - case 'plugin': - return `credentials returned by plugin '${obtainResult.pluginName}'`; - case 'incorrectDefault': - const msg = []; - msg.push(`current credentials (which are for account ${obtainResult.accountId}`); - - if (obtainResult.unusedPlugins.length > 0) { - msg.push(`, and none of the following plugins provided credentials: ${obtainResult.unusedPlugins.join(', ')}`); - } - msg.push(')'); - - return msg.join(''); - } -} - -/** - * Instantiate an SDK for context providers. This function ensures that all - * lookup assume role options are used when context providers perform lookups. - */ -export async function initContextProviderSdk(aws: SdkProvider, options: ContextLookupRoleOptions): Promise { - const account = options.account; - const region = options.region; - - const creds: CredentialsOptions = { - assumeRoleArn: options.lookupRoleArn, - assumeRoleExternalId: options.lookupRoleExternalId, - assumeRoleAdditionalOptions: options.assumeRoleAdditionalOptions, - }; - - return (await aws.forEnvironment(EnvironmentUtils.make(account, region), Mode.ForReading, creds)).sdk; -} diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk.ts b/packages/aws-cdk/lib/api/aws-auth/sdk.ts deleted file mode 100644 index 27c98d4cdbb51..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/sdk.ts +++ /dev/null @@ -1,987 +0,0 @@ -import { - AppSyncClient, - FunctionConfiguration, - GetSchemaCreationStatusCommand, - type GetSchemaCreationStatusCommandInput, - type GetSchemaCreationStatusCommandOutput, - type ListFunctionsCommandInput, - paginateListFunctions, - StartSchemaCreationCommand, - type StartSchemaCreationCommandInput, - type StartSchemaCreationCommandOutput, - UpdateApiKeyCommand, - type UpdateApiKeyCommandInput, - type UpdateApiKeyCommandOutput, - UpdateFunctionCommand, - type UpdateFunctionCommandInput, - type UpdateFunctionCommandOutput, - UpdateResolverCommand, - type UpdateResolverCommandInput, - type UpdateResolverCommandOutput, -} from '@aws-sdk/client-appsync'; -import { - CloudFormationClient, - ContinueUpdateRollbackCommand, - ContinueUpdateRollbackCommandInput, - ContinueUpdateRollbackCommandOutput, - CreateChangeSetCommand, - type CreateChangeSetCommandInput, - type CreateChangeSetCommandOutput, - CreateGeneratedTemplateCommand, - type CreateGeneratedTemplateCommandInput, - type CreateGeneratedTemplateCommandOutput, - CreateStackCommand, - type CreateStackCommandInput, - type CreateStackCommandOutput, - DeleteChangeSetCommand, - type DeleteChangeSetCommandInput, - type DeleteChangeSetCommandOutput, - DeleteGeneratedTemplateCommand, - type DeleteGeneratedTemplateCommandInput, - type DeleteGeneratedTemplateCommandOutput, - DeleteStackCommand, - type DeleteStackCommandInput, - type DeleteStackCommandOutput, - DescribeChangeSetCommand, - type DescribeChangeSetCommandInput, - type DescribeChangeSetCommandOutput, - DescribeGeneratedTemplateCommand, - type DescribeGeneratedTemplateCommandInput, - type DescribeGeneratedTemplateCommandOutput, - DescribeResourceScanCommand, - type DescribeResourceScanCommandInput, - type DescribeResourceScanCommandOutput, - DescribeStackEventsCommand, - type DescribeStackEventsCommandInput, - DescribeStackEventsCommandOutput, - DescribeStackResourcesCommand, - DescribeStackResourcesCommandInput, - DescribeStackResourcesCommandOutput, - DescribeStacksCommand, - type DescribeStacksCommandInput, - type DescribeStacksCommandOutput, - ExecuteChangeSetCommand, - type ExecuteChangeSetCommandInput, - type ExecuteChangeSetCommandOutput, - GetGeneratedTemplateCommand, - type GetGeneratedTemplateCommandInput, - type GetGeneratedTemplateCommandOutput, - GetTemplateCommand, - type GetTemplateCommandInput, - type GetTemplateCommandOutput, - GetTemplateSummaryCommand, - type GetTemplateSummaryCommandInput, - type GetTemplateSummaryCommandOutput, - ListExportsCommand, - type ListExportsCommandInput, - type ListExportsCommandOutput, - ListResourceScanRelatedResourcesCommand, - type ListResourceScanRelatedResourcesCommandInput, - type ListResourceScanRelatedResourcesCommandOutput, - ListResourceScanResourcesCommand, - type ListResourceScanResourcesCommandInput, - type ListResourceScanResourcesCommandOutput, - ListResourceScansCommand, - type ListResourceScansCommandInput, - type ListResourceScansCommandOutput, - type ListStackResourcesCommandInput, - ListStacksCommand, - ListStacksCommandInput, - ListStacksCommandOutput, - paginateListStackResources, - RollbackStackCommand, - RollbackStackCommandInput, - RollbackStackCommandOutput, - StackResourceSummary, - StartResourceScanCommand, - type StartResourceScanCommandInput, - type StartResourceScanCommandOutput, - UpdateStackCommand, - type UpdateStackCommandInput, - type UpdateStackCommandOutput, - UpdateTerminationProtectionCommand, - type UpdateTerminationProtectionCommandInput, - type UpdateTerminationProtectionCommandOutput, -} from '@aws-sdk/client-cloudformation'; -import { - CloudWatchLogsClient, - DescribeLogGroupsCommand, - type DescribeLogGroupsCommandInput, - type DescribeLogGroupsCommandOutput, - FilterLogEventsCommand, - FilterLogEventsCommandInput, - FilterLogEventsCommandOutput, -} from '@aws-sdk/client-cloudwatch-logs'; -import { - CodeBuildClient, - UpdateProjectCommand, - type UpdateProjectCommandInput, - type UpdateProjectCommandOutput, -} from '@aws-sdk/client-codebuild'; -import { - DescribeAvailabilityZonesCommand, - type DescribeAvailabilityZonesCommandInput, - type DescribeAvailabilityZonesCommandOutput, - DescribeImagesCommand, - type DescribeImagesCommandInput, - type DescribeImagesCommandOutput, - DescribeInstancesCommand, - type DescribeInstancesCommandInput, - type DescribeInstancesCommandOutput, - DescribeRouteTablesCommand, - type DescribeRouteTablesCommandInput, - type DescribeRouteTablesCommandOutput, - DescribeSecurityGroupsCommand, - type DescribeSecurityGroupsCommandInput, - type DescribeSecurityGroupsCommandOutput, - DescribeSubnetsCommand, - type DescribeSubnetsCommandInput, - type DescribeSubnetsCommandOutput, - DescribeVpcEndpointServicesCommand, - type DescribeVpcEndpointServicesCommandInput, - type DescribeVpcEndpointServicesCommandOutput, - DescribeVpcsCommand, - type DescribeVpcsCommandInput, - type DescribeVpcsCommandOutput, - DescribeVpnGatewaysCommand, - type DescribeVpnGatewaysCommandInput, - type DescribeVpnGatewaysCommandOutput, - EC2Client, -} from '@aws-sdk/client-ec2'; -import { - BatchDeleteImageCommand, - BatchDeleteImageCommandInput, - BatchDeleteImageCommandOutput, - CreateRepositoryCommand, - type CreateRepositoryCommandInput, - type CreateRepositoryCommandOutput, - DescribeImagesCommand as ECRDescribeImagesCommand, - type DescribeImagesCommandInput as ECRDescribeImagesCommandInput, - type DescribeImagesCommandOutput as ECRDescribeImagesCommandOutput, - DescribeRepositoriesCommand, - type DescribeRepositoriesCommandInput, - type DescribeRepositoriesCommandOutput, - ECRClient, - GetAuthorizationTokenCommand, - type GetAuthorizationTokenCommandInput, - type GetAuthorizationTokenCommandOutput, - ListImagesCommand, - ListImagesCommandInput, - ListImagesCommandOutput, - PutImageCommand, - PutImageCommandInput, - PutImageCommandOutput, - PutImageScanningConfigurationCommand, - type PutImageScanningConfigurationCommandInput, - type PutImageScanningConfigurationCommandOutput, - BatchGetImageCommandInput, - BatchGetImageCommand, - BatchGetImageCommandOutput, -} from '@aws-sdk/client-ecr'; -import { - DescribeServicesCommandInput, - ECSClient, - ListClustersCommand, - type ListClustersCommandInput, - type ListClustersCommandOutput, - RegisterTaskDefinitionCommand, - RegisterTaskDefinitionCommandInput, - type RegisterTaskDefinitionCommandOutput, - UpdateServiceCommand, - type UpdateServiceCommandInput, - type UpdateServiceCommandOutput, - waitUntilServicesStable, -} from '@aws-sdk/client-ecs'; -import { - DescribeListenersCommand, - type DescribeListenersCommandInput, - type DescribeListenersCommandOutput, - DescribeLoadBalancersCommand, - type DescribeLoadBalancersCommandInput, - type DescribeLoadBalancersCommandOutput, - DescribeTagsCommand, - type DescribeTagsCommandInput, - type DescribeTagsCommandOutput, - ElasticLoadBalancingV2Client, - Listener, - LoadBalancer, - paginateDescribeListeners, - paginateDescribeLoadBalancers, -} from '@aws-sdk/client-elastic-load-balancing-v2'; -import { - CreatePolicyCommand, - type CreatePolicyCommandInput, - type CreatePolicyCommandOutput, - GetPolicyCommand, - type GetPolicyCommandInput, - type GetPolicyCommandOutput, - GetRoleCommand, - type GetRoleCommandInput, - type GetRoleCommandOutput, - IAMClient, -} from '@aws-sdk/client-iam'; -import { - DescribeKeyCommand, - type DescribeKeyCommandInput, - type DescribeKeyCommandOutput, - KMSClient, - ListAliasesCommand, - type ListAliasesCommandInput, - type ListAliasesCommandOutput, -} from '@aws-sdk/client-kms'; -import { - InvokeCommand, - type InvokeCommandInput, - type InvokeCommandOutput, - LambdaClient, - PublishVersionCommand, - type PublishVersionCommandInput, - type PublishVersionCommandOutput, - UpdateAliasCommand, - type UpdateAliasCommandInput, - type UpdateAliasCommandOutput, - UpdateFunctionCodeCommand, - type UpdateFunctionCodeCommandInput, - type UpdateFunctionCodeCommandOutput, - UpdateFunctionConfigurationCommand, - type UpdateFunctionConfigurationCommandInput, - type UpdateFunctionConfigurationCommandOutput, - waitUntilFunctionUpdatedV2, -} from '@aws-sdk/client-lambda'; -import { - GetHostedZoneCommand, - type GetHostedZoneCommandInput, - type GetHostedZoneCommandOutput, - ListHostedZonesByNameCommand, - type ListHostedZonesByNameCommandInput, - type ListHostedZonesByNameCommandOutput, - ListHostedZonesCommand, - type ListHostedZonesCommandInput, - type ListHostedZonesCommandOutput, - Route53Client, -} from '@aws-sdk/client-route-53'; -import { - type CompleteMultipartUploadCommandOutput, - DeleteObjectsCommand, - DeleteObjectsCommandInput, - DeleteObjectsCommandOutput, - DeleteObjectTaggingCommand, - DeleteObjectTaggingCommandInput, - DeleteObjectTaggingCommandOutput, - GetBucketEncryptionCommand, - type GetBucketEncryptionCommandInput, - type GetBucketEncryptionCommandOutput, - GetBucketLocationCommand, - type GetBucketLocationCommandInput, - type GetBucketLocationCommandOutput, - GetObjectCommand, - type GetObjectCommandInput, - type GetObjectCommandOutput, - GetObjectTaggingCommand, - GetObjectTaggingCommandInput, - GetObjectTaggingCommandOutput, - ListObjectsV2Command, - type ListObjectsV2CommandInput, - type ListObjectsV2CommandOutput, - type PutObjectCommandInput, - PutObjectTaggingCommand, - PutObjectTaggingCommandInput, - PutObjectTaggingCommandOutput, - S3Client, -} from '@aws-sdk/client-s3'; -import { - GetSecretValueCommand, - type GetSecretValueCommandInput, - type GetSecretValueCommandOutput, - SecretsManagerClient, -} from '@aws-sdk/client-secrets-manager'; -import { - SFNClient, - UpdateStateMachineCommand, - UpdateStateMachineCommandInput, - UpdateStateMachineCommandOutput, -} from '@aws-sdk/client-sfn'; -import { - GetParameterCommand, - type GetParameterCommandInput, - type GetParameterCommandOutput, - SSMClient, -} from '@aws-sdk/client-ssm'; -import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts'; -import { Upload } from '@aws-sdk/lib-storage'; -import { getEndpointFromInstructions } from '@smithy/middleware-endpoint'; -import type { NodeHttpHandlerOptions } from '@smithy/node-http-handler'; -import { AwsCredentialIdentityProvider, Logger } from '@smithy/types'; -import { ConfiguredRetryStrategy } from '@smithy/util-retry'; -import { WaiterResult } from '@smithy/util-waiter'; -import { AccountAccessKeyCache } from './account-cache'; -import { cachedAsync } from './cached'; -import { Account } from './sdk-provider'; -import { defaultCliUserAgent } from './user-agent'; -import { debug } from '../../logging'; -import { AuthenticationError } from '../../toolkit/error'; -import { formatErrorMessage } from '../../util/error'; -import { traceMethods } from '../../util/tracing'; - -export interface S3ClientOptions { - /** - * If APIs are used that require MD5 checksums. - * - * Some S3 APIs in SDKv2 have a bug that always requires them to use a MD5 checksum. - * These APIs are not going to be supported in a FIPS environment. - */ - needsMd5Checksums?: boolean; -} - -/** - * Additional SDK configuration options - */ -export interface SdkOptions { - /** - * Additional descriptive strings that indicate where the "AssumeRole" credentials are coming from - * - * Will be printed in an error message to help users diagnose auth problems. - */ - readonly assumeRoleCredentialsSourceDescription?: string; -} - -// TODO: still some cleanup here. Make the pagination functions do all the work here instead of in individual packages. -// Also add async/await. Does that actually matter in this context? Find out and update accordingly. - -// Also add notes to the PR about why you imported everything individually and used 'type' so reviewers don't have to ask. - -export interface ConfigurationOptions { - region: string; - credentials: AwsCredentialIdentityProvider; - requestHandler: NodeHttpHandlerOptions; - retryStrategy: ConfiguredRetryStrategy; - customUserAgent: string; - logger?: Logger; - s3DisableBodySigning?: boolean; - computeChecksums?: boolean; -} - -export interface IAppSyncClient { - getSchemaCreationStatus(input: GetSchemaCreationStatusCommandInput): Promise; - startSchemaCreation(input: StartSchemaCreationCommandInput): Promise; - updateApiKey(input: UpdateApiKeyCommandInput): Promise; - updateFunction(input: UpdateFunctionCommandInput): Promise; - updateResolver(input: UpdateResolverCommandInput): Promise; - // Pagination functions - listFunctions(input: ListFunctionsCommandInput): Promise; -} - -export interface ICloudFormationClient { - continueUpdateRollback(input: ContinueUpdateRollbackCommandInput): Promise; - createChangeSet(input: CreateChangeSetCommandInput): Promise; - createGeneratedTemplate(input: CreateGeneratedTemplateCommandInput): Promise; - createStack(input: CreateStackCommandInput): Promise; - deleteChangeSet(input: DeleteChangeSetCommandInput): Promise; - deleteGeneratedTemplate(input: DeleteGeneratedTemplateCommandInput): Promise; - deleteStack(input: DeleteStackCommandInput): Promise; - describeChangeSet(input: DescribeChangeSetCommandInput): Promise; - describeGeneratedTemplate( - input: DescribeGeneratedTemplateCommandInput, - ): Promise; - describeResourceScan(input: DescribeResourceScanCommandInput): Promise; - describeStacks(input: DescribeStacksCommandInput): Promise; - describeStackResources(input: DescribeStackResourcesCommandInput): Promise; - executeChangeSet(input: ExecuteChangeSetCommandInput): Promise; - getGeneratedTemplate(input: GetGeneratedTemplateCommandInput): Promise; - getTemplate(input: GetTemplateCommandInput): Promise; - getTemplateSummary(input: GetTemplateSummaryCommandInput): Promise; - listExports(input: ListExportsCommandInput): Promise; - listResourceScanRelatedResources( - input: ListResourceScanRelatedResourcesCommandInput, - ): Promise; - listResourceScanResources( - input: ListResourceScanResourcesCommandInput, - ): Promise; - listResourceScans(input?: ListResourceScansCommandInput): Promise; - listStacks(input: ListStacksCommandInput): Promise; - rollbackStack(input: RollbackStackCommandInput): Promise; - startResourceScan(input: StartResourceScanCommandInput): Promise; - updateStack(input: UpdateStackCommandInput): Promise; - updateTerminationProtection( - input: UpdateTerminationProtectionCommandInput, - ): Promise; - // Pagination functions - describeStackEvents(input: DescribeStackEventsCommandInput): Promise; - listStackResources(input: ListStackResourcesCommandInput): Promise; -} - -export interface ICloudWatchLogsClient { - describeLogGroups(input: DescribeLogGroupsCommandInput): Promise; - filterLogEvents(input: FilterLogEventsCommandInput): Promise; -} - -export interface ICodeBuildClient { - updateProject(input: UpdateProjectCommandInput): Promise; -} -export interface IEC2Client { - describeAvailabilityZones( - input: DescribeAvailabilityZonesCommandInput, - ): Promise; - describeImages(input: DescribeImagesCommandInput): Promise; - describeInstances(input: DescribeInstancesCommandInput): Promise; - describeRouteTables(input: DescribeRouteTablesCommandInput): Promise; - describeSecurityGroups(input: DescribeSecurityGroupsCommandInput): Promise; - describeSubnets(input: DescribeSubnetsCommandInput): Promise; - describeVpcEndpointServices( - input: DescribeVpcEndpointServicesCommandInput, - ): Promise; - describeVpcs(input: DescribeVpcsCommandInput): Promise; - describeVpnGateways(input: DescribeVpnGatewaysCommandInput): Promise; -} - -export interface IECRClient { - batchDeleteImage(input: BatchDeleteImageCommandInput): Promise; - batchGetImage(input: BatchGetImageCommandInput): Promise; - createRepository(input: CreateRepositoryCommandInput): Promise; - describeImages(input: ECRDescribeImagesCommandInput): Promise; - describeRepositories(input: DescribeRepositoriesCommandInput): Promise; - getAuthorizationToken(input: GetAuthorizationTokenCommandInput): Promise; - listImages(input: ListImagesCommandInput): Promise; - putImage(input: PutImageCommandInput): Promise; - putImageScanningConfiguration( - input: PutImageScanningConfigurationCommandInput, - ): Promise; -} - -export interface IECSClient { - listClusters(input: ListClustersCommandInput): Promise; - registerTaskDefinition(input: RegisterTaskDefinitionCommandInput): Promise; - updateService(input: UpdateServiceCommandInput): Promise; - // Waiters - waitUntilServicesStable(input: DescribeServicesCommandInput): Promise; -} - -export interface IElasticLoadBalancingV2Client { - describeListeners(input: DescribeListenersCommandInput): Promise; - describeLoadBalancers(input: DescribeLoadBalancersCommandInput): Promise; - describeTags(input: DescribeTagsCommandInput): Promise; - // Pagination - paginateDescribeListeners(input: DescribeListenersCommandInput): Promise; - paginateDescribeLoadBalancers(input: DescribeLoadBalancersCommandInput): Promise; -} - -export interface IIAMClient { - createPolicy(input: CreatePolicyCommandInput): Promise; - getPolicy(input: GetPolicyCommandInput): Promise; - getRole(input: GetRoleCommandInput): Promise; -} - -export interface IKMSClient { - describeKey(input: DescribeKeyCommandInput): Promise; - listAliases(input: ListAliasesCommandInput): Promise; -} - -export interface ILambdaClient { - invokeCommand(input: InvokeCommandInput): Promise; - publishVersion(input: PublishVersionCommandInput): Promise; - updateAlias(input: UpdateAliasCommandInput): Promise; - updateFunctionCode(input: UpdateFunctionCodeCommandInput): Promise; - updateFunctionConfiguration( - input: UpdateFunctionConfigurationCommandInput, - ): Promise; - // Waiters - waitUntilFunctionUpdated(delaySeconds: number, input: UpdateFunctionConfigurationCommandInput): Promise; -} - -export interface IRoute53Client { - getHostedZone(input: GetHostedZoneCommandInput): Promise; - listHostedZones(input: ListHostedZonesCommandInput): Promise; - listHostedZonesByName(input: ListHostedZonesByNameCommandInput): Promise; -} - -export interface IS3Client { - deleteObjects(input: DeleteObjectsCommandInput): Promise; - deleteObjectTagging(input: DeleteObjectTaggingCommandInput): Promise; - getBucketEncryption(input: GetBucketEncryptionCommandInput): Promise; - getBucketLocation(input: GetBucketLocationCommandInput): Promise; - getObject(input: GetObjectCommandInput): Promise; - getObjectTagging(input: GetObjectTaggingCommandInput): Promise; - listObjectsV2(input: ListObjectsV2CommandInput): Promise; - putObjectTagging(input: PutObjectTaggingCommandInput): Promise; - upload(input: PutObjectCommandInput): Promise; -} - -export interface ISecretsManagerClient { - getSecretValue(input: GetSecretValueCommandInput): Promise; -} - -export interface ISSMClient { - getParameter(input: GetParameterCommandInput): Promise; -} - -export interface IStepFunctionsClient { - updateStateMachine(input: UpdateStateMachineCommandInput): Promise; -} - -/** - * Base functionality of SDK without credential fetching - */ -@traceMethods -export class SDK { - private static readonly accountCache = new AccountAccessKeyCache(); - - public readonly currentRegion: string; - - public readonly config: ConfigurationOptions; - - /** - * STS is used to check credential validity, don't do too many retries. - */ - private readonly stsRetryStrategy = new ConfiguredRetryStrategy(3, (attempt) => 100 * (2 ** attempt)); - - /** - * Whether we have proof that the credentials have not expired - * - * We need to do some manual plumbing around this because the JS SDKv2 treats `ExpiredToken` - * as retriable and we have hefty retries on CFN calls making the CLI hang for a good 15 minutes - * if the credentials have expired. - */ - private _credentialsValidated = false; - - constructor( - private readonly credProvider: AwsCredentialIdentityProvider, - region: string, - requestHandler: NodeHttpHandlerOptions, - logger?: Logger, - ) { - this.config = { - region, - credentials: credProvider, - requestHandler, - retryStrategy: new ConfiguredRetryStrategy(7, (attempt) => 300 * (2 ** attempt)), - customUserAgent: defaultCliUserAgent(), - logger, - }; - this.currentRegion = region; - } - - public appendCustomUserAgent(userAgentData?: string): void { - if (!userAgentData) { - return; - } - - const currentCustomUserAgent = this.config.customUserAgent; - this.config.customUserAgent = currentCustomUserAgent ? `${currentCustomUserAgent} ${userAgentData}` : userAgentData; - } - - public removeCustomUserAgent(userAgentData: string): void { - this.config.customUserAgent = this.config.customUserAgent?.replace(userAgentData, ''); - } - - public appsync(): IAppSyncClient { - const client = new AppSyncClient(this.config); - return { - getSchemaCreationStatus: ( - input: GetSchemaCreationStatusCommandInput, - ): Promise => client.send(new GetSchemaCreationStatusCommand(input)), - startSchemaCreation: (input: StartSchemaCreationCommandInput): Promise => - client.send(new StartSchemaCreationCommand(input)), - updateApiKey: (input: UpdateApiKeyCommandInput): Promise => - client.send(new UpdateApiKeyCommand(input)), - updateFunction: (input: UpdateFunctionCommandInput): Promise => - client.send(new UpdateFunctionCommand(input)), - updateResolver: (input: UpdateResolverCommandInput): Promise => - client.send(new UpdateResolverCommand(input)), - - // Pagination Functions - listFunctions: async (input: ListFunctionsCommandInput): Promise => { - const functions = Array(); - const paginator = paginateListFunctions({ client }, input); - for await (const page of paginator) { - functions.push(...(page.functions || [])); - } - return functions; - }, - }; - } - - public cloudFormation(): ICloudFormationClient { - const client = new CloudFormationClient({ - ...this.config, - retryStrategy: new ConfiguredRetryStrategy(11, (attempt: number) => 1000 * (2 ** attempt)), - }); - return { - continueUpdateRollback: async ( - input: ContinueUpdateRollbackCommandInput, - ): Promise => client.send(new ContinueUpdateRollbackCommand(input)), - createChangeSet: (input: CreateChangeSetCommandInput): Promise => - client.send(new CreateChangeSetCommand(input)), - createGeneratedTemplate: ( - input: CreateGeneratedTemplateCommandInput, - ): Promise => client.send(new CreateGeneratedTemplateCommand(input)), - createStack: (input: CreateStackCommandInput): Promise => - client.send(new CreateStackCommand(input)), - deleteChangeSet: (input: DeleteChangeSetCommandInput): Promise => - client.send(new DeleteChangeSetCommand(input)), - deleteGeneratedTemplate: ( - input: DeleteGeneratedTemplateCommandInput, - ): Promise => client.send(new DeleteGeneratedTemplateCommand(input)), - deleteStack: (input: DeleteStackCommandInput): Promise => - client.send(new DeleteStackCommand(input)), - describeChangeSet: (input: DescribeChangeSetCommandInput): Promise => - client.send(new DescribeChangeSetCommand(input)), - describeGeneratedTemplate: ( - input: DescribeGeneratedTemplateCommandInput, - ): Promise => client.send(new DescribeGeneratedTemplateCommand(input)), - describeResourceScan: (input: DescribeResourceScanCommandInput): Promise => - client.send(new DescribeResourceScanCommand(input)), - describeStacks: (input: DescribeStacksCommandInput): Promise => - client.send(new DescribeStacksCommand(input)), - describeStackResources: (input: DescribeStackResourcesCommandInput): Promise => - client.send(new DescribeStackResourcesCommand(input)), - executeChangeSet: (input: ExecuteChangeSetCommandInput): Promise => - client.send(new ExecuteChangeSetCommand(input)), - getGeneratedTemplate: (input: GetGeneratedTemplateCommandInput): Promise => - client.send(new GetGeneratedTemplateCommand(input)), - getTemplate: (input: GetTemplateCommandInput): Promise => - client.send(new GetTemplateCommand(input)), - getTemplateSummary: (input: GetTemplateSummaryCommandInput): Promise => - client.send(new GetTemplateSummaryCommand(input)), - listExports: (input: ListExportsCommandInput): Promise => - client.send(new ListExportsCommand(input)), - listResourceScanRelatedResources: ( - input: ListResourceScanRelatedResourcesCommandInput, - ): Promise => - client.send(new ListResourceScanRelatedResourcesCommand(input)), - listResourceScanResources: ( - input: ListResourceScanResourcesCommandInput, - ): Promise => client.send(new ListResourceScanResourcesCommand(input)), - listResourceScans: (input: ListResourceScansCommandInput): Promise => - client.send(new ListResourceScansCommand(input)), - listStacks: (input: ListStacksCommandInput): Promise => - client.send(new ListStacksCommand(input)), - rollbackStack: (input: RollbackStackCommandInput): Promise => - client.send(new RollbackStackCommand(input)), - startResourceScan: (input: StartResourceScanCommandInput): Promise => - client.send(new StartResourceScanCommand(input)), - updateStack: (input: UpdateStackCommandInput): Promise => - client.send(new UpdateStackCommand(input)), - updateTerminationProtection: ( - input: UpdateTerminationProtectionCommandInput, - ): Promise => - client.send(new UpdateTerminationProtectionCommand(input)), - describeStackEvents: (input: DescribeStackEventsCommandInput): Promise => { - return client.send(new DescribeStackEventsCommand(input)); - }, - listStackResources: async (input: ListStackResourcesCommandInput): Promise => { - const stackResources = Array(); - const paginator = paginateListStackResources({ client }, input); - for await (const page of paginator) { - stackResources.push(...(page?.StackResourceSummaries || [])); - } - return stackResources; - }, - }; - } - - public cloudWatchLogs(): ICloudWatchLogsClient { - const client = new CloudWatchLogsClient(this.config); - return { - describeLogGroups: (input: DescribeLogGroupsCommandInput): Promise => - client.send(new DescribeLogGroupsCommand(input)), - filterLogEvents: (input: FilterLogEventsCommandInput): Promise => - client.send(new FilterLogEventsCommand(input)), - }; - } - - public codeBuild(): ICodeBuildClient { - const client = new CodeBuildClient(this.config); - return { - updateProject: (input: UpdateProjectCommandInput): Promise => - client.send(new UpdateProjectCommand(input)), - }; - } - - public ec2(): IEC2Client { - const client = new EC2Client(this.config); - return { - describeAvailabilityZones: ( - input: DescribeAvailabilityZonesCommandInput, - ): Promise => client.send(new DescribeAvailabilityZonesCommand(input)), - describeImages: (input: DescribeImagesCommandInput): Promise => - client.send(new DescribeImagesCommand(input)), - describeInstances: (input: DescribeInstancesCommandInput): Promise => - client.send(new DescribeInstancesCommand(input)), - describeRouteTables: (input: DescribeRouteTablesCommandInput): Promise => - client.send(new DescribeRouteTablesCommand(input)), - describeSecurityGroups: ( - input: DescribeSecurityGroupsCommandInput, - ): Promise => client.send(new DescribeSecurityGroupsCommand(input)), - describeSubnets: (input: DescribeSubnetsCommandInput): Promise => - client.send(new DescribeSubnetsCommand(input)), - describeVpcEndpointServices: ( - input: DescribeVpcEndpointServicesCommandInput, - ): Promise => - client.send(new DescribeVpcEndpointServicesCommand(input)), - describeVpcs: (input: DescribeVpcsCommandInput): Promise => - client.send(new DescribeVpcsCommand(input)), - describeVpnGateways: (input: DescribeVpnGatewaysCommandInput): Promise => - client.send(new DescribeVpnGatewaysCommand(input)), - }; - } - - public ecr(): IECRClient { - const client = new ECRClient(this.config); - return { - batchDeleteImage: (input: BatchDeleteImageCommandInput): Promise => - client.send(new BatchDeleteImageCommand(input)), - batchGetImage: (input: BatchGetImageCommandInput): Promise => - client.send(new BatchGetImageCommand(input)), - createRepository: (input: CreateRepositoryCommandInput): Promise => - client.send(new CreateRepositoryCommand(input)), - describeImages: (input: ECRDescribeImagesCommandInput): Promise => - client.send(new ECRDescribeImagesCommand(input)), - describeRepositories: (input: DescribeRepositoriesCommandInput): Promise => - client.send(new DescribeRepositoriesCommand(input)), - getAuthorizationToken: (input: GetAuthorizationTokenCommandInput): Promise => - client.send(new GetAuthorizationTokenCommand(input)), - listImages: (input: ListImagesCommandInput): Promise => - client.send(new ListImagesCommand(input)), - putImage: (input: PutImageCommandInput): Promise => - client.send(new PutImageCommand(input)), - putImageScanningConfiguration: ( - input: PutImageScanningConfigurationCommandInput, - ): Promise => - client.send(new PutImageScanningConfigurationCommand(input)), - }; - } - - public ecs(): IECSClient { - const client = new ECSClient(this.config); - return { - listClusters: (input: ListClustersCommandInput): Promise => - client.send(new ListClustersCommand(input)), - registerTaskDefinition: ( - input: RegisterTaskDefinitionCommandInput, - ): Promise => client.send(new RegisterTaskDefinitionCommand(input)), - updateService: (input: UpdateServiceCommandInput): Promise => - client.send(new UpdateServiceCommand(input)), - // Waiters - waitUntilServicesStable: (input: DescribeServicesCommandInput): Promise => { - return waitUntilServicesStable( - { - client, - maxWaitTime: 600, - minDelay: 6, - maxDelay: 6, - }, - input, - ); - }, - }; - } - - public elbv2(): IElasticLoadBalancingV2Client { - const client = new ElasticLoadBalancingV2Client(this.config); - return { - describeListeners: (input: DescribeListenersCommandInput): Promise => - client.send(new DescribeListenersCommand(input)), - describeLoadBalancers: (input: DescribeLoadBalancersCommandInput): Promise => - client.send(new DescribeLoadBalancersCommand(input)), - describeTags: (input: DescribeTagsCommandInput): Promise => - client.send(new DescribeTagsCommand(input)), - // Pagination Functions - paginateDescribeListeners: async (input: DescribeListenersCommandInput): Promise => { - const listeners = Array(); - const paginator = paginateDescribeListeners({ client }, input); - for await (const page of paginator) { - listeners.push(...(page?.Listeners || [])); - } - return listeners; - }, - paginateDescribeLoadBalancers: async (input: DescribeLoadBalancersCommandInput): Promise => { - const loadBalancers = Array(); - const paginator = paginateDescribeLoadBalancers({ client }, input); - for await (const page of paginator) { - loadBalancers.push(...(page?.LoadBalancers || [])); - } - return loadBalancers; - }, - }; - } - - public iam(): IIAMClient { - const client = new IAMClient(this.config); - return { - createPolicy: (input: CreatePolicyCommandInput): Promise => - client.send(new CreatePolicyCommand(input)), - getPolicy: (input: GetPolicyCommandInput): Promise => - client.send(new GetPolicyCommand(input)), - getRole: (input: GetRoleCommandInput): Promise => client.send(new GetRoleCommand(input)), - }; - } - - public kms(): IKMSClient { - const client = new KMSClient(this.config); - return { - describeKey: (input: DescribeKeyCommandInput): Promise => - client.send(new DescribeKeyCommand(input)), - listAliases: (input: ListAliasesCommandInput): Promise => - client.send(new ListAliasesCommand(input)), - }; - } - - public lambda(): ILambdaClient { - const client = new LambdaClient(this.config); - return { - invokeCommand: (input: InvokeCommandInput): Promise => client.send(new InvokeCommand(input)), - publishVersion: (input: PublishVersionCommandInput): Promise => - client.send(new PublishVersionCommand(input)), - updateAlias: (input: UpdateAliasCommandInput): Promise => - client.send(new UpdateAliasCommand(input)), - updateFunctionCode: (input: UpdateFunctionCodeCommandInput): Promise => - client.send(new UpdateFunctionCodeCommand(input)), - updateFunctionConfiguration: ( - input: UpdateFunctionConfigurationCommandInput, - ): Promise => - client.send(new UpdateFunctionConfigurationCommand(input)), - // Waiters - waitUntilFunctionUpdated: ( - delaySeconds: number, - input: UpdateFunctionConfigurationCommandInput, - ): Promise => { - return waitUntilFunctionUpdatedV2( - { - client, - maxDelay: delaySeconds, - minDelay: delaySeconds, - maxWaitTime: delaySeconds * 60, - }, - input, - ); - }, - }; - } - - public route53(): IRoute53Client { - const client = new Route53Client(this.config); - return { - getHostedZone: (input: GetHostedZoneCommandInput): Promise => - client.send(new GetHostedZoneCommand(input)), - listHostedZones: (input: ListHostedZonesCommandInput): Promise => - client.send(new ListHostedZonesCommand(input)), - listHostedZonesByName: (input: ListHostedZonesByNameCommandInput): Promise => - client.send(new ListHostedZonesByNameCommand(input)), - }; - } - - public s3(): IS3Client { - const client = new S3Client(this.config); - return { - deleteObjects: (input: DeleteObjectsCommandInput): Promise => - client.send(new DeleteObjectsCommand({ - ...input, - ChecksumAlgorithm: 'SHA256', - })), - deleteObjectTagging: (input: DeleteObjectTaggingCommandInput): Promise => - client.send(new DeleteObjectTaggingCommand(input)), - getBucketEncryption: (input: GetBucketEncryptionCommandInput): Promise => - client.send(new GetBucketEncryptionCommand(input)), - getBucketLocation: (input: GetBucketLocationCommandInput): Promise => - client.send(new GetBucketLocationCommand(input)), - getObject: (input: GetObjectCommandInput): Promise => - client.send(new GetObjectCommand(input)), - getObjectTagging: (input: GetObjectTaggingCommandInput): Promise => - client.send(new GetObjectTaggingCommand(input)), - listObjectsV2: (input: ListObjectsV2CommandInput): Promise => - client.send(new ListObjectsV2Command(input)), - putObjectTagging: (input: PutObjectTaggingCommandInput): Promise => - client.send(new PutObjectTaggingCommand({ - ...input, - ChecksumAlgorithm: 'SHA256', - })), - upload: (input: PutObjectCommandInput): Promise => { - try { - const upload = new Upload({ - client, - params: { ...input, ChecksumAlgorithm: 'SHA256' }, - }); - - return upload.done(); - } catch (e: any) { - throw new AuthenticationError(`Upload failed: ${formatErrorMessage(e)}`); - } - }, - }; - } - - public secretsManager(): ISecretsManagerClient { - const client = new SecretsManagerClient(this.config); - return { - getSecretValue: (input: GetSecretValueCommandInput): Promise => - client.send(new GetSecretValueCommand(input)), - }; - } - - public ssm(): ISSMClient { - const client = new SSMClient(this.config); - return { - getParameter: (input: GetParameterCommandInput): Promise => - client.send(new GetParameterCommand(input)), - }; - } - - public stepFunctions(): IStepFunctionsClient { - const client = new SFNClient(this.config); - return { - updateStateMachine: (input: UpdateStateMachineCommandInput): Promise => - client.send(new UpdateStateMachineCommand(input)), - }; - } - - /** - * The AWS SDK v3 requires a client config and a command in order to get an endpoint for - * any given service. - */ - public async getUrlSuffix(region: string): Promise { - const cfn = new CloudFormationClient({ region }); - const endpoint = await getEndpointFromInstructions({}, DescribeStackResourcesCommand, { ...cfn.config }); - return endpoint.url.hostname.split(`${region}.`).pop()!; - } - - public async currentAccount(): Promise { - return cachedAsync(this, CURRENT_ACCOUNT_KEY, async () => { - const creds = await this.credProvider(); - return SDK.accountCache.fetch(creds.accessKeyId, async () => { - // if we don't have one, resolve from STS and store in cache. - debug('Looking up default account ID from STS'); - const client = new STSClient({ - ...this.config, - retryStrategy: this.stsRetryStrategy, - }); - const command = new GetCallerIdentityCommand({}); - const result = await client.send(command); - const accountId = result.Account; - const partition = result.Arn!.split(':')[1]; - if (!accountId) { - throw new AuthenticationError("STS didn't return an account ID"); - } - debug('Default account ID:', accountId); - - // Save another STS call later if this one already succeeded - this._credentialsValidated = true; - return { accountId, partition }; - }); - }); - } - - /** - * Make sure the the current credentials are not expired - */ - public async validateCredentials() { - if (this._credentialsValidated) { - return; - } - - const client = new STSClient({ ...this.config, retryStrategy: this.stsRetryStrategy }); - await client.send(new GetCallerIdentityCommand({})); - this._credentialsValidated = true; - } -} - -const CURRENT_ACCOUNT_KEY = Symbol('current_account_key'); diff --git a/packages/aws-cdk/lib/api/aws-auth/user-agent.ts b/packages/aws-cdk/lib/api/aws-auth/user-agent.ts deleted file mode 100644 index 98e2f716d57d1..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/user-agent.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as path from 'path'; -import { readIfPossible } from './util'; -import { rootDir } from '../../util/directories'; - -/** - * Find the package.json from the main toolkit. - * - * If we can't read it for some reason, try to do something reasonable anyway. - * Fall back to argv[1], or a standard string if that is undefined for some reason. - */ -export function defaultCliUserAgent() { - const root = rootDir(false); - const pkg = JSON.parse((root ? readIfPossible(path.join(root, 'package.json')) : undefined) ?? '{}'); - const name = pkg.name ?? path.basename(process.argv[1] ?? 'cdk-cli'); - const version = pkg.version ?? ''; - return `${name}/${version}`; -} diff --git a/packages/aws-cdk/lib/api/aws-auth/util.ts b/packages/aws-cdk/lib/api/aws-auth/util.ts deleted file mode 100644 index b5c6f57b7032b..0000000000000 --- a/packages/aws-cdk/lib/api/aws-auth/util.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as fs from 'fs-extra'; -import { debug } from '../../logging'; - -/** - * Read a file if it exists, or return undefined - * - * Not async because it is used in the constructor - */ -export function readIfPossible(filename: string): string | undefined { - try { - if (!fs.pathExistsSync(filename)) { - return undefined; - } - return fs.readFileSync(filename, { encoding: 'utf-8' }); - } catch (e: any) { - debug(e); - return undefined; - } -} diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts b/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts deleted file mode 100644 index ad0acf18bf477..0000000000000 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts +++ /dev/null @@ -1,378 +0,0 @@ -import { info } from 'console'; -import * as path from 'path'; -import * as cxapi from '@aws-cdk/cx-api'; -import type { BootstrapEnvironmentOptions, BootstrappingParameters } from './bootstrap-props'; -import { BootstrapStack, bootstrapVersionFromTemplate } from './deploy-bootstrap'; -import { legacyBootstrapTemplate } from './legacy-template'; -import { warning } from '../../logging'; -import { loadStructuredFile, serializeStructure } from '../../serialize'; -import { ToolkitError } from '../../toolkit/error'; -import { rootDir } from '../../util/directories'; -import type { SDK, SdkProvider } from '../aws-auth'; -import type { SuccessfulDeployStackResult } from '../deploy-stack'; -import { Mode } from '../plugin/mode'; - -export type BootstrapSource = { source: 'legacy' } | { source: 'default' } | { source: 'custom'; templateFile: string }; - -export class Bootstrapper { - constructor(private readonly source: BootstrapSource = { source: 'default' }) {} - - public bootstrapEnvironment( - environment: cxapi.Environment, - sdkProvider: SdkProvider, - options: BootstrapEnvironmentOptions = {}, - ): Promise { - switch (this.source.source) { - case 'legacy': - return this.legacyBootstrap(environment, sdkProvider, options); - case 'default': - return this.modernBootstrap(environment, sdkProvider, options); - case 'custom': - return this.customBootstrap(environment, sdkProvider, options); - } - } - - public async showTemplate(json: boolean) { - const template = await this.loadTemplate(); - process.stdout.write(`${serializeStructure(template, json)}\n`); - } - - /** - * Deploy legacy bootstrap stack - * - */ - private async legacyBootstrap( - environment: cxapi.Environment, - sdkProvider: SdkProvider, - options: BootstrapEnvironmentOptions = {}, - ): Promise { - const params = options.parameters ?? {}; - - if (params.trustedAccounts?.length) { - throw new ToolkitError('--trust can only be passed for the modern bootstrap experience.'); - } - if (params.cloudFormationExecutionPolicies?.length) { - throw new ToolkitError('--cloudformation-execution-policies can only be passed for the modern bootstrap experience.'); - } - if (params.createCustomerMasterKey !== undefined) { - throw new ToolkitError('--bootstrap-customer-key can only be passed for the modern bootstrap experience.'); - } - if (params.qualifier) { - throw new ToolkitError('--qualifier can only be passed for the modern bootstrap experience.'); - } - - const current = await BootstrapStack.lookup(sdkProvider, environment, options.toolkitStackName); - return current.update( - await this.loadTemplate(params), - {}, - { - ...options, - terminationProtection: options.terminationProtection ?? current.terminationProtection, - }, - ); - } - - /** - * Deploy CI/CD-ready bootstrap stack from template - * - */ - private async modernBootstrap( - environment: cxapi.Environment, - sdkProvider: SdkProvider, - options: BootstrapEnvironmentOptions = {}, - ): Promise { - const params = options.parameters ?? {}; - - const bootstrapTemplate = await this.loadTemplate(); - - const current = await BootstrapStack.lookup(sdkProvider, environment, options.toolkitStackName); - const partition = await current.partition(); - - if (params.createCustomerMasterKey !== undefined && params.kmsKeyId) { - throw new ToolkitError( - "You cannot pass '--bootstrap-kms-key-id' and '--bootstrap-customer-key' together. Specify one or the other", - ); - } - - // If people re-bootstrap, existing parameter values are reused so that people don't accidentally change the configuration - // on their bootstrap stack (this happens automatically in deployStack). However, to do proper validation on the - // combined arguments (such that if --trust has been given, --cloudformation-execution-policies is necessary as well) - // we need to take this parameter reuse into account. - // - // Ideally we'd do this inside the template, but the `Rules` section of CFN - // templates doesn't seem to be able to express the conditions that we need - // (can't use Fn::Join or reference Conditions) so we do it here instead. - const trustedAccounts = params.trustedAccounts ?? splitCfnArray(current.parameters.TrustedAccounts); - info(`Trusted accounts for deployment: ${trustedAccounts.length > 0 ? trustedAccounts.join(', ') : '(none)'}`); - - const trustedAccountsForLookup = - params.trustedAccountsForLookup ?? splitCfnArray(current.parameters.TrustedAccountsForLookup); - info( - `Trusted accounts for lookup: ${trustedAccountsForLookup.length > 0 ? trustedAccountsForLookup.join(', ') : '(none)'}`, - ); - - const cloudFormationExecutionPolicies = - params.cloudFormationExecutionPolicies ?? splitCfnArray(current.parameters.CloudFormationExecutionPolicies); - if (trustedAccounts.length === 0 && cloudFormationExecutionPolicies.length === 0) { - // For self-trust it's okay to default to AdministratorAccess, and it improves the usability of bootstrapping a lot. - // - // We don't actually make the implicitly policy a physical parameter. The template will infer it instead, - // we simply do the UI advertising that behavior here. - // - // If we DID make it an explicit parameter, we wouldn't be able to tell the difference between whether - // we inferred it or whether the user told us, and the sequence: - // - // $ cdk bootstrap - // $ cdk bootstrap --trust 1234 - // - // Would leave AdministratorAccess policies with a trust relationship, without the user explicitly - // approving the trust policy. - const implicitPolicy = `arn:${partition}:iam::aws:policy/AdministratorAccess`; - warning( - `Using default execution policy of '${implicitPolicy}'. Pass '--cloudformation-execution-policies' to customize.`, - ); - } else if (cloudFormationExecutionPolicies.length === 0) { - throw new ToolkitError( - `Please pass \'--cloudformation-execution-policies\' when using \'--trust\' to specify deployment permissions. Try a managed policy of the form \'arn:${partition}:iam::aws:policy/\'.`, - ); - } else { - // Remind people what the current settings are - info(`Execution policies: ${cloudFormationExecutionPolicies.join(', ')}`); - } - - // * If an ARN is given, that ARN. Otherwise: - // * '-' if customerKey = false - // * '' if customerKey = true - // * if customerKey is also not given - // * undefined if we already had a value in place (reusing what we had) - // * '-' if this is the first time we're deploying this stack (or upgrading from old to new bootstrap) - const currentKmsKeyId = current.parameters.FileAssetsBucketKmsKeyId; - const kmsKeyId = - params.kmsKeyId ?? - (params.createCustomerMasterKey === true - ? CREATE_NEW_KEY - : params.createCustomerMasterKey === false || currentKmsKeyId === undefined - ? USE_AWS_MANAGED_KEY - : undefined); - - /* A permissions boundary can be provided via: - * - the flag indicating the example one should be used - * - the name indicating the custom permissions boundary to be used - * Re-bootstrapping will NOT be blocked by either tightening or relaxing the permissions' boundary. - */ - - // InputPermissionsBoundary is an `any` type and if it is not defined it - // appears as an empty string ''. We need to force it to evaluate an empty string - // as undefined - const currentPermissionsBoundary: string | undefined = current.parameters.InputPermissionsBoundary || undefined; - const inputPolicyName = params.examplePermissionsBoundary - ? CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY - : params.customPermissionsBoundary; - let policyName: string | undefined; - if (inputPolicyName) { - // If the example policy is not already in place, it must be created. - const sdk = (await sdkProvider.forEnvironment(environment, Mode.ForWriting)).sdk; - policyName = await this.getPolicyName(environment, sdk, inputPolicyName, partition, params); - } - if (currentPermissionsBoundary !== policyName) { - if (!currentPermissionsBoundary) { - warning(`Adding new permissions boundary ${policyName}`); - } else if (!policyName) { - warning(`Removing existing permissions boundary ${currentPermissionsBoundary}`); - } else { - warning(`Changing permissions boundary from ${currentPermissionsBoundary} to ${policyName}`); - } - } - - return current.update( - bootstrapTemplate, - { - FileAssetsBucketName: params.bucketName, - FileAssetsBucketKmsKeyId: kmsKeyId, - // Empty array becomes empty string - TrustedAccounts: trustedAccounts.join(','), - TrustedAccountsForLookup: trustedAccountsForLookup.join(','), - CloudFormationExecutionPolicies: cloudFormationExecutionPolicies.join(','), - Qualifier: params.qualifier, - PublicAccessBlockConfiguration: - params.publicAccessBlockConfiguration || params.publicAccessBlockConfiguration === undefined - ? 'true' - : 'false', - InputPermissionsBoundary: policyName, - }, - { - ...options, - terminationProtection: options.terminationProtection ?? current.terminationProtection, - }, - ); - } - - private async getPolicyName( - environment: cxapi.Environment, - sdk: SDK, - permissionsBoundary: string, - partition: string, - params: BootstrappingParameters, - ): Promise { - if (permissionsBoundary !== CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY) { - this.validatePolicyName(permissionsBoundary); - return Promise.resolve(permissionsBoundary); - } - // if no Qualifier is supplied, resort to the default one - const arn = await this.getExamplePermissionsBoundary( - params.qualifier ?? 'hnb659fds', - partition, - environment.account, - sdk, - ); - const policyName = arn.split('/').pop(); - if (!policyName) { - throw new ToolkitError('Could not retrieve the example permission boundary!'); - } - return Promise.resolve(policyName); - } - - private async getExamplePermissionsBoundary( - qualifier: string, - partition: string, - account: string, - sdk: SDK, - ): Promise { - const iam = sdk.iam(); - - let policyName = `cdk-${qualifier}-permissions-boundary`; - const arn = `arn:${partition}:iam::${account}:policy/${policyName}`; - - try { - let getPolicyResp = await iam.getPolicy({ PolicyArn: arn }); - if (getPolicyResp.Policy) { - return arn; - } - } catch (e: any) { - // https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicy.html#API_GetPolicy_Errors - if (e.name === 'NoSuchEntity') { - //noop, proceed with creating the policy - } else { - throw e; - } - } - - const policyDoc = { - Version: '2012-10-17', - Statement: [ - { - Action: ['*'], - Resource: '*', - Effect: 'Allow', - Sid: 'ExplicitAllowAll', - }, - { - Condition: { - StringEquals: { - 'iam:PermissionsBoundary': `arn:${partition}:iam::${account}:policy/cdk-${qualifier}-permissions-boundary`, - }, - }, - Action: [ - 'iam:CreateUser', - 'iam:CreateRole', - 'iam:PutRolePermissionsBoundary', - 'iam:PutUserPermissionsBoundary', - ], - Resource: '*', - Effect: 'Allow', - Sid: 'DenyAccessIfRequiredPermBoundaryIsNotBeingApplied', - }, - { - Action: [ - 'iam:CreatePolicyVersion', - 'iam:DeletePolicy', - 'iam:DeletePolicyVersion', - 'iam:SetDefaultPolicyVersion', - ], - Resource: `arn:${partition}:iam::${account}:policy/cdk-${qualifier}-permissions-boundary`, - Effect: 'Deny', - Sid: 'DenyPermBoundaryIAMPolicyAlteration', - }, - { - Action: ['iam:DeleteUserPermissionsBoundary', 'iam:DeleteRolePermissionsBoundary'], - Resource: '*', - Effect: 'Deny', - Sid: 'DenyRemovalOfPermBoundaryFromAnyUserOrRole', - }, - ], - }; - const request = { - PolicyName: policyName, - PolicyDocument: JSON.stringify(policyDoc), - }; - const createPolicyResponse = await iam.createPolicy(request); - if (createPolicyResponse.Policy?.Arn) { - return createPolicyResponse.Policy.Arn; - } else { - throw new ToolkitError(`Could not retrieve the example permission boundary ${arn}!`); - } - } - - private validatePolicyName(permissionsBoundary: string) { - // https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicy.html - // Added support for policy names with a path - // See https://github.com/aws/aws-cdk/issues/26320 - const regexp: RegExp = /[\w+\/=,.@-]+/; - const matches = regexp.exec(permissionsBoundary); - if (!(matches && matches.length === 1 && matches[0] === permissionsBoundary)) { - throw new ToolkitError(`The permissions boundary name ${permissionsBoundary} does not match the IAM conventions.`); - } - } - - private async customBootstrap( - environment: cxapi.Environment, - sdkProvider: SdkProvider, - options: BootstrapEnvironmentOptions = {}, - ): Promise { - // Look at the template, decide whether it's most likely a legacy or modern bootstrap - // template, and use the right bootstrapper for that. - const version = bootstrapVersionFromTemplate(await this.loadTemplate()); - if (version === 0) { - return this.legacyBootstrap(environment, sdkProvider, options); - } else { - return this.modernBootstrap(environment, sdkProvider, options); - } - } - - private async loadTemplate(params: BootstrappingParameters = {}): Promise { - switch (this.source.source) { - case 'custom': - return loadStructuredFile(this.source.templateFile); - case 'default': - return loadStructuredFile(path.join(rootDir(), 'lib', 'api', 'bootstrap', 'bootstrap-template.yaml')); - case 'legacy': - return legacyBootstrapTemplate(params); - } - } -} - -/** - * Magic parameter value that will cause the bootstrap-template.yml to NOT create a CMK but use the default key - */ -const USE_AWS_MANAGED_KEY = 'AWS_MANAGED_KEY'; - -/** - * Magic parameter value that will cause the bootstrap-template.yml to create a CMK - */ -const CREATE_NEW_KEY = ''; -/** - * Parameter value indicating the use of the default, CDK provided permissions boundary for bootstrap-template.yml - */ -const CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY = 'CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY'; - -/** - * Split an array-like CloudFormation parameter on , - * - * An empty string is the empty array (instead of `['']`). - */ -function splitCfnArray(xs: string | undefined): string[] { - if (xs === '' || xs === undefined) { - return []; - } - return xs.split(','); -} diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts b/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts deleted file mode 100644 index 079a0322b73e5..0000000000000 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { BootstrapSource } from './bootstrap-environment'; -import { Tag } from '../../cdk-toolkit'; -import { StringWithoutPlaceholders } from '../util/placeholders'; - -export const BUCKET_NAME_OUTPUT = 'BucketName'; -export const REPOSITORY_NAME_OUTPUT = 'ImageRepositoryName'; -export const BUCKET_DOMAIN_NAME_OUTPUT = 'BucketDomainName'; -export const BOOTSTRAP_VERSION_OUTPUT = 'BootstrapVersion'; -export const BOOTSTRAP_VERSION_RESOURCE = 'CdkBootstrapVersion'; -export const BOOTSTRAP_VARIANT_PARAMETER = 'BootstrapVariant'; - -/** - * The assumed vendor of a template in case it is not set - */ -export const DEFAULT_BOOTSTRAP_VARIANT = 'AWS CDK: Default Resources'; - -/** - * Options for the bootstrapEnvironment operation(s) - */ -export interface BootstrapEnvironmentOptions { - readonly toolkitStackName?: string; - readonly roleArn?: StringWithoutPlaceholders; - readonly parameters?: BootstrappingParameters; - readonly force?: boolean; - - /** - * The source of the bootstrap stack - * - * @default - modern v2-style bootstrapping - */ - readonly source?: BootstrapSource; - - /** - * Whether to execute the changeset or only create it and leave it in review. - * @default true - */ - readonly execute?: boolean; - - /** - * Tags for cdktoolkit stack. - * - * @default - None. - */ - readonly tags?: Tag[]; - - /** - * Whether the stacks created by the bootstrap process should be protected from termination. - * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-protect-stacks.html - * @default true - */ - readonly terminationProtection?: boolean; - - /** - * Use previous values for unspecified parameters - * - * If not set, all parameters must be specified for every deployment. - * - * @default true - */ - usePreviousParameters?: boolean; -} - -/** - * Parameters for the bootstrapping template - */ -export interface BootstrappingParameters { - /** - * The name to be given to the CDK Bootstrap bucket. - * - * @default - a name is generated by CloudFormation. - */ - readonly bucketName?: string; - - /** - * The ID of an existing KMS key to be used for encrypting items in the bucket. - * - * @default - use the default KMS key or create a custom one - */ - readonly kmsKeyId?: string; - - /** - * Whether or not to create a new customer master key (CMK) - * - * Only applies to modern bootstrapping. Legacy bootstrapping will never create - * a CMK, only use the default S3 key. - * - * @default false - */ - readonly createCustomerMasterKey?: boolean; - - /** - * The list of AWS account IDs that are trusted to deploy into the environment being bootstrapped. - * - * @default - only the bootstrapped account can deploy into this environment - */ - readonly trustedAccounts?: string[]; - - /** - * The list of AWS account IDs that are trusted to look up values in the environment being bootstrapped. - * - * @default - only the bootstrapped account can look up values in this environment - */ - readonly trustedAccountsForLookup?: string[]; - - /** - * The ARNs of the IAM managed policies that should be attached to the role performing CloudFormation deployments. - * In most cases, this will be the AdministratorAccess policy. - * At least one policy is required if `trustedAccounts` were passed. - * - * @default - the role will have no policies attached - */ - readonly cloudFormationExecutionPolicies?: string[]; - - /** - * Identifier to distinguish multiple bootstrapped environments - * - * @default - Default qualifier - */ - readonly qualifier?: string; - - /** - * Whether or not to enable S3 Staging Bucket Public Access Block Configuration - * - * @default true - */ - readonly publicAccessBlockConfiguration?: boolean; - - /** - * Flag for using the default permissions boundary for bootstrapping - * - * @default - No value, optional argument - */ - readonly examplePermissionsBoundary?: boolean; - - /** - * Name for the customer's custom permissions boundary for bootstrapping - * - * @default - No value, optional argument - */ - readonly customPermissionsBoundary?: string; -} diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml deleted file mode 100644 index 15acb72c4927d..0000000000000 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml +++ /dev/null @@ -1,692 +0,0 @@ -Description: This stack includes resources needed to deploy AWS CDK apps into this - environment -Parameters: - TrustedAccounts: - Description: List of AWS accounts that are trusted to publish assets and deploy - stacks to this environment - Default: '' - Type: CommaDelimitedList - TrustedAccountsForLookup: - Description: List of AWS accounts that are trusted to look up values in this - environment - Default: '' - Type: CommaDelimitedList - CloudFormationExecutionPolicies: - Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation - deployment role - Default: '' - Type: CommaDelimitedList - FileAssetsBucketName: - Description: The name of the S3 bucket used for file assets - Default: '' - Type: String - FileAssetsBucketKmsKeyId: - Description: Empty to create a new key (default), 'AWS_MANAGED_KEY' to use a managed - S3 key, or the ID/ARN of an existing key. - Default: '' - Type: String - ContainerAssetsRepositoryName: - Description: A user-provided custom name to use for the container assets ECR repository - Default: '' - Type: String - Qualifier: - Description: An identifier to distinguish multiple bootstrap stacks in the same environment - Default: hnb659fds - Type: String - # "cdk-(qualifier)-image-publishing-role-(account)-(region)" needs to be <= 64 chars - # account = 12, region <= 14, 10 chars for qualifier and 28 for rest of role name - AllowedPattern: "[A-Za-z0-9_-]{1,10}" - ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters - PublicAccessBlockConfiguration: - Description: Whether or not to enable S3 Staging Bucket Public Access Block Configuration - Default: 'true' - Type: 'String' - AllowedValues: ['true', 'false'] - InputPermissionsBoundary: - Description: Whether or not to use either the CDK supplied or custom permissions boundary - Default: '' - Type: 'String' - UseExamplePermissionsBoundary: - Default: 'false' - AllowedValues: [ 'true', 'false' ] - Type: String - BootstrapVariant: - Type: String - Default: 'AWS CDK: Default Resources' - Description: Describe the provenance of the resources in this bootstrap - stack. Change this when you customize the template. To prevent accidents, - the CDK CLI will not overwrite bootstrap stacks with a different variant. -Conditions: - HasTrustedAccounts: - Fn::Not: - - Fn::Equals: - - '' - - Fn::Join: - - '' - - Ref: TrustedAccounts - HasTrustedAccountsForLookup: - Fn::Not: - - Fn::Equals: - - '' - - Fn::Join: - - '' - - Ref: TrustedAccountsForLookup - HasCloudFormationExecutionPolicies: - Fn::Not: - - Fn::Equals: - - '' - - Fn::Join: - - '' - - Ref: CloudFormationExecutionPolicies - HasCustomFileAssetsBucketName: - Fn::Not: - - Fn::Equals: - - '' - - Ref: FileAssetsBucketName - CreateNewKey: - Fn::Equals: - - '' - - Ref: FileAssetsBucketKmsKeyId - UseAwsManagedKey: - Fn::Equals: - - 'AWS_MANAGED_KEY' - - Ref: FileAssetsBucketKmsKeyId - ShouldCreatePermissionsBoundary: - Fn::Equals: - - 'true' - - Ref: UseExamplePermissionsBoundary - PermissionsBoundarySet: - Fn::Not: - - Fn::Equals: - - '' - - Ref: InputPermissionsBoundary - HasCustomContainerAssetsRepositoryName: - Fn::Not: - - Fn::Equals: - - '' - - Ref: ContainerAssetsRepositoryName - UsePublicAccessBlockConfiguration: - Fn::Equals: - - 'true' - - Ref: PublicAccessBlockConfiguration -Resources: - FileAssetsBucketEncryptionKey: - Type: AWS::KMS::Key - Properties: - KeyPolicy: - Statement: - - Action: - - kms:Create* - - kms:Describe* - - kms:Enable* - - kms:List* - - kms:Put* - - kms:Update* - - kms:Revoke* - - kms:Disable* - - kms:Get* - - kms:Delete* - - kms:ScheduleKeyDeletion - - kms:CancelKeyDeletion - - kms:GenerateDataKey - - kms:TagResource - - kms:UntagResource - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - Resource: "*" - - Action: - - kms:Decrypt - - kms:DescribeKey - - kms:Encrypt - - kms:ReEncrypt* - - kms:GenerateDataKey* - Effect: Allow - Principal: - # Not actually everyone -- see below for Conditions - AWS: "*" - Resource: "*" - Condition: - StringEquals: - # See https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-caller-account - kms:CallerAccount: - Ref: AWS::AccountId - kms:ViaService: - - Fn::Sub: s3.${AWS::Region}.amazonaws.com - - Action: - - kms:Decrypt - - kms:DescribeKey - - kms:Encrypt - - kms:ReEncrypt* - - kms:GenerateDataKey* - Effect: Allow - Principal: - AWS: - Fn::Sub: "${FilePublishingRole.Arn}" - Resource: "*" - Condition: CreateNewKey - FileAssetsBucketEncryptionKeyAlias: - Condition: CreateNewKey - Type: AWS::KMS::Alias - Properties: - AliasName: - Fn::Sub: "alias/cdk-${Qualifier}-assets-key" - TargetKeyId: - Ref: FileAssetsBucketEncryptionKey - StagingBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: - Fn::If: - - HasCustomFileAssetsBucketName - - Fn::Sub: "${FileAssetsBucketName}" - - Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region} - AccessControl: Private - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: aws:kms - KMSMasterKeyID: - Fn::If: - - CreateNewKey - - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" - - Fn::If: - - UseAwsManagedKey - - Ref: AWS::NoValue - - Fn::Sub: "${FileAssetsBucketKmsKeyId}" - PublicAccessBlockConfiguration: - Fn::If: - - UsePublicAccessBlockConfiguration - - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - - Ref: AWS::NoValue - VersioningConfiguration: - Status: Enabled - LifecycleConfiguration: - Rules: - # Objects will only be noncurrent if they are deleted via garbage collection. - - Id: CleanupOldVersions - Status: Enabled - NoncurrentVersionExpiration: - NoncurrentDays: 30 - - Id: AbortIncompleteMultipartUploads - Status: Enabled - AbortIncompleteMultipartUpload: - DaysAfterInitiation: 1 - UpdateReplacePolicy: Retain - DeletionPolicy: Retain - StagingBucketPolicy: - Type: 'AWS::S3::BucketPolicy' - Properties: - Bucket: { Ref: 'StagingBucket' } - PolicyDocument: - Id: 'AccessControl' - Version: '2012-10-17' - Statement: - - Sid: 'AllowSSLRequestsOnly' - Action: 's3:*' - Effect: 'Deny' - Resource: - - { 'Fn::Sub': '${StagingBucket.Arn}' } - - { 'Fn::Sub': '${StagingBucket.Arn}/*' } - Condition: - Bool: { 'aws:SecureTransport': 'false' } - Principal: '*' - ContainerAssetsRepository: - Type: AWS::ECR::Repository - Properties: - ImageTagMutability: IMMUTABLE - # Untagged images should never exist but Security Hub wants this rule to exist - LifecyclePolicy: - LifecyclePolicyText: | - { - "rules": [ - { - "rulePriority": 1, - "description": "Untagged images should not exist, but expire any older than one year", - "selection": { - "tagStatus": "untagged", - "countType": "sinceImagePushed", - "countUnit": "days", - "countNumber": 365 - }, - "action": { "type": "expire" } - } - ] - } - RepositoryName: - Fn::If: - - HasCustomContainerAssetsRepositoryName - - Fn::Sub: "${ContainerAssetsRepositoryName}" - - Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region} - RepositoryPolicyText: - Version: "2012-10-17" - Statement: - # Necessary for Lambda container images - # https://docs.aws.amazon.com/lambda/latest/dg/configuration-images.html#configuration-images-permissions - - Sid: LambdaECRImageRetrievalPolicy - Effect: Allow - Principal: { Service: "lambda.amazonaws.com" } - Action: - - ecr:BatchGetImage - - ecr:GetDownloadUrlForLayer - Condition: - StringLike: - "aws:sourceArn": { "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" } - FilePublishingRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - # allows this role to be assumed with session tags. - # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - - Action: sts:TagSession - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Fn::If: - - HasTrustedAccounts - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: TrustedAccounts - - Ref: AWS::NoValue - RoleName: - Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region} - Tags: - - Key: aws-cdk:bootstrap-role - Value: file-publishing - ImagePublishingRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - # allows this role to be assumed with session tags. - # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - - Action: sts:TagSession - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Fn::If: - - HasTrustedAccounts - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: TrustedAccounts - - Ref: AWS::NoValue - RoleName: - Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region} - Tags: - - Key: aws-cdk:bootstrap-role - Value: image-publishing - LookupRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - # allows this role to be assumed with session tags. - # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - - Action: sts:TagSession - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Fn::If: - - HasTrustedAccountsForLookup - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: TrustedAccountsForLookup - - Ref: AWS::NoValue - - Fn::If: - - HasTrustedAccounts - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: TrustedAccounts - - Ref: AWS::NoValue - RoleName: - Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region} - ManagedPolicyArns: - - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess" - Policies: - - PolicyDocument: - Statement: - - Sid: DontReadSecrets - Effect: Deny - Action: - - kms:Decrypt - Resource: "*" - Version: '2012-10-17' - PolicyName: LookupRolePolicy - Tags: - - Key: aws-cdk:bootstrap-role - Value: lookup - FilePublishingRoleDefaultPolicy: - Type: AWS::IAM::Policy - Properties: - PolicyDocument: - Statement: - - Action: - - s3:GetObject* - - s3:GetBucket* - - s3:GetEncryptionConfiguration - - s3:List* - - s3:DeleteObject* - - s3:PutObject* - - s3:Abort* - Resource: - - Fn::Sub: "${StagingBucket.Arn}" - - Fn::Sub: "${StagingBucket.Arn}/*" - Condition: - StringEquals: - aws:ResourceAccount: - - Fn::Sub: ${AWS::AccountId} - Effect: Allow - - Action: - - kms:Decrypt - - kms:DescribeKey - - kms:Encrypt - - kms:ReEncrypt* - - kms:GenerateDataKey* - Effect: Allow - Resource: - Fn::If: - - CreateNewKey - - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" - - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId} - Version: '2012-10-17' - Roles: - - Ref: FilePublishingRole - PolicyName: - Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} - ImagePublishingRoleDefaultPolicy: - Type: AWS::IAM::Policy - Properties: - PolicyDocument: - Statement: - - Action: - - ecr:PutImage - - ecr:InitiateLayerUpload - - ecr:UploadLayerPart - - ecr:CompleteLayerUpload - - ecr:BatchCheckLayerAvailability - - ecr:DescribeRepositories - - ecr:DescribeImages - - ecr:BatchGetImage - - ecr:GetDownloadUrlForLayer - Resource: - Fn::Sub: "${ContainerAssetsRepository.Arn}" - Effect: Allow - - Action: - - ecr:GetAuthorizationToken - Resource: "*" - Effect: Allow - Version: '2012-10-17' - Roles: - - Ref: ImagePublishingRole - PolicyName: - Fn::Sub: cdk-${Qualifier}-image-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} - DeploymentActionRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - # allows this role to be assumed with session tags. - # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - - Action: sts:TagSession - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: AWS::AccountId - - Fn::If: - - HasTrustedAccounts - - Action: sts:AssumeRole - Effect: Allow - Principal: - AWS: - Ref: TrustedAccounts - - Ref: AWS::NoValue - Policies: - - PolicyDocument: - Statement: - - Sid: CloudFormationPermissions - Effect: Allow - Action: - - cloudformation:CreateChangeSet - - cloudformation:DeleteChangeSet - - cloudformation:DescribeChangeSet - - cloudformation:DescribeStacks - - cloudformation:ExecuteChangeSet - - cloudformation:CreateStack - - cloudformation:UpdateStack - - cloudformation:RollbackStack - - cloudformation:ContinueUpdateRollback - Resource: "*" - - Sid: PipelineCrossAccountArtifactsBucket - # Read/write buckets in different accounts. Permissions to buckets in - # same account are granted by bucket policies. - # - # Write permissions necessary to write outputs to the cross-region artifact replication bucket - # https://aws.amazon.com/premiumsupport/knowledge-center/codepipeline-deploy-cloudformation/. - Effect: Allow - Action: - - s3:GetObject* - - s3:GetBucket* - - s3:List* - - s3:Abort* - - s3:DeleteObject* - - s3:PutObject* - Resource: "*" - Condition: - StringNotEquals: - s3:ResourceAccount: - Ref: 'AWS::AccountId' - - Sid: PipelineCrossAccountArtifactsKey - # Use keys only for the purposes of reading encrypted files from S3. - Effect: Allow - Action: - - kms:Decrypt - - kms:DescribeKey - - kms:Encrypt - - kms:ReEncrypt* - - kms:GenerateDataKey* - Resource: "*" - Condition: - StringEquals: - kms:ViaService: - Fn::Sub: s3.${AWS::Region}.amazonaws.com - - Action: iam:PassRole - Resource: - Fn::Sub: "${CloudFormationExecutionRole.Arn}" - Effect: Allow - - Sid: CliPermissions - Action: - # Permissions needed by the CLI when doing `cdk deploy`. - # Our CI/CD does not need DeleteStack, - # but we also want to use this role from the CLI, - # and there you can call `cdk destroy` - - cloudformation:DescribeStackEvents - - cloudformation:GetTemplate - - cloudformation:DeleteStack - - cloudformation:UpdateTerminationProtection - - sts:GetCallerIdentity - # `cdk import` - - cloudformation:GetTemplateSummary - Resource: "*" - Effect: Allow - - Sid: CliStagingBucket - Effect: Allow - Action: - - s3:GetObject* - - s3:GetBucket* - - s3:List* - Resource: - - Fn::Sub: ${StagingBucket.Arn} - - Fn::Sub: ${StagingBucket.Arn}/* - - Sid: ReadVersion - Effect: Allow - Action: - - ssm:GetParameter - - ssm:GetParameters # CreateChangeSet uses this to evaluate any SSM parameters (like `CdkBootstrapVersion`) - Resource: - - Fn::Sub: "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}" - Version: '2012-10-17' - PolicyName: default - RoleName: - Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region} - Tags: - - Key: aws-cdk:bootstrap-role - Value: deploy - CloudFormationExecutionRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Action: sts:AssumeRole - Effect: Allow - Principal: - Service: cloudformation.amazonaws.com - Version: '2012-10-17' - ManagedPolicyArns: - Fn::If: - - HasCloudFormationExecutionPolicies - - Ref: CloudFormationExecutionPolicies - - Fn::If: - - HasTrustedAccounts - # The CLI will prevent this case from occurring - - Ref: AWS::NoValue - # The CLI will advertise that we picked this implicitly - - - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" - RoleName: - Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region} - PermissionsBoundary: - Fn::If: - - PermissionsBoundarySet - - Fn::Sub: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}' - - Ref: AWS::NoValue - CdkBoostrapPermissionsBoundaryPolicy: - # Edit the template prior to boostrap in order to have this example policy created - Condition: ShouldCreatePermissionsBoundary - Type: AWS::IAM::ManagedPolicy - Properties: - PolicyDocument: - Statement: - # If permission boundaries do not have an explicit `allow`, then the effect is `deny` - - Sid: ExplicitAllowAll - Action: - - "*" - Effect: Allow - Resource: "*" - # Default permissions to prevent privilege escalation - - Sid: DenyAccessIfRequiredPermBoundaryIsNotBeingApplied - Action: - - iam:CreateUser - - iam:CreateRole - - iam:PutRolePermissionsBoundary - - iam:PutUserPermissionsBoundary - Condition: - StringNotEquals: - iam:PermissionsBoundary: - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} - Effect: Deny - Resource: "*" - # Forbid the policy itself being edited - - Sid: DenyPermBoundaryIAMPolicyAlteration - Action: - - iam:CreatePolicyVersion - - iam:DeletePolicy - - iam:DeletePolicyVersion - - iam:SetDefaultPolicyVersion - Effect: Deny - Resource: - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} - # Forbid removing the permissions boundary from any user or role that has it associated - - Sid: DenyRemovalOfPermBoundaryFromAnyUserOrRole - Action: - - iam:DeleteUserPermissionsBoundary - - iam:DeleteRolePermissionsBoundary - Effect: Deny - Resource: "*" - # Add your specific organizational security policy here - # Uncomment the example to deny access to AWS Config - #- Sid: OrganizationalSecurityPolicy - # Action: - # - "config:*" - # Effect: Deny - # Resource: "*" - Version: "2012-10-17" - Description: "Bootstrap Permission Boundary" - ManagedPolicyName: - Fn::Sub: cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} - Path: / - # The SSM parameter is used in pipeline-deployed templates to verify the version - # of the bootstrap resources. - CdkBootstrapVersion: - Type: AWS::SSM::Parameter - Properties: - Type: String - Name: - Fn::Sub: '/cdk-bootstrap/${Qualifier}/version' - Value: '25' -Outputs: - BucketName: - Description: The name of the S3 bucket owned by the CDK toolkit stack - Value: - Fn::Sub: "${StagingBucket}" - BucketDomainName: - Description: The domain name of the S3 bucket owned by the CDK toolkit stack - Value: - Fn::Sub: "${StagingBucket.RegionalDomainName}" - # @deprecated - This Export can be removed at some future point in time. - # We can't do it today because if there are stacks that use it, the bootstrap - # stack cannot be updated. Not used anymore by apps >= 1.60.0 - FileAssetKeyArn: - Description: The ARN of the KMS key used to encrypt the asset bucket (deprecated) - Value: - Fn::If: - - CreateNewKey - - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" - - Fn::Sub: "${FileAssetsBucketKmsKeyId}" - Export: - Name: - Fn::Sub: CdkBootstrap-${Qualifier}-FileAssetKeyArn - ImageRepositoryName: - Description: The name of the ECR repository which hosts docker image assets - Value: - Fn::Sub: "${ContainerAssetsRepository}" - # The Output is used by the CLI to verify the version of the bootstrap resources. - BootstrapVersion: - Description: The version of the bootstrap resources that are currently mastered - in this stack - Value: - Fn::GetAtt: [CdkBootstrapVersion, Value] diff --git a/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts b/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts deleted file mode 100644 index ad6ac4516c95c..0000000000000 --- a/packages/aws-cdk/lib/api/bootstrap/deploy-bootstrap.ts +++ /dev/null @@ -1,167 +0,0 @@ -import * as os from 'os'; -import * as path from 'path'; -import { ArtifactType } from '@aws-cdk/cloud-assembly-schema'; -import { CloudAssemblyBuilder, Environment, EnvironmentUtils } from '@aws-cdk/cx-api'; -import * as fs from 'fs-extra'; -import { - BOOTSTRAP_VARIANT_PARAMETER, - BOOTSTRAP_VERSION_OUTPUT, - BOOTSTRAP_VERSION_RESOURCE, - BootstrapEnvironmentOptions, - DEFAULT_BOOTSTRAP_VARIANT, -} from './bootstrap-props'; -import * as logging from '../../logging'; -import type { SDK, SdkProvider } from '../aws-auth'; -import { assertIsSuccessfulDeployStackResult, deployStack, SuccessfulDeployStackResult } from '../deploy-stack'; -import { NoBootstrapStackEnvironmentResources } from '../environment-resources'; -import { Mode } from '../plugin/mode'; -import { DEFAULT_TOOLKIT_STACK_NAME, ToolkitInfo } from '../toolkit-info'; - -/** - * A class to hold state around stack bootstrapping - * - * This class exists so we can break bootstrapping into 2 phases: - * - * ```ts - * const current = BootstrapStack.lookup(...); - * // ... - * current.update(newTemplate, ...); - * ``` - * - * And do something in between the two phases (such as look at the - * current bootstrap stack and doing something intelligent). - */ -export class BootstrapStack { - public static async lookup(sdkProvider: SdkProvider, environment: Environment, toolkitStackName?: string) { - toolkitStackName = toolkitStackName ?? DEFAULT_TOOLKIT_STACK_NAME; - - const resolvedEnvironment = await sdkProvider.resolveEnvironment(environment); - const sdk = (await sdkProvider.forEnvironment(resolvedEnvironment, Mode.ForWriting)).sdk; - - const currentToolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, sdk, toolkitStackName); - - return new BootstrapStack(sdkProvider, sdk, resolvedEnvironment, toolkitStackName, currentToolkitInfo); - } - - protected constructor( - private readonly sdkProvider: SdkProvider, - private readonly sdk: SDK, - private readonly resolvedEnvironment: Environment, - private readonly toolkitStackName: string, - private readonly currentToolkitInfo: ToolkitInfo, - ) {} - - public get parameters(): Record { - return this.currentToolkitInfo.found ? this.currentToolkitInfo.bootstrapStack.parameters : {}; - } - - public get terminationProtection() { - return this.currentToolkitInfo.found ? this.currentToolkitInfo.bootstrapStack.terminationProtection : undefined; - } - - public async partition(): Promise { - return (await this.sdk.currentAccount()).partition; - } - - /** - * Perform the actual deployment of a bootstrap stack, given a template and some parameters - */ - public async update( - template: any, - parameters: Record, - options: Omit, - ): Promise { - if (this.currentToolkitInfo.found && !options.force) { - // Safety checks - const abortResponse = { - type: 'did-deploy-stack', - noOp: true, - outputs: {}, - stackArn: this.currentToolkitInfo.bootstrapStack.stackId, - } satisfies SuccessfulDeployStackResult; - - // Validate that the bootstrap stack we're trying to replace is from the same variant as the one we're trying to deploy - const currentVariant = this.currentToolkitInfo.variant; - const newVariant = bootstrapVariantFromTemplate(template); - if (currentVariant !== newVariant) { - logging.warning( - `Bootstrap stack already exists, containing '${currentVariant}'. Not overwriting it with a template containing '${newVariant}' (use --force if you intend to overwrite)`, - ); - return abortResponse; - } - - // Validate that we're not downgrading the bootstrap stack - const newVersion = bootstrapVersionFromTemplate(template); - const currentVersion = this.currentToolkitInfo.version; - if (newVersion < currentVersion) { - logging.warning( - `Bootstrap stack already at version ${currentVersion}. Not downgrading it to version ${newVersion} (use --force if you intend to downgrade)`, - ); - if (newVersion === 0) { - // A downgrade with 0 as target version means we probably have a new-style bootstrap in the account, - // and an old-style bootstrap as current target, which means the user probably forgot to put this flag in. - logging.warning("(Did you set the '@aws-cdk/core:newStyleStackSynthesis' feature flag in cdk.json?)"); - } - return abortResponse; - } - } - - const outdir = await fs.mkdtemp(path.join(os.tmpdir(), 'cdk-bootstrap')); - const builder = new CloudAssemblyBuilder(outdir); - const templateFile = `${this.toolkitStackName}.template.json`; - await fs.writeJson(path.join(builder.outdir, templateFile), template, { - spaces: 2, - }); - - builder.addArtifact(this.toolkitStackName, { - type: ArtifactType.AWS_CLOUDFORMATION_STACK, - environment: EnvironmentUtils.format(this.resolvedEnvironment.account, this.resolvedEnvironment.region), - properties: { - templateFile, - terminationProtection: options.terminationProtection ?? false, - }, - }); - - const assembly = builder.buildAssembly(); - - const ret = await deployStack({ - stack: assembly.getStackByName(this.toolkitStackName), - resolvedEnvironment: this.resolvedEnvironment, - sdk: this.sdk, - sdkProvider: this.sdkProvider, - force: options.force, - roleArn: options.roleArn, - tags: options.tags, - deploymentMethod: { method: 'change-set', execute: options.execute }, - parameters, - usePreviousParameters: options.usePreviousParameters ?? true, - // Obviously we can't need a bootstrap stack to deploy a bootstrap stack - envResources: new NoBootstrapStackEnvironmentResources(this.resolvedEnvironment, this.sdk), - }); - - assertIsSuccessfulDeployStackResult(ret); - - return ret; - } -} - -export function bootstrapVersionFromTemplate(template: any): number { - const versionSources = [ - template.Outputs?.[BOOTSTRAP_VERSION_OUTPUT]?.Value, - template.Resources?.[BOOTSTRAP_VERSION_RESOURCE]?.Properties?.Value, - ]; - - for (const vs of versionSources) { - if (typeof vs === 'number') { - return vs; - } - if (typeof vs === 'string' && !isNaN(parseInt(vs, 10))) { - return parseInt(vs, 10); - } - } - return 0; -} - -export function bootstrapVariantFromTemplate(template: any): string { - return template.Parameters?.[BOOTSTRAP_VARIANT_PARAMETER]?.Default ?? DEFAULT_BOOTSTRAP_VARIANT; -} diff --git a/packages/aws-cdk/lib/api/bootstrap/index.ts b/packages/aws-cdk/lib/api/bootstrap/index.ts deleted file mode 100644 index ad6c0dcb2ec13..0000000000000 --- a/packages/aws-cdk/lib/api/bootstrap/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './bootstrap-environment'; -export * from './bootstrap-props'; diff --git a/packages/aws-cdk/lib/api/bootstrap/legacy-template.ts b/packages/aws-cdk/lib/api/bootstrap/legacy-template.ts deleted file mode 100644 index dd9e89f1f659b..0000000000000 --- a/packages/aws-cdk/lib/api/bootstrap/legacy-template.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { BootstrappingParameters, BUCKET_DOMAIN_NAME_OUTPUT, BUCKET_NAME_OUTPUT } from './bootstrap-props'; - -export function legacyBootstrapTemplate(params: BootstrappingParameters): any { - return { - Description: 'The CDK Toolkit Stack. It was created by `cdk bootstrap` and manages resources necessary for managing your Cloud Applications with AWS CDK.', - Conditions: { - UsePublicAccessBlockConfiguration: { - 'Fn::Equals': [ - params.publicAccessBlockConfiguration || params.publicAccessBlockConfiguration === undefined ? 'true' : 'false', - 'true', - ], - }, - }, - Resources: { - StagingBucket: { - Type: 'AWS::S3::Bucket', - Properties: { - BucketName: params.bucketName, - AccessControl: 'Private', - BucketEncryption: { - ServerSideEncryptionConfiguration: [{ - ServerSideEncryptionByDefault: { - SSEAlgorithm: 'aws:kms', - KMSMasterKeyID: params.kmsKeyId, - }, - }], - }, - PublicAccessBlockConfiguration: { - 'Fn::If': [ - 'UsePublicAccessBlockConfiguration', - { - BlockPublicAcls: true, - BlockPublicPolicy: true, - IgnorePublicAcls: true, - RestrictPublicBuckets: true, - }, - { Ref: 'AWS::NoValue' }, - ], - }, - }, - }, - StagingBucketPolicy: { - Type: 'AWS::S3::BucketPolicy', - Properties: { - Bucket: { Ref: 'StagingBucket' }, - PolicyDocument: { - Id: 'AccessControl', - Version: '2012-10-17', - Statement: [ - { - Sid: 'AllowSSLRequestsOnly', - Action: 's3:*', - Effect: 'Deny', - Resource: [ - { 'Fn::Sub': '${StagingBucket.Arn}' }, - { 'Fn::Sub': '${StagingBucket.Arn}/*' }, - ], - Condition: { - Bool: { 'aws:SecureTransport': 'false' }, - }, - Principal: '*', - }, - ], - }, - }, - }, - }, - Outputs: { - [BUCKET_NAME_OUTPUT]: { - Description: 'The name of the S3 bucket owned by the CDK toolkit stack', - Value: { Ref: 'StagingBucket' }, - }, - [BUCKET_DOMAIN_NAME_OUTPUT]: { - Description: 'The domain name of the S3 bucket owned by the CDK toolkit stack', - Value: { 'Fn::GetAtt': ['StagingBucket', 'RegionalDomainName'] }, - }, - }, - }; -} diff --git a/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts b/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts deleted file mode 100644 index 4f0fa296ec123..0000000000000 --- a/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts +++ /dev/null @@ -1,387 +0,0 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import * as chalk from 'chalk'; -import { minimatch } from 'minimatch'; -import * as semver from 'semver'; -import { error, info, warning } from '../../logging'; -import { ToolkitError } from '../../toolkit/error'; -import { flatten } from '../../util'; - -export enum DefaultSelection { - /** - * Returns an empty selection in case there are no selectors. - */ - None = 'none', - - /** - * If the app includes a single stack, returns it. Otherwise throws an exception. - * This behavior is used by "deploy". - */ - OnlySingle = 'single', - - /** - * Returns all stacks in the main (top level) assembly only. - */ - MainAssembly = 'main', - - /** - * If no selectors are provided, returns all stacks in the app, - * including stacks inside nested assemblies. - */ - AllStacks = 'all', -} - -export interface SelectStacksOptions { - /** - * Extend the selection to upstread/downstream stacks - * @default ExtendedStackSelection.None only select the specified stacks. - */ - extend?: ExtendedStackSelection; - - /** - * The behavior if no selectors are provided. - */ - defaultBehavior: DefaultSelection; - - /** - * Whether to deploy if the app contains no stacks. - * - * @default false - */ - ignoreNoStacks?: boolean; -} - -/** - * When selecting stacks, what other stacks to include because of dependencies - */ -export enum ExtendedStackSelection { - /** - * Don't select any extra stacks - */ - None, - - /** - * Include stacks that this stack depends on - */ - Upstream, - - /** - * Include stacks that depend on this stack - */ - Downstream, -} - -/** - * A specification of which stacks should be selected - */ -export interface StackSelector { - /** - * Whether all stacks at the top level assembly should - * be selected and nothing else - */ - allTopLevel?: boolean; - - /** - * A list of patterns to match the stack hierarchical ids - */ - patterns: string[]; -} - -/** - * A single Cloud Assembly and the operations we do on it to deploy the artifacts inside - */ -export class CloudAssembly { - /** - * The directory this CloudAssembly was read from - */ - public readonly directory: string; - - constructor(public readonly assembly: cxapi.CloudAssembly) { - this.directory = assembly.directory; - } - - public async selectStacks(selector: StackSelector, options: SelectStacksOptions): Promise { - const asm = this.assembly; - const topLevelStacks = asm.stacks; - const stacks = semver.major(asm.version) < 10 ? asm.stacks : asm.stacksRecursively; - const allTopLevel = selector.allTopLevel ?? false; - const patterns = sanitizePatterns(selector.patterns); - - if (stacks.length === 0) { - if (options.ignoreNoStacks) { - return new StackCollection(this, []); - } - throw new ToolkitError('This app contains no stacks'); - } - - if (allTopLevel) { - return this.selectTopLevelStacks(stacks, topLevelStacks, options.extend); - } else if (patterns.length > 0) { - return this.selectMatchingStacks(stacks, patterns, options.extend); - } else { - return this.selectDefaultStacks(stacks, topLevelStacks, options.defaultBehavior); - } - } - - private selectTopLevelStacks( - stacks: cxapi.CloudFormationStackArtifact[], - topLevelStacks: cxapi.CloudFormationStackArtifact[], - extend: ExtendedStackSelection = ExtendedStackSelection.None, - ): StackCollection { - if (topLevelStacks.length > 0) { - return this.extendStacks(topLevelStacks, stacks, extend); - } else { - throw new ToolkitError('No stack found in the main cloud assembly. Use "list" to print manifest'); - } - } - - private selectMatchingStacks( - stacks: cxapi.CloudFormationStackArtifact[], - patterns: string[], - extend: ExtendedStackSelection = ExtendedStackSelection.None, - ): StackCollection { - - const matchingPattern = (pattern: string) => (stack: cxapi.CloudFormationStackArtifact) => minimatch(stack.hierarchicalId, pattern); - const matchedStacks = flatten(patterns.map(pattern => stacks.filter(matchingPattern(pattern)))); - - return this.extendStacks(matchedStacks, stacks, extend); - } - - private selectDefaultStacks( - stacks: cxapi.CloudFormationStackArtifact[], - topLevelStacks: cxapi.CloudFormationStackArtifact[], - defaultSelection: DefaultSelection, - ) { - switch (defaultSelection) { - case DefaultSelection.MainAssembly: - return new StackCollection(this, topLevelStacks); - case DefaultSelection.AllStacks: - return new StackCollection(this, stacks); - case DefaultSelection.None: - return new StackCollection(this, []); - case DefaultSelection.OnlySingle: - if (topLevelStacks.length === 1) { - return new StackCollection(this, topLevelStacks); - } else { - throw new ToolkitError('Since this app includes more than a single stack, specify which stacks to use (wildcards are supported) or specify `--all`\n' + - `Stacks: ${stacks.map(x => x.hierarchicalId).join(' · ')}`); - } - default: - throw new ToolkitError(`invalid default behavior: ${defaultSelection}`); - } - } - - private extendStacks( - matched: cxapi.CloudFormationStackArtifact[], - all: cxapi.CloudFormationStackArtifact[], - extend: ExtendedStackSelection = ExtendedStackSelection.None, - ) { - const allStacks = new Map(); - for (const stack of all) { - allStacks.set(stack.hierarchicalId, stack); - } - - const index = indexByHierarchicalId(matched); - - switch (extend) { - case ExtendedStackSelection.Downstream: - includeDownstreamStacks(index, allStacks); - break; - case ExtendedStackSelection.Upstream: - includeUpstreamStacks(index, allStacks); - break; - } - - // Filter original array because it is in the right order - const selectedList = all.filter(s => index.has(s.hierarchicalId)); - - return new StackCollection(this, selectedList); - } - - /** - * Select a single stack by its ID - */ - public stackById(stackId: string) { - return new StackCollection(this, [this.assembly.getStackArtifact(stackId)]); - } -} - -/** - * A collection of stacks and related artifacts - * - * In practice, not all artifacts in the CloudAssembly are created equal; - * stacks can be selected independently, but other artifacts such as asset - * bundles cannot. - */ -export class StackCollection { - constructor(public readonly assembly: CloudAssembly, public readonly stackArtifacts: cxapi.CloudFormationStackArtifact[]) { - } - - public get stackCount() { - return this.stackArtifacts.length; - } - - public get firstStack() { - if (this.stackCount < 1) { - throw new ToolkitError('StackCollection contains no stack artifacts (trying to access the first one)'); - } - return this.stackArtifacts[0]; - } - - public get stackIds(): string[] { - return this.stackArtifacts.map(s => s.id); - } - - public reversed() { - const arts = [...this.stackArtifacts]; - arts.reverse(); - return new StackCollection(this.assembly, arts); - } - - public filter(predicate: (art: cxapi.CloudFormationStackArtifact) => boolean): StackCollection { - return new StackCollection(this.assembly, this.stackArtifacts.filter(predicate)); - } - - public concat(other: StackCollection): StackCollection { - return new StackCollection(this.assembly, this.stackArtifacts.concat(other.stackArtifacts)); - } - - /** - * Extracts 'aws:cdk:warning|info|error' metadata entries from the stack synthesis - */ - public processMetadataMessages(options: MetadataMessageOptions = {}) { - let warnings = false; - let errors = false; - - for (const stack of this.stackArtifacts) { - for (const message of stack.messages) { - switch (message.level) { - case cxapi.SynthesisMessageLevel.WARNING: - warnings = true; - printMessage(warning, 'Warning', message.id, message.entry); - break; - case cxapi.SynthesisMessageLevel.ERROR: - errors = true; - printMessage(error, 'Error', message.id, message.entry); - break; - case cxapi.SynthesisMessageLevel.INFO: - printMessage(info, 'Info', message.id, message.entry); - break; - } - } - } - - if (errors && !options.ignoreErrors) { - throw new ToolkitError('Found errors'); - } - - if (options.strict && warnings) { - throw new ToolkitError('Found warnings (--strict mode)'); - } - - function printMessage(logFn: (s: string) => void, prefix: string, id: string, entry: cxapi.MetadataEntry) { - logFn(`[${prefix} at ${id}] ${entry.data}`); - - if (options.verbose && entry.trace) { - logFn(` ${entry.trace.join('\n ')}`); - } - } - } -} - -export interface MetadataMessageOptions { - /** - * Whether to be verbose - * - * @default false - */ - verbose?: boolean; - - /** - * Don't stop on error metadata - * - * @default false - */ - ignoreErrors?: boolean; - - /** - * Treat warnings in metadata as errors - * - * @default false - */ - strict?: boolean; -} - -function indexByHierarchicalId(stacks: cxapi.CloudFormationStackArtifact[]): Map { - const result = new Map(); - - for (const stack of stacks) { - result.set(stack.hierarchicalId, stack); - } - - return result; -} - -/** - * Calculate the transitive closure of stack dependents. - * - * Modifies `selectedStacks` in-place. - */ -function includeDownstreamStacks( - selectedStacks: Map, - allStacks: Map) { - const added = new Array(); - - let madeProgress; - do { - madeProgress = false; - - for (const [id, stack] of allStacks) { - // Select this stack if it's not selected yet AND it depends on a stack that's in the selected set - if (!selectedStacks.has(id) && (stack.dependencies || []).some(dep => selectedStacks.has(dep.id))) { - selectedStacks.set(id, stack); - added.push(id); - madeProgress = true; - } - } - } while (madeProgress); - - if (added.length > 0) { - info('Including depending stacks: %s', chalk.bold(added.join(', '))); - } -} - -/** - * Calculate the transitive closure of stack dependencies. - * - * Modifies `selectedStacks` in-place. - */ -function includeUpstreamStacks( - selectedStacks: Map, - allStacks: Map) { - const added = new Array(); - let madeProgress = true; - while (madeProgress) { - madeProgress = false; - - for (const stack of selectedStacks.values()) { - // Select an additional stack if it's not selected yet and a dependency of a selected stack (and exists, obviously) - for (const dependencyId of stack.dependencies.map(x => x.manifest.displayName ?? x.id)) { - if (!selectedStacks.has(dependencyId) && allStacks.has(dependencyId)) { - added.push(dependencyId); - selectedStacks.set(dependencyId, allStacks.get(dependencyId)!); - madeProgress = true; - } - } - } - } - - if (added.length > 0) { - info('Including dependency stacks: %s', chalk.bold(added.join(', '))); - } -} - -function sanitizePatterns(patterns: string[]): string[] { - let sanitized = patterns.filter(s => s != null); // filter null/undefined - sanitized = [...new Set(sanitized)]; // make them unique - return sanitized; -} diff --git a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts b/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts deleted file mode 100644 index 5c0f1a2c2a289..0000000000000 --- a/packages/aws-cdk/lib/api/cxapp/cloud-executable.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import { CloudAssembly } from './cloud-assembly'; -import * as contextproviders from '../../context-providers'; -import { debug } from '../../logging'; -import { Configuration } from '../../settings'; -import { ToolkitError } from '../../toolkit/error'; -import { SdkProvider } from '../aws-auth'; - -/** - * @returns output directory - */ -export type Synthesizer = (aws: SdkProvider, config: Configuration) => Promise; - -export interface CloudExecutableProps { - /** - * Application configuration (settings and context) - */ - configuration: Configuration; - - /** - * AWS object (used by synthesizer and contextprovider) - */ - sdkProvider: SdkProvider; - - /** - * Callback invoked to synthesize the actual stacks - */ - synthesizer: Synthesizer; -} - -/** - * Represent the Cloud Executable and the synthesis we can do on it - */ -export class CloudExecutable { - private _cloudAssembly?: CloudAssembly; - - constructor(private readonly props: CloudExecutableProps) { - } - - /** - * Return whether there is an app command from the configuration - */ - public get hasApp() { - return !!this.props.configuration.settings.get(['app']); - } - - /** - * Synthesize a set of stacks. - * - * @param cacheCloudAssembly whether to cache the Cloud Assembly after it has been first synthesized. - * This is 'true' by default, and only set to 'false' for 'cdk watch', - * which needs to re-synthesize the Assembly each time it detects a change to the project files - */ - public async synthesize(cacheCloudAssembly: boolean = true): Promise { - if (!this._cloudAssembly || !cacheCloudAssembly) { - this._cloudAssembly = await this.doSynthesize(); - } - return this._cloudAssembly; - } - - private async doSynthesize(): Promise { - // We may need to run the cloud executable multiple times in order to satisfy all missing context - // (When the executable runs, it will tell us about context it wants to use - // but it missing. We'll then look up the context and run the executable again, and - // again, until it doesn't complain anymore or we've stopped making progress). - let previouslyMissingKeys: Set | undefined; - while (true) { - const assembly = await this.props.synthesizer(this.props.sdkProvider, this.props.configuration); - - if (assembly.manifest.missing && assembly.manifest.missing.length > 0) { - const missingKeys = missingContextKeys(assembly.manifest.missing); - - if (!this.canLookup) { - throw new ToolkitError( - 'Context lookups have been disabled. ' - + 'Make sure all necessary context is already in \'cdk.context.json\' by running \'cdk synth\' on a machine with sufficient AWS credentials and committing the result. ' - + `Missing context keys: '${Array.from(missingKeys).join(', ')}'`); - } - - let tryLookup = true; - if (previouslyMissingKeys && setsEqual(missingKeys, previouslyMissingKeys)) { - debug('Not making progress trying to resolve environmental context. Giving up.'); - tryLookup = false; - } - - previouslyMissingKeys = missingKeys; - - if (tryLookup) { - debug('Some context information is missing. Fetching...'); - - await contextproviders.provideContextValues( - assembly.manifest.missing, - this.props.configuration.context, - this.props.sdkProvider); - - // Cache the new context to disk - await this.props.configuration.saveContext(); - - // Execute again - continue; - } - } - - return new CloudAssembly(assembly); - } - } - - private get canLookup() { - return !!(this.props.configuration.settings.get(['lookups']) ?? true); - } -} - -/** - * Return all keys of missing context items - */ -function missingContextKeys(missing?: cxapi.MissingContext[]): Set { - return new Set((missing || []).map(m => m.key)); -} - -function setsEqual(a: Set, b: Set) { - if (a.size !== b.size) { return false; } - for (const x of a) { - if (!b.has(x)) { return false; } - } - return true; -} diff --git a/packages/aws-cdk/lib/api/cxapp/environments.ts b/packages/aws-cdk/lib/api/cxapp/environments.ts deleted file mode 100644 index a1b8c66e6f442..0000000000000 --- a/packages/aws-cdk/lib/api/cxapp/environments.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import { minimatch } from 'minimatch'; -import { StackCollection } from './cloud-assembly'; -import { ToolkitError } from '../../toolkit/error'; -import { SdkProvider } from '../aws-auth'; - -export function looksLikeGlob(environment: string) { - return environment.indexOf('*') > -1; -} - -// eslint-disable-next-line max-len -export async function globEnvironmentsFromStacks(stacks: StackCollection, environmentGlobs: string[], sdk: SdkProvider): Promise { - if (environmentGlobs.length === 0) { return []; } - - const availableEnvironments = new Array(); - for (const stack of stacks.stackArtifacts) { - const actual = await sdk.resolveEnvironment(stack.environment); - availableEnvironments.push(actual); - } - - const environments = distinct(availableEnvironments).filter(env => environmentGlobs.find(glob => minimatch(env!.name, glob))); - if (environments.length === 0) { - const globs = JSON.stringify(environmentGlobs); - const envList = availableEnvironments.length > 0 ? availableEnvironments.map(env => env!.name).join(', ') : ''; - throw new ToolkitError(`No environments were found when selecting across ${globs} (available: ${envList})`); - } - - return environments; -} - -/** - * Given a set of "/" strings, construct environments for them - */ -export function environmentsFromDescriptors(envSpecs: string[]): cxapi.Environment[] { - const ret = new Array(); - - for (const spec of envSpecs) { - const parts = spec.replace(/^aws:\/\//, '').split('/'); - if (parts.length !== 2) { - throw new ToolkitError(`Expected environment name in format 'aws:///', got: ${spec}`); - } - - ret.push({ - name: spec, - account: parts[0], - region: parts[1], - }); - } - - return ret; -} - -/** - * De-duplicates a list of environments, such that a given account and region is only represented exactly once - * in the result. - * - * @param envs the possibly full-of-duplicates list of environments. - * - * @return a de-duplicated list of environments. - */ -function distinct(envs: cxapi.Environment[]): cxapi.Environment[] { - const unique: { [id: string]: cxapi.Environment } = {}; - for (const env of envs) { - const id = `${env.account || 'default'}/${env.region || 'default'}`; - if (id in unique) { continue; } - unique[id] = env; - } - return Object.values(unique); -} diff --git a/packages/aws-cdk/lib/api/cxapp/exec.ts b/packages/aws-cdk/lib/api/cxapp/exec.ts deleted file mode 100644 index b02ad38445a07..0000000000000 --- a/packages/aws-cdk/lib/api/cxapp/exec.ts +++ /dev/null @@ -1,311 +0,0 @@ -import * as childProcess from 'child_process'; -import * as os from 'os'; -import * as path from 'path'; -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; -import * as fs from 'fs-extra'; -import * as semver from 'semver'; -import { debug, warning } from '../../logging'; -import { Configuration, PROJECT_CONFIG, USER_DEFAULTS } from '../../settings'; -import { ToolkitError } from '../../toolkit/error'; -import { loadTree, some } from '../../tree'; -import { splitBySize } from '../../util/objects'; -import { versionNumber } from '../../version'; -import { SdkProvider } from '../aws-auth'; -import { RWLock, ILock } from '../util/rwlock'; - -export interface ExecProgramResult { - readonly assembly: cxapi.CloudAssembly; - readonly lock: ILock; -} - -/** Invokes the cloud executable and returns JSON output */ -export async function execProgram(aws: SdkProvider, config: Configuration): Promise { - const env = await prepareDefaultEnvironment(aws); - const context = await prepareContext(config, env); - - const build = config.settings.get(['build']); - if (build) { - await exec(build); - } - - const app = config.settings.get(['app']); - if (!app) { - throw new ToolkitError(`--app is required either in command-line, in ${PROJECT_CONFIG} or in ${USER_DEFAULTS}`); - } - - // bypass "synth" if app points to a cloud assembly - if (await fs.pathExists(app) && (await fs.stat(app)).isDirectory()) { - debug('--app points to a cloud assembly, so we bypass synth'); - - // Acquire a read lock on this directory - const lock = await new RWLock(app).acquireRead(); - - return { assembly: createAssembly(app), lock }; - } - - const commandLine = await guessExecutable(appToArray(app)); - - const outdir = config.settings.get(['output']); - if (!outdir) { - throw new ToolkitError('unexpected: --output is required'); - } - if (typeof outdir !== 'string') { - throw new ToolkitError(`--output takes a string, got ${JSON.stringify(outdir)}`); - } - try { - await fs.mkdirp(outdir); - } catch (error: any) { - throw new ToolkitError(`Could not create output directory ${outdir} (${error.message})`); - } - - debug('outdir:', outdir); - env[cxapi.OUTDIR_ENV] = outdir; - - // Acquire a lock on the output directory - const writerLock = await new RWLock(outdir).acquireWrite(); - - try { - // Send version information - env[cxapi.CLI_ASM_VERSION_ENV] = cxschema.Manifest.version(); - env[cxapi.CLI_VERSION_ENV] = versionNumber(); - - debug('env:', env); - - const envVariableSizeLimit = os.platform() === 'win32' ? 32760 : 131072; - const [smallContext, overflow] = splitBySize(context, spaceAvailableForContext(env, envVariableSizeLimit)); - - // Store the safe part in the environment variable - env[cxapi.CONTEXT_ENV] = JSON.stringify(smallContext); - - // If there was any overflow, write it to a temporary file - let contextOverflowLocation; - if (Object.keys(overflow ?? {}).length > 0) { - const contextDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cdk-context')); - contextOverflowLocation = path.join(contextDir, 'context-overflow.json'); - fs.writeJSONSync(contextOverflowLocation, overflow); - env[cxapi.CONTEXT_OVERFLOW_LOCATION_ENV] = contextOverflowLocation; - } - - await exec(commandLine.join(' ')); - - const assembly = createAssembly(outdir); - - contextOverflowCleanup(contextOverflowLocation, assembly); - - return { assembly, lock: await writerLock.convertToReaderLock() }; - } catch (e) { - await writerLock.release(); - throw e; - } - - async function exec(commandAndArgs: string) { - return new Promise((ok, fail) => { - // We use a slightly lower-level interface to: - // - // - Pass arguments in an array instead of a string, to get around a - // number of quoting issues introduced by the intermediate shell layer - // (which would be different between Linux and Windows). - // - // - Inherit stderr from controlling terminal. We don't use the captured value - // anyway, and if the subprocess is printing to it for debugging purposes the - // user gets to see it sooner. Plus, capturing doesn't interact nicely with some - // processes like Maven. - const proc = childProcess.spawn(commandAndArgs, { - stdio: ['ignore', 'inherit', 'inherit'], - detached: false, - shell: true, - env: { - ...process.env, - ...env, - }, - }); - - proc.on('error', fail); - - proc.on('exit', code => { - if (code === 0) { - return ok(); - } else { - debug('failed command:', commandAndArgs); - return fail(new ToolkitError(`Subprocess exited with error ${code}`)); - } - }); - }); - } -} - -/** - * Creates an assembly with error handling - */ -export function createAssembly(appDir: string) { - try { - return new cxapi.CloudAssembly(appDir, { - // We sort as we deploy - topoSort: false, - }); - } catch (error: any) { - if (error.message.includes(cxschema.VERSION_MISMATCH)) { - // this means the CLI version is too old. - // we instruct the user to upgrade. - throw new ToolkitError(`This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version.\n(${error.message})`); - } - throw error; - } -} - -/** - * If we don't have region/account defined in context, we fall back to the default SDK behavior - * where region is retrieved from ~/.aws/config and account is based on default credentials provider - * chain and then STS is queried. - * - * This is done opportunistically: for example, if we can't access STS for some reason or the region - * is not configured, the context value will be 'null' and there could failures down the line. In - * some cases, synthesis does not require region/account information at all, so that might be perfectly - * fine in certain scenarios. - * - * @param context The context key/value bash. - */ -export async function prepareDefaultEnvironment(aws: SdkProvider): Promise<{ [key: string]: string }> { - const env: { [key: string]: string } = { }; - - env[cxapi.DEFAULT_REGION_ENV] = aws.defaultRegion; - debug(`Setting "${cxapi.DEFAULT_REGION_ENV}" environment variable to`, env[cxapi.DEFAULT_REGION_ENV]); - - const accountId = (await aws.defaultAccount())?.accountId; - if (accountId) { - env[cxapi.DEFAULT_ACCOUNT_ENV] = accountId; - debug(`Setting "${cxapi.DEFAULT_ACCOUNT_ENV}" environment variable to`, env[cxapi.DEFAULT_ACCOUNT_ENV]); - } - - return env; -} - -/** - * Settings related to synthesis are read from context. - * The merging of various configuration sources like cli args or cdk.json has already happened. - * We now need to set the final values to the context. - */ -export async function prepareContext(config: Configuration, env: { [key: string]: string | undefined}) { - const context = config.context.all; - - const debugMode: boolean = config.settings.get(['debug']) ?? true; - if (debugMode) { - env.CDK_DEBUG = 'true'; - } - - const pathMetadata: boolean = config.settings.get(['pathMetadata']) ?? true; - if (pathMetadata) { - context[cxapi.PATH_METADATA_ENABLE_CONTEXT] = true; - } - - const assetMetadata: boolean = config.settings.get(['assetMetadata']) ?? true; - if (assetMetadata) { - context[cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT] = true; - } - - const versionReporting: boolean = config.settings.get(['versionReporting']) ?? true; - if (versionReporting) { context[cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT] = true; } - // We need to keep on doing this for framework version from before this flag was deprecated. - if (!versionReporting) { context['aws:cdk:disable-version-reporting'] = true; } - - const stagingEnabled = config.settings.get(['staging']) ?? true; - if (!stagingEnabled) { - context[cxapi.DISABLE_ASSET_STAGING_CONTEXT] = true; - } - - const bundlingStacks = config.settings.get(['bundlingStacks']) ?? ['**']; - context[cxapi.BUNDLING_STACKS] = bundlingStacks; - - debug('context:', context); - - return context; -} - -/** - * Make sure the 'app' is an array - * - * If it's a string, split on spaces as a trivial way of tokenizing the command line. - */ -function appToArray(app: any) { - return typeof app === 'string' ? app.split(' ') : app; -} - -type CommandGenerator = (file: string) => string[]; - -/** - * Execute the given file with the same 'node' process as is running the current process - */ -function executeNode(scriptFile: string): string[] { - return [process.execPath, scriptFile]; -} - -/** - * Mapping of extensions to command-line generators - */ -const EXTENSION_MAP = new Map([ - ['.js', executeNode], -]); - -/** - * Guess the executable from the command-line argument - * - * Only do this if the file is NOT marked as executable. If it is, - * we'll defer to the shebang inside the file itself. - * - * If we're on Windows, we ALWAYS take the handler, since it's hard to - * verify if registry associations have or have not been set up for this - * file type, so we'll assume the worst and take control. - */ -async function guessExecutable(commandLine: string[]) { - if (commandLine.length === 1) { - let fstat; - - try { - fstat = await fs.stat(commandLine[0]); - } catch { - debug(`Not a file: '${commandLine[0]}'. Using '${commandLine}' as command-line`); - return commandLine; - } - - // eslint-disable-next-line no-bitwise - const isExecutable = (fstat.mode & fs.constants.X_OK) !== 0; - const isWindows = process.platform === 'win32'; - - const handler = EXTENSION_MAP.get(path.extname(commandLine[0])); - if (handler && (!isExecutable || isWindows)) { - return handler(commandLine[0]); - } - } - return commandLine; -} - -function contextOverflowCleanup(location: string | undefined, assembly: cxapi.CloudAssembly) { - if (location) { - fs.removeSync(path.dirname(location)); - - const tree = loadTree(assembly); - const frameworkDoesNotSupportContextOverflow = some(tree, node => { - const fqn = node.constructInfo?.fqn; - const version = node.constructInfo?.version; - return (fqn === 'aws-cdk-lib.App' && version != null && semver.lte(version, '2.38.0')) - || fqn === '@aws-cdk/core.App'; // v1 - }); - - // We're dealing with an old version of the framework here. It is unaware of the temporary - // file, which means that it will ignore the context overflow. - if (frameworkDoesNotSupportContextOverflow) { - warning('Part of the context could not be sent to the application. Please update the AWS CDK library to the latest version.'); - } - } -} - -function spaceAvailableForContext(env: { [key: string]: string }, limit: number) { - const size = (value: string) => value != null ? Buffer.byteLength(value) : 0; - - const usedSpace = Object.entries(env) - .map(([k, v]) => k === cxapi.CONTEXT_ENV ? size(k) : size(k) + size(v)) - .reduce((a, b) => a + b, 0); - - return Math.max(0, limit - usedSpace); -} diff --git a/packages/aws-cdk/lib/api/deploy-stack.ts b/packages/aws-cdk/lib/api/deploy-stack.ts deleted file mode 100644 index b9853737ec832..0000000000000 --- a/packages/aws-cdk/lib/api/deploy-stack.ts +++ /dev/null @@ -1,876 +0,0 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import type { - CreateChangeSetCommandInput, - CreateStackCommandInput, - DescribeChangeSetCommandOutput, - ExecuteChangeSetCommandInput, - UpdateStackCommandInput, - Tag, -} from '@aws-sdk/client-cloudformation'; -import * as chalk from 'chalk'; -import * as uuid from 'uuid'; -import type { SDK, SdkProvider, ICloudFormationClient } from './aws-auth'; -import type { EnvironmentResources } from './environment-resources'; -import { CfnEvaluationException } from './evaluate-cloudformation-template'; -import { HotswapMode, HotswapPropertyOverrides, ICON } from './hotswap/common'; -import { tryHotswapDeployment } from './hotswap-deployments'; -import { addMetadataAssetsToManifest } from '../assets'; -import { debug, info, warning } from '../logging'; -import { - changeSetHasNoChanges, - CloudFormationStack, - TemplateParameters, - waitForChangeSet, - waitForStackDeploy, - waitForStackDelete, - ParameterValues, - ParameterChanges, - ResourcesToImport, -} from './util/cloudformation'; -import { StackActivityMonitor, type StackActivityProgress } from './util/cloudformation/stack-activity-monitor'; -import { type TemplateBodyParameter, makeBodyParameter } from './util/template-body-parameter'; -import { AssetManifestBuilder } from '../util/asset-manifest-builder'; -import { determineAllowCrossAccountAssetPublishing } from './util/checks'; -import { publishAssets } from '../util/asset-publishing'; -import { StringWithoutPlaceholders } from './util/placeholders'; -import { formatErrorMessage } from '../util/error'; - -export type DeployStackResult = - | SuccessfulDeployStackResult - | NeedRollbackFirstDeployStackResult - | ReplacementRequiresRollbackStackResult - ; - -/** Successfully deployed a stack */ -export interface SuccessfulDeployStackResult { - readonly type: 'did-deploy-stack'; - readonly noOp: boolean; - readonly outputs: { [name: string]: string }; - readonly stackArn: string; -} - -/** The stack is currently in a failpaused state, and needs to be rolled back before the deployment */ -export interface NeedRollbackFirstDeployStackResult { - readonly type: 'failpaused-need-rollback-first'; - readonly reason: 'not-norollback' | 'replacement'; - readonly status: string; -} - -/** The upcoming change has a replacement, which requires deploying with --rollback */ -export interface ReplacementRequiresRollbackStackResult { - readonly type: 'replacement-requires-rollback'; -} - -export function assertIsSuccessfulDeployStackResult(x: DeployStackResult): asserts x is SuccessfulDeployStackResult { - if (x.type !== 'did-deploy-stack') { - throw new Error(`Unexpected deployStack result. This should not happen: ${JSON.stringify(x)}. If you are seeing this error, please report it at https://github.com/aws/aws-cdk/issues/new/choose.`); - } -} - -export interface DeployStackOptions { - /** - * The stack to be deployed - */ - readonly stack: cxapi.CloudFormationStackArtifact; - - /** - * The environment to deploy this stack in - * - * The environment on the stack artifact may be unresolved, this one - * must be resolved. - */ - readonly resolvedEnvironment: cxapi.Environment; - - /** - * The SDK to use for deploying the stack - * - * Should have been initialized with the correct role with which - * stack operations should be performed. - */ - readonly sdk: SDK; - - /** - * SDK provider (seeded with default credentials) - * - * Will be used to: - * - * - Publish assets, either legacy assets or large CFN templates - * that aren't themselves assets from a manifest. (Needs an SDK - * Provider because the file publishing role is declared as part - * of the asset). - * - Hotswap - */ - readonly sdkProvider: SdkProvider; - - /** - * Information about the bootstrap stack found in the target environment - */ - readonly envResources: EnvironmentResources; - - /** - * Role to pass to CloudFormation to execute the change set - * - * To obtain a `StringWithoutPlaceholders`, run a regular - * string though `TargetEnvironment.replacePlaceholders`. - * - * @default - No execution role; CloudFormation either uses the role currently associated with - * the stack, or otherwise uses current AWS credentials. - */ - readonly roleArn?: StringWithoutPlaceholders; - - /** - * Notification ARNs to pass to CloudFormation to notify when the change set has completed - * - * @default - No notifications - */ - readonly notificationArns?: string[]; - - /** - * Name to deploy the stack under - * - * @default - Name from assembly - */ - readonly deployName?: string; - - /** - * Quiet or verbose deployment - * - * @default false - */ - readonly quiet?: boolean; - - /** - * List of asset IDs which shouldn't be built - * - * @default - Build all assets - */ - readonly reuseAssets?: string[]; - - /** - * Tags to pass to CloudFormation to add to stack - * - * @default - No tags - */ - readonly tags?: Tag[]; - - /** - * What deployment method to use - * - * @default - Change set with defaults - */ - readonly deploymentMethod?: DeploymentMethod; - - /** - * The collection of extra parameters - * (in addition to those used for assets) - * to pass to the deployed template. - * Note that parameters with `undefined` or empty values will be ignored, - * and not passed to the template. - * - * @default - no additional parameters will be passed to the template - */ - readonly parameters?: { [name: string]: string | undefined }; - - /** - * Use previous values for unspecified parameters - * - * If not set, all parameters must be specified for every deployment. - * - * @default false - */ - readonly usePreviousParameters?: boolean; - - /** - * Display mode for stack deployment progress. - * - * @default StackActivityProgress.Bar stack events will be displayed for - * the resource currently being deployed. - */ - readonly progress?: StackActivityProgress; - - /** - * Deploy even if the deployed template is identical to the one we are about to deploy. - * @default false - */ - readonly force?: boolean; - - /** - * Whether we are on a CI system - * - * @default false - */ - readonly ci?: boolean; - - /** - * Rollback failed deployments - * - * @default true - */ - readonly rollback?: boolean; - - /* - * Whether to perform a 'hotswap' deployment. - * A 'hotswap' deployment will attempt to short-circuit CloudFormation - * and update the affected resources like Lambda functions directly. - * - * @default - `HotswapMode.FULL_DEPLOYMENT` for regular deployments, `HotswapMode.HOTSWAP_ONLY` for 'watch' deployments - */ - readonly hotswap?: HotswapMode; - - /** - * Extra properties that configure hotswap behavior - */ - readonly hotswapPropertyOverrides?: HotswapPropertyOverrides; - - /** - * The extra string to append to the User-Agent header when performing AWS SDK calls. - * - * @default - nothing extra is appended to the User-Agent header - */ - readonly extraUserAgent?: string; - - /** - * If set, change set of type IMPORT will be created, and resourcesToImport - * passed to it. - */ - readonly resourcesToImport?: ResourcesToImport; - - /** - * If present, use this given template instead of the stored one - * - * @default - Use the stored template - */ - readonly overrideTemplate?: any; - - /** - * Whether to build/publish assets in parallel - * - * @default true To remain backward compatible. - */ - readonly assetParallelism?: boolean; -} - -export type DeploymentMethod = DirectDeploymentMethod | ChangeSetDeploymentMethod; - -export interface DirectDeploymentMethod { - readonly method: 'direct'; -} - -export interface ChangeSetDeploymentMethod { - readonly method: 'change-set'; - - /** - * Whether to execute the changeset or leave it in review. - * - * @default true - */ - readonly execute?: boolean; - - /** - * Optional name to use for the CloudFormation change set. - * If not provided, a name will be generated automatically. - */ - readonly changeSetName?: string; - - /** - * Indicates if the change set imports resources that already exist. - * - * @default false - */ - readonly importExistingResources?: boolean; -} - -export async function deployStack(options: DeployStackOptions): Promise { - const stackArtifact = options.stack; - - const stackEnv = options.resolvedEnvironment; - - options.sdk.appendCustomUserAgent(options.extraUserAgent); - const cfn = options.sdk.cloudFormation(); - const deployName = options.deployName || stackArtifact.stackName; - let cloudFormationStack = await CloudFormationStack.lookup(cfn, deployName); - - if (cloudFormationStack.stackStatus.isCreationFailure) { - debug( - `Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`, - ); - await cfn.deleteStack({ StackName: deployName }); - const deletedStack = await waitForStackDelete(cfn, deployName); - if (deletedStack && deletedStack.stackStatus.name !== 'DELETE_COMPLETE') { - throw new Error( - `Failed deleting stack ${deployName} that had previously failed creation (current state: ${deletedStack.stackStatus})`, - ); - } - // Update variable to mark that the stack does not exist anymore, but avoid - // doing an actual lookup in CloudFormation (which would be silly to do if - // we just deleted it). - cloudFormationStack = CloudFormationStack.doesNotExist(cfn, deployName); - } - - // Detect "legacy" assets (which remain in the metadata) and publish them via - // an ad-hoc asset manifest, while passing their locations via template - // parameters. - const legacyAssets = new AssetManifestBuilder(); - const assetParams = await addMetadataAssetsToManifest( - stackArtifact, - legacyAssets, - options.envResources, - options.reuseAssets, - ); - - const finalParameterValues = { ...options.parameters, ...assetParams }; - - const templateParams = TemplateParameters.fromTemplate(stackArtifact.template); - const stackParams = options.usePreviousParameters - ? templateParams.updateExisting(finalParameterValues, cloudFormationStack.parameters) - : templateParams.supplyAll(finalParameterValues); - - const hotswapMode = options.hotswap ?? HotswapMode.FULL_DEPLOYMENT; - const hotswapPropertyOverrides = options.hotswapPropertyOverrides ?? new HotswapPropertyOverrides(); - - if (await canSkipDeploy(options, cloudFormationStack, stackParams.hasChanges(cloudFormationStack.parameters))) { - debug(`${deployName}: skipping deployment (use --force to override)`); - // if we can skip deployment and we are performing a hotswap, let the user know - // that no hotswap deployment happened - if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) { - info( - `\n ${ICON} %s\n`, - chalk.bold('hotswap deployment skipped - no changes were detected (use --force to override)'), - ); - } - return { - type: 'did-deploy-stack', - noOp: true, - outputs: cloudFormationStack.outputs, - stackArn: cloudFormationStack.stackId, - }; - } else { - debug(`${deployName}: deploying...`); - } - - const bodyParameter = await makeBodyParameter( - stackArtifact, - options.resolvedEnvironment, - legacyAssets, - options.envResources, - options.overrideTemplate, - ); - let bootstrapStackName: string | undefined; - try { - bootstrapStackName = (await options.envResources.lookupToolkit()).stackName; - } catch (e) { - debug(`Could not determine the bootstrap stack name: ${e}`); - } - await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, { - parallel: options.assetParallelism, - allowCrossAccount: await determineAllowCrossAccountAssetPublishing(options.sdk, bootstrapStackName), - }); - - if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) { - // attempt to short-circuit the deployment if possible - try { - const hotswapDeploymentResult = await tryHotswapDeployment( - options.sdkProvider, - stackParams.values, - cloudFormationStack, - stackArtifact, - hotswapMode, hotswapPropertyOverrides, - ); - if (hotswapDeploymentResult) { - return hotswapDeploymentResult; - } - info( - 'Could not perform a hotswap deployment, as the stack %s contains non-Asset changes', - stackArtifact.displayName, - ); - } catch (e) { - if (!(e instanceof CfnEvaluationException)) { - throw e; - } - info( - 'Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: %s', - formatErrorMessage(e), - ); - } - - if (hotswapMode === HotswapMode.FALL_BACK) { - info('Falling back to doing a full deployment'); - options.sdk.appendCustomUserAgent('cdk-hotswap/fallback'); - } else { - return { - type: 'did-deploy-stack', - noOp: true, - stackArn: cloudFormationStack.stackId, - outputs: cloudFormationStack.outputs, - }; - } - } - - // could not short-circuit the deployment, perform a full CFN deploy instead - const fullDeployment = new FullCloudFormationDeployment( - options, - cloudFormationStack, - stackArtifact, - stackParams, - bodyParameter, - ); - return fullDeployment.performDeployment(); -} - -type CommonPrepareOptions = keyof CreateStackCommandInput & -keyof UpdateStackCommandInput & -keyof CreateChangeSetCommandInput; -type CommonExecuteOptions = keyof CreateStackCommandInput & -keyof UpdateStackCommandInput & -keyof ExecuteChangeSetCommandInput; - -/** - * This class shares state and functionality between the different full deployment modes - */ -class FullCloudFormationDeployment { - private readonly cfn: ICloudFormationClient; - private readonly stackName: string; - private readonly update: boolean; - private readonly verb: string; - private readonly uuid: string; - - constructor( - private readonly options: DeployStackOptions, - private readonly cloudFormationStack: CloudFormationStack, - private readonly stackArtifact: cxapi.CloudFormationStackArtifact, - private readonly stackParams: ParameterValues, - private readonly bodyParameter: TemplateBodyParameter, - ) { - this.cfn = options.sdk.cloudFormation(); - this.stackName = options.deployName ?? stackArtifact.stackName; - - this.update = cloudFormationStack.exists && cloudFormationStack.stackStatus.name !== 'REVIEW_IN_PROGRESS'; - this.verb = this.update ? 'update' : 'create'; - this.uuid = uuid.v4(); - } - - public async performDeployment(): Promise { - const deploymentMethod = this.options.deploymentMethod ?? { - method: 'change-set', - }; - - if (deploymentMethod.method === 'direct' && this.options.resourcesToImport) { - throw new Error('Importing resources requires a changeset deployment'); - } - - switch (deploymentMethod.method) { - case 'change-set': - return this.changeSetDeployment(deploymentMethod); - - case 'direct': - return this.directDeployment(); - } - } - - private async changeSetDeployment(deploymentMethod: ChangeSetDeploymentMethod): Promise { - const changeSetName = deploymentMethod.changeSetName ?? 'cdk-deploy-change-set'; - const execute = deploymentMethod.execute ?? true; - const importExistingResources = deploymentMethod.importExistingResources ?? false; - const changeSetDescription = await this.createChangeSet(changeSetName, execute, importExistingResources); - await this.updateTerminationProtection(); - - if (changeSetHasNoChanges(changeSetDescription)) { - debug('No changes are to be performed on %s.', this.stackName); - if (execute) { - debug('Deleting empty change set %s', changeSetDescription.ChangeSetId); - await this.cfn.deleteChangeSet({ - StackName: this.stackName, - ChangeSetName: changeSetName, - }); - } - - if (this.options.force) { - warning( - [ - 'You used the --force flag, but CloudFormation reported that the deployment would not make any changes.', - 'According to CloudFormation, all resources are already up-to-date with the state in your CDK app.', - '', - 'You cannot use the --force flag to get rid of changes you made in the console. Try using', - 'CloudFormation drift detection instead: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift.html', - ].join('\n'), - ); - } - - return { - type: 'did-deploy-stack', - noOp: true, - outputs: this.cloudFormationStack.outputs, - stackArn: changeSetDescription.StackId!, - }; - } - - if (!execute) { - info( - 'Changeset %s created and waiting in review for manual execution (--no-execute)', - changeSetDescription.ChangeSetId, - ); - return { - type: 'did-deploy-stack', - noOp: false, - outputs: this.cloudFormationStack.outputs, - stackArn: changeSetDescription.StackId!, - }; - } - - // If there are replacements in the changeset, check the rollback flag and stack status - const replacement = hasReplacement(changeSetDescription); - const isPausedFailState = this.cloudFormationStack.stackStatus.isRollbackable; - const rollback = this.options.rollback ?? true; - if (isPausedFailState && replacement) { - return { type: 'failpaused-need-rollback-first', reason: 'replacement', status: this.cloudFormationStack.stackStatus.name }; - } - if (isPausedFailState && rollback) { - return { type: 'failpaused-need-rollback-first', reason: 'not-norollback', status: this.cloudFormationStack.stackStatus.name }; - } - if (!rollback && replacement) { - return { type: 'replacement-requires-rollback' }; - } - - return this.executeChangeSet(changeSetDescription); - } - - private async createChangeSet(changeSetName: string, willExecute: boolean, importExistingResources: boolean) { - await this.cleanupOldChangeset(changeSetName); - - debug(`Attempting to create ChangeSet with name ${changeSetName} to ${this.verb} stack ${this.stackName}`); - info('%s: creating CloudFormation changeset...', chalk.bold(this.stackName)); - const changeSet = await this.cfn.createChangeSet({ - StackName: this.stackName, - ChangeSetName: changeSetName, - ChangeSetType: this.options.resourcesToImport ? 'IMPORT' : this.update ? 'UPDATE' : 'CREATE', - ResourcesToImport: this.options.resourcesToImport, - Description: `CDK Changeset for execution ${this.uuid}`, - ClientToken: `create${this.uuid}`, - ImportExistingResources: importExistingResources, - ...this.commonPrepareOptions(), - }); - - debug('Initiated creation of changeset: %s; waiting for it to finish creating...', changeSet.Id); - // Fetching all pages if we'll execute, so we can have the correct change count when monitoring. - return waitForChangeSet(this.cfn, this.stackName, changeSetName, { - fetchAll: willExecute, - }); - } - - private async executeChangeSet(changeSet: DescribeChangeSetCommandOutput): Promise { - debug('Initiating execution of changeset %s on stack %s', changeSet.ChangeSetId, this.stackName); - - await this.cfn.executeChangeSet({ - StackName: this.stackName, - ChangeSetName: changeSet.ChangeSetName!, - ClientRequestToken: `exec${this.uuid}`, - ...this.commonExecuteOptions(), - }); - - debug( - 'Execution of changeset %s on stack %s has started; waiting for the update to complete...', - changeSet.ChangeSetId, - this.stackName, - ); - - // +1 for the extra event emitted from updates. - const changeSetLength: number = (changeSet.Changes ?? []).length + (this.update ? 1 : 0); - return this.monitorDeployment(changeSet.CreationTime!, changeSetLength); - } - - private async cleanupOldChangeset(changeSetName: string) { - if (this.cloudFormationStack.exists) { - // Delete any existing change sets generated by CDK since change set names must be unique. - // The delete request is successful as long as the stack exists (even if the change set does not exist). - debug(`Removing existing change set with name ${changeSetName} if it exists`); - await this.cfn.deleteChangeSet({ - StackName: this.stackName, - ChangeSetName: changeSetName, - }); - } - } - - private async updateTerminationProtection() { - // Update termination protection only if it has changed. - const terminationProtection = this.stackArtifact.terminationProtection ?? false; - if (!!this.cloudFormationStack.terminationProtection !== terminationProtection) { - debug( - 'Updating termination protection from %s to %s for stack %s', - this.cloudFormationStack.terminationProtection, - terminationProtection, - this.stackName, - ); - await this.cfn.updateTerminationProtection({ - StackName: this.stackName, - EnableTerminationProtection: terminationProtection, - }); - debug('Termination protection updated to %s for stack %s', terminationProtection, this.stackName); - } - } - - private async directDeployment(): Promise { - info('%s: %s stack...', chalk.bold(this.stackName), this.update ? 'updating' : 'creating'); - - const startTime = new Date(); - - if (this.update) { - await this.updateTerminationProtection(); - - try { - await this.cfn.updateStack({ - StackName: this.stackName, - ClientRequestToken: `update${this.uuid}`, - ...this.commonPrepareOptions(), - ...this.commonExecuteOptions(), - }); - } catch (err: any) { - if (err.message === 'No updates are to be performed.') { - debug('No updates are to be performed for stack %s', this.stackName); - return { - type: 'did-deploy-stack', - noOp: true, - outputs: this.cloudFormationStack.outputs, - stackArn: this.cloudFormationStack.stackId, - }; - } - throw err; - } - - return this.monitorDeployment(startTime, undefined); - } else { - // Take advantage of the fact that we can set termination protection during create - const terminationProtection = this.stackArtifact.terminationProtection ?? false; - - await this.cfn.createStack({ - StackName: this.stackName, - ClientRequestToken: `create${this.uuid}`, - ...(terminationProtection ? { EnableTerminationProtection: true } : undefined), - ...this.commonPrepareOptions(), - ...this.commonExecuteOptions(), - }); - - return this.monitorDeployment(startTime, undefined); - } - } - - private async monitorDeployment(startTime: Date, expectedChanges: number | undefined): Promise { - const monitor = this.options.quiet - ? undefined - : StackActivityMonitor.withDefaultPrinter(this.cfn, this.stackName, this.stackArtifact, { - resourcesTotal: expectedChanges, - progress: this.options.progress, - changeSetCreationTime: startTime, - ci: this.options.ci, - }).start(); - - let finalState = this.cloudFormationStack; - try { - const successStack = await waitForStackDeploy(this.cfn, this.stackName); - - // This shouldn't really happen, but catch it anyway. You never know. - if (!successStack) { - throw new Error('Stack deploy failed (the stack disappeared while we were deploying it)'); - } - finalState = successStack; - } catch (e: any) { - throw new Error(suffixWithErrors(formatErrorMessage(e), monitor?.errors)); - } finally { - await monitor?.stop(); - } - debug('Stack %s has completed updating', this.stackName); - return { - type: 'did-deploy-stack', - noOp: false, - outputs: finalState.outputs, - stackArn: finalState.stackId, - }; - } - - /** - * Return the options that are shared between CreateStack, UpdateStack and CreateChangeSet - */ - private commonPrepareOptions(): Partial> { - return { - Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], - NotificationARNs: this.options.notificationArns, - Parameters: this.stackParams.apiParameters, - RoleARN: this.options.roleArn, - TemplateBody: this.bodyParameter.TemplateBody, - TemplateURL: this.bodyParameter.TemplateURL, - Tags: this.options.tags, - }; - } - - /** - * Return the options that are shared between UpdateStack and CreateChangeSet - * - * Be careful not to add in keys for options that aren't used, as the features may not have been - * deployed everywhere yet. - */ - private commonExecuteOptions(): Partial> { - const shouldDisableRollback = this.options.rollback === false; - - return { - StackName: this.stackName, - ...(shouldDisableRollback ? { DisableRollback: true } : undefined), - }; - } -} - -export interface DestroyStackOptions { - /** - * The stack to be destroyed - */ - stack: cxapi.CloudFormationStackArtifact; - - sdk: SDK; - roleArn?: string; - deployName?: string; - quiet?: boolean; - ci?: boolean; -} - -export async function destroyStack(options: DestroyStackOptions) { - const deployName = options.deployName || options.stack.stackName; - const cfn = options.sdk.cloudFormation(); - - const currentStack = await CloudFormationStack.lookup(cfn, deployName); - if (!currentStack.exists) { - return; - } - const monitor = options.quiet - ? undefined - : StackActivityMonitor.withDefaultPrinter(cfn, deployName, options.stack, { - ci: options.ci, - }).start(); - - try { - await cfn.deleteStack({ StackName: deployName, RoleARN: options.roleArn }); - const destroyedStack = await waitForStackDelete(cfn, deployName); - if (destroyedStack && destroyedStack.stackStatus.name !== 'DELETE_COMPLETE') { - throw new Error(`Failed to destroy ${deployName}: ${destroyedStack.stackStatus}`); - } - } catch (e: any) { - throw new Error(suffixWithErrors(formatErrorMessage(e), monitor?.errors)); - } finally { - if (monitor) { - await monitor.stop(); - } - } -} - -/** - * Checks whether we can skip deployment - * - * We do this in a complicated way by preprocessing (instead of just - * looking at the changeset), because if there are nested stacks involved - * the changeset will always show the nested stacks as needing to be - * updated, and the deployment will take a long time to in effect not - * do anything. - */ -async function canSkipDeploy( - deployStackOptions: DeployStackOptions, - cloudFormationStack: CloudFormationStack, - parameterChanges: ParameterChanges, -): Promise { - const deployName = deployStackOptions.deployName || deployStackOptions.stack.stackName; - debug(`${deployName}: checking if we can skip deploy`); - - // Forced deploy - if (deployStackOptions.force) { - debug(`${deployName}: forced deployment`); - return false; - } - - // Creating changeset only (default true), never skip - if ( - deployStackOptions.deploymentMethod?.method === 'change-set' && - deployStackOptions.deploymentMethod.execute === false - ) { - debug(`${deployName}: --no-execute, always creating change set`); - return false; - } - - // No existing stack - if (!cloudFormationStack.exists) { - debug(`${deployName}: no existing stack`); - return false; - } - - // Template has changed (assets taken into account here) - if (JSON.stringify(deployStackOptions.stack.template) !== JSON.stringify(await cloudFormationStack.template())) { - debug(`${deployName}: template has changed`); - return false; - } - - // Tags have changed - if (!compareTags(cloudFormationStack.tags, deployStackOptions.tags ?? [])) { - debug(`${deployName}: tags have changed`); - return false; - } - - // Notification arns have changed - if (!arrayEquals(cloudFormationStack.notificationArns, deployStackOptions.notificationArns ?? [])) { - debug(`${deployName}: notification arns have changed`); - return false; - } - - // Termination protection has been updated - if (!!deployStackOptions.stack.terminationProtection !== !!cloudFormationStack.terminationProtection) { - debug(`${deployName}: termination protection has been updated`); - return false; - } - - // Parameters have changed - if (parameterChanges) { - if (parameterChanges === 'ssm') { - debug(`${deployName}: some parameters come from SSM so we have to assume they may have changed`); - } else { - debug(`${deployName}: parameters have changed`); - } - return false; - } - - // Existing stack is in a failed state - if (cloudFormationStack.stackStatus.isFailure) { - debug(`${deployName}: stack is in a failure state`); - return false; - } - - // We can skip deploy - return true; -} - -/** - * Compares two list of tags, returns true if identical. - */ -function compareTags(a: Tag[], b: Tag[]): boolean { - if (a.length !== b.length) { - return false; - } - - for (const aTag of a) { - const bTag = b.find((tag) => tag.Key === aTag.Key); - - if (!bTag || bTag.Value !== aTag.Value) { - return false; - } - } - - return true; -} - -function suffixWithErrors(msg: string, errors?: string[]) { - return errors && errors.length > 0 ? `${msg}: ${errors.join(', ')}` : msg; -} - -function arrayEquals(a: any[], b: any[]): boolean { - return a.every((item) => b.includes(item)) && b.every((item) => a.includes(item)); -} - -function hasReplacement(cs: DescribeChangeSetCommandOutput) { - return (cs.Changes ?? []).some(c => { - const a = c.ResourceChange?.PolicyAction; - return a === 'ReplaceAndDelete' || a === 'ReplaceAndRetain' || a === 'ReplaceAndSnapshot'; - }); -} diff --git a/packages/aws-cdk/lib/api/deployments.ts b/packages/aws-cdk/lib/api/deployments.ts deleted file mode 100644 index 32650ac7a3549..0000000000000 --- a/packages/aws-cdk/lib/api/deployments.ts +++ /dev/null @@ -1,797 +0,0 @@ -import { randomUUID } from 'crypto'; -import * as cxapi from '@aws-cdk/cx-api'; -import * as cdk_assets from 'cdk-assets'; -import { AssetManifest, IManifestEntry } from 'cdk-assets'; -import * as chalk from 'chalk'; -import type { SdkProvider } from './aws-auth/sdk-provider'; -import { type DeploymentMethod, deployStack, DeployStackResult, destroyStack } from './deploy-stack'; -import { type EnvironmentResources } from './environment-resources'; -import type { Tag } from '../cdk-toolkit'; -import { debug, warning } from '../logging'; -import { EnvironmentAccess } from './environment-access'; -import { HotswapMode, HotswapPropertyOverrides } from './hotswap/common'; -import { - loadCurrentTemplate, - loadCurrentTemplateWithNestedStacks, - type RootTemplateWithNestedStacks, -} from './nested-stack-helpers'; -import { DEFAULT_TOOLKIT_STACK_NAME } from './toolkit-info'; -import { determineAllowCrossAccountAssetPublishing } from './util/checks'; -import { - CloudFormationStack, - type ResourceIdentifierSummaries, - ResourcesToImport, - stabilizeStack, - Template, - uploadStackTemplateAssets, -} from './util/cloudformation'; -import { StackActivityMonitor, StackActivityProgress } from './util/cloudformation/stack-activity-monitor'; -import { StackEventPoller } from './util/cloudformation/stack-event-poller'; -import { RollbackChoice } from './util/cloudformation/stack-status'; -import { makeBodyParameter } from './util/template-body-parameter'; -import { AssetManifestBuilder } from '../util/asset-manifest-builder'; -import { - buildAssets, - type BuildAssetsOptions, - EVENT_TO_LOGGER, - publishAssets, - type PublishAssetsOptions, - PublishingAws, -} from '../util/asset-publishing'; -import { formatErrorMessage } from '../util/error'; - -const BOOTSTRAP_STACK_VERSION_FOR_ROLLBACK = 23; - -export interface DeployStackOptions { - /** - * Stack to deploy - */ - readonly stack: cxapi.CloudFormationStackArtifact; - - /** - * Execution role for the deployment (pass through to CloudFormation) - * - * @default - Current role - */ - readonly roleArn?: string; - - /** - * Topic ARNs to send a message when deployment finishes (pass through to CloudFormation) - * - * @default - No notifications - */ - readonly notificationArns?: string[]; - - /** - * Override name under which stack will be deployed - * - * @default - Use artifact default - */ - readonly deployName?: string; - - /** - * Don't show stack deployment events, just wait - * - * @default false - */ - readonly quiet?: boolean; - - /** - * Name of the toolkit stack, if not the default name - * - * @default 'CDKToolkit' - */ - readonly toolkitStackName?: string; - - /** - * List of asset IDs which should NOT be built or uploaded - * - * @default - Build all assets - */ - readonly reuseAssets?: string[]; - - /** - * Stack tags (pass through to CloudFormation) - */ - readonly tags?: Tag[]; - - /** - * Stage the change set but don't execute it - * - * @default - true - * @deprecated Use 'deploymentMethod' instead - */ - readonly execute?: boolean; - - /** - * Optional name to use for the CloudFormation change set. - * If not provided, a name will be generated automatically. - * - * @deprecated Use 'deploymentMethod' instead - */ - readonly changeSetName?: string; - - /** - * Select the deployment method (direct or using a change set) - * - * @default - Change set with default options - */ - readonly deploymentMethod?: DeploymentMethod; - - /** - * Force deployment, even if the deployed template is identical to the one we are about to deploy. - * @default false deployment will be skipped if the template is identical - */ - readonly force?: boolean; - - /** - * Extra parameters for CloudFormation - * @default - no additional parameters will be passed to the template - */ - readonly parameters?: { [name: string]: string | undefined }; - - /** - * Use previous values for unspecified parameters - * - * If not set, all parameters must be specified for every deployment. - * - * @default true - */ - readonly usePreviousParameters?: boolean; - - /** - * Display mode for stack deployment progress. - * - * @default - StackActivityProgress.Bar - stack events will be displayed for - * the resource currently being deployed. - */ - readonly progress?: StackActivityProgress; - - /** - * Whether we are on a CI system - * - * @default false - */ - readonly ci?: boolean; - - /** - * Rollback failed deployments - * - * @default true - */ - readonly rollback?: boolean; - - /* - * Whether to perform a 'hotswap' deployment. - * A 'hotswap' deployment will attempt to short-circuit CloudFormation - * and update the affected resources like Lambda functions directly. - * - * @default - `HotswapMode.FULL_DEPLOYMENT` for regular deployments, `HotswapMode.HOTSWAP_ONLY` for 'watch' deployments - */ - readonly hotswap?: HotswapMode; - - /** - * Properties that configure hotswap behavior - */ - readonly hotswapPropertyOverrides?: HotswapPropertyOverrides; - - /** - * The extra string to append to the User-Agent header when performing AWS SDK calls. - * - * @default - nothing extra is appended to the User-Agent header - */ - readonly extraUserAgent?: string; - - /** - * List of existing resources to be IMPORTED into the stack, instead of being CREATED - */ - readonly resourcesToImport?: ResourcesToImport; - - /** - * If present, use this given template instead of the stored one - * - * @default - Use the stored template - */ - readonly overrideTemplate?: any; - - /** - * Whether to build/publish assets in parallel - * - * @default true To remain backward compatible. - */ - readonly assetParallelism?: boolean; - - /** - * Whether to deploy if the app contains no stacks. - * - * @default false - */ - ignoreNoStacks?: boolean; -} - -export interface RollbackStackOptions { - /** - * Stack to roll back - */ - readonly stack: cxapi.CloudFormationStackArtifact; - - /** - * Execution role for the deployment (pass through to CloudFormation) - * - * @default - Current role - */ - readonly roleArn?: string; - - /** - * Don't show stack deployment events, just wait - * - * @default false - */ - readonly quiet?: boolean; - - /** - * Whether we are on a CI system - * - * @default false - */ - readonly ci?: boolean; - - /** - * Name of the toolkit stack, if not the default name - * - * @default 'CDKToolkit' - */ - readonly toolkitStackName?: string; - - /** - * Whether to force a rollback or not - * - * Forcing a rollback will orphan all undeletable resources. - * - * @default false - */ - readonly force?: boolean; - - /** - * Orphan the resources with the given logical IDs - * - * @default - No orphaning - */ - readonly orphanLogicalIds?: string[]; - - /** - * Display mode for stack deployment progress. - * - * @default - StackActivityProgress.Bar - stack events will be displayed for - * the resource currently being deployed. - */ - readonly progress?: StackActivityProgress; - - /** - * Whether to validate the version of the bootstrap stack permissions - * - * @default true - */ - readonly validateBootstrapStackVersion?: boolean; -} - -export interface RollbackStackResult { - readonly notInRollbackableState?: boolean; - readonly success?: boolean; -} - -interface AssetOptions { - /** - * Stack with assets to build. - */ - readonly stack: cxapi.CloudFormationStackArtifact; - - /** - * Execution role for the building. - * - * @default - Current role - */ - readonly roleArn?: string; -} - -export interface BuildStackAssetsOptions extends AssetOptions { - /** - * Options to pass on to `buildAssets()` function - */ - readonly buildOptions?: BuildAssetsOptions; - - /** - * Stack name this asset is for - */ - readonly stackName?: string; -} - -interface PublishStackAssetsOptions extends AssetOptions { - /** - * Options to pass on to `publishAsests()` function - */ - readonly publishOptions?: Omit; - - /** - * Stack name this asset is for - */ - readonly stackName?: string; -} - -export interface DestroyStackOptions { - stack: cxapi.CloudFormationStackArtifact; - deployName?: string; - roleArn?: string; - quiet?: boolean; - force?: boolean; - ci?: boolean; -} - -export interface StackExistsOptions { - stack: cxapi.CloudFormationStackArtifact; - deployName?: string; - tryLookupRole?: boolean; -} - -export interface DeploymentsProps { - sdkProvider: SdkProvider; - readonly toolkitStackName?: string; - readonly quiet?: boolean; -} - -/** - * Scope for a single set of deployments from a set of Cloud Assembly Artifacts - * - * Manages lookup of SDKs, Bootstrap stacks, etc. - */ -export class Deployments { - public readonly envs: EnvironmentAccess; - - /** - * SDK provider for asset publishing (do not use for anything else). - * - * This SDK provider is only allowed to be used for that purpose, nothing else. - * - * It's not a different object, but the field name should imply that this - * object should not be used directly, except to pass to asset handling routines. - */ - private readonly assetSdkProvider: SdkProvider; - - /** - * SDK provider for passing to deployStack - * - * This SDK provider is only allowed to be used for that purpose, nothing else. - * - * It's not a different object, but the field name should imply that this - * object should not be used directly, except to pass to `deployStack`. - */ - private readonly deployStackSdkProvider: SdkProvider; - - private readonly publisherCache = new Map(); - - private _allowCrossAccountAssetPublishing: boolean | undefined; - constructor(private readonly props: DeploymentsProps) { - this.assetSdkProvider = props.sdkProvider; - this.deployStackSdkProvider = props.sdkProvider; - this.envs = new EnvironmentAccess(props.sdkProvider, props.toolkitStackName ?? DEFAULT_TOOLKIT_STACK_NAME); - } - - /** - * Resolves the environment for a stack. - */ - public async resolveEnvironment(stack: cxapi.CloudFormationStackArtifact): Promise { - return this.envs.resolveStackEnvironment(stack); - } - - public async readCurrentTemplateWithNestedStacks( - rootStackArtifact: cxapi.CloudFormationStackArtifact, - retrieveProcessedTemplate: boolean = false, - ): Promise { - const env = await this.envs.accessStackForLookupBestEffort(rootStackArtifact); - return loadCurrentTemplateWithNestedStacks(rootStackArtifact, env.sdk, retrieveProcessedTemplate); - } - - public async readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise