From 52331c8e38645fbf804fe1c9e528a8faf9185c0c Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 21 Jul 2021 17:29:54 +0300 Subject: [PATCH 01/27] chore: npm-check-updates && yarn upgrade (#15693) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- package.json | 2 +- .../aws-apigatewayv2-authorizers/package.json | 2 +- .../package.json | 4 +- .../@aws-cdk/aws-cloudformation/package.json | 2 +- .../aws-global-table-coordinator/package.json | 2 +- packages/@aws-cdk/aws-dynamodb/package.json | 2 +- packages/@aws-cdk/aws-ec2/package.json | 2 +- packages/@aws-cdk/aws-eks/package.json | 2 +- packages/@aws-cdk/aws-iam/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- packages/@aws-cdk/aws-logs/package.json | 2 +- packages/@aws-cdk/aws-route53/package.json | 2 +- packages/@aws-cdk/aws-s3/package.json | 2 +- packages/@aws-cdk/aws-ses/package.json | 2 +- packages/@aws-cdk/core/package.json | 2 +- .../@aws-cdk/custom-resources/package.json | 2 +- packages/@aws-cdk/pipelines/package.json | 2 +- packages/aws-cdk/package.json | 2 +- packages/awslint/package.json | 8 +- tools/cdk-build-tools/package.json | 8 +- tools/eslint-plugin-cdk/package.json | 4 +- tools/pkglint/package.json | 8 +- yarn.lock | 316 +++++++++--------- 23 files changed, 191 insertions(+), 191 deletions(-) diff --git a/package.json b/package.json index 96281e2f0dd36..66025c149aa84 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "jsii-rosetta": "^1.31.0", "lerna": "^4.0.0", "patch-package": "^6.4.7", - "standard-version": "^9.3.0", + "standard-version": "^9.3.1", "typescript": "~3.9.10" }, "tap-mocha-reporter-resolutions-comment": "should be removed or reviewed when nodeunit dependency is dropped or adjusted", diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index 23690776093aa..748efa7077158 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -74,7 +74,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/jest": "^26.0.24", - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "cdk-build-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json index e3ab68c8a7dd6..63784c4cf30ce 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json @@ -29,12 +29,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/sinon": "^9.0.11", "cdk-build-tools": "0.0.0", "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.2.1", - "eslint": "^7.30.0", + "eslint": "^7.31.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.23.4", "eslint-plugin-node": "^11.1.0", diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index f0d492aace535..b3d32b8636b09 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -74,7 +74,7 @@ "@aws-cdk/aws-sns-subscriptions": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@aws-cdk/aws-ssm": "0.0.0", - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/nodeunit": "^0.0.32", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json index 5df277b6064ca..130fed06d99c7 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json @@ -31,7 +31,7 @@ "devDependencies": { "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.2.1", - "eslint": "^7.30.0", + "eslint": "^7.31.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.23.4", "eslint-plugin-node": "^11.1.0", diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index a9ffc922c528e..e6d4627e3455e 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -73,7 +73,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/jest": "^26.0.24", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 52dede95bb318..985422e5da1fb 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -73,7 +73,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/jest": "^26.0.24", "@aws-cdk/cx-api": "0.0.0", "cdk-build-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index bf23bff54c899..290e23df60d2c 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -72,7 +72,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/sinon": "^9.0.11", "@types/nodeunit": "^0.0.32", "@types/yaml": "1.9.6", diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index 2cbf971197ef3..ef766c83c1a25 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -73,7 +73,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/jest": "^26.0.24", "@types/sinon": "^9.0.11", "cdk-build-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index da9f2ff159fd6..e3a1ec3289d46 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -78,7 +78,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/jest": "^26.0.24", - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/lodash": "^4.14.171", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index a7838115e50db..ebaa38cd41ad5 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -73,7 +73,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/nodeunit": "^0.0.32", - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", "aws-sdk-mock": "^5.2.1", diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 4d72b2ed05616..90608982f090b 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -73,7 +73,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/jest": "^26.0.24", "@types/nodeunit": "^0.0.32", "aws-sdk": "^2.848.0", diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 06e4248c943e7..da9bdd3d05df4 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -73,7 +73,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/jest": "^26.0.24", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 36c7fca9442e3..5b56def33afd4 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -72,7 +72,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/nodeunit": "^0.0.32", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index 80033471fbdc5..f4bfcb141730e 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -169,7 +169,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/fs-extra": "^8.1.2", "@types/jest": "^26.0.24", "@types/lodash": "^4.14.171", diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 67f181d8844f6..d81b9a11edc0c 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -77,7 +77,7 @@ "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ssm": "0.0.0", - "@types/aws-lambda": "^8.10.78", + "@types/aws-lambda": "^8.10.79", "@types/fs-extra": "^8.1.2", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", diff --git a/packages/@aws-cdk/pipelines/package.json b/packages/@aws-cdk/pipelines/package.json index 45252fe965ac7..25c9099e64074 100644 --- a/packages/@aws-cdk/pipelines/package.json +++ b/packages/@aws-cdk/pipelines/package.json @@ -37,7 +37,7 @@ "@aws-cdk/aws-ecr-assets": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@aws-cdk/aws-sns-subscriptions": "0.0.0", - "@types/jest": "^26.0.23", + "@types/jest": "^26.0.24", "aws-sdk": "^2.848.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 71d15e6442fdf..4485c964c105e 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -41,7 +41,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/core": "0.0.0", - "@octokit/rest": "^18.6.7", + "@octokit/rest": "^18.6.8", "@types/archiver": "^5.3.0", "@types/fs-extra": "^8.1.2", "@types/glob": "^7.1.4", diff --git a/packages/awslint/package.json b/packages/awslint/package.json index da5906149f506..6edbaaf52399e 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -31,14 +31,14 @@ "@types/yargs": "^15.0.14", "pkglint": "0.0.0", "typescript": "~3.9.10", - "@typescript-eslint/eslint-plugin": "^4.28.3", - "@typescript-eslint/parser": "^4.28.3", - "eslint": "^7.30.0", + "@typescript-eslint/eslint-plugin": "^4.28.4", + "@typescript-eslint/parser": "^4.28.4", + "eslint": "^7.31.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-jest": "^24.3.6", + "eslint-plugin-jest": "^24.3.7", "jest": "^26.6.3" }, "repository": { diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 3443058c4ea6b..c4c3df70fc154 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -42,16 +42,16 @@ "pkglint": "0.0.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.28.3", - "@typescript-eslint/parser": "^4.28.3", + "@typescript-eslint/eslint-plugin": "^4.28.4", + "@typescript-eslint/parser": "^4.28.4", "awslint": "0.0.0", "colors": "^1.4.0", - "eslint": "^7.30.0", + "eslint": "^7.31.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-jest": "^24.3.6", + "eslint-plugin-jest": "^24.3.7", "fs-extra": "^9.1.0", "jest": "^26.6.3", "jest-junit": "^11.1.0", diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index b075f070208d0..4095ff1d19297 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -24,8 +24,8 @@ "typescript": "~3.9.10" }, "dependencies": { - "@typescript-eslint/parser": "^4.28.3", - "eslint": "^7.30.0", + "@typescript-eslint/parser": "^4.28.4", + "eslint": "^7.31.0", "fs-extra": "^9.1.0" }, "jest": { diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index 9f6f3446c31b3..a0a988e330e6c 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -42,14 +42,14 @@ "@types/jest": "^26.0.24", "@types/semver": "^7.3.7", "@types/yargs": "^15.0.14", - "@typescript-eslint/eslint-plugin": "^4.28.3", - "@typescript-eslint/parser": "^4.28.3", - "eslint": "^7.30.0", + "@typescript-eslint/eslint-plugin": "^4.28.4", + "@typescript-eslint/parser": "^4.28.4", + "eslint": "^7.31.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", "eslint-plugin-import": "^2.23.4", - "eslint-plugin-jest": "^24.3.6", + "eslint-plugin-jest": "^24.3.7", "jest": "^26.6.3", "typescript": "~3.9.10" }, diff --git a/yarn.lock b/yarn.lock index 51786b613db78..7e67161d74f5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,19 +43,19 @@ integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" - integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" + integrity sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" + "@babel/generator" "^7.14.8" "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helpers" "^7.14.6" - "@babel/parser" "^7.14.6" + "@babel/helper-module-transforms" "^7.14.8" + "@babel/helpers" "^7.14.8" + "@babel/parser" "^7.14.8" "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.14.8" + "@babel/types" "^7.14.8" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -63,12 +63,12 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.14.5", "@babel/generator@^7.4.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" - integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== +"@babel/generator@^7.14.8", "@babel/generator@^7.4.0": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070" + integrity sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.14.8" jsesc "^2.5.1" source-map "^0.5.0" @@ -119,19 +119,19 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-module-transforms@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" - integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== +"@babel/helper-module-transforms@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" + integrity sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA== dependencies: "@babel/helper-module-imports" "^7.14.5" "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-simple-access" "^7.14.8" "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.8" "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.14.8" + "@babel/types" "^7.14.8" "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" @@ -155,12 +155,12 @@ "@babel/traverse" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-simple-access@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" - integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== +"@babel/helper-simple-access@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" + integrity sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.14.8" "@babel/helper-split-export-declaration@^7.14.5": version "7.14.5" @@ -169,24 +169,24 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-validator-identifier@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" - integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== +"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" + integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow== "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== -"@babel/helpers@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" - integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== +"@babel/helpers@^7.14.8": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" + integrity sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw== dependencies: "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/traverse" "^7.14.8" + "@babel/types" "^7.14.8" "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" @@ -197,10 +197,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.4.3": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" - integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8", "@babel/parser@^7.4.3": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" + integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -295,27 +295,27 @@ "@babel/parser" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.4.3": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" - integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.14.8", "@babel/traverse@^7.4.3": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce" + integrity sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" + "@babel/generator" "^7.14.8" "@babel/helper-function-name" "^7.14.5" "@babel/helper-hoist-variables" "^7.14.5" "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.7" - "@babel/types" "^7.14.5" + "@babel/parser" "^7.14.8" + "@babel/types" "^7.14.8" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" - integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== +"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728" + integrity sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q== dependencies: - "@babel/helper-validator-identifier" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.8" to-fast-properties "^2.0.0" "@balena/dockerignore@^1.0.2": @@ -336,10 +336,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" - integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -1352,10 +1352,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^8.3.0": - version "8.3.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-8.3.0.tgz#8bc912edae8c03e002882cf1e29b595b7da9b441" - integrity sha512-ZFyQ30tNpoATI7o+Z9MWFUzUgWisB8yduhcky7S4UYsRijgIGSnwUKzPBDGzf/Xkx1DuvUtqzvmuFlDSqPJqmQ== +"@octokit/openapi-types@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-9.0.0.tgz#05d33f999326785445c915d25167d68bd5eddb24" + integrity sha512-GSpv5VUFqarOXZl6uWPsDnjChkKCxnaMALmQhzvCWGiMxONQxX7ZwlomCMS+wB1KqxLPCA5n6gYt016oEMkHmQ== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -1389,12 +1389,12 @@ "@octokit/types" "^2.0.1" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@5.4.1": - version "5.4.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.4.1.tgz#540ec90bb753dcaa682ee9f2cd6efdde9132fa90" - integrity sha512-Nx0g7I5ayAYghsLJP4Q1Ch2W9jYYM0FlWWWZocUro8rNxVwuZXGfFd7Rcqi9XDWepSXjg1WByiNJnZza2hIOvQ== +"@octokit/plugin-rest-endpoint-methods@5.4.2": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.4.2.tgz#d090e93ee68ec09985e1ff0a1d2d28581cc883a5" + integrity sha512-imNDDvUMy9YzECcP6zTcKNjwutSwqCYGMZjLPnBHF0kdb3V9URrHWmalD0ZvNEYjwbpm2zw8RPewj3ebCpMBRw== dependencies: - "@octokit/types" "^6.18.1" + "@octokit/types" "^6.19.1" deprecation "^2.3.1" "@octokit/request-error@^1.0.2": @@ -1449,15 +1449,15 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@octokit/rest@^18.1.0", "@octokit/rest@^18.6.7": - version "18.6.7" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.6.7.tgz#89b8ecd13edd9603f00453640d1fb0b4175d4b31" - integrity sha512-Kn6WrI2ZvmAztdx+HEaf88RuJn+LK72S8g6OpciE4kbZddAN84fu4fiPGxcEu052WmqKVnA/cnQsbNlrYC6rqQ== +"@octokit/rest@^18.1.0", "@octokit/rest@^18.6.8": + version "18.6.8" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.6.8.tgz#f73ef3b59686df18206183551c2a835d1db1424f" + integrity sha512-n2aT0mJL9N/idCPmnBynCino1qNScfRHvr8OeskQdBNhUYAMc7cxoc8KLlv1DMWxlZUNhed+5kVdu7majVdVag== dependencies: "@octokit/core" "^3.5.0" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.4.1" + "@octokit/plugin-rest-endpoint-methods" "5.4.2" "@octokit/types@^2.0.0", "@octokit/types@^2.0.1": version "2.16.2" @@ -1466,12 +1466,12 @@ dependencies: "@types/node" ">= 8" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.18.1": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.19.0.tgz#e2b6fedb10c8b53cf4574aa5d1a8a5611295297a" - integrity sha512-9wdZFiJfonDyU6DjIgDHxAIn92vdSUBOwAXbO2F9rOFt6DJwuAkyGLu1CvdJPphCbPBoV9iSDMX7y4fu0v6AtA== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.18.0", "@octokit/types@^6.19.1": + version "6.19.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.19.1.tgz#6ea5f759d8d37e892e59c0a65f10892789b84a25" + integrity sha512-hMI2EokQzMG8ABWcnvcrabqQFuFHqUdN0HUOG4DPTaOtnf/jqhzhK1SHOGu5vDlI/x+hWJ60e28VxB7QhOP0CQ== dependencies: - "@octokit/openapi-types" "^8.3.0" + "@octokit/openapi-types" "^9.0.0" "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1", "@sinonjs/commons@^1.8.3": version "1.8.3" @@ -1529,10 +1529,10 @@ dependencies: "@types/glob" "*" -"@types/aws-lambda@^8.10.78": - version "8.10.78" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.78.tgz#dbb509837b6082962d6e7bc19f814e067ac9f5a2" - integrity sha512-+lZ8NuHT0qKEEpiZR4bF1G24SLrLwzdu0i9Cjdc3BGq6XJU6gBBYS5I0RJ8RdDCtgqgGdW8sOwsiZGHrC6mp0Q== +"@types/aws-lambda@^8.10.79": + version "8.10.79" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.79.tgz#c87fcc10500d0524f583b7fd7828025d8aa834fd" + integrity sha512-YgpllvHcDrPfzyf8/a2+ScRoEtJQjAP0pSgIneWcVpfqHGmiAaS2oMO3e4y3InJMgrMyuxGPde72CBZS/X8vJA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.15" @@ -1688,9 +1688,9 @@ integrity sha512-uv53RrNdhbkV/3VmVCtfImfYCWC3GTTRn3R11Whni3EJ+gb178tkZBVNj2edLY5CMrB749dQi+SJkg87jsN8UQ== "@types/node@*", "@types/node@>= 8": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.2.tgz#655432817f83b51ac869c2d51dd8305fb8342e16" - integrity sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw== + version "16.4.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.0.tgz#2c219eaa3b8d1e4d04f4dd6e40bc68c7467d5272" + integrity sha512-HrJuE7Mlqcjj+00JqMWpZ3tY8w7EUd+S0U3L1+PQSWiXZbOgyQDvi+ogoUxaHApPJq5diKxYBQwA3iIlNcPqOg== "@types/node@^10.17.60": version "10.17.60" @@ -1816,73 +1816,73 @@ resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.5.tgz#9639020e1fb65120a2f4387db8f1e8b63efdf229" integrity sha512-8NYnGOctzsI4W0ApsP/BIHD/LnxpJ6XaGf2AZmz4EyDYJMxtprN4279dLNI1CPZcwC9H18qYcaFv4bXi0wmokg== -"@typescript-eslint/eslint-plugin@^4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz#36cdcd9ca6f9e5cb49b9f61b970b1976708d084b" - integrity sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg== +"@typescript-eslint/eslint-plugin@^4.28.4": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.4.tgz#e73c8cabbf3f08dee0e1bda65ed4e622ae8f8921" + integrity sha512-s1oY4RmYDlWMlcV0kKPBaADn46JirZzvvH7c2CtAqxCY96S538JRBAzt83RrfkDheV/+G/vWNK0zek+8TB3Gmw== dependencies: - "@typescript-eslint/experimental-utils" "4.28.3" - "@typescript-eslint/scope-manager" "4.28.3" + "@typescript-eslint/experimental-utils" "4.28.4" + "@typescript-eslint/scope-manager" "4.28.4" debug "^4.3.1" functional-red-black-tree "^1.0.1" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.28.3", "@typescript-eslint/experimental-utils@^4.0.1": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz#976f8c1191b37105fd06658ed57ddfee4be361ca" - integrity sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw== +"@typescript-eslint/experimental-utils@4.28.4", "@typescript-eslint/experimental-utils@^4.0.1": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.4.tgz#9c70c35ebed087a5c70fb0ecd90979547b7fec96" + integrity sha512-OglKWOQRWTCoqMSy6pm/kpinEIgdcXYceIcH3EKWUl4S8xhFtN34GQRaAvTIZB9DD94rW7d/U7tUg3SYeDFNHA== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.28.3" - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/typescript-estree" "4.28.3" + "@typescript-eslint/scope-manager" "4.28.4" + "@typescript-eslint/types" "4.28.4" + "@typescript-eslint/typescript-estree" "4.28.4" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@^4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.3.tgz#95f1d475c08268edffdcb2779993c488b6434b44" - integrity sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ== +"@typescript-eslint/parser@^4.28.4": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.4.tgz#bc462dc2779afeefdcf49082516afdc3e7b96fab" + integrity sha512-4i0jq3C6n+og7/uCHiE6q5ssw87zVdpUj1k6VlVYMonE3ILdFApEzTWgppSRG4kVNB/5jxnH+gTeKLMNfUelQA== dependencies: - "@typescript-eslint/scope-manager" "4.28.3" - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/typescript-estree" "4.28.3" + "@typescript-eslint/scope-manager" "4.28.4" + "@typescript-eslint/types" "4.28.4" + "@typescript-eslint/typescript-estree" "4.28.4" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz#c32ad4491b3726db1ba34030b59ea922c214e371" - integrity sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ== +"@typescript-eslint/scope-manager@4.28.4": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.4.tgz#bdbce9b6a644e34f767bd68bc17bb14353b9fe7f" + integrity sha512-ZJBNs4usViOmlyFMt9X9l+X0WAFcDH7EdSArGqpldXu7aeZxDAuAzHiMAeI+JpSefY2INHrXeqnha39FVqXb8w== dependencies: - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/visitor-keys" "4.28.3" + "@typescript-eslint/types" "4.28.4" + "@typescript-eslint/visitor-keys" "4.28.4" -"@typescript-eslint/types@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.3.tgz#8fffd436a3bada422c2c1da56060a0566a9506c7" - integrity sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA== +"@typescript-eslint/types@4.28.4": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.4.tgz#41acbd79b5816b7c0dd7530a43d97d020d3aeb42" + integrity sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww== -"@typescript-eslint/typescript-estree@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz#253d7088100b2a38aefe3c8dd7bd1f8232ec46fb" - integrity sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w== +"@typescript-eslint/typescript-estree@4.28.4": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.4.tgz#252e6863278dc0727244be9e371eb35241c46d00" + integrity sha512-z7d8HK8XvCRyN2SNp+OXC2iZaF+O2BTquGhEYLKLx5k6p0r05ureUtgEfo5f6anLkhCxdHtCf6rPM1p4efHYDQ== dependencies: - "@typescript-eslint/types" "4.28.3" - "@typescript-eslint/visitor-keys" "4.28.3" + "@typescript-eslint/types" "4.28.4" + "@typescript-eslint/visitor-keys" "4.28.4" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.28.3": - version "4.28.3" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz#26ac91e84b23529968361045829da80a4e5251c4" - integrity sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg== +"@typescript-eslint/visitor-keys@4.28.4": + version "4.28.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.4.tgz#92dacfefccd6751cbb0a964f06683bfd72d0c4d3" + integrity sha512-NIAXAdbz1XdOuzqkJHjNKXKj8QQ4cv5cxR/g0uQhCYf/6//XrmfpaYsM7PnBcNbfvTDLUkqQ5TPNm1sozDdTWg== dependencies: - "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/types" "4.28.4" eslint-visitor-keys "^2.0.0" "@yarnpkg/lockfile@^1.1.0": @@ -1976,9 +1976,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.6.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.1.tgz#ae65764bf1edde8cd861281cda5057852364a295" - integrity sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ== + version "8.6.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" + integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2269,9 +2269,9 @@ aws-sdk-mock@^5.2.1: traverse "^0.6.6" aws-sdk@^2.596.0, aws-sdk@^2.848.0, aws-sdk@^2.928.0: - version "2.945.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.945.0.tgz#ebd90832a664a192b12edf755af31be70dc18909" - integrity sha512-tkcoFAUol7c+9ZBnXsBTKfsj9bNckJ7uzj7FdD/a8AMt/6/18LlEISCiuHFl9qr8MItcON7UgnphJdFCTV7zBw== + version "2.950.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.950.0.tgz#cffb65590c50de9479c87ed04df57d355d1d8a22" + integrity sha512-iFC5fKLuFLEV27xeKmxDHDZzIDj4upm5+Ts3NpYYRbwPlOG0nE0gZzf9fRYkLkLgTr0TQq26CbKorgeo+6ailw== dependencies: buffer "4.9.2" events "1.1.1" @@ -2619,9 +2619,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001219: - version "1.0.30001245" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz#45b941bbd833cb0fa53861ff2bae746b3c6ca5d4" - integrity sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA== + version "1.0.30001246" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001246.tgz#fe17d9919f87124d6bb416ef7b325356d69dc76c" + integrity sha512-Tc+ff0Co/nFNbLOrziBXmMVtpt9S2c2Y+Z9Nk9Khj09J+0zR9ejvIW5qkZAErCbOrVODCx/MN+GpB5FNBs5GFA== capture-exit@^2.0.0: version "2.0.0" @@ -2958,9 +2958,9 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= constructs@^3.3.69: - version "3.3.97" - resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.97.tgz#751cc8955ee29381da9ee05f39141f02d6164aee" - integrity sha512-KDemmmUBgTDd2OKVOZkVEJM1LwP/bzm+cs2l/v1UYctIUl2X4LW+MrK7Ajd8blKkS5Vp6edkQSTSHUJnR/413w== + version "3.3.99" + resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.99.tgz#e614595cb0a1f7986f72597a4d9b98572a4ada8d" + integrity sha512-uX3bZtp6Zn53Utyurp4DrKolIDUuiDddHVTgsQ39KhVRkQ8TRMtl0nyXllysMtu78t8zLo9QygeyQ0QOBy3LHw== conventional-changelog-angular@^5.0.12: version "5.0.12" @@ -3644,9 +3644,9 @@ ejs@^2.5.2: integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== electron-to-chromium@^1.3.723: - version "1.3.775" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.775.tgz#046517d1f2cea753e06fff549995b9dc45e20082" - integrity sha512-EGuiJW4yBPOTj2NtWGZcX93ZE8IGj33HJAx4d3ouE2zOfW2trbWU+t1e0yzLr1qQIw81++txbM3BH52QwSRE6Q== + version "1.3.782" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.782.tgz#522740fe6b4b5255ca754c68d9c406a17b0998e2" + integrity sha512-6AI2se1NqWA1SBf/tlD6tQD/6ZOt+yAhqmrTlh4XZw4/g0Mt3p6JhTQPZxRPxPZiOg0o7ss1EBP/CpYejfnoIA== emittery@^0.7.1: version "0.7.2" @@ -3876,10 +3876,10 @@ eslint-plugin-import@^2.23.4: resolve "^1.20.0" tsconfig-paths "^3.9.0" -eslint-plugin-jest@^24.3.6: - version "24.3.6" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz#5f0ca019183c3188c5ad3af8e80b41de6c8e9173" - integrity sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg== +eslint-plugin-jest@^24.3.7: + version "24.3.7" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.3.7.tgz#a4deaa9e88182b92533a9c25cc4f3c369d7f33eb" + integrity sha512-pXED2NA4q2M/5mxlN6GyuUXAFJndT0uosOkQCHaUED9pqgBPd89ZzpcZEU6c5HtZNahC00M36FkwLdDHMDqaHw== dependencies: "@typescript-eslint/experimental-utils" "^4.0.1" @@ -3942,13 +3942,13 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.30.0: - version "7.30.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8" - integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg== +eslint@^7.31.0: + version "7.31.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca" + integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.2" + "@eslint/eslintrc" "^0.4.3" "@humanwhocodes/config-array" "^0.5.0" ajv "^6.10.0" chalk "^4.0.0" @@ -6554,9 +6554,9 @@ make-fetch-happen@^8.0.9: ssri "^8.0.0" make-fetch-happen@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.3.tgz#57bbfb5b859807cd28005ca85aa6a72568675e24" - integrity sha512-uZ/9Cf2vKqsSWZyXhZ9wHHyckBrkntgbnqV68Bfe8zZenlf7D6yuGMXvHZQ+jSnzPkjosuNP1HGasj1J4h8OlQ== + version "9.0.4" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz#ceaa100e60e0ef9e8d1ede94614bb2ba83c8bb24" + integrity sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg== dependencies: agentkeepalive "^4.1.3" cacache "^15.2.0" @@ -6808,9 +6808,9 @@ minipass-collect@^1.0.2: minipass "^3.0.0" minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" - integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + version "1.3.4" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.4.tgz#63f5af868a38746ca7b33b03393ddf8c291244fe" + integrity sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ== dependencies: minipass "^3.1.0" minipass-sized "^1.0.3" @@ -8911,10 +8911,10 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" -standard-version@^9.3.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/standard-version/-/standard-version-9.3.0.tgz#2e6ff439aa49b2ea8952262f30ae6b70c02467d3" - integrity sha512-cYxxKXhYfI3S9+CA84HmrJa9B88H56V5FQ302iFF2TNwJukJCNoU8FgWt+11YtwKFXRkQQFpepC2QOF7aDq2Ow== +standard-version@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/standard-version/-/standard-version-9.3.1.tgz#786c16c318847f58a31a2434f97e8db33a635853" + integrity sha512-5qMxXw/FxLouC5nANyx/5RY1kiorJx9BppUso8gN07MG64q2uLRmrPb4KfXp3Ql4s/gxjZwZ89e0FwxeLubGww== dependencies: chalk "^2.4.2" conventional-changelog "3.1.24" From 524e532a7bc525346ea510d237130964e1f883e7 Mon Sep 17 00:00:00 2001 From: Jordie Bodlay Date: Thu, 22 Jul 2021 02:10:01 +1000 Subject: [PATCH 02/27] chore(rds): add AuroraPostgresEngineVersion 12.6 (#15686) Version 12.6 of the Aurora PostgreSQL cluster engine has been released as per https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Updates.20180305.html Screen Shot 2021-07-21 at 6 39 16 pm Can also be seen via aws CLI: ![Screen Shot 2021-07-21 at 6 38 17 pm](https://user-images.githubusercontent.com/712360/126459517-176804ac-5d72-47e6-8de7-e669e359650c.png) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-rds/lib/cluster-engine.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts index 96346bcb9e03f..2a9c3b378e1fd 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts @@ -459,6 +459,8 @@ export class AuroraPostgresEngineVersion { public static readonly VER_11_9 = AuroraPostgresEngineVersion.of('11.9', '11', { s3Import: true, s3Export: true }); /** Version "12.4". */ public static readonly VER_12_4 = AuroraPostgresEngineVersion.of('12.4', '12', { s3Import: true, s3Export: true }); + /** Version "12.6". */ + public static readonly VER_12_6 = AuroraPostgresEngineVersion.of('12.6', '12', { s3Import: true, s3Export: true }); /** * Create a new AuroraPostgresEngineVersion with an arbitrary version. From 56f3275af26b6bfbcbb65241151d8b9d092d1ce2 Mon Sep 17 00:00:00 2001 From: Jerry Kindall <52084730+Jerry-AWS@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:18:08 -0700 Subject: [PATCH 03/27] docs(appmesh): Suggest improvements for App Mesh readme (#15506) Did a quick review of this README and made some generally minor suggestions. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-appmesh/README.md | 129 +++++++++++------------- 1 file changed, 61 insertions(+), 68 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 6354289442d16..1b04f9dd93cb9 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -17,7 +17,7 @@ App Mesh gives you consistent visibility and network traffic controls for every App Mesh supports microservice applications that use service discovery naming for their components. To use App Mesh, you must have an existing application running on AWS Fargate, Amazon ECS, Amazon EKS, Kubernetes on AWS, or Amazon EC2. -For further information on **AWS AppMesh** visit the [AWS Docs for AppMesh](https://docs.aws.amazon.com/app-mesh/index.html). +For further information on **AWS App Mesh**, visit the [AWS App Mesh Documentation](https://docs.aws.amazon.com/app-mesh/index.html). ## Create the App and Stack @@ -32,7 +32,7 @@ A service mesh is a logical boundary for network traffic between the services th After you create your service mesh, you can create virtual services, virtual nodes, virtual routers, and routes to distribute traffic between the applications in your mesh. -The following example creates the `AppMesh` service mesh with the default filter of `DROP_ALL`, see [docs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appmesh-mesh-egressfilter.html) here for more info on egress filters. +The following example creates the `AppMesh` service mesh with the default egress filter of `DROP_ALL`. See [the AWS CloudFormation `EgressFilter` resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appmesh-mesh-egressfilter.html) for more info on egress filters. ```ts const mesh = new Mesh(stack, 'AppMesh', { @@ -40,7 +40,7 @@ const mesh = new Mesh(stack, 'AppMesh', { }); ``` -The mesh can also be created with the "ALLOW_ALL" egress filter by overwritting the property. +The mesh can instead be created with the `ALLOW_ALL` egress filter by providing the `egressFilter` property. ```ts const mesh = new Mesh(stack, 'AppMesh', { @@ -51,7 +51,7 @@ const mesh = new Mesh(stack, 'AppMesh', { ## Adding VirtualRouters -The _Mesh_ needs _VirtualRouters_ as logical units to route requests to _VirtualNodes_. +A _mesh_ uses _virtual routers_ as logical units to route requests to _virtual nodes_. Virtual routers handle traffic for one or more virtual services within your mesh. After you create a virtual router, you can create and associate routes to your virtual router that direct incoming requests to different virtual nodes. @@ -62,12 +62,10 @@ const router = mesh.addVirtualRouter('router', { }); ``` -Note that creating the router using the `addVirtualRouter()` method places it in the same Stack that the mesh belongs to -(which might be different from the current Stack). -The router can also be created using the constructor of `VirtualRouter` and passing in the mesh instead of calling the `addVirtualRouter()` method. -This is particularly useful when splitting your resources between many Stacks, -like creating the `mesh` as part of an infrastructure stack, -but the other resources, such as routers, in the application stack: +Note that creating the router using the `addVirtualRouter()` method places it in the same stack as the mesh +(which might be different from the current stack). +The router can also be created using the `VirtualRouter` constructor (passing in the mesh) instead of calling the `addVirtualRouter()` method. +This is particularly useful when splitting your resources between many stacks: for example, defining the mesh itself as part of an infrastructure stack, but defining the other resources, such as routers, in the application stack: ```ts const mesh = new Mesh(infraStack, 'AppMesh', { @@ -83,23 +81,23 @@ const router = new VirtualRouter(appStack, 'router', { }); ``` -The same is true for other `add*()` methods in the AppMesh library. +The same is true for other `add*()` methods in the App Mesh construct library. -The _VirtualRouterListener_ class provides an easy interface for defining new protocol specific listeners. -The `http()`, `http2()`, `grpc()` and `tcp()` methods are available for use. -They accept a single port parameter, that is used to define what port to match requests on. -The port parameter can be omitted, and it will default to port 8080. +The `VirtualRouterListener` class lets you define protocol-specific listeners. +The `http()`, `http2()`, `grpc()` and `tcp()` methods create listeners for the named protocols. +They accept a single parameter that defines the port to on which requests will be matched. +The port parameter defaults to 8080 if omitted. -## Adding VirtualService +## Adding a VirtualService -A virtual service is an abstraction of a real service that is provided by a virtual node directly or indirectly by means of a virtual router. Dependent services call your virtual service by its virtualServiceName, and those requests are routed to the virtual node or virtual router that is specified as the provider for the virtual service. +A _virtual service_ is an abstraction of a real service that is provided by a virtual node directly, or indirectly by means of a virtual router. Dependent services call your virtual service by its `virtualServiceName`, and those requests are routed to the virtual node or virtual router specified as the provider for the virtual service. We recommend that you use the service discovery name of the real service that you're targeting (such as `my-service.default.svc.cluster.local`). When creating a virtual service: -- If you want the virtual service to spread traffic across multiple virtual nodes, specify a Virtual router. -- If you want the virtual service to reach a virtual node directly, without a virtual router, specify a Virtual node. +- If you want the virtual service to spread traffic across multiple virtual nodes, specify a virtual router. +- If you want the virtual service to reach a virtual node directly, without a virtual router, specify a virtual node. Adding a virtual router as the provider: @@ -121,14 +119,14 @@ new appmesh.VirtualService('virtual-service', { ## Adding a VirtualNode -A `virtual node` acts as a logical pointer to a particular task group, such as an Amazon ECS service or a Kubernetes deployment. +A _virtual node_ acts as a logical pointer to a particular task group, such as an Amazon ECS service or a Kubernetes deployment. -When you create a `virtual node`, any inbound traffic that your `virtual node` expects should be specified as a listener. Any outbound traffic that your `virtual node` expects to reach should be specified as a backend. +When you create a virtual node, accept inbound traffic by specifying a *listener*. Outbound traffic that your virtual node expects to send should be specified as a *back end*. -The response metadata for your new `virtual node` contains the Amazon Resource Name (ARN) that is associated with the `virtual node`. Set this value (either the full ARN or the truncated resource name) as the APPMESH_VIRTUAL_NODE_NAME environment variable for your task group's Envoy proxy container in your task definition or pod spec. For example, the value could be mesh/default/virtualNode/simpleapp. This is then mapped to the node.id and node.cluster Envoy parameters. +The response metadata for your new virtual node contains the Amazon Resource Name (ARN) that is associated with the virtual node. Set this value (either the full ARN or the truncated resource name) as the `APPMESH_VIRTUAL_NODE_NAME` environment variable for your task group's Envoy proxy container in your task definition or pod spec. For example, the value could be `mesh/default/virtualNode/simpleapp`. This is then mapped to the `node.id` and `node.cluster` Envoy parameters. -> Note -> If you require your Envoy stats or tracing to use a different name, you can override the node.cluster value that is set by APPMESH_VIRTUAL_NODE_NAME with the APPMESH_VIRTUAL_NODE_CLUSTER environment variable. +> **Note** +> If you require your Envoy stats or tracing to use a different name, you can override the `node.cluster` value that is set by `APPMESH_VIRTUAL_NODE_NAME` with the `APPMESH_VIRTUAL_NODE_CLUSTER` environment variable. ```ts const vpc = new ec2.Vpc(stack, 'vpc'); @@ -216,22 +214,22 @@ const virtualService = new appmesh.VirtualService(stack, 'service-1', { node.addBackend(appmesh.Backend.virtualService(virtualService)); ``` -The `listeners` property can be left blank and added later with the `node.addListener()` method. The `healthcheck` and `timeout` properties are optional but if specifying a listener, the `port` must be added. +The `listeners` property can be left blank and added later with the `node.addListener()` method. The `serviceDiscovery` property must be specified when specifying a listener. -The `backends` property can be added with `node.addBackend()`. We define a virtual service and add it to the virtual node to allow egress traffic to other node. +The `backends` property can be added with `node.addBackend()`. In the example, we define a virtual service and add it to the virtual node to allow egress traffic to other nodes. -The `backendDefaults` property are added to the node while creating the virtual node. These are virtual node's default settings for all backends. +The `backendDefaults` property is added to the node while creating the virtual node. These are the virtual node's default settings for all backends. ## Adding TLS to a listener -The `tls` property can be provided when creating a Virtual Node listener, or a Virtual Gateway listener to add TLS configuration. -App Mesh allows you to provide the TLS certificate to the proxy in the following ways: +The `tls` property specifies TLS configuration when creating a listener for a virtual node or a virtual gateway. +Provide the TLS certificate to the proxy in one of the following ways: -- A certificate from AWS Certificate Manager (ACM) can be used. +- A certificate from AWS Certificate Manager (ACM). -- A customer provided certificate can be specified with a `certificateChain` path file and a `privateKey` file path. +- A customer-provided certificate (specify a `certificateChain` path file and a `privateKey` file path). -- A certificate provided by a Secrets Discovery Service (SDS) endpoint over local Unix Domain Socket can be specified with its `secretName`. +- A certificate provided by a Secrets Discovery Service (SDS) endpoint over local Unix Domain Socket (specify its `secretName`). ```typescript import * as certificatemanager from '@aws-cdk/aws-certificatemanager'; @@ -281,16 +279,16 @@ const gateway2 = new appmesh.VirtualGateway(this, 'gateway2', { ## Adding mutual TLS authentication Mutual TLS authentication is an optional component of TLS that offers two-way peer authentication. -To enable mutual TLS authentication, -add `mutualTlsCertificate` property to TLS Client Policy and/or `mutualTlsValidation` property to TLS Listener. +To enable mutual TLS authentication, add the `mutualTlsCertificate` property to TLS client policy and/or the `mutualTlsValidation` property to your TLS listener. `tls.mutualTlsValidation` and `tlsClientPolicy.mutualTlsCertificate` can be sourced from either: -- A customer provided certificate can be specified with a `certificateChain` path file and a `privateKey` file path. +- A customer-provided certificate (specify a `certificateChain` path file and a `privateKey` file path). -- A certificate provided by a Secrets Discovery Service (SDS) endpoint over local Unix Domain Socket can be specified with its `secretName`. +- A certificate provided by a Secrets Discovery Service (SDS) endpoint over local Unix Domain Socket (specify its `secretName`). -**Note**: Currently, a certificate from AWS Certificate Manager (ACM) cannot be sourced for above two properties. +> **Note** +> Currently, a certificate from AWS Certificate Manager (ACM) cannot be used for mutual TLS authentication. ```typescript import * as certificatemanager from '@aws-cdk/aws-certificatemanager'; @@ -331,8 +329,8 @@ const node2 = new appmesh.VirtualNode(stack, 'node2', { ## Adding outlier detection to a Virtual Node listener -The `outlierDetection` property can be added to a Virtual Node listener to add outlier detection. The 4 parameters -(`baseEjectionDuration`, `interval`, `maxEjectionPercent`, `maxServerErrors`) are required. +The `outlierDetection` property adds outlier detection to a Virtual Node listener. The properties +`baseEjectionDuration`, `interval`, `maxEjectionPercent`, and `maxServerErrors` are required. ```typescript // Cloud Map service discovery is currently required for host ejection by outlier detection @@ -356,8 +354,7 @@ const node = mesh.addVirtualNode('virtual-node', { ## Adding a connection pool to a listener -The `connectionPool` property can be added to a Virtual Node listener or Virtual Gateway listener to add a request connection pool. There are different -connection pool properties per listener protocol types. +The `connectionPool` property can be added to a Virtual Node listener or Virtual Gateway listener to add a request connection pool. Each listener protocol type has its own connection pool properties. ```typescript // A Virtual Node with a gRPC listener with a connection pool set @@ -392,19 +389,17 @@ const gateway = new appmesh.VirtualGateway(this, 'gateway', { ## Adding a Route -A `route` is associated with a virtual router, and it's used to match requests for a virtual router and distribute traffic accordingly to its associated virtual nodes. +A _route_ matches requests with an associated virtual router and distributes traffic to its associated virtual nodes. +The route distributes matching requests to one or more target virtual nodes with relative weighting. -If your `route` matches a request, you can distribute traffic to one or more target virtual nodes with relative weighting. +The `RouteSpec` class lets you define protocol-specific route specifications. +The `tcp()`, `http()`, `http2()`, and `grpc()` methods create a specification for the named protocols. -The _RouteSpec_ class provides an easy interface for defining new protocol specific route specs. -The `tcp()`, `http()`, `http2()`, and `grpc()` methods provide the spec necessary to define a protocol specific spec. +For HTTP-based routes, the match field can match on path (prefix, exact, or regex), HTTP method, scheme, +HTTP headers, and query parameters. By default, HTTP-based routes match all requests. -For HTTP based routes, the match field can be used to match on -path (prefix, exact, or regex), HTTP method, scheme, HTTP headers, and query parameters. -By default, an HTTP based route will match all requests. - -For gRPC based routes, the match field can be used to match on service name, method name, and metadata. -When specifying the method name, service name must also be specified. +For gRPC-based routes, the match field can match on service name, method name, and metadata. +When specifying the method name, the service name must also be specified. For example, here's how to add an HTTP route that matches based on a prefix of the URL path: @@ -452,7 +447,7 @@ router.addRoute('route-http2', { }); ``` -Add a single route with multiple targets and split traffic 50/50 +Add a single route with two targets and split traffic 50/50: ```ts router.addRoute('route-http', { @@ -538,7 +533,7 @@ router.addRoute('route-grpc-retry', { }); ``` -Add a gRPC route with time out: +Add a gRPC route with timeout: ```ts router.addRoute('route-http', { @@ -561,12 +556,12 @@ router.addRoute('route-http', { ## Adding a Virtual Gateway -A _virtual gateway_ allows resources outside your mesh to communicate to resources that are inside your mesh. +A _virtual gateway_ allows resources outside your mesh to communicate with resources inside your mesh. The virtual gateway represents an Envoy proxy running in an Amazon ECS task, in a Kubernetes service, or on an Amazon EC2 instance. -Unlike a virtual node, which represents an Envoy running with an application, a virtual gateway represents Envoy deployed by itself. +Unlike a virtual node, which represents Envoy running with an application, a virtual gateway represents Envoy deployed by itself. A virtual gateway is similar to a virtual node in that it has a listener that accepts traffic for a particular port and protocol (HTTP, HTTP2, GRPC). -The traffic that the virtual gateway receives, is directed to other services in your mesh, +Traffic received by the virtual gateway is directed to other services in your mesh using rules defined in gateway routes which can be added to your virtual gateway. Create a virtual gateway with the constructor: @@ -611,19 +606,18 @@ const gateway = mesh.addVirtualGateway('gateway', { }); ``` -The listeners field can be omitted which will default to an HTTP Listener on port 8080. +The `listeners` field defaults to an HTTP Listener on port 8080 if omitted. A gateway route can be added using the `gateway.addGatewayRoute()` method. -The `backendDefaults` property is added to the node while creating the virtual gateway. These are virtual gateway's default settings for all backends. +The `backendDefaults` property, provided when creating the virtual gateway, specifies the virtual gateway's default settings for all backends. ## Adding a Gateway Route -A _gateway route_ is attached to a virtual gateway and routes traffic to an existing virtual service. -If a route matches a request, it can distribute traffic to a target virtual service. +A _gateway route_ is attached to a virtual gateway and routes matching traffic to an existing virtual service. -For HTTP based gateway routes, the match field can be used to match on +For HTTP-based gateway routes, the `match` field can be used to match on path (prefix, exact, or regex), HTTP method, host name, HTTP headers, and query parameters. -By default, an HTTP based route will match all requests. +By default, HTTP-based gateway routes match all requests. ```ts gateway.addGatewayRoute('gateway-route-http', { @@ -636,7 +630,7 @@ gateway.addGatewayRoute('gateway-route-http', { }); ``` -For gRPC based gateway routes, the match field can be used to match on service name, host name, and metadata. +For gRPC-based gateway routes, the `match` field can be used to match on service name, host name, and metadata. ```ts gateway.addGatewayRoute('gateway-route-grpc', { @@ -709,9 +703,8 @@ gateway.addGatewayRoute('gateway-route-grpc', { ## Importing Resources -Each mesh resource comes with two static methods for importing a reference to an existing App Mesh resource. -These imported resources can be used as references for other resources in your mesh. -There are two static methods, `fromArn` and `fromAttributes` where the `` is replaced with the resource name. +Each App Mesh resource class comes with two static methods, `fromArn` and `fromAttributes` (where `` is replaced with the resource name, such as `VirtualNode`) for importing a reference to an existing App Mesh resource. +These imported resources can be used with other resources in your mesh as if they were defined directly in your CDK application. ```ts const arn = "arn:aws:appmesh:us-east-1:123456789012:mesh/testMesh/virtualNode/testNode"; @@ -725,7 +718,7 @@ appmesh.VirtualNode.fromVirtualNodeAttributes(stack, 'imported-virtual-node', { }); ``` -To import a mesh, there are two static methods, `fromMeshArn` and `fromMeshName`. +To import a mesh, again there are two static methods, `fromMeshArn` and `fromMeshName`. ```ts const arn = 'arn:aws:appmesh:us-east-1:123456789012:mesh/testMesh'; @@ -738,7 +731,7 @@ appmesh.Mesh.fromMeshName(stack, 'imported-mesh', 'abc'); ## IAM Grants -Virtual Node and Virtual Gateway implement `grantStreamAggregatedResources` that will grant identities that are running +`VirtualNode` and `VirtualGateway` provide `grantStreamAggregatedResources` methods that grant identities that are running Envoy access to stream generated config from App Mesh. ```ts From 2e4cfaeb8612179c79e293ba52a8afcdcfd6ef52 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 22 Jul 2021 11:58:19 +0200 Subject: [PATCH 04/27] feat(pipelines): CDK Pipelines is now Generally Available (#15667) Switch the CDK Pipelines API to stable and GA. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/README.md | 8 +------- packages/@aws-cdk/pipelines/package.json | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index 83e53348a04e2..8eb49268b2a24 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -3,13 +3,7 @@ --- -![cdk-constructs: Developer Preview](https://img.shields.io/badge/cdk--constructs-developer--preview-informational.svg?style=for-the-badge) - -> The APIs of higher level constructs in this module are in **developer preview** before they -> become stable. We will only make breaking changes to address unforeseen API issues. Therefore, -> these APIs are not subject to [Semantic Versioning](https://semver.org/), and breaking changes -> will be announced in release notes. This means that while you may use them, you may need to -> update your source code when upgrading to a newer version of this package. +![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge) --- diff --git a/packages/@aws-cdk/pipelines/package.json b/packages/@aws-cdk/pipelines/package.json index 25c9099e64074..5edf5a75d7f85 100644 --- a/packages/@aws-cdk/pipelines/package.json +++ b/packages/@aws-cdk/pipelines/package.json @@ -95,8 +95,8 @@ "node": ">= 10.13.0 <13 || >=13.7.0" }, "license": "Apache-2.0", - "stability": "experimental", - "maturity": "developer-preview", + "stability": "stable", + "maturity": "stable", "cdk-build": { "jest": true, "env": { From 9022d6f6ebb1335e67747dfb4034dce0c81b5d27 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 22 Jul 2021 12:37:34 +0200 Subject: [PATCH 05/27] docs(pipelines): add migration guide (#15696) Add a migration guide for original to modern API. [Rendered version](https://github.com/aws/aws-cdk/blob/huijbers/pipelines-migration-guide/packages/@aws-cdk/pipelines/ORIGINAL_API.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/ORIGINAL_API.md | 183 +++++++++++++++++++- packages/@aws-cdk/pipelines/README.md | 72 +++++++- 2 files changed, 245 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/pipelines/ORIGINAL_API.md b/packages/@aws-cdk/pipelines/ORIGINAL_API.md index d46acb44af989..14152b5b7f6d6 100644 --- a/packages/@aws-cdk/pipelines/ORIGINAL_API.md +++ b/packages/@aws-cdk/pipelines/ORIGINAL_API.md @@ -2,8 +2,185 @@ This document describes the API the CDK Pipelines library originally went into Developer Preview with. The API has since been reworked, but the original one -left in place because of popular uptake. The original API still works and is -still supported, but the revised one is preferred for future projects. +left in place because of popular adoption. The original API still works and is +still supported, but the revised one is preferred for future projects as it +is more flexible and abstracts more unnecessary details from the user. + +## Migrating from the original to the modern API + +It's possible to migrate a pipeline in-place from the original to the modern API. +The changes necessary are the following: + +### The Pipeline + +Replace `new CdkPipeline` with `new CodePipeline`. Some +configuration properties have been changed: + +| Old API | New API | +|--------------------------------+------------------------------------------------------------------------------------------------| +| `cloudAssemblyArtifact` | removed | +| `sourceAction` | removed | +| `synthAction` | `synth` | +| `crossAccountKeys` | new default is `false`; specify `crossAccountKeys: true` if you need cross-account deployments | +| `cdkCliVersion` | `cliVersion` | +| `selfMutating` | `selfMutation` | +| `vpc`, `subnetSelection` | `codeBuildDefaults.vpc`, codeBuildDefaults.subnetSelection` | +| `selfMutationBuildSpec` | `selfMutationCodeBuildDefaults.partialBuildSpec` | +| `assetBuildSpec` | `assetPublishingCodeBuildDefaults.partialBuildSpec` | +| `assetPreinstallCommands` | use `assetPublishingCodeBuildDefaults.partialBuildSpec` instead | +| `singlePublisherPerType: true` | `publishAssetsInParallel: false` | +| `supportDockerAssets` | `dockerEnabledForSelfMutation` | + +### The synth + +As the argument to `synth`, use `new ShellStep` or `new CodeBuildStep`, +depending on whether or not you want to customize the AWS CodeBuild Project that gets generated. + +Contrary to `SimpleSynthAction.standardNpmSynth`, you need to specify +all commands necessary to do a full CDK build and synth, so do include +installing dependencies and running the CDK CLI. For example, the old API: + +```ts +SimpleSynthAction.standardNpmSynth({ + sourceArtifact, + cloudAssemblyArtifact, + + // Use this if you need a build step (if you're not using ts-node + // or if you have TypeScript Lambdas that need to be compiled). + buildCommand: 'npm run build', +}), +``` + +Becomes: + +```ts +new ShellStep('Synth', { + input: /* source */, + commands: [ + 'npm ci', + 'npm run build', + 'npx cdk synth', + ], +}); +``` + +Instead of specifying the pipeline source with the `sourceAction` property to +the pipeline, specify it as the `input` property to the `ShellStep` instead. +You can use any of the factory functions on `CodePipelineSource`. + +For example, for a GitHub source, the following old API: + +```ts +sourceAction: new codepipeline_actions.GitHubSourceAction({ + actionName: 'GitHub', + output: sourceArtifact, + // Replace these with your actual GitHub project name + owner: 'OWNER', + repo: 'REPO', + branch: 'main', // default: 'master' +}), +``` + +Translates into: + +```ts +input: CodePipelineSource.gitHub('OWNER/REPO', 'main', { + authentication: SecretValue.secretsManager('GITHUB_TOKEN_NAME'), +}), +``` + +### Deployments + +Adding CDK Stages to deploy is done by calling `addStage()`, or +potentially `addWave().addStage()`. All stages inside a wave are +deployed in parallel, which was not a capability of the original API. + +| Old API | New API | +|-------------------------------+-------------------------------------------------------------------------------------------------------------------------------| +| `addApplicationStage()` | `addStage()` | +| `addStage().addApplication()` | `addStage()`. Adding multiple CDK Stages into a single Pipeline stage is not supported, add multiple Pipeline stages instead. | + +### Approvals + +Approvals are added by adding `pre` and `post` options to `addStage()`, with +steps to execute before and after the deployments, respectively. We recommend +putting manual approvals in `pre` steps, and automated approvals in `post` steps. + +#### Manual approvals + +For example, specifying a manual approval on a stage deployment in old API: + +```ts +const stage = pipeline.addApplicationStage(...); +stage.addAction(new ManualApprovalAction({ + actionName: 'ManualApproval', + runOrder: testingStage.nextSequentialRunOrder(), +})); +``` + +Becomes: + +```ts +pipeline.addStage(..., { + pre: [ + new ManualApprovalStep('ManualApproval'), + ], +}); +``` + +Note that this we've used `pre` to put the manual approval *before* a Stage +deployment (this was not possible in the old API). Be sure to put the manual +approval in the `pre` steps list of the *next* Stage to keep +it in the same location in the pipeline. + +#### Automated approvals + +For example, specifying an automated approval after a stage is deployed in the following old API: + +```ts +const stage = pipeline.addApplicationStage(...); +stage.addActions(new ShellScriptAction({ + actionName: 'MyValidation', + commands: ['curl -Ssf $VAR'], + useOutputs: { + VAR: pipeline.stackOutput(stage.cfnOutput), + }, + // Optionally specify a BuildEnvironment + environment: { ... }, +})); +``` + +Becomes: + +```ts +const stage = new MyStage(...); +pipeline.addStage(stage, { + post: [ + new CodeBuildStep('MyValidation', { + commands: ['curl -Ssf $VAR'], + envFromCfnOutput: { + VAR: stage.cfnOutput, + }, + // Optionally specify a BuildEnvironment + buildEnvironment: { ... }, + }), + ], +}); +``` + +You can also use `ShellStep` if you don't need any of the CodeBuild Project +customizations (like `buildEnvironment`). + +#### Change set approvals + +In the old API, there were two properties that were used to add actions to the pipeline +in between the `CreateChangeSet` and `ExecuteChangeSet` actions: `manualApprovals` and `extraRunOrderSpace`. These are not supported in the new API. + +### Custom CodePipeline Actions + +See the section [**Arbitrary CodePipeline actions** in the +main `README`](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/pipelines/README.md#arbitrary-codepipeline-actions) for an example of how to inject arbitrary +CodeBuild Actions. ## Definining the pipeline @@ -551,4 +728,4 @@ const stage = pipeline.addApplicationStage(new MyApplication(this, 'PreProd'), { ``` **Note**: Manual Approvals notifications only apply when an application has security -check enabled. \ No newline at end of file +check enabled. diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index 8eb49268b2a24..103715704ec62 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -22,7 +22,7 @@ to the new version if possible. > allows more control of CodeBuild project generation; supports deployment > engines other than CodePipeline. > -> The README for the original API can be found in [our GitHub repository](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/pipelines/ORIGINAL_API.md). +> The README for the original API, as well as a migration guide, can be found in [our GitHub repository](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/pipelines/ORIGINAL_API.md). ## At a glance @@ -198,10 +198,10 @@ expected to produce the CDK Cloud Assembly as its single output (the contents of the `cdk.out` directory after running `cdk synth`). "Steps" are arbitrary actions in the pipeline, typically used to run scripts or commands. -For the synth, use a `ShellStep` and specify the commands necessary to build -your project and run `cdk synth`; the specific commands required will depend on -the programming language you are using. For a typical NPM-based project, the synth -will look like this: +For the synth, use a `ShellStep` and specify the commands necessary to install +dependencies, the CDK CLI, build your project and run `cdk synth`; the specific +commands required will depend on the programming language you are using. For a +typical NPM-based project, the synth will look like this: ```ts const source = /* the repository source */; @@ -244,6 +244,63 @@ earlier by calling `pipeline.buildPipeline()`. After you've called that method, you can inspect the constructs that were produced by accessing the properties of the `pipeline` object. +#### Commands for other languages and package managers + +The commands you pass to `new ShellStep` will be very similar to the commands +you run on your own workstation to install dependencies and synth your CDK +project. Here are some (non-exhaustive) examples for what those commands might +look like in a number of different situations. + +For Yarn, the install commands are different: + +```ts +const pipeline = new CodePipeline(this, 'Pipeline', { + synth: new ShellStep('Synth', { + input: source, + commands: [ + 'yarn install --frozen-lockfile', + 'yarn build', + 'npx cdk synth', + ], + }) +}); +``` + +For Python projects, remember to install the CDK CLI globally (as +there is no `package.json` to automatically install it for you): + +```ts +const pipeline = new CodePipeline(this, 'Pipeline', { + synth: new ShellStep('Synth', { + input: source, + commands: [ + 'pip install -r requirements.txt', + 'npm install -g aws-cdk', + 'cdk synth', + ], + }) +}); +``` + +For Java projects, remember to install the CDK CLI globally (as +there is no `package.json` to automatically install it for you), +and the Maven compilation step is automatically executed for you +as you run `cdk synth`: + +```ts +const pipeline = new CodePipeline(this, 'Pipeline', { + synth: new ShellStep('Synth', { + input: source, + commands: [ + 'npm install -g aws-cdk', + 'cdk synth', + ], + }) +}); +``` + +You can adapt these examples to your own situation. + #### CodePipeline Sources In CodePipeline, *Sources* define where the source of your application lives. @@ -399,7 +456,8 @@ const pipeline = new CodePipeline(this, 'Pipeline', { Every `addStage()` and `addWave()` command takes additional options. As part of these options, you can specify `pre` and `post` steps, which are arbitrary steps that run before or after the contents of the stage or wave, respectively. You can use these to add validations like -manual or automated gates to your pipeline. +manual or automated gates to your pipeline. We recommend putting manual approval gates in the set of `pre` steps, and automated approval gates in +the set of `post` steps. The following example shows both an automated approval in the form of a `ShellStep`, and a manual approvel in the form of a `ManualApprovalStep` added to the pipeline. Both must @@ -566,7 +624,7 @@ If you want to add a type of CodePipeline action to the CDK Pipeline that doesn't have a matching class yet, you can define your own step class that extends `Step` and implements `ICodePipelineActionFactory`. -Here's a simple example that adds a Jenkins step: +Here's an example that adds a Jenkins step: ```ts class MyJenkinsStep extends Step implements ICodePipelineActionFactory { From 2c4ef0131893e77d373c52b41c62d31847023446 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Thu, 22 Jul 2021 20:32:13 +0300 Subject: [PATCH 06/27] feat(cfnspec): cloudformation spec v39.7.0 (#15719) Co-authored-by: AWS CDK Team --- .../aws-lookoutequipment/.eslintrc.js | 3 + .../@aws-cdk/aws-lookoutequipment/.gitignore | 19 + .../@aws-cdk/aws-lookoutequipment/.npmignore | 29 ++ .../@aws-cdk/aws-lookoutequipment/LICENSE | 201 +++++++++ packages/@aws-cdk/aws-lookoutequipment/NOTICE | 2 + .../@aws-cdk/aws-lookoutequipment/README.md | 20 + .../aws-lookoutequipment/jest.config.js | 2 + .../aws-lookoutequipment/lib/index.ts | 2 + .../aws-lookoutequipment/package.json | 103 +++++ .../test/lookoutequipment.test.ts | 6 + packages/@aws-cdk/cfnspec/CHANGELOG.md | 78 ++++ packages/@aws-cdk/cfnspec/cfn.version | 2 +- ...0_CloudFormationResourceSpecification.json | 388 ++++++++++++++++-- .../cfn-lint/StatefulResources/000.json | 1 + .../cloudformation-include/package.json | 2 + packages/aws-cdk-lib/package.json | 1 + packages/decdk/package.json | 1 + packages/monocdk/package.json | 1 + .../rosetta/portfolio-product.ts-fixture | 28 ++ 19 files changed, 860 insertions(+), 29 deletions(-) create mode 100644 packages/@aws-cdk/aws-lookoutequipment/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-lookoutequipment/.gitignore create mode 100644 packages/@aws-cdk/aws-lookoutequipment/.npmignore create mode 100644 packages/@aws-cdk/aws-lookoutequipment/LICENSE create mode 100644 packages/@aws-cdk/aws-lookoutequipment/NOTICE create mode 100644 packages/@aws-cdk/aws-lookoutequipment/README.md create mode 100644 packages/@aws-cdk/aws-lookoutequipment/jest.config.js create mode 100644 packages/@aws-cdk/aws-lookoutequipment/lib/index.ts create mode 100644 packages/@aws-cdk/aws-lookoutequipment/package.json create mode 100644 packages/@aws-cdk/aws-lookoutequipment/test/lookoutequipment.test.ts create mode 100644 packages/monocdk/rosetta/portfolio-product.ts-fixture diff --git a/packages/@aws-cdk/aws-lookoutequipment/.eslintrc.js b/packages/@aws-cdk/aws-lookoutequipment/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-lookoutequipment/.gitignore b/packages/@aws-cdk/aws-lookoutequipment/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-lookoutequipment/.npmignore b/packages/@aws-cdk/aws-lookoutequipment/.npmignore new file mode 100644 index 0000000000000..f931fede67c44 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/.npmignore @@ -0,0 +1,29 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ +!*.lit.ts diff --git a/packages/@aws-cdk/aws-lookoutequipment/LICENSE b/packages/@aws-cdk/aws-lookoutequipment/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-lookoutequipment/NOTICE b/packages/@aws-cdk/aws-lookoutequipment/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-lookoutequipment/README.md b/packages/@aws-cdk/aws-lookoutequipment/README.md new file mode 100644 index 0000000000000..9985449bd1ea8 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/README.md @@ -0,0 +1,20 @@ +# AWS::LookoutEquipment Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import lookoutequipment = require('@aws-cdk/aws-lookoutequipment'); +``` diff --git a/packages/@aws-cdk/aws-lookoutequipment/jest.config.js b/packages/@aws-cdk/aws-lookoutequipment/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-lookoutequipment/lib/index.ts b/packages/@aws-cdk/aws-lookoutequipment/lib/index.ts new file mode 100644 index 0000000000000..c90623c23ff71 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::LookoutEquipment CloudFormation Resources: +export * from './lookoutequipment.generated'; diff --git a/packages/@aws-cdk/aws-lookoutequipment/package.json b/packages/@aws-cdk/aws-lookoutequipment/package.json new file mode 100644 index 0000000000000..e34187a2c7e57 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/package.json @@ -0,0 +1,103 @@ +{ + "name": "@aws-cdk/aws-lookoutequipment", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::LookoutEquipment", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.LookoutEquipment", + "packageId": "Amazon.CDK.AWS.LookoutEquipment", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.lookoutequipment", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "lookoutequipment" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-lookoutequipment", + "module": "aws_cdk.aws_lookoutequipment" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-lookoutequipment" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract", + "build+extract": "yarn build && yarn rosetta:extract", + "build+test+extract": "yarn build+test && yarn rosetta:extract" + }, + "cdk-build": { + "cloudformation": "AWS::LookoutEquipment", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::LookoutEquipment", + "aws-lookoutequipment" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-lookoutequipment/test/lookoutequipment.test.ts b/packages/@aws-cdk/aws-lookoutequipment/test/lookoutequipment.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutequipment/test/lookoutequipment.test.ts @@ -0,0 +1,6 @@ +import '@aws-cdk/assert-internal/jest'; +import {} from '../lib'; + +test('No tests are specified for this package', () => { + expect(true).toBe(true); +}); diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 794305b7dd323..2e7ba5e22bf56 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,81 @@ +# CloudFormation Resource Specification v39.7.0 + +## New Resource Types + +* AWS::Logs::ResourcePolicy +* AWS::LookoutEquipment::InferenceScheduler + +## Attribute Changes + +* AWS::Amplify::Domain AutoSubDomainCreationPatterns.DuplicatesAllowed (__added__) +* AWS::S3::AccessPoint Arn (__added__) +* AWS::S3::AccessPoint Name (__added__) + +## Property Changes + +* AWS::Amplify::Domain AutoSubDomainCreationPatterns.DuplicatesAllowed (__added__) +* AWS::Amplify::Domain SubDomainSettings.DuplicatesAllowed (__added__) +* AWS::Cassandra::Table EncryptionSpecification (__added__) +* AWS::EC2::TransitGateway AssociationDefaultRouteTableId (__added__) +* AWS::EC2::TransitGateway PropagationDefaultRouteTableId (__added__) +* AWS::EC2::TransitGateway TransitGatewayCidrBlocks (__added__) +* AWS::EC2::TransitGateway AutoAcceptSharedAttachments.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::TransitGateway DefaultRouteTableAssociation.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::TransitGateway DefaultRouteTablePropagation.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::TransitGateway Description.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::TransitGateway DnsSupport.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::TransitGateway Tags.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::TransitGateway VpnEcmpSupport.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::EC2::VPCCidrBlock Ipv6CidrBlock (__added__) +* AWS::EC2::VPCCidrBlock Ipv6Pool (__added__) +* AWS::Glue::Crawler RecrawlPolicy (__added__) +* AWS::ImageBuilder::ContainerRecipe InstanceConfiguration.PrimitiveType (__deleted__) +* AWS::ImageBuilder::ImageRecipe AdditionalInstanceConfiguration (__added__) +* AWS::LookoutMetrics::Alert Action.PrimitiveType (__deleted__) +* AWS::LookoutMetrics::Alert Action.Type (__added__) +* AWS::LookoutMetrics::AnomalyDetector AnomalyDetectorConfig.PrimitiveType (__deleted__) +* AWS::LookoutMetrics::AnomalyDetector AnomalyDetectorConfig.Type (__added__) +* AWS::S3::AccessPoint Name (__deleted__) +* AWS::S3::AccessPoint PolicyStatus (__added__) +* AWS::SSMIncidents::ReplicationSet Regions.DuplicatesAllowed (__added__) + +## Property Type Changes + +* AWS::Cassandra::Table.EncryptionSpecification (__added__) +* AWS::Glue::Crawler.RecrawlPolicy (__added__) +* AWS::Glue::Database.DataLakePrincipal (__added__) +* AWS::Glue::Database.PrincipalPrivileges (__added__) +* AWS::ImageBuilder::ImageRecipe.AdditionalInstanceConfiguration (__added__) +* AWS::ImageBuilder::ImageRecipe.ComponentParameter (__added__) +* AWS::ImageBuilder::ImageRecipe.SystemsManagerAgent (__added__) +* AWS::LookoutMetrics::Alert.Action (__added__) +* AWS::LookoutMetrics::Alert.LambdaConfiguration (__added__) +* AWS::LookoutMetrics::Alert.SNSConfiguration (__added__) +* AWS::LookoutMetrics::AnomalyDetector.AnomalyDetectorConfig (__added__) +* AWS::CloudWatch::Alarm.MetricDataQuery AccountId (__added__) +* AWS::Glue::Database.DatabaseInput CreateTableDefaultPermissions (__added__) +* AWS::Glue::Partition.SchemaReference SchameVersionId (__deleted__) +* AWS::Glue::Partition.SchemaReference SchemaVersionId (__added__) +* AWS::Glue::Table.SchemaReference SchameVersionId (__deleted__) +* AWS::Glue::Table.SchemaReference SchemaVersionId (__added__) +* AWS::ImageBuilder::ImageRecipe.ComponentConfiguration Parameters (__added__) +* AWS::QuickSight::DataSet.RowLevelPermissionDataSet FormatVersion (__added__) + + # CloudFormation Resource Specification v39.5.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 21aa1374d7809..ac8cf4b235806 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -39.5.0 +39.7.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 5986caf0ad273..7ea26213ba6ce 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -10315,6 +10315,23 @@ } } }, + "AWS::Cassandra::Table.EncryptionSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cassandra-table-encryptionspecification.html", + "Properties": { + "EncryptionType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cassandra-table-encryptionspecification.html#cfn-cassandra-table-encryptionspecification-encryptiontype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "KmsKeyIdentifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cassandra-table-encryptionspecification.html#cfn-cassandra-table-encryptionspecification-kmskeyidentifier", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::Cassandra::Table.ProvisionedThroughput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cassandra-table-provisionedthroughput.html", "Properties": { @@ -11987,6 +12004,12 @@ "AWS::CloudWatch::Alarm.MetricDataQuery": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-alarm-metricdataquery.html", "Properties": { + "AccountId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-alarm-metricdataquery.html#cfn-cloudwatch-alarm-metricdataquery-accountid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Expression": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-alarm-metricdataquery.html#cfn-cloudwatch-alarm-metricdataquery-expression", "PrimitiveType": "String", @@ -28580,6 +28603,17 @@ } } }, + "AWS::Glue::Crawler.RecrawlPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-crawler-recrawlpolicy.html", + "Properties": { + "RecrawlBehavior": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-crawler-recrawlpolicy.html#cfn-glue-crawler-recrawlpolicy-recrawlbehavior", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::Glue::Crawler.S3Target": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-crawler-s3target.html", "Properties": { @@ -28716,6 +28750,17 @@ } } }, + "AWS::Glue::Database.DataLakePrincipal": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-datalakeprincipal.html", + "Properties": { + "DataLakePrincipalIdentifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-datalakeprincipal.html#cfn-glue-database-datalakeprincipal-datalakeprincipalidentifier", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::Glue::Database.DatabaseIdentifier": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-databaseidentifier.html", "Properties": { @@ -28736,6 +28781,13 @@ "AWS::Glue::Database.DatabaseInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-databaseinput.html", "Properties": { + "CreateTableDefaultPermissions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-databaseinput.html#cfn-glue-database-databaseinput-createtabledefaultpermissions", + "ItemType": "PrincipalPrivileges", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "Description": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-databaseinput.html#cfn-glue-database-databaseinput-description", "PrimitiveType": "String", @@ -28768,6 +28820,24 @@ } } }, + "AWS::Glue::Database.PrincipalPrivileges": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-principalprivileges.html", + "Properties": { + "Permissions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-principalprivileges.html#cfn-glue-database-principalprivileges-permissions", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Principal": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-database-principalprivileges.html#cfn-glue-database-principalprivileges-principal", + "Required": false, + "Type": "DataLakePrincipal", + "UpdateType": "Mutable" + } + } + }, "AWS::Glue::Job.ConnectionsList": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-job-connectionslist.html", "Properties": { @@ -29036,18 +29106,18 @@ "AWS::Glue::Partition.SchemaReference": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-partition-schemareference.html", "Properties": { - "SchameVersionId": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-partition-schemareference.html#cfn-glue-partition-schemareference-schameversionid", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "SchemaId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-partition-schemareference.html#cfn-glue-partition-schemareference-schemaid", "Required": false, "Type": "SchemaId", "UpdateType": "Mutable" }, + "SchemaVersionId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-partition-schemareference.html#cfn-glue-partition-schemareference-schemaversionid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "SchemaVersionNumber": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-partition-schemareference.html#cfn-glue-partition-schemareference-schemaversionnumber", "PrimitiveType": "Integer", @@ -29394,18 +29464,18 @@ "AWS::Glue::Table.SchemaReference": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-schemareference.html", "Properties": { - "SchameVersionId": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-schemareference.html#cfn-glue-table-schemareference-schameversionid", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "SchemaId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-schemareference.html#cfn-glue-table-schemareference-schemaid", "Required": false, "Type": "SchemaId", "UpdateType": "Mutable" }, + "SchemaVersionId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-schemareference.html#cfn-glue-table-schemareference-schemaversionid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "SchemaVersionNumber": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-table-schemareference.html#cfn-glue-table-schemareference-schemaversionnumber", "PrimitiveType": "Integer", @@ -31943,6 +32013,23 @@ } } }, + "AWS::ImageBuilder::ImageRecipe.AdditionalInstanceConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-additionalinstanceconfiguration.html", + "Properties": { + "SystemsManagerAgent": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-additionalinstanceconfiguration.html#cfn-imagebuilder-imagerecipe-additionalinstanceconfiguration-systemsmanageragent", + "Required": false, + "Type": "SystemsManagerAgent", + "UpdateType": "Mutable" + }, + "UserDataOverride": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-additionalinstanceconfiguration.html#cfn-imagebuilder-imagerecipe-additionalinstanceconfiguration-userdataoverride", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::ImageBuilder::ImageRecipe.ComponentConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-componentconfiguration.html", "Properties": { @@ -31951,6 +32038,31 @@ "PrimitiveType": "String", "Required": false, "UpdateType": "Immutable" + }, + "Parameters": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-componentconfiguration.html#cfn-imagebuilder-imagerecipe-componentconfiguration-parameters", + "ItemType": "ComponentParameter", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, + "AWS::ImageBuilder::ImageRecipe.ComponentParameter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-componentparameter.html", + "Properties": { + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-componentparameter.html#cfn-imagebuilder-imagerecipe-componentparameter-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-componentparameter.html#cfn-imagebuilder-imagerecipe-componentparameter-value", + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" } } }, @@ -32030,6 +32142,17 @@ } } }, + "AWS::ImageBuilder::ImageRecipe.SystemsManagerAgent": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-systemsmanageragent.html", + "Properties": { + "UninstallAfterBuild": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-imagerecipe-systemsmanageragent.html#cfn-imagebuilder-imagerecipe-systemsmanageragent-uninstallafterbuild", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::ImageBuilder::InfrastructureConfiguration.Logging": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-infrastructureconfiguration-logging.html", "Properties": { @@ -40479,6 +40602,68 @@ } } }, + "AWS::LookoutMetrics::Alert.Action": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-action.html", + "Properties": { + "LambdaConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-action.html#cfn-lookoutmetrics-alert-action-lambdaconfiguration", + "Required": false, + "Type": "LambdaConfiguration", + "UpdateType": "Immutable" + }, + "SNSConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-action.html#cfn-lookoutmetrics-alert-action-snsconfiguration", + "Required": false, + "Type": "SNSConfiguration", + "UpdateType": "Immutable" + } + } + }, + "AWS::LookoutMetrics::Alert.LambdaConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-lambdaconfiguration.html", + "Properties": { + "LambdaArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-lambdaconfiguration.html#cfn-lookoutmetrics-alert-lambdaconfiguration-lambdaarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-lambdaconfiguration.html#cfn-lookoutmetrics-alert-lambdaconfiguration-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::LookoutMetrics::Alert.SNSConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-snsconfiguration.html", + "Properties": { + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-snsconfiguration.html#cfn-lookoutmetrics-alert-snsconfiguration-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "SnsTopicArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-snsconfiguration.html#cfn-lookoutmetrics-alert-snsconfiguration-snstopicarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::LookoutMetrics::AnomalyDetector.AnomalyDetectorConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-anomalydetector-anomalydetectorconfig.html", + "Properties": { + "AnomalyDetectorFrequency": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-anomalydetector-anomalydetectorconfig.html#cfn-lookoutmetrics-anomalydetector-anomalydetectorconfig-anomalydetectorfrequency", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::LookoutMetrics::AnomalyDetector.AppFlowConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-anomalydetector-appflowconfig.html", "Properties": { @@ -50845,6 +51030,12 @@ "Required": true, "UpdateType": "Mutable" }, + "FormatVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-quicksight-dataset-rowlevelpermissiondataset.html#cfn-quicksight-dataset-rowlevelpermissiondataset-formatversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Namespace": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-quicksight-dataset-rowlevelpermissiondataset.html#cfn-quicksight-dataset-rowlevelpermissiondataset-namespace", "PrimitiveType": "String", @@ -60932,7 +61123,7 @@ } } }, - "ResourceSpecificationVersion": "39.5.0", + "ResourceSpecificationVersion": "39.7.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -61554,6 +61745,7 @@ "PrimitiveType": "String" }, "AutoSubDomainCreationPatterns": { + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Type": "List" }, @@ -61586,6 +61778,7 @@ }, "AutoSubDomainCreationPatterns": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-amplify-domain.html#cfn-amplify-domain-autosubdomaincreationpatterns", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -61611,6 +61804,7 @@ }, "SubDomainSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-amplify-domain.html#cfn-amplify-domain-subdomainsettings", + "DuplicatesAllowed": true, "ItemType": "SubDomainSetting", "Required": true, "Type": "List", @@ -66353,6 +66547,12 @@ "Type": "List", "UpdateType": "Immutable" }, + "EncryptionSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cassandra-table.html#cfn-cassandra-table-encryptionspecification", + "Required": false, + "Type": "EncryptionSpecification", + "UpdateType": "Mutable" + }, "KeyspaceName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cassandra-table.html#cfn-cassandra-table-keyspacename", "PrimitiveType": "String", @@ -74251,35 +74451,41 @@ "Required": false, "UpdateType": "Immutable" }, + "AssociationDefaultRouteTableId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-associationdefaultroutetableid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "AutoAcceptSharedAttachments": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-autoacceptsharedattachments", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "DefaultRouteTableAssociation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-defaultroutetableassociation", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "DefaultRouteTablePropagation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-defaultroutetablepropagation", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "Description": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-description", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "DnsSupport": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-dnssupport", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "MulticastSupport": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-multicastsupport", @@ -74287,19 +74493,32 @@ "Required": false, "UpdateType": "Immutable" }, + "PropagationDefaultRouteTableId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-propagationdefaultroutetableid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-tags", "DuplicatesAllowed": true, "ItemType": "Tag", "Required": false, "Type": "List", - "UpdateType": "Immutable" + "UpdateType": "Mutable" + }, + "TransitGatewayCidrBlocks": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-transitgatewaycidrblocks", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" }, "VpnEcmpSupport": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgateway.html#cfn-ec2-transitgateway-vpnecmpsupport", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, @@ -74741,6 +74960,18 @@ "Required": false, "UpdateType": "Immutable" }, + "Ipv6CidrBlock": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpccidrblock.html#cfn-ec2-vpccidrblock-ipv6cidrblock", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Ipv6Pool": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpccidrblock.html#cfn-ec2-vpccidrblock-ipv6pool", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "VpcId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpccidrblock.html#cfn-ec2-vpccidrblock-vpcid", "PrimitiveType": "String", @@ -79855,6 +80086,12 @@ "Required": false, "UpdateType": "Immutable" }, + "RecrawlPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-recrawlpolicy", + "Required": false, + "Type": "RecrawlPolicy", + "UpdateType": "Mutable" + }, "Role": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-glue-crawler.html#cfn-glue-crawler-role", "PrimitiveType": "String", @@ -82165,7 +82402,6 @@ }, "InstanceConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-instanceconfiguration", - "PrimitiveType": "Json", "Required": false, "Type": "InstanceConfiguration", "UpdateType": "Immutable" @@ -82410,6 +82646,12 @@ }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagerecipe.html", "Properties": { + "AdditionalInstanceConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagerecipe.html#cfn-imagebuilder-imagerecipe-additionalinstanceconfiguration", + "Required": false, + "Type": "AdditionalInstanceConfiguration", + "UpdateType": "Mutable" + }, "BlockDeviceMappings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagerecipe.html#cfn-imagebuilder-imagerecipe-blockdevicemappings", "ItemType": "InstanceBlockDeviceMapping", @@ -86127,6 +86369,23 @@ } } }, + "AWS::Logs::ResourcePolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-resourcepolicy.html", + "Properties": { + "PolicyDocument": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-resourcepolicy.html#cfn-logs-resourcepolicy-policydocument", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "PolicyName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-resourcepolicy.html#cfn-logs-resourcepolicy-policyname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::Logs::SubscriptionFilter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-subscriptionfilter.html", "Properties": { @@ -86156,6 +86415,72 @@ } } }, + "AWS::LookoutEquipment::InferenceScheduler": { + "Attributes": { + "InferenceSchedulerArn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html", + "Properties": { + "DataDelayOffsetInMinutes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-datadelayoffsetinminutes", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "DataInputConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-datainputconfiguration", + "PrimitiveType": "Json", + "Required": true, + "UpdateType": "Mutable" + }, + "DataOutputConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-dataoutputconfiguration", + "PrimitiveType": "Json", + "Required": true, + "UpdateType": "Mutable" + }, + "DataUploadFrequency": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-datauploadfrequency", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "InferenceSchedulerName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-inferenceschedulername", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "ModelName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-modelname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ServerSideKmsKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-serversidekmskeyid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::LookoutMetrics::Alert": { "Attributes": { "Arn": { @@ -86166,8 +86491,8 @@ "Properties": { "Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutmetrics-alert.html#cfn-lookoutmetrics-alert-action", - "PrimitiveType": "Json", "Required": true, + "Type": "Action", "UpdateType": "Immutable" }, "AlertDescription": { @@ -86206,8 +86531,8 @@ "Properties": { "AnomalyDetectorConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutmetrics-anomalydetector.html#cfn-lookoutmetrics-anomalydetector-anomalydetectorconfig", - "PrimitiveType": "Json", "Required": true, + "Type": "AnomalyDetectorConfig", "UpdateType": "Mutable" }, "AnomalyDetectorDescription": { @@ -93291,6 +93616,12 @@ }, "AWS::S3::AccessPoint": { "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "Name": { + "PrimitiveType": "String" + }, "NetworkOrigin": { "PrimitiveType": "String" } @@ -93303,14 +93634,14 @@ "Required": true, "UpdateType": "Immutable" }, - "Name": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-name", - "PrimitiveType": "String", + "Policy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-policy", + "PrimitiveType": "Json", "Required": false, "UpdateType": "Mutable" }, - "Policy": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-policy", + "PolicyStatus": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-policystatus", "PrimitiveType": "Json", "Required": false, "UpdateType": "Mutable" @@ -94753,6 +95084,7 @@ }, "Regions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-replicationset.html#cfn-ssmincidents-replicationset-regions", + "DuplicatesAllowed": false, "ItemType": "ReplicationRegion", "Required": true, "Type": "List", diff --git a/packages/@aws-cdk/cfnspec/spec-source/cfn-lint/StatefulResources/000.json b/packages/@aws-cdk/cfnspec/spec-source/cfn-lint/StatefulResources/000.json index 36a17855e22b7..7037f14238497 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/cfn-lint/StatefulResources/000.json +++ b/packages/@aws-cdk/cfnspec/spec-source/cfn-lint/StatefulResources/000.json @@ -5,6 +5,7 @@ "AWS::Cognito::UserPool" : {}, "AWS::DocDB::DBCluster" : {}, "AWS::DocDB::DBInstance" : {}, + "AWS::DynamoDB::GlobalTable" : {}, "AWS::DynamoDB::Table" : {}, "AWS::EC2::Volume" : {}, "AWS::EFS::FileSystem" : {}, diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index 94c805c24cafd..f8c09b12fc8ea 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -173,6 +173,7 @@ "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-location": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-lookoutequipment": "0.0.0", "@aws-cdk/aws-lookoutmetrics": "0.0.0", "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", @@ -340,6 +341,7 @@ "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-location": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-lookoutequipment": "0.0.0", "@aws-cdk/aws-lookoutmetrics": "0.0.0", "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 1a609044fbbdb..a200e97145db6 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -251,6 +251,7 @@ "@aws-cdk/aws-location": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", + "@aws-cdk/aws-lookoutequipment": "0.0.0", "@aws-cdk/aws-lookoutmetrics": "0.0.0", "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index bb1a848f9c335..0c38c44ff0625 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -158,6 +158,7 @@ "@aws-cdk/aws-location": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", + "@aws-cdk/aws-lookoutequipment": "0.0.0", "@aws-cdk/aws-lookoutmetrics": "0.0.0", "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index dc27ea678f33c..33bbc89af3414 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -252,6 +252,7 @@ "@aws-cdk/aws-location": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", + "@aws-cdk/aws-lookoutequipment": "0.0.0", "@aws-cdk/aws-lookoutmetrics": "0.0.0", "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", diff --git a/packages/monocdk/rosetta/portfolio-product.ts-fixture b/packages/monocdk/rosetta/portfolio-product.ts-fixture new file mode 100644 index 0000000000000..20a1db30bf3ee --- /dev/null +++ b/packages/monocdk/rosetta/portfolio-product.ts-fixture @@ -0,0 +1,28 @@ +// Fixture with packages imported, but nothing else +import { Construct, Stack } from '@aws-cdk/core'; +import * as servicecatalog from '@aws-cdk/aws-servicecatalog'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const portfolio = new servicecatalog.Portfolio(this, "MyFirstPortfolio", { + displayName: "MyFirstPortfolio", + providerName: "MyTeam", + }); + + const product = new servicecatalog.CloudFormationProduct(this, 'MyFirstProduct', { + productName: "My Product", + owner: "Product Owner", + productVersions: [ + { + productVersionName: "v1", + cloudFormationTemplate: servicecatalog.CloudFormationTemplate.fromUrl( + 'https://raw.githubusercontent.com/awslabs/aws-cloudformation-templates/master/aws/services/ServiceCatalog/Product.yaml'), + }, + ] + }); + + /// here + } +} From f59331193b5a2cc4a33d71d775f6650d66bb1bf8 Mon Sep 17 00:00:00 2001 From: Filip Piskor Date: Thu, 22 Jul 2021 19:31:13 +0100 Subject: [PATCH 07/27] fix(aws-cloudwatch): unable to use generic extended statistics for cloudwatch alarms (#15720) This change adds support for alarming on custom statistics and extends the testing done for metrics to ensure if a custom statistic is passed it preserves the case ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts | 3 +++ .../aws-cloudwatch/lib/private/statistic.ts | 4 ++-- .../aws-cloudwatch/test/alarm.test.ts | 20 +++++++++++++++++++ .../aws-cloudwatch/test/metrics.test.ts | 7 ++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts index ef97dc3d79c7f..6b102d567125f 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts @@ -392,7 +392,10 @@ function renderIfExtendedStatistic(statistic?: string): string | undefined { // Already percentile. Avoid parsing because we might get into // floating point rounding issues, return as-is but lowercase the p. return statistic.toLowerCase(); + } else if (parsed.type === 'generic') { + return statistic; } + return undefined; } diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts b/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts index 8f1aae4ff99e6..a306c1d925817 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts @@ -53,13 +53,13 @@ export function parseStatistic(stat: string): SimpleStatistic | PercentileStatis return { type: 'generic', - statistic: lowerStat, + statistic: stat, }; } export function normalizeStatistic(stat: string): string { const parsed = parseStatistic(stat); - if (parsed.type === 'simple') { + if (parsed.type === 'simple' || parsed.type === 'generic') { return parsed.statistic; } else { // Already percentile. Avoid parsing because we might get into diff --git a/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts index c143e8baec65b..bfe3eca913b67 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts @@ -257,6 +257,26 @@ describe('Alarm', () => { }); + + test('can use a generic string for extended statistic to make alarm', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + testMetric.createAlarm(stack, 'Alarm', { + threshold: 1000, + evaluationPeriods: 2, + statistic: 'tm99.9999999999', + }); + + // THEN + expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Statistic: ABSENT, + ExtendedStatistic: 'tm99.9999999999', + }); + + }); + }); class TestAlarmAction implements IAlarmAction { diff --git a/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts index dc958860b6a4c..ff1620f91ed50 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts @@ -232,12 +232,13 @@ describe('Metrics', () => { }); test('metric accepts a variety of statistics', () => { - new Metric({ + const customStat = 'myCustomStatistic'; + const metric = new Metric({ namespace: 'Test', metricName: 'Metric', - statistic: 'myCustomStatistic', + statistic: customStat, }); - + expect(metric.statistic).toEqual(customStat); }); }); From c92548b2242478d22db030842014e7646715c2ef Mon Sep 17 00:00:00 2001 From: arcrank Date: Thu, 22 Jul 2021 15:13:56 -0400 Subject: [PATCH 08/27] feat(servicecatalog): add ability to set launch Role and deploy with StackSets (#15678) Adds 2 constraints, launch role and stackset. Users can specify a specific role users must assume when launching product. StackSets deployments allows you to deploy products using Cloudformation StackSets. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-servicecatalog/README.md | 72 +++++++-- .../aws-servicecatalog/lib/constraints.ts | 33 +++++ .../aws-servicecatalog/lib/portfolio.ts | 35 ++++- .../lib/private/association-manager.ts | 88 +++++++++-- .../test/integ.portfolio.expected.json | 120 +++++++++++++++ .../test/integ.portfolio.ts | 23 +++ .../aws-servicecatalog/test/portfolio.test.ts | 138 ++++++++++++++++++ 7 files changed, 484 insertions(+), 25 deletions(-) diff --git a/packages/@aws-cdk/aws-servicecatalog/README.md b/packages/@aws-cdk/aws-servicecatalog/README.md index d2cd893efb4e8..b90748a2125ad 100644 --- a/packages/@aws-cdk/aws-servicecatalog/README.md +++ b/packages/@aws-cdk/aws-servicecatalog/README.md @@ -33,8 +33,11 @@ enables organizations to create and manage catalogs of products for their end us - [Adding a product to a portfolio](#adding-a-product-to-a-portfolio) - [TagOptions](#tag-options) - [Constraints](#constraints) - - [Notify on stack events](#notify-on-stack-events) - [Tag update constraint](#tag-update-constraint) + - [Notify on stack events](#notify-on-stack-events) + - [Set launch role](#set-launch-role) + - [Deploy with StackSets](#deploy-with-stacksets) + The `@aws-cdk/aws-servicecatalog` package contains resources that enable users to automate governance and management of their AWS resources at scale. @@ -186,6 +189,27 @@ If a misconfigured constraint is added, `synth` will fail with an error message. Read more at [Service Catalog Constraints](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/constraints.html). +### Tag update constraint + +Tag update constraints allow or disallow end users to update tags on resources associated with an AWS Service Catalog product upon provisioning. +By default, tag updating is not permitted. +If tag updating is allowed, then new tags associated with the product or portfolio will be applied to provisioned resources during a provisioned product update. + +```ts fixture=portfolio-product +portfolio.addProduct(product); + +portfolio.constrainTagUpdates(product); +``` + +If you want to disable this feature later on, you can update it by setting the "allow" parameter to `false`: + +```ts fixture=portfolio-product +// to disable tag updates: +portfolio.constrainTagUpdates(product, { + allow: false, +}); +``` + ### Notify on stack events Allows users to subscribe an AWS `SNS` topic to the stack events of the product. @@ -204,23 +228,49 @@ portfolio.notifyOnStackEvents(product, topic2, { }); ``` -### Tag update constraint +### Set launch role -Tag update constraints allow or disallow end users to update tags on resources associated with an AWS Service Catalog product upon provisioning. -By default, tag updating is not permitted. -If tag updating is allowed, then new tags associated with the product or portfolio will be applied to provisioned resources during a provisioned product update. +Allows you to configure a specific AWS `IAM` role that a user must assume when launching a product. +By setting this launch role, you can control what policies and privileges end users can have. +The launch role must be assumed by the service catalog principal. +You can only have one launch role set for a portfolio-product association, and you cannot set a launch role if a StackSets deployment has been configured. ```ts fixture=portfolio-product -portfolio.addProduct(product); +import * as iam from '@aws-cdk/aws-iam'; -portfolio.constrainTagUpdates(product); +const launchRole = new iam.Role(this, 'LaunchRole', { + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), +}); + +portfolio.setLaunchRole(product, launchRole); ``` -If you want to disable this feature later on, you can update it by setting the "allow" parameter to `false`: +See [Launch Constraint](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/constraints-launch.html) documentation +to understand permissions roles need. + +### Deploy with StackSets + +A StackSets deployment constraint allows you to configure product deployment options using +[AWS CloudFormation StackSets](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/using-stacksets.html). +You can specify multiple accounts and regions for the product launch following StackSets conventions. +There is an additional field `allowStackSetInstanceOperations` that configures ability for end users to create, edit, or delete the stacks. +By default, this field is set to `false`. +End users can manage those accounts and determine where products deploy and the order of deployment. +You can only define one StackSets deployment configuration per portfolio-product association, +and you cannot both set a launch role and StackSets deployment configuration for an assocation. ```ts fixture=portfolio-product -// to disable tag updates: -portfolio.constrainTagUpdates(product, { - allow: false, +import * as iam from '@aws-cdk/aws-iam'; + +const adminRole = new iam.Role(this, 'AdminRole', { + assumedBy: new iam.AccountRootPrincipal(), +}); + +portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678902', '012345678903'], + regions: ['us-west-1', 'us-east-1', 'us-west-2', 'us-east-1'], + adminRole: adminRole, + executionRoleName: 'SCStackSetExecutionRole', // Name of role deployed in end users accounts. + allowStackSetInstanceOperations: true, }); ``` diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/constraints.ts b/packages/@aws-cdk/aws-servicecatalog/lib/constraints.ts index 54a6e40973c4f..50cb619aa0889 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/constraints.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/constraints.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import { MessageLanguage } from './common'; /** @@ -20,6 +21,38 @@ export interface CommonConstraintOptions { readonly description?: string; } +/** + * Properties for deploying with Stackset, which creates a StackSet constraint. + */ +export interface StackSetsConstraintOptions extends CommonConstraintOptions { + /** + * List of accounts to deploy stacks to. + */ + readonly accounts: string[]; + + /** + * List of regions to deploy stacks to. + */ + readonly regions: string[]; + + /** + * IAM role used to administer the StackSets configuration. + */ + readonly adminRole: iam.IRole; + + /** + * IAM role used to provision the products in the Stacks. + */ + readonly executionRoleName: string; + + /** + * Wether to allow end users to create, update, and delete stacks. + * + * @default false + */ + readonly allowStackSetInstanceOperations?: boolean; +} + /** * Properties for ResourceUpdateConstraint. */ diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts b/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts index ba8bc6c301d12..73d4452ce348b 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/portfolio.ts @@ -2,7 +2,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; import { MessageLanguage } from './common'; -import { CommonConstraintOptions, TagUpdateConstraintOptions } from './constraints'; +import { CommonConstraintOptions, StackSetsConstraintOptions, TagUpdateConstraintOptions } from './constraints'; import { AssociationManager } from './private/association-manager'; import { hashValues } from './private/util'; import { InputValidator } from './private/validation'; @@ -88,6 +88,11 @@ export interface IPortfolio extends cdk.IResource { */ associateTagOptions(tagOptions: TagOptions): void; + /** + * Add a Resource Update Constraint. + */ + constrainTagUpdates(product: IProduct, options?: TagUpdateConstraintOptions): void; + /** * Add notifications for supplied topics on the provisioned product. * @param product A service catalog product. @@ -96,9 +101,21 @@ export interface IPortfolio extends cdk.IResource { notifyOnStackEvents(product: IProduct, topic: sns.ITopic, options?: CommonConstraintOptions): void; /** - * Add a Resource Update Constraint. + * Force users to assume a certain role when launching a product. + * + * @param product A service catalog product. + * @param launchRole The IAM role a user must assume when provisioning the product. + * @param options options for the constraint. */ - constrainTagUpdates(product: IProduct, options?: TagUpdateConstraintOptions): void; + setLaunchRole(product: IProduct, launchRole: iam.IRole, options?: CommonConstraintOptions): void; + + /** + * Configure deployment options using AWS Cloudformaiton StackSets + * + * @param product A service catalog product. + * @param options Configuration options for the constraint. + */ + deployWithStackSets(product: IProduct, options: StackSetsConstraintOptions): void; } abstract class PortfolioBase extends cdk.Resource implements IPortfolio { @@ -136,12 +153,20 @@ abstract class PortfolioBase extends cdk.Resource implements IPortfolio { AssociationManager.associateTagOptions(this, tagOptions); } + public constrainTagUpdates(product: IProduct, options: TagUpdateConstraintOptions = {}): void { + AssociationManager.constrainTagUpdates(this, product, options); + } + public notifyOnStackEvents(product: IProduct, topic: sns.ITopic, options: CommonConstraintOptions = {}): void { AssociationManager.notifyOnStackEvents(this, product, topic, options); } - public constrainTagUpdates(product: IProduct, options: TagUpdateConstraintOptions = {}): void { - AssociationManager.constrainTagUpdates(this, product, options); + public setLaunchRole(product: IProduct, launchRole: iam.IRole, options: CommonConstraintOptions = {}): void { + AssociationManager.setLaunchRole(this, product, launchRole, options); + } + + public deployWithStackSets(product: IProduct, options: StackSetsConstraintOptions) { + AssociationManager.deployWithStackSets(this, product, options); } /** diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts b/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts index 519679e3df5a0..e4e8326f2bb19 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts @@ -1,9 +1,13 @@ +import * as iam from '@aws-cdk/aws-iam'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; -import { CommonConstraintOptions, TagUpdateConstraintOptions } from '../constraints'; +import { CommonConstraintOptions, StackSetsConstraintOptions, TagUpdateConstraintOptions } from '../constraints'; import { IPortfolio } from '../portfolio'; import { IProduct } from '../product'; -import { CfnLaunchNotificationConstraint, CfnPortfolioProductAssociation, CfnResourceUpdateConstraint, CfnTagOption, CfnTagOptionAssociation } from '../servicecatalog.generated'; +import { + CfnLaunchNotificationConstraint, CfnLaunchRoleConstraint, CfnPortfolioProductAssociation, + CfnResourceUpdateConstraint, CfnStackSetConstraint, CfnTagOption, CfnTagOptionAssociation, +} from '../servicecatalog.generated'; import { TagOptions } from '../tag-options'; import { hashValues } from './util'; import { InputValidator } from './validation'; @@ -28,8 +32,29 @@ export class AssociationManager { }; } + public static constrainTagUpdates(portfolio: IPortfolio, product: IProduct, options: TagUpdateConstraintOptions): void { + this.validateCommonConstraintOptions(portfolio, product, options); + const association = this.associateProductWithPortfolio(portfolio, product); + const constructId = `ResourceUpdateConstraint${association.associationKey}`; + + if (!portfolio.node.tryFindChild(constructId)) { + const constraint = new CfnResourceUpdateConstraint(portfolio as unknown as cdk.Resource, constructId, { + acceptLanguage: options.messageLanguage, + description: options.description, + portfolioId: portfolio.portfolioId, + productId: product.productId, + tagUpdateOnProvisionedProduct: options.allow === false ? 'NOT_ALLOWED' : 'ALLOWED', + }); + + // Add dependsOn to force proper order in deployment. + constraint.addDependsOn(association.cfnPortfolioProductAssociation); + } else { + throw new Error(`Cannot have multiple tag update constraints for association ${this.prettyPrintAssociation(portfolio, product)}`); + } + } + public static notifyOnStackEvents(portfolio: IPortfolio, product: IProduct, topic: sns.ITopic, options: CommonConstraintOptions): void { - InputValidator.validateLength(this.prettyPrintAssociation(portfolio, product), 'description', 0, 2000, options.description); + this.validateCommonConstraintOptions(portfolio, product, options); const association = this.associateProductWithPortfolio(portfolio, product); const constructId = `LaunchNotificationConstraint${hashValues(topic.node.addr, topic.stack.node.addr, association.associationKey)}`; @@ -49,24 +74,57 @@ export class AssociationManager { } } - public static constrainTagUpdates(portfolio: IPortfolio, product: IProduct, options: TagUpdateConstraintOptions): void { - InputValidator.validateLength(this.prettyPrintAssociation(portfolio, product), 'description', 0, 2000, options.description); + public static setLaunchRole(portfolio: IPortfolio, product: IProduct, launchRole: iam.IRole, options: CommonConstraintOptions): void { + this.validateCommonConstraintOptions(portfolio, product, options); const association = this.associateProductWithPortfolio(portfolio, product); - const constructId = `ResourceUpdateConstraint${association.associationKey}`; + // Check if a stackset deployment constraint has already been configured. + if (portfolio.node.tryFindChild(this.stackSetConstraintLogicalId(association.associationKey))) { + throw new Error(`Cannot set launch role when a StackSet rule is already defined for association ${this.prettyPrintAssociation(portfolio, product)}`); + } + const constructId = this.launchRoleConstraintLogicalId(association.associationKey); if (!portfolio.node.tryFindChild(constructId)) { - const constraint = new CfnResourceUpdateConstraint(portfolio as unknown as cdk.Resource, constructId, { + const constraint = new CfnLaunchRoleConstraint(portfolio as unknown as cdk.Resource, constructId, { acceptLanguage: options.messageLanguage, description: options.description, portfolioId: portfolio.portfolioId, productId: product.productId, - tagUpdateOnProvisionedProduct: options.allow === false ? 'NOT_ALLOWED' : 'ALLOWED', + roleArn: launchRole.roleArn, }); // Add dependsOn to force proper order in deployment. constraint.addDependsOn(association.cfnPortfolioProductAssociation); } else { - throw new Error(`Cannot have multiple tag update constraints for association ${this.prettyPrintAssociation(portfolio, product)}`); + throw new Error(`Cannot set multiple launch roles for association ${this.prettyPrintAssociation(portfolio, product)}`); + } + } + + public static deployWithStackSets(portfolio: IPortfolio, product: IProduct, options: StackSetsConstraintOptions) { + this.validateCommonConstraintOptions(portfolio, product, options); + const association = this.associateProductWithPortfolio(portfolio, product); + // Check if a launch role has already been set. + if (portfolio.node.tryFindChild(this.launchRoleConstraintLogicalId(association.associationKey))) { + throw new Error(`Cannot configure StackSet deployment when a launch role is already defined for association ${this.prettyPrintAssociation(portfolio, product)}`); + } + + const constructId = this.stackSetConstraintLogicalId(association.associationKey); + if (!portfolio.node.tryFindChild(constructId)) { + const constraint = new CfnStackSetConstraint(portfolio as unknown as cdk.Resource, constructId, { + acceptLanguage: options.messageLanguage, + description: options.description ?? '', + portfolioId: portfolio.portfolioId, + productId: product.productId, + accountList: options.accounts, + regionList: options.regions, + adminRole: options.adminRole.roleArn, + executionRole: options.executionRoleName, + stackInstanceControl: options.allowStackSetInstanceOperations ? 'ALLOWED' : 'NOT_ALLOWED', + }); + + // Add dependsOn to force proper order in deployment. + constraint.addDependsOn(association.cfnPortfolioProductAssociation); + } else { + throw new Error(`Cannot configure multiple StackSet deployment constraints for association ${this.prettyPrintAssociation(portfolio, product)}`); } } @@ -98,7 +156,19 @@ export class AssociationManager { }; } + private static stackSetConstraintLogicalId(associationKey: string): string { + return `StackSetConstraint${associationKey}`; + } + + private static launchRoleConstraintLogicalId(associationKey:string): string { + return `LaunchRoleConstraint${associationKey}`; + } + private static prettyPrintAssociation(portfolio: IPortfolio, product: IProduct): string { return `- Portfolio: ${portfolio.node.path} | Product: ${product.node.path}`; } + + private static validateCommonConstraintOptions(portfolio: IPortfolio, product: IProduct, options: CommonConstraintOptions): void { + InputValidator.validateLength(this.prettyPrintAssociation(portfolio, product), 'description', 0, 2000, options.description); + } } diff --git a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.expected.json b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.expected.json index d7f4510a7b01a..bc21e236fd3dd 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.expected.json +++ b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.expected.json @@ -182,6 +182,26 @@ "TestPortfolioPortfolioProductAssociationa0185761d231B0D998A7" ] }, + "TestPortfolioLaunchRoleConstrainta0185761d2312183162C": { + "Type": "AWS::ServiceCatalog::LaunchRoleConstraint", + "Properties": { + "PortfolioId": { + "Ref": "TestPortfolio4AC794EB" + }, + "ProductId": { + "Ref": "TestProduct7606930B" + }, + "RoleArn": { + "Fn::GetAtt": [ + "LaunchRole2CFB2E44", + "Arn" + ] + } + }, + "DependsOn": [ + "TestPortfolioPortfolioProductAssociationa0185761d231B0D998A7" + ] + }, "TagOptionc0d88a3c4b8b": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { @@ -226,6 +246,106 @@ }, "specialTopic7664DE4C": { "Type": "AWS::SNS::Topic" + }, + "LaunchRole2CFB2E44": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "servicecatalog.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "SecondTestPortfolio96C8F4BA": { + "Type": "AWS::ServiceCatalog::Portfolio", + "Properties": { + "DisplayName": "SecondTestPortfolio", + "ProviderName": "TestProvider" + } + }, + "SecondTestPortfolioPortfolioProductAssociationcda67a671c209FE862F2": { + "Type": "AWS::ServiceCatalog::PortfolioProductAssociation", + "Properties": { + "PortfolioId": { + "Ref": "SecondTestPortfolio96C8F4BA" + }, + "ProductId": { + "Ref": "TestProduct7606930B" + } + } + }, + "SecondTestPortfolioStackSetConstraintcda67a671c20A02367A6": { + "Type": "AWS::ServiceCatalog::StackSetConstraint", + "Properties": { + "AccountList": [ + "000000000000", + "111111111111", + "222222222222" + ], + "AdminRole": { + "Fn::GetAtt": [ + "AdminRole38563C57", + "Arn" + ] + }, + "Description": "", + "ExecutionRole": "StackSetExecutionRole", + "PortfolioId": { + "Ref": "SecondTestPortfolio96C8F4BA" + }, + "ProductId": { + "Ref": "TestProduct7606930B" + }, + "RegionList": [ + "us-east-1", + "us-west-2", + "eu-west-1" + ], + "StackInstanceControl": "ALLOWED" + }, + "DependsOn": [ + "SecondTestPortfolioPortfolioProductAssociationcda67a671c209FE862F2" + ] + }, + "AdminRole38563C57": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts index ae45f171a8c47..c9aa607880619 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts @@ -56,4 +56,27 @@ portfolio.notifyOnStackEvents(product, specialTopic, { messageLanguage: servicecatalog.MessageLanguage.EN, }); +const launchRole = new iam.Role(stack, 'LaunchRole', { + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), +}); + +portfolio.setLaunchRole(product, launchRole); + +const secondPortfolio = new servicecatalog.Portfolio(stack, 'SecondTestPortfolio', { + displayName: 'SecondTestPortfolio', + providerName: 'TestProvider', +}); + +const adminRole = new iam.Role(stack, 'AdminRole', { + assumedBy: new iam.AccountRootPrincipal(), +}); + +secondPortfolio.deployWithStackSets(product, { + accounts: ['000000000000', '111111111111', '222222222222'], + regions: ['us-east-1', 'us-west-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetExecutionRole', + allowStackSetInstanceOperations: true, +}); + app.synth(); diff --git a/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts b/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts index a5f97dc1e7470..3c76619c03a1f 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts @@ -458,4 +458,142 @@ describe('portfolio associations and product constraints', () => { portfolio.notifyOnStackEvents(product, topic); }).toThrowError(`Topic ${topic} is already subscribed to association`); }); + + describe('portfolio constraints that have roles', () => { + let launchRole: iam.IRole, adminRole: iam.IRole; + beforeEach(() => { + adminRole = new iam.Role(stack, 'AdminRole', { + assumedBy: new iam.AccountRootPrincipal(), + }); + launchRole = new iam.Role(stack, 'LaunchRole', { + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), + }); + }), + + test('set a launch role constraint', () => { + portfolio.addProduct(product); + + portfolio.setLaunchRole(product, launchRole, { + description: 'set launch role description', + messageLanguage: servicecatalog.MessageLanguage.EN, + }); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalog::LaunchRoleConstraint', { + PortfolioId: { Ref: 'MyPortfolio59CCA9C9' }, + ProductId: { Ref: 'MyProduct49A3C587' }, + Description: 'set launch role description', + AcceptLanguage: 'en', + RoleArn: { + 'Fn::GetAtt': ['LaunchRole2CFB2E44', 'Arn'], + }, + }); + }), + + test('set launch role constraint still adds without explicit association', () => { + portfolio.setLaunchRole(product, launchRole); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalog::LaunchRoleConstraint'); + }), + + test('fails to add multiple set launch roles', () => { + const otherLaunchRole = new iam.Role(stack, 'otherLaunchRole', { + assumedBy: new iam.ServicePrincipal('servicecatalog.amazonaws.com'), + }); + + portfolio.setLaunchRole(product, launchRole); + + expect(() => { + portfolio.setLaunchRole(product, otherLaunchRole); + }).toThrowError(/Cannot set multiple launch roles for association/); + }), + + test('fails to set launch role if stackset rule is already defined', () => { + portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678901'], + regions: ['us-east-1', 'us-west-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetExecutionRole', + allowStackSetInstanceOperations: false, + }, + ); + + expect(() => { + portfolio.setLaunchRole(product, launchRole); + }).toThrowError(/Cannot set launch role when a StackSet rule is already defined for association/); + }), + + test('deploy with stacksets constraint', () => { + portfolio.addProduct(product); + + portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678901'], + regions: ['us-east-1', 'us-west-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetExecutionRole', + description: 'stackset description', + messageLanguage: servicecatalog.MessageLanguage.JP, + }); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalog::StackSetConstraint', { + PortfolioId: { Ref: 'MyPortfolio59CCA9C9' }, + ProductId: { Ref: 'MyProduct49A3C587' }, + AdminRole: { + 'Fn::GetAtt': [ + 'AdminRole38563C57', + 'Arn', + ], + }, + ExecutionRole: 'StackSetExecutionRole', + Description: 'stackset description', + AccountList: ['012345678901', '012345678901'], + RegionList: ['us-east-1', 'us-west-2', 'eu-west-1'], + StackInstanceControl: 'NOT_ALLOWED', + AcceptLanguage: 'jp', + }); + }), + + test('deployment with stacksets still adds without explicit association', () => { + portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678901'], + regions: ['us-east-1', 'us-west-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetExecutionRole', + allowStackSetInstanceOperations: true, + }); + + expect(stack).toHaveResourceLike('AWS::ServiceCatalog::StackSetConstraint'); + }), + + test('fails to add multiple deploy with stackset constraints', () => { + portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678901'], + regions: ['us-east-1', 'us-west-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetsExecutionRole', + }); + + expect(() => { + portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678901'], + regions: ['ap-east-1', 'ap-northeast-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetExecutionRole', + }); + }).toThrowError(/Cannot configure multiple StackSet deployment constraints for association/); + }), + + test('fails to configure deployment with stacksets if a launch role has been set', () => { + portfolio.setLaunchRole(product, launchRole); + + expect(() => { + portfolio.deployWithStackSets(product, { + accounts: ['012345678901', '012345678901'], + regions: ['us-east-1', 'us-west-2', 'eu-west-1'], + adminRole: adminRole, + executionRoleName: 'StackSetExecutionRole', + allowStackSetInstanceOperations: true, + }); + }).toThrowError(/Cannot configure StackSet deployment when a launch role is already defined for association/); + }); + }); }); \ No newline at end of file From 32ed2290f8efb27bf622998f98808ff18a8cdef1 Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Thu, 22 Jul 2021 13:32:25 -0700 Subject: [PATCH 09/27] feat(core): lazy mappings will only synthesize if keys are unresolved (#15617) This feature adds new static methods to the CfnMapping construct that allow the creation of "lazy" mappings. A lazy mapping will only create a Mappings section in the synthesized CFN template if some "find" operation on the mapping was not able to return a value since one or more of the lookup keys were unresolved. Usage: ```ts // Register the mapping as a lazy mapping. CfnMapping.registerLazyMap('UNIQUEMAPPINGID', { TopLevelKey: { SecondLevelKey: 'value', }, }); // Later, find a value from the mapping. Since the keys are both // resolved, this returns a resolved value and does not create a // CfnMapping. CfnMapping.findInLazyMap(scope, 'UNIQUEMAPPINGID', 'TopLevelKey', 'SecondLevelKey'); // > 'value' // If we try to find a value from the mapping using unresolved keys, a // CfnMapping is created and a Fn::FindInMap is returned. CfnMapping.findInLazyMap(scope, 'UNIQUEMAPPINGID', 'TopLevelKey', Aws.REGION); // > { Fn::FindInMap: [ 'UNIQUEMAPPINGID', 'TopLevelKey', { Ref: 'AWS::Region' } ] } ``` ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/README.md | 32 ++++ packages/@aws-cdk/core/lib/cfn-mapping.ts | 74 ++++++++-- packages/@aws-cdk/core/test/mappings.test.ts | 147 ++++++++++++++++++- packages/aws-cdk-lib/README.md | 34 ++++- 4 files changed, 269 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 44fdda5893019..a6d4d2efc5a9b 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -775,6 +775,38 @@ Mappings: us-east-2: US East (Ohio) ``` +Mappings can also be synthesized "lazily"; lazy mappings will only render a "Mappings" +section in the synthesized CloudFormation template if some `findInMap` call is unable to +immediately return a concrete value due to one or both of the keys being unresolved tokens +(some value only available at deploy-time). + +For example, the following code will not produce anything in the "Mappings" section. The +call to `findInMap` will be able to resolve the value during synthesis and simply return +`'US East (Ohio)'`. + +```ts +const regionTable = new CfnMapping(this, 'RegionTable', { + mapping: { + regionName: { + 'us-east-1': 'US East (N. Virginia)', + 'us-east-2': 'US East (Ohio)', + }, + }, + lazy: true, +}); + +regionTable.findInMap('regionName', 'us-east-2'); +``` + +On the other hand, the following code will produce the "Mappings" section shown above, +since the second-level key is an unresolved token. The call to `findInMap` will return a +token that resolves to `{ Fn::FindInMap: [ 'RegionTable', 'regionName', { Ref: AWS::Region +} ] }`. + +```ts +regionTable.findInMap('regionName', Aws.REGION); +``` + [cfn-mappings]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html ### Dynamic References diff --git a/packages/@aws-cdk/core/lib/cfn-mapping.ts b/packages/@aws-cdk/core/lib/cfn-mapping.ts index 196baf3aadb96..18b670a4ff927 100644 --- a/packages/@aws-cdk/core/lib/cfn-mapping.ts +++ b/packages/@aws-cdk/core/lib/cfn-mapping.ts @@ -1,8 +1,11 @@ import { Construct } from 'constructs'; +import { Annotations } from './annotations'; import { CfnRefElement } from './cfn-element'; import { Fn } from './cfn-fn'; import { Token } from './token'; +type Mapping = { [k1: string]: { [k2: string]: any } }; + export interface CfnMappingProps { /** * Mapping of key to a set of corresponding set of named values. @@ -14,18 +17,34 @@ export interface CfnMappingProps { * * @default - No mapping. */ - readonly mapping?: { [k1: string]: { [k2: string]: any } }; + readonly mapping?: Mapping; + + /* + * Synthesize this map in a lazy fashion. + * + * Lazy maps will only synthesize a mapping if a `findInMap` operation is unable to + * immediately return a value because one or both of the requested keys are unresolved + * tokens. In this case, `findInMap` will return a `Fn::FindInMap` CloudFormation + * intrinsic. + * + * @default false + */ + readonly lazy?: boolean; } /** * Represents a CloudFormation mapping. */ export class CfnMapping extends CfnRefElement { - private mapping: { [k1: string]: { [k2: string]: any } } = { }; + private mapping: Mapping; + private readonly lazy?: boolean; + private lazyRender = false; + private lazyInformed = false; constructor(scope: Construct, id: string, props: CfnMappingProps = {}) { super(scope, id); - this.mapping = props.mapping || { }; + this.mapping = props.mapping ?? { }; + this.lazy = props.lazy; } /** @@ -43,16 +62,25 @@ export class CfnMapping extends CfnRefElement { * @returns A reference to a value in the map based on the two keys. */ public findInMap(key1: string, key2: string): string { - // opportunistically check that the key exists (if the key does not contain tokens) - if (!Token.isUnresolved(key1) && !(key1 in this.mapping)) { - throw new Error(`Mapping doesn't contain top-level key '${key1}'`); + let fullyResolved = false; + if (!Token.isUnresolved(key1)) { + if (!(key1 in this.mapping)) { + throw new Error(`Mapping doesn't contain top-level key '${key1}'`); + } + if (!Token.isUnresolved(key2)) { + if (!(key2 in this.mapping[key1])) { + throw new Error(`Mapping doesn't contain second-level key '${key2}'`); + } + fullyResolved = true; + } } - - // opportunistically check that the second key exists (if the key does not contain tokens) - if (!Token.isUnresolved(key1) && !Token.isUnresolved(key2) && !(key2 in this.mapping[key1])) { - throw new Error(`Mapping doesn't contain second-level key '${key2}'`); + if (fullyResolved) { + if (this.lazy) { + return this.mapping[key1][key2]; + } + } else { + this.lazyRender = true; } - return Fn.findInMap(this.logicalId, key1, key2); } @@ -60,10 +88,24 @@ export class CfnMapping extends CfnRefElement { * @internal */ public _toCloudFormation(): object { - return { - Mappings: { - [this.logicalId]: this.mapping, - }, - }; + if (this.lazy === undefined && !this.lazyRender) { + this.informLazyUse(); + } + if (!this.lazy || (this.lazy && this.lazyRender)) { + return { + Mappings: { + [this.logicalId]: this.mapping, + }, + }; + } else { + return {}; + } + } + + private informLazyUse() { + if (!this.lazyInformed) { + Annotations.of(this).addInfo('Consider making this CfnMapping a lazy mapping by providing `lazy: true`: either no findInMap was called or every findInMap could be immediately resolved without using Fn::FindInMap'); + } + this.lazyInformed = true; } } diff --git a/packages/@aws-cdk/core/test/mappings.test.ts b/packages/@aws-cdk/core/test/mappings.test.ts index 7dd67fbcead45..4b2e15c4d4bf2 100644 --- a/packages/@aws-cdk/core/test/mappings.test.ts +++ b/packages/@aws-cdk/core/test/mappings.test.ts @@ -1,5 +1,7 @@ +import { ArtifactMetadataEntryType } from '@aws-cdk/cloud-assembly-schema'; +import { CloudAssembly } from '@aws-cdk/cx-api'; import { nodeunitShim, Test } from 'nodeunit-shim'; -import { Aws, CfnMapping, CfnResource, Fn, Stack } from '../lib'; +import { App, Aws, CfnMapping, CfnResource, Fn, Stack } from '../lib'; import { toCloudFormation } from './util'; nodeunitShim({ @@ -78,6 +80,13 @@ nodeunitShim({ const expected = { 'Fn::FindInMap': ['mapping', 'instanceCount', { Ref: 'AWS::Region' }] }; test.deepEqual(stack.resolve(v1), expected); test.deepEqual(stack.resolve(v2), expected); + test.deepEqual(toCloudFormation(stack).Mappings, { + mapping: { + instanceCount: { + 'us-east-1': 12, + }, + }, + }); test.done(); }, @@ -99,6 +108,13 @@ nodeunitShim({ test.deepEqual(stack.resolve(v), { 'Fn::FindInMap': ['mapping', { Ref: 'AWS::Region' }, 'size'], }); + test.deepEqual(toCloudFormation(stack).Mappings, { + mapping: { + 'us-east-1': { + size: 12, + }, + }, + }); test.done(); }, @@ -119,6 +135,135 @@ nodeunitShim({ // THEN test.throws(() => mapping.findInMap('not-found', Aws.REGION), /Mapping doesn't contain top-level key 'not-found'/); test.deepEqual(stack.resolve(v), { 'Fn::FindInMap': ['mapping', 'size', { Ref: 'AWS::Region' }] }); + test.deepEqual(toCloudFormation(stack).Mappings, { + mapping: { + size: { + 'us-east-1': 12, + }, + }, + }); test.done(); }, }); + +describe('lazy mapping', () => { + let stack: Stack; + let mapping: CfnMapping; + const backing = { + TopLevelKey1: { + SecondLevelKey1: [1, 2, 3], + SecondLevelKey2: { Hello: 'World' }, + }, + }; + + beforeEach(() => { + stack = new Stack(); + mapping = new CfnMapping(stack, 'Lazy Mapping', { + mapping: backing, + lazy: true, + }); + }); + + it('does not create CfnMapping if findInMap keys can be resolved', () => { + const retrievedValue = mapping.findInMap('TopLevelKey1', 'SecondLevelKey1'); + + expect(stack.resolve(retrievedValue)).toStrictEqual([1, 2, 3]); + expect(toCloudFormation(stack)).toStrictEqual({}); + }); + + it('does not create CfnMapping if findInMap is not called', () => { + expect(toCloudFormation(stack)).toStrictEqual({}); + }); + + it('creates CfnMapping if top level key cannot be resolved', () => { + const retrievedValue = mapping.findInMap(Aws.REGION, 'SecondLevelKey1'); + + expect(stack.resolve(retrievedValue)).toStrictEqual({ 'Fn::FindInMap': ['LazyMapping', { Ref: 'AWS::Region' }, 'SecondLevelKey1'] }); + expect(toCloudFormation(stack)).toStrictEqual({ + Mappings: { + LazyMapping: backing, + }, + }); + }); + + it('creates CfnMapping if second level key cannot be resolved', () => { + const retrievedValue = mapping.findInMap('TopLevelKey1', Aws.REGION); + + expect(stack.resolve(retrievedValue)).toStrictEqual({ 'Fn::FindInMap': ['LazyMapping', 'TopLevelKey1', { Ref: 'AWS::Region' }] }); + expect(toCloudFormation(stack)).toStrictEqual({ + Mappings: { + LazyMapping: backing, + }, + }); + }); + + it('throws if keys can be resolved but are not found in backing', () => { + expect(() => mapping.findInMap('NonExistentKey', 'SecondLevelKey1')) + .toThrowError(/Mapping doesn't contain top-level key .*/); + expect(() => mapping.findInMap('TopLevelKey1', 'NonExistentKey')) + .toThrowError(/Mapping doesn't contain second-level key .*/); + }); +}); + +describe('eager by default', () => { + const backing = { + TopLevelKey1: { + SecondLevelKey1: [1, 2, 3], + SecondLevelKey2: { Hello: 'World' }, + }, + }; + + let app: App; + let stack: Stack; + let mapping: CfnMapping; + + beforeEach(() => { + app = new App(); + stack = new Stack(app, 'Stack'); + mapping = new CfnMapping(stack, 'Lazy Mapping', { + mapping: backing, + }); + }); + + it('emits warning if no findInMap called', () => { + const assembly = app.synth(); + + expect(getInfoAnnotations(assembly)).toStrictEqual([{ + path: '/Stack/Lazy Mapping', + message: 'Consider making this CfnMapping a lazy mapping by providing `lazy: true`: either no findInMap was called or every findInMap could be immediately resolved without using Fn::FindInMap', + }]); + }); + + it('emits warning if every findInMap resolves immediately', () => { + mapping.findInMap('TopLevelKey1', 'SecondLevelKey1'); + + const assembly = app.synth(); + + expect(getInfoAnnotations(assembly)).toStrictEqual([{ + path: '/Stack/Lazy Mapping', + message: 'Consider making this CfnMapping a lazy mapping by providing `lazy: true`: either no findInMap was called or every findInMap could be immediately resolved without using Fn::FindInMap', + }]); + }); + + it('does not emit warning if a findInMap could not resolve immediately', () => { + mapping.findInMap('TopLevelKey1', Aws.REGION); + + const assembly = app.synth(); + + expect(getInfoAnnotations(assembly)).toStrictEqual([]); + }); +}); + +function getInfoAnnotations(casm: CloudAssembly) { + const result = new Array<{ path: string, message: string }>(); + for (const stack of Object.values(casm.manifest.artifacts ?? {})) { + for (const [path, md] of Object.entries(stack.metadata ?? {})) { + for (const x of md) { + if (x.type === ArtifactMetadataEntryType.INFO) { + result.push({ path, message: x.data as string }); + } + } + } + } + return result; +} diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 445346a4a90bc..48d4cd65f93a5 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -808,6 +808,38 @@ Mappings: us-east-2: US East (Ohio) ``` +Mappings can also be synthesized "lazily"; lazy mappings will only render a "Mappings" +section in the synthesized CloudFormation template if some `findInMap` call is unable to +immediately return a concrete value due to one or both of the keys being unresolved tokens +(some value only available at deploy-time). + +For example, the following code will not produce anything in the "Mappings" section. The +call to `findInMap` will be able to resolve the value during synthesis and simply return +`'US East (Ohio)'`. + +```ts +const regionTable = new CfnMapping(this, 'RegionTable', { + mapping: { + regionName: { + 'us-east-1': 'US East (N. Virginia)', + 'us-east-2': 'US East (Ohio)', + }, + }, + lazy: true, +}); + +regionTable.findInMap('regionName', 'us-east-2'); +``` + +On the other hand, the following code will produce the "Mappings" section shown above, +since the second-level key is an unresolved token. The call to `findInMap` will return a +token that resolves to `{ Fn::FindInMap: [ 'RegionTable', 'regionName', { Ref: AWS::Region +} ] }`. + +```ts +regionTable.findInMap('regionName', Aws.REGION); +``` + [cfn-mappings]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html ### Dynamic References @@ -947,4 +979,4 @@ It's possible to synthesize the project with more Resources than the allowed (or Set the context key `@aws-cdk/core:stackResourceLimit` with the proper value, being 0 for disable the limit of resources. - \ No newline at end of file + From 91cf79bc55ffd72b1c79e2218eb76921fbac32b4 Mon Sep 17 00:00:00 2001 From: Joel Cox Date: Fri, 23 Jul 2021 08:46:37 +1000 Subject: [PATCH 10/27] fix(elasticsearch): slow logs incorrectly disabled for Elasticsearch versions lower than 5.1 (#15714) Fixes #15532 As discussed in #15532, this error should not have applied to slow logs in the first place, as they're supported by all Elasticsearch versions. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-elasticsearch/lib/domain.ts | 8 ++------ .../aws-elasticsearch/test/domain.test.ts | 16 ++-------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts index 676d6984234c4..a29b530128c51 100644 --- a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts +++ b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts @@ -1415,12 +1415,8 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { // Validate feature support for the given Elasticsearch version, per // https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-features-by-version.html if (elasticsearchVersionNum < 5.1) { - if ( - props.logging?.slowIndexLogEnabled - || props.logging?.appLogEnabled - || props.logging?.slowSearchLogEnabled - ) { - throw new Error('Error and slow logs publishing requires Elasticsearch version 5.1 or later.'); + if (props.logging?.appLogEnabled) { + throw new Error('Error logs publishing requires Elasticsearch version 5.1 or later.'); } if (props.encryptionAtRest?.enabled) { throw new Error('Encryption of data at rest requires Elasticsearch version 5.1 or later.'); diff --git a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts index 038eb1ebc82db..719341aa29408 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts +++ b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts @@ -1317,26 +1317,14 @@ describe('custom error responses', () => { })).toThrow(/Unknown Elasticsearch version: 5\.4/); }); - test('error when log publishing is enabled for elasticsearch version < 5.1', () => { - const error = /logs publishing requires Elasticsearch version 5.1 or later/; + test('error when error log publishing is enabled for elasticsearch version < 5.1', () => { + const error = /Error logs publishing requires Elasticsearch version 5.1 or later/; expect(() => new Domain(stack, 'Domain1', { version: ElasticsearchVersion.V2_3, logging: { appLogEnabled: true, }, })).toThrow(error); - expect(() => new Domain(stack, 'Domain2', { - version: ElasticsearchVersion.V1_5, - logging: { - slowSearchLogEnabled: true, - }, - })).toThrow(error); - expect(() => new Domain(stack, 'Domain3', { - version: ElasticsearchVersion.V1_5, - logging: { - slowIndexLogEnabled: true, - }, - })).toThrow(error); }); test('error when encryption at rest is enabled for elasticsearch version < 5.1', () => { From 1b5d525cef8ef4209074156c56077eebaa38d57c Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:33:27 -0700 Subject: [PATCH 11/27] feat(aws-kinesisfirehose): DeliveryStream API and basic S3 destination (#15544) This PR implements the minimum DeliveryStream API and S3 destination. More features for DeliveryStream and the S3 destination will follow in future PRs. This work is being tracked in https://github.com/aws/aws-cdk/milestone/16 For more context, see: https://github.com/aws/aws-cdk/pull/15505 and the RFC: https://github.com/aws/aws-cdk-rfcs/pull/342 closes #10810, #15499 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ec2/lib/connections.ts | 3 + packages/@aws-cdk/aws-ec2/package.json | 1 - .../.eslintrc.js | 3 + .../.gitignore | 19 + .../.npmignore | 28 ++ .../aws-kinesisfirehose-destinations/LICENSE | 201 +++++++++ .../aws-kinesisfirehose-destinations/NOTICE | 2 + .../README.md | 22 + .../jest.config.js | 2 + .../lib/common.ts | 32 ++ .../lib/index.ts | 2 + .../lib/private/helpers.ts | 66 +++ .../lib/s3-bucket.ts | 42 ++ .../package.json | 119 +++++ .../rosetta/default.ts-fixture | 11 + .../test/integ.s3-bucket.expected.json | 408 ++++++++++++++++++ .../test/integ.s3-bucket.ts | 28 ++ .../test/s3-bucket.test.ts | 223 ++++++++++ .../@aws-cdk/aws-kinesisfirehose/README.md | 236 +++++++++- .../lib/delivery-stream.ts | 266 ++++++++++++ .../aws-kinesisfirehose/lib/destination.ts | 40 ++ .../@aws-cdk/aws-kinesisfirehose/lib/index.ts | 3 + .../@aws-cdk/aws-kinesisfirehose/package.json | 16 +- .../rosetta/default.ts-fixture | 11 + .../rosetta/with-bucket.ts-fixture | 13 + .../rosetta/with-delivery-stream.ts-fixture | 12 + .../rosetta/with-destination.ts-fixture | 12 + .../test/delivery-stream.test.ts | 314 ++++++++++++++ .../test/integ.delivery-stream.expected.json | 194 +++++++++ .../test/integ.delivery-stream.ts | 37 ++ .../test/kinesisfirehose.test.ts | 6 - .../region-info/build-tools/fact-tables.ts | 29 ++ .../build-tools/generate-static-data.ts | 10 +- packages/@aws-cdk/region-info/lib/fact.ts | 5 + .../@aws-cdk/region-info/lib/region-info.ts | 7 + packages/aws-cdk-lib/package.json | 1 + packages/decdk/package.json | 1 + packages/monocdk/package.json | 1 + tools/pkglint/lib/rules.ts | 1 + 39 files changed, 2414 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/.gitignore create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/.npmignore create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/LICENSE create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/NOTICE create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/README.md create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/jest.config.js create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/common.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/index.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/private/helpers.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/s3-bucket.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/package.json create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/rosetta/default.ts-fixture create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.expected.json create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose-destinations/test/s3-bucket.test.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/lib/delivery-stream.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/lib/destination.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/rosetta/default.ts-fixture create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-bucket.ts-fixture create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-delivery-stream.ts-fixture create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-destination.ts-fixture create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/test/delivery-stream.test.ts create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.expected.json create mode 100644 packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.ts delete mode 100644 packages/@aws-cdk/aws-kinesisfirehose/test/kinesisfirehose.test.ts diff --git a/packages/@aws-cdk/aws-ec2/lib/connections.ts b/packages/@aws-cdk/aws-ec2/lib/connections.ts index 0ecccea97fdb2..b68c04299cdf3 100644 --- a/packages/@aws-cdk/aws-ec2/lib/connections.ts +++ b/packages/@aws-cdk/aws-ec2/lib/connections.ts @@ -20,6 +20,9 @@ import { ISecurityGroup } from './security-group'; * An object that has a Connections object */ export interface IConnectable { + /** + * The network connections associated with this resource. + */ readonly connections: Connections; } diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 985422e5da1fb..2f2dd7ab6e1c4 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -288,7 +288,6 @@ "props-default-doc:@aws-cdk/aws-ec2.AclPortRange.from", "props-default-doc:@aws-cdk/aws-ec2.AclPortRange.to", "docs-public-apis:@aws-cdk/aws-ec2.ConnectionRule", - "docs-public-apis:@aws-cdk/aws-ec2.IConnectable.connections", "docs-public-apis:@aws-cdk/aws-ec2.IInstance", "docs-public-apis:@aws-cdk/aws-ec2.IPrivateSubnet", "docs-public-apis:@aws-cdk/aws-ec2.IPublicSubnet", diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/.eslintrc.js b/packages/@aws-cdk/aws-kinesisfirehose-destinations/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/.gitignore b/packages/@aws-cdk/aws-kinesisfirehose-destinations/.gitignore new file mode 100644 index 0000000000000..147448f7df4fe --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/.gitignore @@ -0,0 +1,19 @@ +*.js +tsconfig.json +*.js.map +*.d.ts +*.generated.ts +dist +lib/generated/resources.ts +.jsii + +.LAST_BUILD +.nyc_output +coverage +nyc.config.js +.LAST_PACKAGE +*.snk +!.eslintrc.js +!jest.config.js + +junit.xml \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/.npmignore b/packages/@aws-cdk/aws-kinesisfirehose-destinations/.npmignore new file mode 100644 index 0000000000000..aaabf1df59065 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ +!*.lit.ts \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/LICENSE b/packages/@aws-cdk/aws-kinesisfirehose-destinations/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/NOTICE b/packages/@aws-cdk/aws-kinesisfirehose-destinations/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/README.md b/packages/@aws-cdk/aws-kinesisfirehose-destinations/README.md new file mode 100644 index 0000000000000..03ef4657b3f78 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/README.md @@ -0,0 +1,22 @@ +# Amazon Kinesis Data Firehose Destinations Library + + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. + +--- + + + +This library provides constructs for adding destinations to a Amazon Kinesis Data Firehose +delivery stream. Destinations can be added by specifying the `destinations` prop when +defining a delivery stream. + +See [Amazon Kinesis Data Firehose module README](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-kinesisfirehose-readme.html) for usage examples. diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/jest.config.js b/packages/@aws-cdk/aws-kinesisfirehose-destinations/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/common.ts b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/common.ts new file mode 100644 index 0000000000000..3a97970d1ddbb --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/common.ts @@ -0,0 +1,32 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as logs from '@aws-cdk/aws-logs'; + +/** + * Generic properties for defining a delivery stream destination. + */ +export interface CommonDestinationProps { + /** + * If true, log errors when data transformation or data delivery fails. + * + * If `logGroup` is provided, this will be implicitly set to `true`. + * + * @default true - errors are logged. + */ + readonly logging?: boolean; + + /** + * The CloudWatch log group where log streams will be created to hold error logs. + * + * @default - if `logging` is set to `true`, a log group will be created for you. + */ + readonly logGroup?: logs.ILogGroup; + + /** + * The IAM role associated with this destination. + * + * Assumed by Kinesis Data Firehose to invoke processors and write to destinations + * + * @default - a role will be created with default permissions. + */ + readonly role?: iam.IRole; +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/index.ts b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/index.ts new file mode 100644 index 0000000000000..7297f91a768c8 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/index.ts @@ -0,0 +1,2 @@ +export * from './common'; +export * from './s3-bucket'; diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/private/helpers.ts b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/private/helpers.ts new file mode 100644 index 0000000000000..a2032c41914a0 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/private/helpers.ts @@ -0,0 +1,66 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as firehose from '@aws-cdk/aws-kinesisfirehose'; +import * as logs from '@aws-cdk/aws-logs'; +import * as cdk from '@aws-cdk/core'; +import { Construct, Node } from 'constructs'; + +export interface DestinationLoggingProps { + /** + * If true, log errors when data transformation or data delivery fails. + * + * If `logGroup` is provided, this will be implicitly set to `true`. + * + * @default true - errors are logged. + */ + readonly logging?: boolean; + + /** + * The CloudWatch log group where log streams will be created to hold error logs. + * + * @default - if `logging` is set to `true`, a log group will be created for you. + */ + readonly logGroup?: logs.ILogGroup; + + /** + * The IAM role associated with this destination. + */ + readonly role: iam.IRole; + + /** + * The ID of the stream that is created in the log group where logs will be placed. + * + * Must be unique within the log group, so should be different every time this function is called. + */ + readonly streamId: string; +} + +export interface DestinationLoggingOutput { + /** + * Logging options that will be injected into the destination configuration. + */ + readonly loggingOptions: firehose.CfnDeliveryStream.CloudWatchLoggingOptionsProperty; + + /** + * Resources that were created by the sub-config creator that must be deployed before the delivery stream is deployed. + */ + readonly dependables: cdk.IDependable[]; +} + +export function createLoggingOptions(scope: Construct, props: DestinationLoggingProps): DestinationLoggingOutput | undefined { + if (props.logging === false && props.logGroup) { + throw new Error('logging cannot be set to false when logGroup is provided'); + } + if (props.logging !== false || props.logGroup) { + const logGroup = props.logGroup ?? Node.of(scope).tryFindChild('LogGroup') as logs.ILogGroup ?? new logs.LogGroup(scope, 'LogGroup'); + const logGroupGrant = logGroup.grantWrite(props.role); + return { + loggingOptions: { + enabled: true, + logGroupName: logGroup.logGroupName, + logStreamName: logGroup.addStream(props.streamId).logStreamName, + }, + dependables: [logGroupGrant], + }; + } + return undefined; +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/s3-bucket.ts b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/s3-bucket.ts new file mode 100644 index 0000000000000..ad3c0313ff061 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/lib/s3-bucket.ts @@ -0,0 +1,42 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as firehose from '@aws-cdk/aws-kinesisfirehose'; +import * as s3 from '@aws-cdk/aws-s3'; +import { Construct } from 'constructs'; +import { CommonDestinationProps } from './common'; +import { createLoggingOptions } from './private/helpers'; + +/** + * Props for defining an S3 destination of a Kinesis Data Firehose delivery stream. + */ +export interface S3BucketProps extends CommonDestinationProps { } + +/** + * An S3 bucket destination for data from a Kinesis Data Firehose delivery stream. + */ +export class S3Bucket implements firehose.IDestination { + constructor(private readonly bucket: s3.IBucket, private readonly props: S3BucketProps = {}) { } + + bind(scope: Construct, _options: firehose.DestinationBindOptions): firehose.DestinationConfig { + const role = this.props.role ?? new iam.Role(scope, 'S3 Destination Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), + }); + + const bucketGrant = this.bucket.grantReadWrite(role); + + const { loggingOptions, dependables: loggingDependables } = createLoggingOptions(scope, { + logging: this.props.logging, + logGroup: this.props.logGroup, + role, + streamId: 'S3Destination', + }) ?? {}; + + return { + extendedS3DestinationConfiguration: { + cloudWatchLoggingOptions: loggingOptions, + roleArn: role.roleArn, + bucketArn: this.bucket.bucketArn, + }, + dependables: [bucketGrant, ...(loggingDependables ?? [])], + }; + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/package.json b/packages/@aws-cdk/aws-kinesisfirehose-destinations/package.json new file mode 100644 index 0000000000000..92cb7c8aa4332 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/package.json @@ -0,0 +1,119 @@ +{ + "name": "@aws-cdk/aws-kinesisfirehose-destinations", + "version": "0.0.0", + "description": "CDK Destinations Constructs for AWS Kinesis Firehose", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awscdk.services.kinesisfirehose.destinations", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "kinesisfirehose-destinations" + } + }, + "dotnet": { + "namespace": "Amazon.CDK.AWS.KinesisFirehose.Destinations", + "packageId": "Amazon.CDK.AWS.KinesisFirehose.Destinations", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-cdk.aws-kinesisfirehose-destinations", + "module": "aws_cdk.aws_kinesisfirehose_destinations", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ] + } + }, + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-kinesisfirehose-destinations" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "build+test+package": "yarn build+test && yarn package", + "build+test": "yarn build && yarn test", + "compat": "cdk-compat", + "rosetta:extract": "yarn --silent jsii-rosetta extract", + "build+extract": "yarn build && yarn rosetta:extract", + "build+test+extract": "yarn build+test && yarn rosetta:extract" + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "kinesisfirehose" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.24", + "cdk-build-tools": "0.0.0", + "cdk-integ-tools": "0.0.0", + "cfn2ts": "0.0.0", + "jest": "^26.6.3", + "pkglint": "0.0.0", + "@aws-cdk/assert-internal": "0.0.0" + }, + "dependencies": { + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/core": "0.0.0", + "constructs": "^3.3.69" + }, + "homepage": "https://github.com/aws/aws-cdk", + "peerDependencies": { + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/core": "0.0.0", + "constructs": "^3.3.69" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "experimental", + "awslint": { + "exclude": [] + }, + "awscdkio": { + "announce": false + }, + "cdk-build": { + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-kinesisfirehose-destinations/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..fe46e06908b34 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { Construct } from '@aws-cdk/core'; +import { S3Bucket } from '@aws-cdk/aws-kinesisfirehose-destinations'; + +class Fixture extends Construct { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.expected.json b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.expected.json new file mode 100644 index 0000000000000..00bc62879e11e --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.expected.json @@ -0,0 +1,408 @@ +{ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "BucketPolicyE9A3008A": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "Bucket83908E77" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "BucketAutoDeleteObjectsCustomResourceBAFD23C2": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "Bucket83908E77" + } + }, + "DependsOn": [ + "BucketPolicyE9A3008A" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1S3BucketF01ADF6B" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1S3VersionKey6FC34F51" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1S3VersionKey6FC34F51" + } + ] + } + ] + } + ] + ] + } + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "Bucket83908E77" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "LogGroupF5B46931": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "LogGroupS3Destination70CE1003": { + "Type": "AWS::Logs::LogStream", + "Properties": { + "LogGroupName": { + "Ref": "LogGroupF5B46931" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "DeliveryStreamServiceRole964EEBCC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "firehose.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "DeliveryStreamS3DestinationRole500FC089": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "firehose.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "DeliveryStreamS3DestinationRoleDefaultPolicy3015D8C7": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "LogGroupF5B46931", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "DeliveryStreamS3DestinationRoleDefaultPolicy3015D8C7", + "Roles": [ + { + "Ref": "DeliveryStreamS3DestinationRole500FC089" + } + ] + } + }, + "DeliveryStreamF6D5572D": { + "Type": "AWS::KinesisFirehose::DeliveryStream", + "Properties": { + "DeliveryStreamType": "DirectPut", + "ExtendedS3DestinationConfiguration": { + "BucketARN": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "CloudWatchLoggingOptions": { + "Enabled": true, + "LogGroupName": { + "Ref": "LogGroupF5B46931" + }, + "LogStreamName": { + "Ref": "LogGroupS3Destination70CE1003" + } + }, + "RoleARN": { + "Fn::GetAtt": [ + "DeliveryStreamS3DestinationRole500FC089", + "Arn" + ] + } + } + }, + "DependsOn": [ + "DeliveryStreamS3DestinationRoleDefaultPolicy3015D8C7" + ] + } + }, + "Parameters": { + "AssetParameters1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1S3BucketF01ADF6B": { + "Type": "String", + "Description": "S3 bucket for asset \"1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1\"" + }, + "AssetParameters1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1S3VersionKey6FC34F51": { + "Type": "String", + "Description": "S3 key for asset version \"1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1\"" + }, + "AssetParameters1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1ArtifactHash9ECACDFD": { + "Type": "String", + "Description": "Artifact hash for asset \"1a8becf42c48697a059094af1e94aa6bc6df0512d30433db8c22618ca02dfca1\"" + } + }, + "Mappings": { + "awscdkawskinesisfirehoseCidrBlocks": { + "af-south-1": { + "FirehoseCidrBlock": "13.244.121.224/27" + }, + "ap-east-1": { + "FirehoseCidrBlock": "18.162.221.32/27" + }, + "ap-northeast-1": { + "FirehoseCidrBlock": "13.113.196.224/27" + }, + "ap-northeast-2": { + "FirehoseCidrBlock": "13.209.1.64/27" + }, + "ap-northeast-3": { + "FirehoseCidrBlock": "13.208.177.192/27" + }, + "ap-south-1": { + "FirehoseCidrBlock": "13.232.67.32/27" + }, + "ap-southeast-1": { + "FirehoseCidrBlock": "13.228.64.192/27" + }, + "ap-southeast-2": { + "FirehoseCidrBlock": "13.210.67.224/27" + }, + "ca-central-1": { + "FirehoseCidrBlock": "35.183.92.128/27" + }, + "cn-north-1": { + "FirehoseCidrBlock": "52.81.151.32/27" + }, + "cn-northwest-1": { + "FirehoseCidrBlock": "161.189.23.64/27" + }, + "eu-central-1": { + "FirehoseCidrBlock": "35.158.127.160/27" + }, + "eu-north-1": { + "FirehoseCidrBlock": "13.53.63.224/27" + }, + "eu-south-1": { + "FirehoseCidrBlock": "15.161.135.128/27" + }, + "eu-west-1": { + "FirehoseCidrBlock": "52.19.239.192/27" + }, + "eu-west-2": { + "FirehoseCidrBlock": "18.130.1.96/27" + }, + "eu-west-3": { + "FirehoseCidrBlock": "35.180.1.96/27" + }, + "me-south-1": { + "FirehoseCidrBlock": "15.185.91.0/27" + }, + "sa-east-1": { + "FirehoseCidrBlock": "18.228.1.128/27" + }, + "us-east-1": { + "FirehoseCidrBlock": "52.70.63.192/27" + }, + "us-east-2": { + "FirehoseCidrBlock": "13.58.135.96/27" + }, + "us-gov-east-1": { + "FirehoseCidrBlock": "18.253.138.96/27" + }, + "us-gov-west-1": { + "FirehoseCidrBlock": "52.61.204.160/27" + }, + "us-west-1": { + "FirehoseCidrBlock": "13.57.135.192/27" + }, + "us-west-2": { + "FirehoseCidrBlock": "52.89.255.224/27" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.ts b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.ts new file mode 100644 index 0000000000000..222eaa6c0fb84 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env node +import * as firehose from '@aws-cdk/aws-kinesisfirehose'; +import * as logs from '@aws-cdk/aws-logs'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as destinations from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-firehose-delivery-stream-s3-all-properties'); + +const bucket = new s3.Bucket(stack, 'Bucket', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + autoDeleteObjects: true, +}); + +const logGroup = new logs.LogGroup(stack, 'LogGroup', { + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [new destinations.S3Bucket(bucket, { + logging: true, + logGroup: logGroup, + })], +}); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/s3-bucket.test.ts b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/s3-bucket.test.ts new file mode 100644 index 0000000000000..50e891b3091d1 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/s3-bucket.test.ts @@ -0,0 +1,223 @@ +import '@aws-cdk/assert-internal/jest'; +import { ABSENT, MatchStyle, ResourcePart, anything, arrayWith } from '@aws-cdk/assert-internal'; +import * as iam from '@aws-cdk/aws-iam'; +import * as firehose from '@aws-cdk/aws-kinesisfirehose'; +import * as logs from '@aws-cdk/aws-logs'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as firehosedestinations from '../lib'; + +describe('S3 destination', () => { + let stack: cdk.Stack; + let bucket: s3.IBucket; + let destinationRole: iam.IRole; + + beforeEach(() => { + stack = new cdk.Stack(); + bucket = new s3.Bucket(stack, 'Bucket'); + destinationRole = new iam.Role(stack, 'Destination Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), + }); + }); + + it('provides defaults when no configuration is provided', () => { + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [new firehosedestinations.S3Bucket(bucket, { role: destinationRole })], + }); + + expect(stack).toHaveResource('AWS::KinesisFirehose::DeliveryStream', { + ExtendedS3DestinationConfiguration: { + BucketARN: stack.resolve(bucket.bucketArn), + CloudWatchLoggingOptions: { + Enabled: true, + LogGroupName: anything(), + LogStreamName: anything(), + }, + RoleARN: stack.resolve(destinationRole.roleArn), + }, + }); + expect(stack).toHaveResource('AWS::Logs::LogGroup'); + expect(stack).toHaveResource('AWS::Logs::LogStream'); + }); + + it('creates a role when none is provided', () => { + + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [new firehosedestinations.S3Bucket(bucket)], + }); + + expect(stack).toHaveResourceLike('AWS::KinesisFirehose::DeliveryStream', { + ExtendedS3DestinationConfiguration: { + RoleARN: { + 'Fn::GetAtt': [ + 'DeliveryStreamS3DestinationRoleD96B8345', + 'Arn', + ], + }, + }, + }); + expect(stack).toMatchTemplate({ + ['DeliveryStreamS3DestinationRoleD96B8345']: { + Type: 'AWS::IAM::Role', + }, + }, MatchStyle.SUPERSET); + }); + + it('grants read/write access to the bucket', () => { + const destination = new firehosedestinations.S3Bucket(bucket, { role: destinationRole }); + + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [destination], + }); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Roles: [stack.resolve(destinationRole.roleName)], + PolicyDocument: { + Statement: [ + { + Action: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:List*', + 's3:DeleteObject*', + 's3:PutObject*', + 's3:Abort*', + ], + Effect: 'Allow', + Resource: [ + stack.resolve(bucket.bucketArn), + { 'Fn::Join': ['', [stack.resolve(bucket.bucketArn), '/*']] }, + ], + }, + ], + }, + }); + }); + + it('bucket and log group grants are depended on by delivery stream', () => { + const logGroup = logs.LogGroup.fromLogGroupName(stack, 'Log Group', 'evergreen'); + const destination = new firehosedestinations.S3Bucket(bucket, { role: destinationRole, logGroup }); + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [destination], + }); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyName: 'DestinationRoleDefaultPolicy1185C75D', + Roles: [stack.resolve(destinationRole.roleName)], + PolicyDocument: { + Statement: [ + { + Action: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:List*', + 's3:DeleteObject*', + 's3:PutObject*', + 's3:Abort*', + ], + Effect: 'Allow', + Resource: [ + stack.resolve(bucket.bucketArn), + { 'Fn::Join': ['', [stack.resolve(bucket.bucketArn), '/*']] }, + ], + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: stack.resolve(logGroup.logGroupArn), + }, + ], + }, + }); + expect(stack).toHaveResourceLike('AWS::KinesisFirehose::DeliveryStream', { + DependsOn: ['DestinationRoleDefaultPolicy1185C75D'], + }, ResourcePart.CompleteDefinition); + }); + + describe('logging', () => { + it('creates resources and configuration by default', () => { + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [new firehosedestinations.S3Bucket(bucket)], + }); + + expect(stack).toHaveResource('AWS::Logs::LogGroup'); + expect(stack).toHaveResource('AWS::Logs::LogStream'); + expect(stack).toHaveResourceLike('AWS::KinesisFirehose::DeliveryStream', { + ExtendedS3DestinationConfiguration: { + CloudWatchLoggingOptions: { + Enabled: true, + LogGroupName: anything(), + LogStreamName: anything(), + }, + }, + }); + }); + + it('does not create resources or configuration if disabled', () => { + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [new firehosedestinations.S3Bucket(bucket, { logging: false })], + }); + + expect(stack).not.toHaveResource('AWS::Logs::LogGroup'); + expect(stack).toHaveResourceLike('AWS::KinesisFirehose::DeliveryStream', { + ExtendedS3DestinationConfiguration: { + CloudWatchLoggingOptions: ABSENT, + }, + }); + }); + + it('uses provided log group', () => { + const logGroup = new logs.LogGroup(stack, 'Log Group'); + + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [new firehosedestinations.S3Bucket(bucket, { logGroup })], + }); + + expect(stack).toCountResources('AWS::Logs::LogGroup', 1); + expect(stack).toHaveResourceLike('AWS::KinesisFirehose::DeliveryStream', { + ExtendedS3DestinationConfiguration: { + CloudWatchLoggingOptions: { + Enabled: true, + LogGroupName: stack.resolve(logGroup.logGroupName), + LogStreamName: anything(), + }, + }, + }); + }); + + it('throws error if logging disabled but log group provided', () => { + const destination = new firehosedestinations.S3Bucket(bucket, { logging: false, logGroup: new logs.LogGroup(stack, 'Log Group') }); + + expect(() => new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [destination], + })).toThrowError('logging cannot be set to false when logGroup is provided'); + }); + + it('grants log group write permissions to destination role', () => { + const logGroup = new logs.LogGroup(stack, 'Log Group'); + + new firehose.DeliveryStream(stack, 'DeliveryStream', { + destinations: [new firehosedestinations.S3Bucket(bucket, { logGroup, role: destinationRole })], + }); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Roles: [stack.resolve(destinationRole.roleName)], + PolicyDocument: { + Statement: arrayWith( + { + Action: [ + 'logs:CreateLogStream', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: stack.resolve(logGroup.logGroupArn), + }, + ), + }, + }); + }); + }); +}); diff --git a/packages/@aws-cdk/aws-kinesisfirehose/README.md b/packages/@aws-cdk/aws-kinesisfirehose/README.md index 9c4d9f96c6f36..5034ac4a0765f 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/README.md +++ b/packages/@aws-cdk/aws-kinesisfirehose/README.md @@ -9,8 +9,242 @@ > > [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. + --- -This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. +[Amazon Kinesis Data Firehose](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html) +is a service for fully-managed delivery of real-time streaming data to storage services +such as Amazon S3, Amazon Redshift, Amazon Elasticsearch, Splunk, or any custom HTTP +endpoint or third-party services such as Datadog, Dynatrace, LogicMonitor, MongoDB, New +Relic, and Sumo Logic. + +Kinesis Data Firehose delivery streams are distinguished from Kinesis data streams in +their models of consumtpion. Whereas consumers read from a data stream by actively pulling +data from the stream, a delivery stream pushes data to its destination on a regular +cadence. This means that data streams are intended to have consumers that do on-demand +processing, like AWS Lambda or Amazon EC2. On the other hand, delivery streams are +intended to have destinations that are sources for offline processing and analytics, such +as Amazon S3 and Amazon Redshift. + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) +project. It allows you to define Kinesis Data Firehose delivery streams. + +## Defining a Delivery Stream + +In order to define a Delivery Stream, you must specify a destination. An S3 bucket can be +used as a destination. More supported destinations are covered [below](#destinations). + +```ts +import * as destinations from '@aws-cdk/aws-kinesisfirehose-destinations'; +import * as s3 from '@aws-cdk/aws-s3'; + +const bucket = new s3.Bucket(this, 'Bucket'); +new DeliveryStream(this, 'Delivery Stream', { + destinations: [new destinations.S3Bucket(bucket)], +}); +``` + +The above example defines the following resources: + +- An S3 bucket +- A Kinesis Data Firehose delivery stream with Direct PUT as the source and CloudWatch + error logging turned on. +- An IAM role which gives the delivery stream permission to write to the S3 bucket. + +## Sources + +There are two main methods of sourcing input data: Kinesis Data Streams and via a "direct +put". This construct library currently only supports "direct put". See [#15500](https://github.com/aws/aws-cdk/issues/15500) to track the status of adding support for Kinesis Data Streams. + +See: [Sending Data to a Delivery Stream](https://docs.aws.amazon.com/firehose/latest/dev/basic-write.html) +in the *Kinesis Data Firehose Developer Guide*. + +### Direct Put + +Data must be provided via "direct put", ie., by using a `PutRecord` or `PutRecordBatch` API call. There are a number of ways of doing +so, such as: + +- Kinesis Agent: a standalone Java application that monitors and delivers files while + handling file rotation, checkpointing, and retries. See: [Writing to Kinesis Data Firehose Using Kinesis Agent](https://docs.aws.amazon.com/firehose/latest/dev/writing-with-agents.html) + in the *Kinesis Data Firehose Developer Guide*. +- AWS SDK: a general purpose solution that allows you to deliver data to a delivery stream + from anywhere using Java, .NET, Node.js, Python, or Ruby. See: [Writing to Kinesis Data Firehose Using the AWS SDK](https://docs.aws.amazon.com/firehose/latest/dev/writing-with-sdk.html) + in the *Kinesis Data Firehose Developer Guide*. +- CloudWatch Logs: subscribe to a log group and receive filtered log events directly into + a delivery stream. See: [logs-destinations](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-logs-destinations-readme.html). +- Eventbridge: add an event rule target to send events to a delivery stream based on the + rule filtering. See: [events-targets](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-events-targets-readme.html). +- SNS: add a subscription to send all notifications from the topic to a delivery + stream. See: [sns-subscriptions](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-sns-subscriptions-readme.html). +- IoT: add an action to an IoT rule to send various IoT information to a delivery stream + +## Destinations + +The following destinations are supported. See [kinesisfirehose-destinations](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-kinesisfirehose-destinations-readme.html) +for the implementations of these destinations. + +### S3 + +Defining a delivery stream with an S3 bucket destination: + +```ts +import * as s3 from '@aws-cdk/aws-s3'; +import * as destinations from '@aws-cdk/aws-kinesisfirehose-destinations'; + +const bucket = new s3.Bucket(this, 'Bucket'); + +const s3Destination = new destinations.S3Bucket(bucket); + +new DeliveryStream(this, 'Delivery Stream', { + destinations: [s3Destination], +}); +``` + +## Monitoring + +Kinesis Data Firehose is integrated with CloudWatch, so you can monitor the performance of +your delivery streams via logs and metrics. + +### Logs + +Kinesis Data Firehose will send logs to CloudWatch when data transformation or data +delivery fails. The CDK will enable logging by default and create a CloudWatch LogGroup +and LogStream for your Delivery Stream. + +You can provide a specific log group to specify where the CDK will create the log streams +where log events will be sent: + +```ts fixture=with-bucket +import * as destinations from '@aws-cdk/aws-kinesisfirehose-destinations'; +import * as logs from '@aws-cdk/aws-logs'; + +const logGroup = new logs.LogGroup(this, 'Log Group'); +const destination = new destinations.S3Bucket(bucket, { + logGroup: logGroup, +}); +new DeliveryStream(this, 'Delivery Stream', { + destinations: [destination], +}); +``` + +Logging can also be disabled: + +```ts fixture=with-bucket +import * as destinations from '@aws-cdk/aws-kinesisfirehose-destinations'; + +const destination = new destinations.S3Bucket(bucket, { + logging: false, +}); +new DeliveryStream(this, 'Delivery Stream', { + destinations: [destination], +}); +``` + +See: [Monitoring using CloudWatch Logs](https://docs.aws.amazon.com/firehose/latest/dev/monitoring-with-cloudwatch-logs.html) +in the *Kinesis Data Firehose Developer Guide*. + +## Specifying an IAM role + +The DeliveryStream class automatically creates IAM service roles with all the minimum +necessary permissions for Kinesis Data Firehose to access the resources referenced by your +delivery stream. One service role is created for the delivery stream that allows Kinesis +Data Firehose to read from a Kinesis data stream (if one is configured as the delivery +stream source) and for server-side encryption. Another service role is created for each +destination, which gives Kinesis Data Firehose write access to the destination resource, +as well as the ability to invoke data transformers and read schemas for record format +conversion. If you wish, you may specify your own IAM role for either the delivery stream +or the destination service role, or both. It must have the correct trust policy (it must +allow Kinesis Data Firehose to assume it) or delivery stream creation or data delivery +will fail. Other required permissions to destination resources, encryption keys, etc., +will be provided automatically. + +```ts fixture=with-bucket +import * as destinations from '@aws-cdk/aws-kinesisfirehose-destinations'; +import * as iam from '@aws-cdk/aws-iam'; + +// Create service roles for the delivery stream and destination. +// These can be used for other purposes and granted access to different resources. +// They must include the Kinesis Data Firehose service principal in their trust policies. +// Two separate roles are shown below, but the same role can be used for both purposes. +const deliveryStreamRole = new iam.Role(this, 'Delivery Stream Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), +}); +const destinationRole = new iam.Role(this, 'Destination Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), +}); + +// Specify the roles created above when defining the destination and delivery stream. +const destination = new destinations.S3Bucket(bucket, { role: destinationRole }); +new DeliveryStream(this, 'Delivery Stream', { + destinations: [destination], + role: deliveryStreamRole, +}); +``` + +See [Controlling Access](https://docs.aws.amazon.com/firehose/latest/dev/controlling-access.html) +in the *Kinesis Data Firehose Developer Guide*. + +## Granting application access to a delivery stream + +IAM roles, users or groups which need to be able to work with delivery streams should be +granted IAM permissions. + +Any object that implements the `IGrantable` interface (ie., has an associated principal) +can be granted permissions to a delivery stream by calling: + +- `grantPutRecords(principal)` - grants the principal the ability to put records onto the + delivery stream +- `grant(principal, ...actions)` - grants the principal permission to a custom set of + actions + +```ts fixture=with-delivery-stream +import * as iam from '@aws-cdk/aws-iam'; +const lambdaRole = new iam.Role(this, 'Role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), +}); + +// Give the role permissions to write data to the delivery stream +deliveryStream.grantPutRecords(lambdaRole); +``` + +The following write permissions are provided to a service principal by the `grantPutRecords()` method: + +- `firehose:PutRecord` +- `firehose:PutRecordBatch` + +## Granting a delivery stream access to a resource + +Conversely to the above, Kinesis Data Firehose requires permissions in order for delivery +streams to interact with resources that you own. For example, if an S3 bucket is specified +as a destination of a delivery stream, the delivery stream must be granted permissions to +put and get objects from the bucket. When using the built-in AWS service destinations +found in the `@aws-cdk/aws-kinesisfirehose-destinations` module, the CDK grants the +permissions automatically. However, custom or third-party destinations may require custom +permissions. In this case, use the delivery stream as an `IGrantable`, as follows: + +```ts fixture=with-delivery-stream +import * as lambda from '@aws-cdk/aws-lambda'; + +const fn = new lambda.Function(this, 'Function', { + code: lambda.Code.fromInline('exports.handler = (event) => {}'), + runtime: lambda.Runtime.NODEJS_14_X, + handler: 'index.handler', +}); + +fn.grantInvoke(deliveryStream); +``` + +## Multiple destinations + +Though the delivery stream allows specifying an array of destinations, only one +destination per delivery stream is currently allowed. This limitation is enforced at CDK +synthesis time and will throw an error. diff --git a/packages/@aws-cdk/aws-kinesisfirehose/lib/delivery-stream.ts b/packages/@aws-cdk/aws-kinesisfirehose/lib/delivery-stream.ts new file mode 100644 index 0000000000000..4968930808be6 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/lib/delivery-stream.ts @@ -0,0 +1,266 @@ +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import { RegionInfo } from '@aws-cdk/region-info'; +import { Construct, Node } from 'constructs'; +import { IDestination } from './destination'; +import { CfnDeliveryStream } from './kinesisfirehose.generated'; + +const PUT_RECORD_ACTIONS = [ + 'firehose:PutRecord', + 'firehose:PutRecordBatch', +]; + +/** + * Represents a Kinesis Data Firehose delivery stream. + */ +export interface IDeliveryStream extends cdk.IResource, iam.IGrantable, ec2.IConnectable { + /** + * The ARN of the delivery stream. + * + * @attribute + */ + readonly deliveryStreamArn: string; + + /** + * The name of the delivery stream. + * + * @attribute + */ + readonly deliveryStreamName: string; + + /** + * Grant the `grantee` identity permissions to perform `actions`. + */ + grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant; + + /** + * Grant the `grantee` identity permissions to perform `firehose:PutRecord` and `firehose:PutRecordBatch` actions on this delivery stream. + */ + grantPutRecords(grantee: iam.IGrantable): iam.Grant; + + /** + * Return the given named metric for this delivery stream. + */ + metric(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric; +} + +/** + * Base class for new and imported Kinesis Data Firehose delivery streams. + */ +abstract class DeliveryStreamBase extends cdk.Resource implements IDeliveryStream { + + public abstract readonly deliveryStreamName: string; + + public abstract readonly deliveryStreamArn: string; + + public abstract readonly grantPrincipal: iam.IPrincipal; + + /** + * Network connections between Kinesis Data Firehose and other resources, i.e. Redshift cluster. + */ + public readonly connections: ec2.Connections; + + constructor(scope: Construct, id: string, props: cdk.ResourceProps = {}) { + super(scope, id, props); + + this.connections = setConnections(this); + } + + public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { + return iam.Grant.addToPrincipal({ + resourceArns: [this.deliveryStreamArn], + grantee: grantee, + actions: actions, + }); + } + + public grantPutRecords(grantee: iam.IGrantable): iam.Grant { + return this.grant(grantee, ...PUT_RECORD_ACTIONS); + } + + public metric(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return new cloudwatch.Metric({ + namespace: 'AWS/Firehose', + metricName: metricName, + dimensions: { + DeliveryStreamName: this.deliveryStreamName, + }, + ...props, + }).attachTo(this); + } +} + +/** + * Properties for a new delivery stream. + */ +export interface DeliveryStreamProps { + /** + * The destinations that this delivery stream will deliver data to. + * + * Only a singleton array is supported at this time. + */ + readonly destinations: IDestination[]; + + /** + * A name for the delivery stream. + * + * @default - a name is generated by CloudFormation. + */ + readonly deliveryStreamName?: string; + + /** + * The IAM role associated with this delivery stream. + * + * Assumed by Kinesis Data Firehose to read from sources and encrypt data server-side. + * + * @default - a role will be created with default permissions. + */ + readonly role?: iam.IRole; +} + +/** + * A full specification of a delivery stream that can be used to import it fluently into the CDK application. + */ +export interface DeliveryStreamAttributes { + /** + * The ARN of the delivery stream. + * + * At least one of deliveryStreamArn and deliveryStreamName must be provided. + * + * @default - derived from `deliveryStreamName`. + */ + readonly deliveryStreamArn?: string; + + /** + * The name of the delivery stream + * + * At least one of deliveryStreamName and deliveryStreamArn must be provided. + * + * @default - derived from `deliveryStreamArn`. + */ + readonly deliveryStreamName?: string; + + /** + * The IAM role associated with this delivery stream. + * + * Assumed by Kinesis Data Firehose to read from sources and encrypt data server-side. + * + * @default - the imported stream cannot be granted access to other resources as an `iam.IGrantable`. + */ + readonly role?: iam.IRole; +} + +/** + * Create a Kinesis Data Firehose delivery stream + * + * @resource AWS::KinesisFirehose::DeliveryStream + */ +export class DeliveryStream extends DeliveryStreamBase { + /** + * Import an existing delivery stream from its name. + */ + static fromDeliveryStreamName(scope: Construct, id: string, deliveryStreamName: string): IDeliveryStream { + return this.fromDeliveryStreamAttributes(scope, id, { deliveryStreamName }); + } + + /** + * Import an existing delivery stream from its ARN. + */ + static fromDeliveryStreamArn(scope: Construct, id: string, deliveryStreamArn: string): IDeliveryStream { + return this.fromDeliveryStreamAttributes(scope, id, { deliveryStreamArn }); + } + + /** + * Import an existing delivery stream from its attributes. + */ + static fromDeliveryStreamAttributes(scope: Construct, id: string, attrs: DeliveryStreamAttributes): IDeliveryStream { + if (!attrs.deliveryStreamName && !attrs.deliveryStreamArn) { + throw new Error('Either deliveryStreamName or deliveryStreamArn must be provided in DeliveryStreamAttributes'); + } + const deliveryStreamName = attrs.deliveryStreamName ?? + cdk.Stack.of(scope).splitArn(attrs.deliveryStreamArn!, cdk.ArnFormat.SLASH_RESOURCE_NAME).resourceName; + + if (!deliveryStreamName) { + throw new Error(`No delivery stream name found in ARN: '${attrs.deliveryStreamArn}'`); + } + const deliveryStreamArn = attrs.deliveryStreamArn ?? cdk.Stack.of(scope).formatArn({ + service: 'firehose', + resource: 'deliverystream', + resourceName: attrs.deliveryStreamName, + arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME, + }); + class Import extends DeliveryStreamBase { + public readonly deliveryStreamName = deliveryStreamName!; + public readonly deliveryStreamArn = deliveryStreamArn; + public readonly grantPrincipal = attrs.role ?? new iam.UnknownPrincipal({ resource: this }); + } + return new Import(scope, id); + } + + readonly deliveryStreamName: string; + + readonly deliveryStreamArn: string; + + readonly grantPrincipal: iam.IPrincipal; + + constructor(scope: Construct, id: string, props: DeliveryStreamProps) { + super(scope, id, { + physicalName: props.deliveryStreamName, + }); + + if (props.destinations.length !== 1) { + throw new Error(`Only one destination is allowed per delivery stream, given ${props.destinations.length}`); + } + + const role = props.role ?? new iam.Role(this, 'Service Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), + }); + this.grantPrincipal = role; + + const destinationConfig = props.destinations[0].bind(this, {}); + + const resource = new CfnDeliveryStream(this, 'Resource', { + deliveryStreamName: props.deliveryStreamName, + deliveryStreamType: 'DirectPut', + ...destinationConfig, + }); + destinationConfig.dependables?.forEach(dependable => resource.node.addDependency(dependable)); + + this.deliveryStreamArn = this.getResourceArnAttribute(resource.attrArn, { + service: 'kinesis', + resource: 'deliverystream', + resourceName: this.physicalName, + }); + this.deliveryStreamName = this.getResourceNameAttribute(resource.ref); + } +} + +function setConnections(scope: Construct) { + const stack = cdk.Stack.of(scope); + + const mappingId = '@aws-cdk/aws-kinesisfirehose.CidrBlocks'; + let cfnMapping = Node.of(stack).tryFindChild(mappingId) as cdk.CfnMapping; + + if (!cfnMapping) { + const mapping: {[region: string]: { FirehoseCidrBlock: string }} = {}; + RegionInfo.regions.forEach((regionInfo) => { + if (regionInfo.firehoseCidrBlock) { + mapping[regionInfo.name] = { + FirehoseCidrBlock: regionInfo.firehoseCidrBlock, + }; + } + }); + cfnMapping = new cdk.CfnMapping(stack, mappingId, { + mapping, + lazy: true, + }); + } + + const cidrBlock = cfnMapping.findInMap(stack.region, 'FirehoseCidrBlock'); + + return new ec2.Connections({ + peer: ec2.Peer.ipv4(cidrBlock), + }); +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose/lib/destination.ts b/packages/@aws-cdk/aws-kinesisfirehose/lib/destination.ts new file mode 100644 index 0000000000000..eb277babcdebb --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/lib/destination.ts @@ -0,0 +1,40 @@ +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnDeliveryStream } from './kinesisfirehose.generated'; + +/** + * A Kinesis Data Firehose delivery stream destination configuration. + */ +export interface DestinationConfig { + /** + * S3 destination configuration properties. + * + * @default - S3 destination is not used. + */ + readonly extendedS3DestinationConfiguration?: CfnDeliveryStream.ExtendedS3DestinationConfigurationProperty; + + /** + * Any resources that were created by the destination when binding it to the stack that must be deployed before the delivery stream is deployed. + * + * @default [] + */ + readonly dependables?: cdk.IDependable[]; +} + +/** + * Options when binding a destination to a delivery stream. + */ +export interface DestinationBindOptions { +} + +/** + * A Kinesis Data Firehose delivery stream destination. + */ +export interface IDestination { + /** + * Binds this destination to the Kinesis Data Firehose delivery stream. + * + * Implementers should use this method to bind resources to the stack and initialize values using the provided stream. + */ + bind(scope: Construct, options: DestinationBindOptions): DestinationConfig; +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose/lib/index.ts b/packages/@aws-cdk/aws-kinesisfirehose/lib/index.ts index dd7beef14d159..3eddb6dec468e 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/lib/index.ts +++ b/packages/@aws-cdk/aws-kinesisfirehose/lib/index.ts @@ -1,2 +1,5 @@ +export * from './delivery-stream'; +export * from './destination'; + // AWS::KinesisFirehose CloudFormation Resources: export * from './kinesisfirehose.generated'; diff --git a/packages/@aws-cdk/aws-kinesisfirehose/package.json b/packages/@aws-cdk/aws-kinesisfirehose/package.json index fc949c6290e88..d6651f8d08c62 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/package.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/package.json @@ -73,26 +73,36 @@ }, "license": "Apache-2.0", "devDependencies": { + "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", "@types/jest": "^26.0.24", "cdk-build-tools": "0.0.0", + "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", - "pkglint": "0.0.0", - "@aws-cdk/assert-internal": "0.0.0" + "pkglint": "0.0.0" }, "dependencies": { + "@aws-cdk/aws-cloudwatch": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/region-info": "0.0.0", "constructs": "^3.3.69" }, "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { + "@aws-cdk/aws-cloudwatch": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/region-info": "0.0.0", "constructs": "^3.3.69" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, "stability": "experimental", - "maturity": "cfn-only", + "maturity": "experimental", "awscdkio": { "announce": false }, diff --git a/packages/@aws-cdk/aws-kinesisfirehose/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..8a68efc25aa8e --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-bucket.ts-fixture b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-bucket.ts-fixture new file mode 100644 index 0000000000000..d0851cff49639 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-bucket.ts-fixture @@ -0,0 +1,13 @@ +// Fixture with a bucket already created +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; +import * as s3 from '@aws-cdk/aws-s3'; +declare const bucket: s3.Bucket; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-delivery-stream.ts-fixture b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-delivery-stream.ts-fixture new file mode 100644 index 0000000000000..c7b75b20d2c1b --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-delivery-stream.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with a delivery stream already created +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; +declare const deliveryStream: DeliveryStream; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-destination.ts-fixture b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-destination.ts-fixture new file mode 100644 index 0000000000000..37d78bf7a43d3 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/rosetta/with-destination.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with a destination already created +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; +declare const destination: IDestination; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-kinesisfirehose/test/delivery-stream.test.ts b/packages/@aws-cdk/aws-kinesisfirehose/test/delivery-stream.test.ts new file mode 100644 index 0000000000000..bb9c3ba744a2f --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/test/delivery-stream.test.ts @@ -0,0 +1,314 @@ +import '@aws-cdk/assert-internal/jest'; +import { ABSENT, ResourcePart, SynthUtils, anything } from '@aws-cdk/assert-internal'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import { Construct, Node } from 'constructs'; +import * as firehose from '../lib'; + +describe('delivery stream', () => { + let stack: cdk.Stack; + let dependable: Construct; + let mockS3Destination: firehose.IDestination; + + const bucketArn = 'arn:aws:s3:::my-bucket'; + const roleArn = 'arn:aws:iam::111122223333:role/my-role'; + + beforeEach(() => { + stack = new cdk.Stack(); + mockS3Destination = { + bind(scope: Construct, _options: firehose.DestinationBindOptions): firehose.DestinationConfig { + dependable = new class extends cdk.Construct { + constructor(depScope: Construct, id: string) { + super(depScope, id); + new cdk.CfnResource(this, 'Resource', { type: 'CDK::Dummy' }); + } + }(scope, 'Dummy Dep'); + return { + extendedS3DestinationConfiguration: { + bucketArn: bucketArn, + roleArn: roleArn, + }, + dependables: [dependable], + }; + }, + }; + }); + + test('creates stream with default values', () => { + new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + expect(stack).toHaveResource('AWS::KinesisFirehose::DeliveryStream', { + DeliveryStreamEncryptionConfigurationInput: ABSENT, + DeliveryStreamName: ABSENT, + DeliveryStreamType: 'DirectPut', + KinesisStreamSourceConfiguration: ABSENT, + ExtendedS3DestinationConfiguration: { + BucketARN: bucketArn, + RoleARN: roleArn, + }, + }); + }); + + test('provided role is set as grant principal', () => { + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), + }); + + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + role: role, + }); + + expect(deliveryStream.grantPrincipal).toBe(role); + }); + + test('not providing role creates one', () => { + new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + expect(stack).toHaveResourceLike('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Principal: { + Service: 'firehose.amazonaws.com', + }, + }, + ], + }, + }); + }); + + test('grant provides access to stream', () => { + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + }); + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + deliveryStream.grant(role, 'firehose:PutRecord'); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'firehose:PutRecord', + Resource: stack.resolve(deliveryStream.deliveryStreamArn), + }, + ], + }, + Roles: [stack.resolve(role.roleName)], + }); + }); + + test('grantPutRecords provides PutRecord* access to stream', () => { + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + }); + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + deliveryStream.grantPutRecords(role); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'firehose:PutRecord', + 'firehose:PutRecordBatch', + ], + Resource: stack.resolve(deliveryStream.deliveryStreamArn), + }, + ], + }, + Roles: [stack.resolve(role.roleName)], + }); + }); + + test('dependables supplied from destination are depended on by just the CFN resource', () => { + const dependableId = stack.resolve((Node.of(dependable).defaultChild as cdk.CfnResource).logicalId); + + new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + expect(stack).toHaveResourceLike('AWS::KinesisFirehose::DeliveryStream', { + DependsOn: [dependableId], + }, ResourcePart.CompleteDefinition); + expect(stack).toHaveResourceLike('AWS::IAM::Role', { + DependsOn: ABSENT, + }, ResourcePart.CompleteDefinition); + }); + + test('supplying 0 or multiple destinations throws', () => { + expect(() => new firehose.DeliveryStream(stack, 'No Destinations', { + destinations: [], + })).toThrowError(/Only one destination is allowed per delivery stream/); + expect(() => new firehose.DeliveryStream(stack, 'Too Many Destinations', { + destinations: [mockS3Destination, mockS3Destination], + })).toThrowError(/Only one destination is allowed per delivery stream/); + }); + + describe('metric methods provide a Metric with configured and attached properties', () => { + beforeEach(() => { + stack = new cdk.Stack(undefined, undefined, { env: { account: '000000000000', region: 'us-west-1' } }); + }); + + test('metric', () => { + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + const metric = deliveryStream.metric('IncomingRecords'); + + expect(metric).toMatchObject({ + account: stack.account, + region: stack.region, + namespace: 'AWS/Firehose', + metricName: 'IncomingRecords', + dimensions: { + DeliveryStreamName: deliveryStream.deliveryStreamName, + }, + }); + }); + }); + + test('allows connections for Firehose IP addresses using map when region not specified', () => { + const vpc = new ec2.Vpc(stack, 'VPC'); + const securityGroup = new ec2.SecurityGroup(stack, 'Security Group', { vpc }); + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + securityGroup.connections.allowFrom(deliveryStream, ec2.Port.allTcp()); + + expect(stack).toHaveResourceLike('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [ + { + CidrIp: { + 'Fn::FindInMap': [ + anything(), + { + Ref: 'AWS::Region', + }, + 'FirehoseCidrBlock', + ], + }, + }, + ], + }); + }); + + test('allows connections for Firehose IP addresses using literal when region specified', () => { + stack = new cdk.Stack(undefined, undefined, { env: { region: 'us-west-1' } }); + const vpc = new ec2.Vpc(stack, 'VPC'); + const securityGroup = new ec2.SecurityGroup(stack, 'Security Group', { vpc }); + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + securityGroup.connections.allowFrom(deliveryStream, ec2.Port.allTcp()); + + expect(stack).toHaveResourceLike('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [ + { + CidrIp: '13.57.135.192/27', + }, + ], + }); + }); + + test('only adds one Firehose IP address mapping to stack even if multiple delivery streams defined', () => { + new firehose.DeliveryStream(stack, 'Delivery Stream 1', { + destinations: [mockS3Destination], + }); + new firehose.DeliveryStream(stack, 'Delivery Stream 2', { + destinations: [mockS3Destination], + }); + + expect(Object.keys(SynthUtils.toCloudFormation(stack).Mappings).length).toBe(1); + }); + + test('can add tags', () => { + const deliveryStream = new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], + }); + + cdk.Tags.of(deliveryStream).add('tagKey', 'tagValue'); + + expect(stack).toHaveResource('AWS::KinesisFirehose::DeliveryStream', { + Tags: [ + { + Key: 'tagKey', + Value: 'tagValue', + }, + ], + }); + }); + + describe('importing', () => { + test('from name', () => { + const deliveryStream = firehose.DeliveryStream.fromDeliveryStreamName(stack, 'DeliveryStream', 'mydeliverystream'); + + expect(deliveryStream.deliveryStreamName).toBe('mydeliverystream'); + expect(stack.resolve(deliveryStream.deliveryStreamArn)).toStrictEqual({ + 'Fn::Join': ['', ['arn:', stack.resolve(stack.partition), ':firehose:', stack.resolve(stack.region), ':', stack.resolve(stack.account), ':deliverystream/mydeliverystream']], + }); + expect(deliveryStream.grantPrincipal).toBeInstanceOf(iam.UnknownPrincipal); + }); + + test('from ARN', () => { + const deliveryStream = firehose.DeliveryStream.fromDeliveryStreamArn(stack, 'DeliveryStream', 'arn:aws:firehose:xx-west-1:111122223333:deliverystream/mydeliverystream'); + + expect(deliveryStream.deliveryStreamName).toBe('mydeliverystream'); + expect(deliveryStream.deliveryStreamArn).toBe('arn:aws:firehose:xx-west-1:111122223333:deliverystream/mydeliverystream'); + expect(deliveryStream.grantPrincipal).toBeInstanceOf(iam.UnknownPrincipal); + }); + + test('from attributes (just name)', () => { + const deliveryStream = firehose.DeliveryStream.fromDeliveryStreamAttributes(stack, 'DeliveryStream', { deliveryStreamName: 'mydeliverystream' }); + + expect(deliveryStream.deliveryStreamName).toBe('mydeliverystream'); + expect(stack.resolve(deliveryStream.deliveryStreamArn)).toStrictEqual({ + 'Fn::Join': ['', ['arn:', stack.resolve(stack.partition), ':firehose:', stack.resolve(stack.region), ':', stack.resolve(stack.account), ':deliverystream/mydeliverystream']], + }); + expect(deliveryStream.grantPrincipal).toBeInstanceOf(iam.UnknownPrincipal); + }); + + test('from attributes (just ARN)', () => { + const deliveryStream = firehose.DeliveryStream.fromDeliveryStreamAttributes(stack, 'DeliveryStream', { deliveryStreamArn: 'arn:aws:firehose:xx-west-1:111122223333:deliverystream/mydeliverystream' }); + + expect(deliveryStream.deliveryStreamName).toBe('mydeliverystream'); + expect(deliveryStream.deliveryStreamArn).toBe('arn:aws:firehose:xx-west-1:111122223333:deliverystream/mydeliverystream'); + expect(deliveryStream.grantPrincipal).toBeInstanceOf(iam.UnknownPrincipal); + }); + + test('from attributes (with role)', () => { + const role = iam.Role.fromRoleArn(stack, 'Delivery Stream Role', 'arn:aws:iam::111122223333:role/DeliveryStreamRole'); + const deliveryStream = firehose.DeliveryStream.fromDeliveryStreamAttributes(stack, 'DeliveryStream', { deliveryStreamName: 'mydeliverystream', role }); + + expect(deliveryStream.deliveryStreamName).toBe('mydeliverystream'); + expect(stack.resolve(deliveryStream.deliveryStreamArn)).toStrictEqual({ + 'Fn::Join': ['', ['arn:', stack.resolve(stack.partition), ':firehose:', stack.resolve(stack.region), ':', stack.resolve(stack.account), ':deliverystream/mydeliverystream']], + }); + expect(deliveryStream.grantPrincipal).toBe(role); + }); + + test('throws when malformatted ARN', () => { + expect(() => firehose.DeliveryStream.fromDeliveryStreamAttributes(stack, 'DeliveryStream', { deliveryStreamArn: 'arn:aws:firehose:xx-west-1:111122223333:deliverystream/' })) + .toThrowError("No delivery stream name found in ARN: 'arn:aws:firehose:xx-west-1:111122223333:deliverystream/'"); + }); + + test('throws when without name or ARN', () => { + expect(() => firehose.DeliveryStream.fromDeliveryStreamAttributes(stack, 'DeliveryStream', {})) + .toThrowError('Either deliveryStreamName or deliveryStreamArn must be provided in DeliveryStreamAttributes'); + }); + }); +}); diff --git a/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.expected.json b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.expected.json new file mode 100644 index 0000000000000..f9e785a3def9e --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.expected.json @@ -0,0 +1,194 @@ +{ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "Role1ABCC5F0": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "firehose.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "RoleDefaultPolicy5FFB7DAB": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "RoleDefaultPolicy5FFB7DAB", + "Roles": [ + { + "Ref": "Role1ABCC5F0" + } + ] + } + }, + "DeliveryStreamServiceRole964EEBCC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "firehose.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "DeliveryStreamF6D5572D": { + "Type": "AWS::KinesisFirehose::DeliveryStream", + "Properties": { + "DeliveryStreamType": "DirectPut", + "ExtendedS3DestinationConfiguration": { + "BucketARN": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "RoleARN": { + "Fn::GetAtt": [ + "Role1ABCC5F0", + "Arn" + ] + } + } + }, + "DependsOn": [ + "RoleDefaultPolicy5FFB7DAB" + ] + } + }, + "Mappings": { + "awscdkawskinesisfirehoseCidrBlocks": { + "af-south-1": { + "FirehoseCidrBlock": "13.244.121.224/27" + }, + "ap-east-1": { + "FirehoseCidrBlock": "18.162.221.32/27" + }, + "ap-northeast-1": { + "FirehoseCidrBlock": "13.113.196.224/27" + }, + "ap-northeast-2": { + "FirehoseCidrBlock": "13.209.1.64/27" + }, + "ap-northeast-3": { + "FirehoseCidrBlock": "13.208.177.192/27" + }, + "ap-south-1": { + "FirehoseCidrBlock": "13.232.67.32/27" + }, + "ap-southeast-1": { + "FirehoseCidrBlock": "13.228.64.192/27" + }, + "ap-southeast-2": { + "FirehoseCidrBlock": "13.210.67.224/27" + }, + "ca-central-1": { + "FirehoseCidrBlock": "35.183.92.128/27" + }, + "cn-north-1": { + "FirehoseCidrBlock": "52.81.151.32/27" + }, + "cn-northwest-1": { + "FirehoseCidrBlock": "161.189.23.64/27" + }, + "eu-central-1": { + "FirehoseCidrBlock": "35.158.127.160/27" + }, + "eu-north-1": { + "FirehoseCidrBlock": "13.53.63.224/27" + }, + "eu-south-1": { + "FirehoseCidrBlock": "15.161.135.128/27" + }, + "eu-west-1": { + "FirehoseCidrBlock": "52.19.239.192/27" + }, + "eu-west-2": { + "FirehoseCidrBlock": "18.130.1.96/27" + }, + "eu-west-3": { + "FirehoseCidrBlock": "35.180.1.96/27" + }, + "me-south-1": { + "FirehoseCidrBlock": "15.185.91.0/27" + }, + "sa-east-1": { + "FirehoseCidrBlock": "18.228.1.128/27" + }, + "us-east-1": { + "FirehoseCidrBlock": "52.70.63.192/27" + }, + "us-east-2": { + "FirehoseCidrBlock": "13.58.135.96/27" + }, + "us-gov-east-1": { + "FirehoseCidrBlock": "18.253.138.96/27" + }, + "us-gov-west-1": { + "FirehoseCidrBlock": "52.61.204.160/27" + }, + "us-west-1": { + "FirehoseCidrBlock": "13.57.135.192/27" + }, + "us-west-2": { + "FirehoseCidrBlock": "52.89.255.224/27" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.ts b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.ts new file mode 100644 index 0000000000000..1adfd9bfc0221 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.ts @@ -0,0 +1,37 @@ +#!/usr/bin/env node +import * as iam from '@aws-cdk/aws-iam'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as constructs from 'constructs'; +import * as firehose from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-firehose-delivery-stream'); + +const bucket = new s3.Bucket(stack, 'Bucket', { + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'), +}); + +const mockS3Destination: firehose.IDestination = { + bind(_scope: constructs.Construct, _options: firehose.DestinationBindOptions): firehose.DestinationConfig { + const bucketGrant = bucket.grantReadWrite(role); + return { + extendedS3DestinationConfiguration: { + bucketArn: bucket.bucketArn, + roleArn: role.roleArn, + }, + dependables: [bucketGrant], + }; + }, +}; + +new firehose.DeliveryStream(stack, 'Delivery Stream', { + destinations: [mockS3Destination], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-kinesisfirehose/test/kinesisfirehose.test.ts b/packages/@aws-cdk/aws-kinesisfirehose/test/kinesisfirehose.test.ts deleted file mode 100644 index c4505ad966984..0000000000000 --- a/packages/@aws-cdk/aws-kinesisfirehose/test/kinesisfirehose.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import '@aws-cdk/assert-internal/jest'; -import {} from '../lib'; - -test('No tests are specified for this package', () => { - expect(true).toBe(true); -}); diff --git a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts index c2ce689f3aaf3..28ec007b00c84 100644 --- a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts +++ b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts @@ -158,3 +158,32 @@ export const APPMESH_ECR_ACCOUNTS: { [region: string]: string } = { 'us-west-1': '840364872350', 'us-west-2': '840364872350', }; + +// https://docs.aws.amazon.com/firehose/latest/dev/controlling-access.html#using-iam-rs-vpc +export const FIREHOSE_CIDR_BLOCKS: { [region: string]: string } = { + 'af-south-1': '13.244.121.224', + 'ap-east-1': '18.162.221.32', + 'ap-northeast-1': '13.113.196.224', + 'ap-northeast-2': '13.209.1.64', + 'ap-northeast-3': '13.208.177.192', + 'ap-south-1': '13.232.67.32', + 'ap-southeast-1': '13.228.64.192', + 'ap-southeast-2': '13.210.67.224', + 'ca-central-1': '35.183.92.128', + 'cn-north-1': '52.81.151.32', + 'cn-northwest-1': '161.189.23.64', + 'eu-central-1': '35.158.127.160', + 'eu-north-1': '13.53.63.224', + 'eu-south-1': '15.161.135.128', + 'eu-west-1': '52.19.239.192', + 'eu-west-2': '18.130.1.96', + 'eu-west-3': '35.180.1.96', + 'me-south-1': '15.185.91.0', + 'sa-east-1': '18.228.1.128', + 'us-east-1': '52.70.63.192', + 'us-east-2': '13.58.135.96', + 'us-gov-east-1': '18.253.138.96', + 'us-gov-west-1': '52.61.204.160', + 'us-west-1': '13.57.135.192', + 'us-west-2': '52.89.255.224', +}; diff --git a/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts b/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts index d23704b6d0062..63455b72ef665 100644 --- a/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts +++ b/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts @@ -3,14 +3,15 @@ import * as fs from 'fs-extra'; import { Default } from '../lib/default'; import { AWS_REGIONS, AWS_SERVICES } from './aws-entities'; import { - APPMESH_ECR_ACCOUNTS, AWS_CDK_METADATA, AWS_OLDER_REGIONS, DLC_REPOSITORY_ACCOUNTS, ELBV2_ACCOUNTS, PARTITION_MAP, - ROUTE_53_BUCKET_WEBSITE_ZONE_IDS, + APPMESH_ECR_ACCOUNTS, AWS_CDK_METADATA, AWS_OLDER_REGIONS, DLC_REPOSITORY_ACCOUNTS, ELBV2_ACCOUNTS, FIREHOSE_CIDR_BLOCKS, + PARTITION_MAP, ROUTE_53_BUCKET_WEBSITE_ZONE_IDS, } from './fact-tables'; async function main(): Promise { checkRegions(APPMESH_ECR_ACCOUNTS); checkRegions(DLC_REPOSITORY_ACCOUNTS); checkRegions(ELBV2_ACCOUNTS); + checkRegions(FIREHOSE_CIDR_BLOCKS); checkRegions(ROUTE_53_BUCKET_WEBSITE_ZONE_IDS); const lines = [ @@ -61,6 +62,11 @@ async function main(): Promise { registerFact(region, 'APPMESH_ECR_ACCOUNT', APPMESH_ECR_ACCOUNTS[region]); + const firehoseCidrBlock = FIREHOSE_CIDR_BLOCKS[region]; + if (firehoseCidrBlock) { + registerFact(region, 'FIREHOSE_CIDR_BLOCK', `${FIREHOSE_CIDR_BLOCKS[region]}/27`); + } + const vpcEndpointServiceNamePrefix = `${domainSuffix.split('.').reverse().join('.')}.vpce`; registerFact(region, 'VPC_ENDPOINT_SERVICE_NAME_PREFIX', vpcEndpointServiceNamePrefix); diff --git a/packages/@aws-cdk/region-info/lib/fact.ts b/packages/@aws-cdk/region-info/lib/fact.ts index 3b5e57835cc7e..6ccef0e8b794f 100644 --- a/packages/@aws-cdk/region-info/lib/fact.ts +++ b/packages/@aws-cdk/region-info/lib/fact.ts @@ -152,6 +152,11 @@ export class FactName { */ public static readonly APPMESH_ECR_ACCOUNT = 'appMeshRepositoryAccount'; + /** + * The CIDR block used by Kinesis Data Firehose servers. + */ + public static readonly FIREHOSE_CIDR_BLOCK = 'firehoseCidrBlock'; + /** * The name of the regional service principal for a given service. * diff --git a/packages/@aws-cdk/region-info/lib/region-info.ts b/packages/@aws-cdk/region-info/lib/region-info.ts index 042b3cec9c177..9e28120a8da62 100644 --- a/packages/@aws-cdk/region-info/lib/region-info.ts +++ b/packages/@aws-cdk/region-info/lib/region-info.ts @@ -117,4 +117,11 @@ export class RegionInfo { public get appMeshRepositoryAccount(): string | undefined { return Fact.find(this.name, FactName.APPMESH_ECR_ACCOUNT); } + + /** + * The CIDR block used by Kinesis Data Firehose servers. + */ + public get firehoseCidrBlock(): string | undefined { + return Fact.find(this.name, FactName.FIREHOSE_CIDR_BLOCK); + } } diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index a200e97145db6..411d6eed9a312 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -239,6 +239,7 @@ "@aws-cdk/aws-kinesisanalytics": "0.0.0", "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-kinesisfirehose-destinations": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lakeformation": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 0c38c44ff0625..73c670cec466d 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -146,6 +146,7 @@ "@aws-cdk/aws-kinesisanalytics": "0.0.0", "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-kinesisfirehose-destinations": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lakeformation": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index 33bbc89af3414..43227b31a4041 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -240,6 +240,7 @@ "@aws-cdk/aws-kinesisanalytics": "0.0.0", "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-kinesisfirehose-destinations": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lakeformation": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", diff --git a/tools/pkglint/lib/rules.ts b/tools/pkglint/lib/rules.ts index a696b43bceabe..add3c58fee4b2 100644 --- a/tools/pkglint/lib/rules.ts +++ b/tools/pkglint/lib/rules.ts @@ -1634,6 +1634,7 @@ export class NoExperimentalDependents extends ValidationRule { ['@aws-cdk/aws-apigatewayv2-integrations', ['@aws-cdk/aws-apigatewayv2']], ['@aws-cdk/aws-apigatewayv2-authorizers', ['@aws-cdk/aws-apigatewayv2']], ['@aws-cdk/aws-events-targets', ['@aws-cdk/aws-kinesisfirehose']], + ['@aws-cdk/aws-kinesisfirehose-destinations', ['@aws-cdk/aws-kinesisfirehose']], ]); private readonly excludedModules = ['@aws-cdk/cloudformation-include']; From cb6e7c9c0c046a5c03bd1a4f1474c9ece1963604 Mon Sep 17 00:00:00 2001 From: Douglas Naphas Date: Thu, 22 Jul 2021 21:39:08 -0400 Subject: [PATCH 12/27] docs: fix link to example integration test in Contributing.md (#15729) Closes gh-15728. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 97bc876d86a0c..423fb1e897ff3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -251,7 +251,7 @@ The steps here are usually AWS CLI commands but they need not be. Examples: * [integ.destinations.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-lambda-destinations/test/integ.destinations.ts#L7) -* [integ.token-authorizer.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.ts#L6) +* [integ.token-authorizer.lit.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.ts#L7-L12) #### yarn watch (Optional) From e133bca61b95b71d51b509b646ff1720099ee31e Mon Sep 17 00:00:00 2001 From: marciocarmona Date: Thu, 22 Jul 2021 19:18:44 -0700 Subject: [PATCH 13/27] fix(stepfunctions): non-object arguments to recurseObject are incorrectly treated as objects (#14631) This doesn't actually fix the issue #12935 as currently the Json paths won't be resolved for Lambda steps where the `Resource` is the Lambda ARN and not `arn:aws:states:::lambda:invoke`, but it at least fixes the issue for Text inputs when `payloadResponseOnly: true` and will avoid the same error from happening again if the `recurseObject` is called with a value that's not an object. Ideally the `TaskInput.value` field should be changed to `{ [key: string]: any } | string` here to ensure the type check when sending the value to methods like `FieldUtils.renderObject`: https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/aws-stepfunctions/lib/input.ts#L65 Or even better the `TaskInput` should be made generic like: ``` export class TaskInput { ... private constructor(public readonly type: T, public readonly value: ValueType[T]) {} } type ValueType = { [InputType.OBJECT]: { [key: string]: any }, [InputType.TEXT]: string } ``` However, any of the changes above wouldn't be backwards compatible and could break not only internal references in the `aws-cdk` but also on any customer packages using the CDK. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-stepfunctions/lib/json-path.ts | 7 +++- .../aws-stepfunctions/test/fields.test.ts | 35 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts b/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts index 7a4a56c536a6b..b4602b5e887d0 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts @@ -82,7 +82,12 @@ interface FieldHandlers { } export function recurseObject(obj: object | undefined, handlers: FieldHandlers, visited: object[] = []): object | undefined { - if (obj === undefined) { return undefined; } + // If the argument received is not actually an object (string, number, boolean, undefined, ...) or null + // just return it as is as there's nothing to be rendered. This should only happen in the original call to + // recurseObject as any recursive calls to it are checking for typeof value === 'object' && value !== null + if (typeof obj !== 'object' || obj === null) { + return obj; + } // Avoiding infinite recursion if (visited.includes(obj)) { return {}; } diff --git a/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts index 15591ddeebd76..382ec424177f9 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts @@ -1,5 +1,5 @@ import '@aws-cdk/assert-internal/jest'; -import { FieldUtils, JsonPath } from '../lib'; +import { FieldUtils, JsonPath, TaskInput } from '../lib'; describe('Fields', () => { const jsonPathValidationErrorMsg = /exactly '\$', '\$\$', start with '\$.', start with '\$\$.' or start with '\$\['/; @@ -153,6 +153,39 @@ describe('Fields', () => { .toStrictEqual(['$.listField', '$.numField', '$.stringField']); }); + test('rendering a non-object value should just return itself', () => { + expect( + FieldUtils.renderObject(TaskInput.fromText('Hello World').value), + ).toEqual( + 'Hello World', + ); + expect( + FieldUtils.renderObject('Hello World' as any), + ).toEqual( + 'Hello World', + ); + expect( + FieldUtils.renderObject(null as any), + ).toEqual( + null, + ); + expect( + FieldUtils.renderObject(3.14 as any), + ).toEqual( + 3.14, + ); + expect( + FieldUtils.renderObject(true as any), + ).toEqual( + true, + ); + expect( + FieldUtils.renderObject(undefined), + ).toEqual( + undefined, + ); + }); + test('repeated object references at different tree paths should not be considered as recursions', () => { const repeatedObject = { field: JsonPath.stringAt('$.stringField'), From 7668400ec8d4e6ee042c05976f95e42147993375 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 23 Jul 2021 12:41:21 +0200 Subject: [PATCH 14/27] fix(pipelines): Secrets Manager permissions not added to asset projects (#15718) We used to use an immutable singleton role with `*` permissions for the assets projects, because if there were many different destinations in a pipeline, and each asset build had to publish to each destination, the policy could grow too long and exceed the maximum policy size. However, this also disabled the automatic policy wrangling that CodeBuild would do for us, like automatically adding permissions to bind to a VPC, and adding permissions to read Secrets Manager secrets. This especially becoming relevant since that now in the modern API, it's possible to modify build the environment in a way that normally automatically adds SecretsManager permission, but now won't (and it's not possible to fix either). Replace the immutable singleton role with a mutable singleton role, but in such a way that it won't add permissions statements for which it already has a `*` statement (to cut down on duplication), and have the CB project do the automatic VPC bind permissions again. Fixes #15628. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-codebuild/lib/project.ts | 46 +- .../aws-codebuild/test/test.project.ts | 35 + .../lib/codepipeline/codepipeline.ts | 113 +- .../@aws-cdk/pipelines/lib/legacy/pipeline.ts | 85 +- .../@aws-cdk/pipelines/lib/legacy/stage.ts | 2 +- .../lib/private/asset-singleton-role.ts | 85 + .../pipelines/test/compliance/assets.test.ts | 117 +- .../integ.newpipeline-with-vpc.expected.json | 2401 +++++++++++++++++ .../test/integ.newpipeline-with-vpc.ts | 57 + 9 files changed, 2730 insertions(+), 211 deletions(-) create mode 100644 packages/@aws-cdk/pipelines/lib/private/asset-singleton-role.ts create mode 100644 packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json create mode 100644 packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.ts diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 30f8be9051751..efb4896534c60 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -29,6 +29,8 @@ import { CODEPIPELINE_SOURCE_ARTIFACTS_TYPE, NO_SOURCE_TYPE } from './source-typ // eslint-disable-next-line import { Construct as CoreConstruct } from '@aws-cdk/core'; +const VPC_POLICY_SYM = Symbol.for('@aws-cdk/aws-codebuild.roleVpcPolicy'); + /** * The type returned from {@link IProject#enableBatchBuilds}. */ @@ -1437,23 +1439,33 @@ export class Project extends ProjectBase { }, })); - const policy = new iam.Policy(this, 'PolicyDocument', { - statements: [ - new iam.PolicyStatement({ - resources: ['*'], - actions: [ - 'ec2:CreateNetworkInterface', - 'ec2:DescribeNetworkInterfaces', - 'ec2:DeleteNetworkInterface', - 'ec2:DescribeSubnets', - 'ec2:DescribeSecurityGroups', - 'ec2:DescribeDhcpOptions', - 'ec2:DescribeVpcs', - ], - }), - ], - }); - this.role.attachInlinePolicy(policy); + // If the same Role is used for multiple Projects, always creating a new `iam.Policy` + // will attach the same policy multiple times, probably exceeding the maximum size of the + // Role policy. Make sure we only do it once for the same role. + // + // This deduplication could be a feature of the Role itself, but that feels risky and + // is hard to implement (what with Tokens and all). Safer to fix it locally for now. + let policy: iam.Policy | undefined = (this.role as any)[VPC_POLICY_SYM]; + if (!policy) { + policy = new iam.Policy(this, 'PolicyDocument', { + statements: [ + new iam.PolicyStatement({ + resources: ['*'], + actions: [ + 'ec2:CreateNetworkInterface', + 'ec2:DescribeNetworkInterfaces', + 'ec2:DeleteNetworkInterface', + 'ec2:DescribeSubnets', + 'ec2:DescribeSecurityGroups', + 'ec2:DescribeDhcpOptions', + 'ec2:DescribeVpcs', + ], + }), + ], + }); + this.role.attachInlinePolicy(policy); + (this.role as any)[VPC_POLICY_SYM] = policy; + } // add an explicit dependency between the EC2 Policy and this Project - // otherwise, creating the Project fails, as it requires these permissions diff --git a/packages/@aws-cdk/aws-codebuild/test/test.project.ts b/packages/@aws-cdk/aws-codebuild/test/test.project.ts index 2dc652e64f39d..038bb2e0e45cf 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.project.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.project.ts @@ -429,6 +429,41 @@ export = { test.done(); }, + 'if a role is shared between projects in a VPC, the VPC Policy is only attached once'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'), + }); + const source = codebuild.Source.gitHubEnterprise({ + httpsCloneUrl: 'https://mygithub-enterprise.com/myuser/myrepo', + }); + + // WHEN + new codebuild.Project(stack, 'Project1', { source, role, vpc, projectName: 'P1' }); + new codebuild.Project(stack, 'Project2', { source, role, vpc, projectName: 'P2' }); + + // THEN + // - 1 is for `ec2:CreateNetworkInterfacePermission`, deduplicated as they're part of a single policy + // - 1 is for `ec2:CreateNetworkInterface`, this is the separate Policy we're deduplicating + // We would have found 3 if the deduplication didn't work. + expect(stack).to(countResources('AWS::IAM::Policy', 2)); + + // THEN - both Projects have a DependsOn on the same policy + expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', { + Properties: { Name: 'P1' }, + DependsOn: ['Project1PolicyDocumentF9761562'], + }, ResourcePart.CompleteDefinition)); + + expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', { + Properties: { Name: 'P1' }, + DependsOn: ['Project1PolicyDocumentF9761562'], + }, ResourcePart.CompleteDefinition)); + + test.done(); + }, + 'can use an imported Role for a Project within a VPC'(test: Test) { const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts index e3c10d29b0740..4e509faee2111 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts @@ -4,13 +4,14 @@ import * as cp from '@aws-cdk/aws-codepipeline'; import * as cpa from '@aws-cdk/aws-codepipeline-actions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; -import { Aws, Fn, IDependable, Lazy, PhysicalName, Stack } from '@aws-cdk/core'; +import { Aws, Fn, Lazy, PhysicalName, Stack } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { Construct, Node } from 'constructs'; import { AssetType, FileSet, IFileSetProducer, ManualApprovalStep, ShellStep, StackAsset, StackDeployment, Step } from '../blueprint'; import { DockerCredential, dockerCredentialsInstallCommands, DockerCredentialUsage } from '../docker-credentials'; import { GraphNode, GraphNodeCollection, isGraph, AGraphNode, PipelineGraph } from '../helpers-internal'; import { PipelineBase } from '../main'; +import { AssetSingletonRole } from '../private/asset-singleton-role'; import { appOf, assemblyBuilderOf, embeddedAsmPath, obtainScope } from '../private/construct-internals'; import { toPosixPath } from '../private/fs'; import { enumerate, flatten, maybeSuffix, noUndefined } from '../private/javascript'; @@ -254,11 +255,6 @@ export class CodePipeline extends PipelineBase { */ private readonly assetCodeBuildRoles: Record = {}; - /** - * Policies created for the build projects that they have to depend on - */ - private readonly assetAttachedPolicies: Record = {}; - /** * Per asset type, the target role ARNs that need to be assumed */ @@ -635,7 +631,7 @@ export class CodePipeline extends PipelineBase { } } - const assetBuildConfig = this.obtainAssetCodeBuildRole(assets[0].assetType); + const role = this.obtainAssetCodeBuildRole(assets[0].assetType); // The base commands that need to be run const script = new CodeBuildStep(node.id, { @@ -647,13 +643,12 @@ export class CodePipeline extends PipelineBase { buildEnvironment: { privileged: assets.some(asset => asset.assetType === AssetType.DOCKER_IMAGE), }, - role: assetBuildConfig.role, + role, }); // Customizations that are not accessible to regular users return CodeBuildFactory.fromCodeBuildStep(node.id, script, { additionalConstructLevel: false, - additionalDependable: assetBuildConfig.dependable, // If we use a single publisher, pass buildspec via file otherwise it'll // grow too big. @@ -775,18 +770,15 @@ export class CodePipeline extends PipelineBase { * Modeled after the CodePipeline role and 'CodePipelineActionRole' roles. * Generates one role per asset type to separate file and Docker/image-based permissions. */ - private obtainAssetCodeBuildRole(assetType: AssetType): AssetCodeBuildRole { + private obtainAssetCodeBuildRole(assetType: AssetType): iam.IRole { if (this.assetCodeBuildRoles[assetType]) { - return { - role: this.assetCodeBuildRoles[assetType], - dependable: this.assetAttachedPolicies[assetType], - }; + return this.assetCodeBuildRoles[assetType]; } const stack = Stack.of(this); const rolePrefix = assetType === AssetType.DOCKER_IMAGE ? 'Docker' : 'File'; - const assetRole = new iam.Role(this.assetsScope, `${rolePrefix}Role`, { + const assetRole = new AssetSingletonRole(this.assetsScope, `${rolePrefix}Role`, { roleName: PhysicalName.GENERATE_IF_NEEDED, assumedBy: new iam.CompositePrincipal( new iam.ServicePrincipal('codebuild.amazonaws.com'), @@ -794,45 +786,6 @@ export class CodePipeline extends PipelineBase { ), }); - // Logging permissions - const logGroupArn = stack.formatArn({ - service: 'logs', - resource: 'log-group', - sep: ':', - resourceName: '/aws/codebuild/*', - }); - assetRole.addToPolicy(new iam.PolicyStatement({ - resources: [logGroupArn], - actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'], - })); - - // CodeBuild report groups - const codeBuildArn = stack.formatArn({ - service: 'codebuild', - resource: 'report-group', - resourceName: '*', - }); - assetRole.addToPolicy(new iam.PolicyStatement({ - actions: [ - 'codebuild:CreateReportGroup', - 'codebuild:CreateReport', - 'codebuild:UpdateReport', - 'codebuild:BatchPutTestCases', - 'codebuild:BatchPutCodeCoverages', - ], - resources: [codeBuildArn], - })); - - // CodeBuild start/stop - assetRole.addToPolicy(new iam.PolicyStatement({ - resources: ['*'], - actions: [ - 'codebuild:BatchGetBuilds', - 'codebuild:StartBuild', - 'codebuild:StopBuild', - ], - })); - // Publishing role access // The ARNs include raw AWS pseudo parameters (e.g., ${AWS::Partition}), which need to be substituted. // Lazy-evaluated so all asset publishing roles are included. @@ -846,51 +799,8 @@ export class CodePipeline extends PipelineBase { this.dockerCredentials.forEach(reg => reg.grantRead(assetRole, DockerCredentialUsage.ASSET_PUBLISHING)); } - // Artifact access - this.pipeline.artifactBucket.grantRead(assetRole); - - // VPC permissions required for CodeBuild - // Normally CodeBuild itself takes care of this but we're creating a singleton role so now - // we need to do this. - const assetCodeBuildOptions = this.codeBuildDefaultsFor(CodeBuildProjectType.ASSETS); - if (assetCodeBuildOptions?.vpc) { - const vpcPolicy = new iam.Policy(assetRole, 'VpcPolicy', { - statements: [ - new iam.PolicyStatement({ - resources: [`arn:${Aws.PARTITION}:ec2:${Aws.REGION}:${Aws.ACCOUNT_ID}:network-interface/*`], - actions: ['ec2:CreateNetworkInterfacePermission'], - conditions: { - StringEquals: { - 'ec2:Subnet': assetCodeBuildOptions.vpc - .selectSubnets(assetCodeBuildOptions.subnetSelection).subnetIds - .map(si => `arn:${Aws.PARTITION}:ec2:${Aws.REGION}:${Aws.ACCOUNT_ID}:subnet/${si}`), - 'ec2:AuthorizedService': 'codebuild.amazonaws.com', - }, - }, - }), - new iam.PolicyStatement({ - resources: ['*'], - actions: [ - 'ec2:CreateNetworkInterface', - 'ec2:DescribeNetworkInterfaces', - 'ec2:DeleteNetworkInterface', - 'ec2:DescribeSubnets', - 'ec2:DescribeSecurityGroups', - 'ec2:DescribeDhcpOptions', - 'ec2:DescribeVpcs', - ], - }), - ], - }); - assetRole.attachInlinePolicy(vpcPolicy); - this.assetAttachedPolicies[assetType] = vpcPolicy; - } - - this.assetCodeBuildRoles[assetType] = assetRole.withoutPolicyUpdates(); - return { - role: this.assetCodeBuildRoles[assetType], - dependable: this.assetAttachedPolicies[assetType], - }; + this.assetCodeBuildRoles[assetType] = assetRole; + return assetRole; } } @@ -903,11 +813,6 @@ function dockerUsageFromCodeBuild(cbt: CodeBuildProjectType): DockerCredentialUs } } -interface AssetCodeBuildRole { - readonly role: iam.IRole; - readonly dependable?: IDependable; -} - enum CodeBuildProjectType { SYNTH = 'SYNTH', ASSETS = 'ASSETS', diff --git a/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts b/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts index e100c8c4a90f0..b8d7769ad3fc0 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts @@ -3,11 +3,12 @@ import * as codebuild from '@aws-cdk/aws-codebuild'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; -import { Annotations, App, Aws, CfnOutput, Fn, Lazy, PhysicalName, Stack, Stage } from '@aws-cdk/core'; +import { Annotations, App, CfnOutput, Fn, Lazy, PhysicalName, Stack, Stage } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { AssetType } from '../blueprint/asset-type'; import { dockerCredentialsInstallCommands, DockerCredential, DockerCredentialUsage } from '../docker-credentials'; import { ApplicationSecurityCheck } from '../private/application-security-check'; +import { AssetSingletonRole } from '../private/asset-singleton-role'; import { appOf, assemblyBuilderOf } from '../private/construct-internals'; import { DeployCdkStackAction, PublishAssetsAction, UpdatePipelineAction } from './actions'; import { AddStageOptions, AssetPublishingCommand, BaseStageOptions, CdkStage, StackOutput } from './stage'; @@ -580,50 +581,11 @@ class AssetPublishing extends CoreConstruct { if (this.assetRoles[assetType]) { return this.assetRoles[assetType]; } const rolePrefix = assetType === AssetType.DOCKER_IMAGE ? 'Docker' : 'File'; - const assetRole = new iam.Role(this, `${rolePrefix}Role`, { + const assetRole = new AssetSingletonRole(this, `${rolePrefix}Role`, { roleName: PhysicalName.GENERATE_IF_NEEDED, assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('codebuild.amazonaws.com'), new iam.AccountPrincipal(Stack.of(this).account)), }); - // Logging permissions - const logGroupArn = Stack.of(this).formatArn({ - service: 'logs', - resource: 'log-group', - sep: ':', - resourceName: '/aws/codebuild/*', - }); - assetRole.addToPolicy(new iam.PolicyStatement({ - resources: [logGroupArn], - actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'], - })); - - // CodeBuild report groups - const codeBuildArn = Stack.of(this).formatArn({ - service: 'codebuild', - resource: 'report-group', - resourceName: '*', - }); - assetRole.addToPolicy(new iam.PolicyStatement({ - actions: [ - 'codebuild:CreateReportGroup', - 'codebuild:CreateReport', - 'codebuild:UpdateReport', - 'codebuild:BatchPutTestCases', - 'codebuild:BatchPutCodeCoverages', - ], - resources: [codeBuildArn], - })); - - // CodeBuild start/stop - assetRole.addToPolicy(new iam.PolicyStatement({ - resources: ['*'], - actions: [ - 'codebuild:BatchGetBuilds', - 'codebuild:StartBuild', - 'codebuild:StopBuild', - ], - })); - // Publishing role access // The ARNs include raw AWS pseudo parameters (e.g., ${AWS::Partition}), which need to be substituted. // Lazy-evaluated so all asset publishing roles are included. @@ -637,46 +599,7 @@ class AssetPublishing extends CoreConstruct { this.dockerCredentials.forEach(reg => reg.grantRead(assetRole, DockerCredentialUsage.ASSET_PUBLISHING)); } - // Artifact access - this.pipeline.artifactBucket.grantRead(assetRole); - - // VPC permissions required for CodeBuild - // Normally CodeBuild itself takes care of this but we're creating a singleton role so now - // we need to do this. - if (this.props.vpc) { - const vpcPolicy = new iam.Policy(assetRole, 'VpcPolicy', { - statements: [ - new iam.PolicyStatement({ - resources: [`arn:${Aws.PARTITION}:ec2:${Aws.REGION}:${Aws.ACCOUNT_ID}:network-interface/*`], - actions: ['ec2:CreateNetworkInterfacePermission'], - conditions: { - StringEquals: { - 'ec2:Subnet': this.props.vpc - .selectSubnets(this.props.subnetSelection).subnetIds - .map(si => `arn:${Aws.PARTITION}:ec2:${Aws.REGION}:${Aws.ACCOUNT_ID}:subnet/${si}`), - 'ec2:AuthorizedService': 'codebuild.amazonaws.com', - }, - }, - }), - new iam.PolicyStatement({ - resources: ['*'], - actions: [ - 'ec2:CreateNetworkInterface', - 'ec2:DescribeNetworkInterfaces', - 'ec2:DeleteNetworkInterface', - 'ec2:DescribeSubnets', - 'ec2:DescribeSecurityGroups', - 'ec2:DescribeDhcpOptions', - 'ec2:DescribeVpcs', - ], - }), - ], - }); - assetRole.attachInlinePolicy(vpcPolicy); - this.assetAttachedPolicies[assetType] = vpcPolicy; - } - - this.assetRoles[assetType] = assetRole.withoutPolicyUpdates(); + this.assetRoles[assetType] = assetRole; return this.assetRoles[assetType]; } } diff --git a/packages/@aws-cdk/pipelines/lib/legacy/stage.ts b/packages/@aws-cdk/pipelines/lib/legacy/stage.ts index 3b4140fdba5ce..bfb997e908196 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/stage.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/stage.ts @@ -9,6 +9,7 @@ import { Construct, Node } from 'constructs'; import { AssetType } from '../blueprint/asset-type'; import { ApplicationSecurityCheck } from '../private/application-security-check'; import { AssetManifestReader, DockerImageManifestEntry, FileManifestEntry } from '../private/asset-manifest'; +import { pipelineSynth } from '../private/construct-internals'; import { topologicalSort } from '../private/toposort'; import { DeployCdkStackAction } from './actions'; import { CdkPipeline } from './pipeline'; @@ -16,7 +17,6 @@ import { CdkPipeline } from './pipeline'; // v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch. // eslint-disable-next-line import { Construct as CoreConstruct } from '@aws-cdk/core'; -import { pipelineSynth } from '../private/construct-internals'; /** * Construction properties for a CdkStage diff --git a/packages/@aws-cdk/pipelines/lib/private/asset-singleton-role.ts b/packages/@aws-cdk/pipelines/lib/private/asset-singleton-role.ts new file mode 100644 index 0000000000000..5d52c1d5a47a9 --- /dev/null +++ b/packages/@aws-cdk/pipelines/lib/private/asset-singleton-role.ts @@ -0,0 +1,85 @@ +import * as iam from '@aws-cdk/aws-iam'; +import { PolicyStatement } from '@aws-cdk/aws-iam'; +import { ConcreteDependable, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +/** + * Role which will be reused across asset jobs + * + * Has some '*' resources to save IAM policy space, and will not + * actually add policies that look like policies that were already added. + */ +export class AssetSingletonRole extends iam.Role { + private _rejectDuplicates = false; + + constructor(scope: Construct, id: string, props: iam.RoleProps) { + super(scope, id, props); + + // Logging permissions + this.addToPolicy(new iam.PolicyStatement({ + resources: [Stack.of(this).formatArn({ + service: 'logs', + resource: 'log-group', + sep: ':', + resourceName: '/aws/codebuild/*', + })], + actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'], + })); + + // CodeBuild report groups + this.addToPolicy(new iam.PolicyStatement({ + actions: [ + 'codebuild:CreateReportGroup', + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + 'codebuild:BatchPutCodeCoverages', + ], + resources: [Stack.of(this).formatArn({ + service: 'codebuild', + resource: 'report-group', + resourceName: '*', + })], + })); + + // CodeBuild start/stop + this.addToPolicy(new iam.PolicyStatement({ + resources: ['*'], + actions: [ + 'codebuild:BatchGetBuilds', + 'codebuild:StartBuild', + 'codebuild:StopBuild', + ], + })); + + this._rejectDuplicates = true; + } + + public addToPrincipalPolicy(statement: PolicyStatement): iam.AddToPrincipalPolicyResult { + const json = statement.toStatementJson(); + const acts = JSON.stringify(json.Action); + + // These have already been added with wildcard resources on creation + const alreadyAdded = [ + '["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"]', + '["codebuild:CreateReportGroup","codebuild:CreateReport","codebuild:UpdateReport","codebuild:BatchPutTestCases","codebuild:BatchPutCodeCoverages"]', + '["codebuild:BatchGetBuilds","codebuild:StartBuild","codebuild:StopBuild"]', + ]; + + if (this._rejectDuplicates && alreadyAdded.includes(acts)) { + // Pretend we did it + return { statementAdded: true, policyDependable: new ConcreteDependable() }; + } + + // These are added in duplicate (specifically these come from + // Project#bindToCodePipeline) -- the original singleton asset role didn't + // have these, and they're not necessary either, so in order to not cause + // unnecessary diffs, recognize and drop them there as well. + if (acts === '["kms:Decrypt","kms:Encrypt","kms:ReEncrypt*","kms:GenerateDataKey*"]') { + // Pretend we did it + return { statementAdded: true, policyDependable: new ConcreteDependable() }; + } + + return super.addToPrincipalPolicy(statement); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/compliance/assets.test.ts b/packages/@aws-cdk/pipelines/test/compliance/assets.test.ts index 8a465309016ef..c1b72cf7ab316 100644 --- a/packages/@aws-cdk/pipelines/test/compliance/assets.test.ts +++ b/packages/@aws-cdk/pipelines/test/compliance/assets.test.ts @@ -670,7 +670,7 @@ describe('pipeline with VPC', () => { } }); - behavior('Asset publishing CodeBuild Projects have a dependency on attached policies to the role', (suite) => { + behavior('Asset publishing CodeBuild Projects have correct VPC permissions', (suite) => { suite.legacy(() => { const pipeline = new LegacyTestGitHubNpmPipeline(pipelineStack, 'Cdk', { vpc, @@ -690,17 +690,31 @@ describe('pipeline with VPC', () => { function THEN_codePipelineExpectation() { // Assets Project + expect(pipelineStack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Resource: '*', + Action: [ + 'ec2:CreateNetworkInterface', + 'ec2:DescribeNetworkInterfaces', + 'ec2:DeleteNetworkInterface', + 'ec2:DescribeSubnets', + 'ec2:DescribeSecurityGroups', + 'ec2:DescribeDhcpOptions', + 'ec2:DescribeVpcs', + ], + }, + ], + }, + Roles: [{ Ref: 'CdkAssetsDockerRole484B6DD3' }], + }); expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', { Properties: { - ServiceRole: { - 'Fn::GetAtt': [ - 'CdkAssetsDockerRole484B6DD3', - 'Arn', - ], - }, + ServiceRole: { 'Fn::GetAtt': ['CdkAssetsDockerRole484B6DD3', 'Arn'] }, }, DependsOn: [ - 'CdkAssetsDockerRoleVpcPolicy86CA024B', + 'CdkAssetsDockerAsset1PolicyDocument8DA96A22', ], }, ResourcePart.CompleteDefinition); } @@ -939,3 +953,90 @@ function expectedAssetRolePolicy(assumeRolePattern: string | string[], attachedR Roles: [{ Ref: attachedRole }], }; } + + +behavior('necessary secrets manager permissions get added to asset roles', suite => { + // Not possible to configure this for legacy pipelines + suite.doesNotApply.legacy(); + + suite.modern(() => { + const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Pipeline', { + assetPublishingCodeBuildDefaults: { + buildEnvironment: { + environmentVariables: { + FOOBAR: { + value: 'FoobarSecret', + type: cb.BuildEnvironmentVariableType.SECRETS_MANAGER, + }, + }, + }, + }, + }); + pipeline.addStage(new FileAssetApp(pipelineStack, 'MyApp')); + + THEN_codePipelineExpectation(); + }); + + function THEN_codePipelineExpectation() { + expect(pipelineStack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith({ + Action: 'secretsmanager:GetSecretValue', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':secretsmanager:us-pipeline:123pipeline:secret:FoobarSecret-??????', + ], + ], + }, + }), + }, + Roles: [ + { Ref: 'PipelineAssetsFileRole59943A77' }, + ], + }); + } +}); + +behavior('adding environment variable to assets job adds SecretsManager permissions', suite => { + // No way to manipulate buildEnvironment in legacy API + suite.doesNotApply.legacy(); + + suite.modern(() => { + const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Pipeline', { + assetPublishingCodeBuildDefaults: { + buildEnvironment: { + environmentVariables: { + FOOBAR: { + value: 'FoobarSecret', + type: cb.BuildEnvironmentVariableType.SECRETS_MANAGER, + }, + }, + }, + }, + }); + pipeline.addStage(new FileAssetApp(pipelineStack, 'MyApp')); + + expect(pipelineStack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith( + objectLike({ + Action: 'secretsmanager:GetSecretValue', + Effect: 'Allow', + Resource: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':secretsmanager:us-pipeline:123pipeline:secret:FoobarSecret-??????', + ]], + }, + }), + ), + }, + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json new file mode 100644 index 0000000000000..848406b0ad02e --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json @@ -0,0 +1,2401 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.32.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet3SubnetBE12F0B6": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3RouteTable93458DBB": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3RouteTableAssociation1F1EDF02": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet3RouteTable93458DBB" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + } + } + }, + "VpcPublicSubnet3DefaultRoute4697774F": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet3RouteTable93458DBB" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet3EIP3A666A23": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3NATGateway7640CD1D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet3EIP3A666A23", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.96.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcPrivateSubnet3SubnetF258B56E": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.160.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PrivateSubnet3" + } + ] + } + }, + "VpcPrivateSubnet3RouteTableD98824C7": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc/PrivateSubnet3" + } + ] + } + }, + "VpcPrivateSubnet3RouteTableAssociation16BDDC43": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet3RouteTableD98824C7" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + } + }, + "VpcPrivateSubnet3DefaultRoute94B74F0D": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet3RouteTableD98824C7" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet3NATGateway7640CD1D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "PipelineStack/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "PipelineArtifactsBucketAEA9A052": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "PipelineArtifactsBucketPolicyF53CCC52": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleB27FAA37": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleDefaultPolicy7BDC1ABB": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset1CodePipelineActionRoleC0EC649A", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset2CodePipelineActionRole06965A59", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineRoleDefaultPolicy7BDC1ABB", + "Roles": [ + { + "Ref": "PipelineRoleB27FAA37" + } + ] + } + }, + "Pipeline9850B417": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleB27FAA37", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "ThirdParty", + "Provider": "GitHub", + "Version": "1" + }, + "Configuration": { + "Owner": "rix0rrr", + "Repo": "cdk-pipelines-demo", + "Branch": "main", + "OAuthToken": "{{resolve:secretsmanager:github-token:SecretString:::}}", + "PollForSourceChanges": false + }, + "Name": "rix0rrr_cdk-pipelines-demo", + "OutputArtifacts": [ + { + "Name": "rix0rrr_cdk-pipelines-demo_Source" + } + ], + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"00ebacfb32b1bde8d3638577308e7b7144dfa3b0a58a83bc6ff38a3b1f26951c\"}]" + }, + "InputArtifacts": [ + { + "Name": "rix0rrr_cdk-pipelines-demo_Source" + } + ], + "Name": "Synth", + "OutputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Build" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + }, + "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"9eda7f97d24aac861052bb47a41b80eecdd56096bf9a88a27c88d94c463785c8\"}]" + }, + "InputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "Name": "SelfMutate", + "RoleArn": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "UpdatePipeline" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineAssetsFileAsset185A67CB4" + } + }, + "InputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "Name": "FileAsset1", + "RoleArn": { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset1CodePipelineActionRoleC0EC649A", + "Arn" + ] + }, + "RunOrder": 1 + }, + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineAssetsFileAsset24D2D639B" + } + }, + "InputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "Name": "FileAsset2", + "RoleArn": { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset2CodePipelineActionRole06965A59", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Assets" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "Beta-Stack1", + "Capabilities": "CAPABILITY_NAMED_IAM", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-cfn-exec-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "ActionMode": "CHANGE_SET_REPLACE", + "ChangeSetName": "PipelineChange", + "TemplatePath": "Synth_Output::assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.template.json" + }, + "InputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "Name": "Prepare", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "RunOrder": 1 + }, + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "Beta-Stack1", + "ActionMode": "CHANGE_SET_EXECUTE", + "ChangeSetName": "PipelineChange" + }, + "Name": "Deploy", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "RunOrder": 2 + } + ], + "Name": "Beta" + } + ], + "ArtifactStore": { + "Location": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "Type": "S3" + }, + "RestartExecutionOnUpdate": true + }, + "DependsOn": [ + "PipelineRoleDefaultPolicy7BDC1ABB", + "PipelineRoleB27FAA37" + ] + }, + "PipelineSourcerix0rrrcdkpipelinesdemoWebhookResourceDB0C1BCA": { + "Type": "AWS::CodePipeline::Webhook", + "Properties": { + "Authentication": "GITHUB_HMAC", + "AuthenticationConfiguration": { + "SecretToken": "{{resolve:secretsmanager:github-token:SecretString:::}}" + }, + "Filters": [ + { + "JsonPath": "$.ref", + "MatchEquals": "refs/heads/{Branch}" + } + ], + "TargetAction": "rix0rrr_cdk-pipelines-demo", + "TargetPipeline": { + "Ref": "Pipeline9850B417" + }, + "TargetPipelineVersion": 1, + "RegisterWithThirdParty": true + } + }, + "PipelineBuildSynthCdkBuildProjectRole231EEA2A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:CreateNetworkInterfacePermission", + "Condition": { + "StringEquals": { + "ec2:Subnet": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ] + ] + } + ], + "ec2:AuthorizedService": "codebuild.amazonaws.com" + } + }, + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":network-interface/*" + ] + ] + } + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + ":*" + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C", + "Roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProjectSecurityGroup84F92459": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Automatic generated security group for CodeBuild PipelineStackPipelineBuildSynthCdkBuildProject225CEB2C", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "PipelineBuildSynthCdkBuildProject6BEFA8E6": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectRole231EEA2A", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"npm ci\",\n \"npm run build\",\n \"npx cdk synth\"\n ]\n }\n },\n \"artifacts\": {\n \"base-directory\": \"cdk.out\",\n \"files\": \"**/*\"\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": "alias/aws/s3", + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectSecurityGroup84F92459", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "DependsOn": [ + "PipelineBuildSynthCdkBuildProjectPolicyDocument4D16371A" + ] + }, + "PipelineBuildSynthCdkBuildProjectPolicyDocument4D16371A": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeDhcpOptions", + "ec2:DescribeVpcs" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCdkBuildProjectPolicyDocument4D16371A", + "Roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "PipelineBuildSynthCodePipelineActionRole4E7A6C97": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineBuildSynthCodePipelineActionRoleDefaultPolicy92C90290": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProject6BEFA8E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCodePipelineActionRoleDefaultPolicy92C90290", + "Roles": [ + { + "Ref": "PipelineBuildSynthCodePipelineActionRole4E7A6C97" + } + ] + } + }, + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleDefaultPolicyE626265B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutationDAA41400", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleDefaultPolicyE626265B", + "Roles": [ + { + "Ref": "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF" + } + ] + } + }, + "PipelineAssetsFileAsset1CodePipelineActionRoleC0EC649A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineAssetsFileAsset1CodePipelineActionRoleDefaultPolicy5F0BE7E8": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset185A67CB4", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineAssetsFileAsset1CodePipelineActionRoleDefaultPolicy5F0BE7E8", + "Roles": [ + { + "Ref": "PipelineAssetsFileAsset1CodePipelineActionRoleC0EC649A" + } + ] + } + }, + "PipelineAssetsFileAsset2CodePipelineActionRole06965A59": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineAssetsFileAsset2CodePipelineActionRoleDefaultPolicy2399F4BC": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset24D2D639B", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineAssetsFileAsset2CodePipelineActionRoleDefaultPolicy2399F4BC", + "Roles": [ + { + "Ref": "PipelineAssetsFileAsset2CodePipelineActionRole06965A59" + } + ] + } + }, + "PipelineUpdatePipelineSelfMutationRole57E559E8": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineUpdatePipelineSelfMutationRoleDefaultPolicyA225DA4E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:CreateNetworkInterfacePermission", + "Condition": { + "StringEquals": { + "ec2:Subnet": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ] + ] + } + ], + "ec2:AuthorizedService": "codebuild.amazonaws.com" + } + }, + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":network-interface/*" + ] + ] + } + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + }, + ":*" + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/", + { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + }, + "-*" + ] + ] + } + }, + { + "Action": "sts:AssumeRole", + "Condition": { + "ForAnyValue:StringEquals": { + "iam:ResourceTag/aws-cdk:bootstrap-role": [ + "image-publishing", + "file-publishing", + "deploy" + ] + } + }, + "Effect": "Allow", + "Resource": "arn:*:iam::12345678:role/*" + }, + { + "Action": "cloudformation:DescribeStacks", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineUpdatePipelineSelfMutationRoleDefaultPolicyA225DA4E", + "Roles": [ + { + "Ref": "PipelineUpdatePipelineSelfMutationRole57E559E8" + } + ] + } + }, + "PipelineUpdatePipelineSelfMutationSecurityGroup94164EDC": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Automatic generated security group for CodeBuild PipelineStackPipelineUpdatePipelineSelfMutationE51045FC", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "PipelineUpdatePipelineSelfMutationDAA41400": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutationRole57E559E8", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"commands\": [\n \"npm install -g aws-cdk\"\n ]\n },\n \"build\": {\n \"commands\": [\n \"cdk -a . deploy PipelineStack --require-approval=never --verbose\"\n ]\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": "alias/aws/s3", + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutationSecurityGroup94164EDC", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "DependsOn": [ + "PipelineUpdatePipelineSelfMutationPolicyDocumentD327DC74" + ] + }, + "PipelineUpdatePipelineSelfMutationPolicyDocumentD327DC74": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeDhcpOptions", + "ec2:DescribeVpcs" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineUpdatePipelineSelfMutationPolicyDocumentD327DC74", + "Roles": [ + { + "Ref": "PipelineUpdatePipelineSelfMutationRole57E559E8" + } + ] + } + }, + "PipelineAssetsFileRole59943A77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com", + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineAssetsFileRoleDefaultPolicy14DB8755": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/*" + ] + ] + } + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/*" + ] + ] + } + }, + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + ] + }, + { + "Action": "ec2:CreateNetworkInterfacePermission", + "Condition": { + "StringEquals": { + "ec2:Subnet": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":subnet/", + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ] + ] + } + ], + "ec2:AuthorizedService": "codebuild.amazonaws.com" + } + }, + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":network-interface/*" + ] + ] + } + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineAssetsFileRoleDefaultPolicy14DB8755", + "Roles": [ + { + "Ref": "PipelineAssetsFileRole59943A77" + } + ] + } + }, + "PipelineAssetsFileAsset1SecurityGroupF04F1AD4": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Automatic generated security group for CodeBuild PipelineStackPipelineAssetsFileAsset10191BEFB", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "PipelineAssetsFileAsset185A67CB4": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"commands\": [\n \"npm install -g cdk-assets\"\n ]\n },\n \"build\": {\n \"commands\": [\n \"cdk-assets --path \\\"assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json\\\" --verbose publish \\\"8289faf53c7da377bb2b90615999171adef5e1d8f6b88810e5fef75e6ca09ba5:current_account-current_region\\\"\"\n ]\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": "alias/aws/s3", + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset1SecurityGroupF04F1AD4", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "DependsOn": [ + "PipelineAssetsFileAsset1PolicyDocument4681543E" + ] + }, + "PipelineAssetsFileAsset1PolicyDocument4681543E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeDhcpOptions", + "ec2:DescribeVpcs" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineAssetsFileAsset1PolicyDocument4681543E", + "Roles": [ + { + "Ref": "PipelineAssetsFileRole59943A77" + } + ] + } + }, + "PipelineAssetsFileAsset2SecurityGroupA400C1A5": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Automatic generated security group for CodeBuild PipelineStackPipelineAssetsFileAsset24DB856A2", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "PipelineAssetsFileAsset24D2D639B": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"commands\": [\n \"npm install -g cdk-assets\"\n ]\n },\n \"build\": {\n \"commands\": [\n \"cdk-assets --path \\\"assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json\\\" --verbose publish \\\"ac76997971c3f6ddf37120660003f1ced72b4fc58c498dfd99c78fa77e721e0e:current_account-current_region\\\"\"\n ]\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": "alias/aws/s3", + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset2SecurityGroupA400C1A5", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + }, + { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "DependsOn": [ + "PipelineAssetsFileAsset1PolicyDocument4681543E" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.ts b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.ts new file mode 100644 index 0000000000000..590757335081f --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.ts @@ -0,0 +1,57 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +/// !cdk-integ PipelineStack +import * as path from 'path'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as s3_assets from '@aws-cdk/aws-s3-assets'; +import * as sqs from '@aws-cdk/aws-sqs'; +import { App, Stack, StackProps, Stage, StageProps } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as pipelines from '../lib'; + +class PipelineStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const vpc = new ec2.Vpc(this, 'Vpc'); + + const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { + codeBuildDefaults: { vpc }, + synth: new pipelines.ShellStep('Synth', { + input: pipelines.CodePipelineSource.gitHub('rix0rrr/cdk-pipelines-demo', 'main'), + commands: [ + 'npm ci', + 'npm run build', + 'npx cdk synth', + ], + }), + }); + + pipeline.addStage(new AppStage(this, 'Beta')); + } +} + +class AppStage extends Stage { + constructor(scope: Construct, id: string, props?: StageProps) { + super(scope, id, props); + + const stack = new Stack(this, 'Stack1'); + new s3_assets.Asset(stack, 'Asset', { + path: path.join(__dirname, 'testhelpers/assets/test-file-asset.txt'), + }); + new s3_assets.Asset(stack, 'Asset2', { + path: path.join(__dirname, 'testhelpers/assets/test-file-asset-two.txt'), + }); + + new sqs.Queue(stack, 'OtherQueue'); + } +} + +const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': '1', + }, +}); +new PipelineStack(app, 'PipelineStack', { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, +}); +app.synth(); \ No newline at end of file From 774b9ed209558dbb94475ad66d854a907205028a Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 23 Jul 2021 13:22:16 +0200 Subject: [PATCH 15/27] docs(pipelines): fix table rendering in MarkDown (#15734) GitHub's MarkDown rendering doesn't recognize the `+` in a table column separator. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/ORIGINAL_API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/pipelines/ORIGINAL_API.md b/packages/@aws-cdk/pipelines/ORIGINAL_API.md index 14152b5b7f6d6..119e0037408e8 100644 --- a/packages/@aws-cdk/pipelines/ORIGINAL_API.md +++ b/packages/@aws-cdk/pipelines/ORIGINAL_API.md @@ -17,7 +17,7 @@ Replace `new CdkPipeline` with `new CodePipeline`. Some configuration properties have been changed: | Old API | New API | -|--------------------------------+------------------------------------------------------------------------------------------------| +|--------------------------------|------------------------------------------------------------------------------------------------| | `cloudAssemblyArtifact` | removed | | `sourceAction` | removed | | `synthAction` | `synth` | @@ -96,7 +96,7 @@ potentially `addWave().addStage()`. All stages inside a wave are deployed in parallel, which was not a capability of the original API. | Old API | New API | -|-------------------------------+-------------------------------------------------------------------------------------------------------------------------------| +|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------| | `addApplicationStage()` | `addStage()` | | `addStage().addApplication()` | `addStage()`. Adding multiple CDK Stages into a single Pipeline stage is not supported, add multiple Pipeline stages instead. | From 81cbfec5ddf065aac442d925484a358ee8cd26a1 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Sat, 24 Jul 2021 00:22:22 +0200 Subject: [PATCH 16/27] fix(elasticsearch): advancedOptions in domain has no effect (#15330) Property `advancedOptions` in ElasticSearch domain did have no effect because the assignment was missing. * add assignment for advancedOptions to fix issue * test cases * describe function in readme Fixes #14067 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-elasticsearch/README.md | 15 ++++ .../@aws-cdk/aws-elasticsearch/lib/domain.ts | 2 + .../aws-elasticsearch/test/domain.test.ts | 28 ++++++++ .../test/integ.elasticsearch.expected.json | 68 +++++++++++++++---- .../test/integ.elasticsearch.ts | 7 +- 5 files changed, 106 insertions(+), 14 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticsearch/README.md b/packages/@aws-cdk/aws-elasticsearch/README.md index 8585ffdd8bc55..7a002f3d2cfde 100644 --- a/packages/@aws-cdk/aws-elasticsearch/README.md +++ b/packages/@aws-cdk/aws-elasticsearch/README.md @@ -290,3 +290,18 @@ new Domain(stack, 'Domain', { It is also possible to specify a custom certificate instead of the auto-generated one. Additionally, an automatic CNAME-Record is created if a hosted zone is provided for the custom endpoint + +## Advanced options + +[Advanced options](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-createupdatedomains.html#es-createdomain-configure-advanced-options) can used to configure additional options. + +```ts +new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_7, + advancedOptions: { + 'rest.action.multi.allow_explicit_index': 'false', + 'indices.fielddata.cache.size': '25', + 'indices.query.bool.max_clause_count': '2048', + }, +}); +``` diff --git a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts index a29b530128c51..842071fa0ec68 100644 --- a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts +++ b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts @@ -410,6 +410,7 @@ export interface DomainProps { /** * Additional options to specify for the Amazon ES domain. * + * @see https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-createupdatedomains.html#es-createdomain-configure-advanced-options * @default - no advanced options are specified */ readonly advancedOptions?: { [key: string]: (string) }; @@ -1678,6 +1679,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { }, } : undefined, + advancedOptions: props.advancedOptions, }); this.domain.applyRemovalPolicy(props.removalPolicy); diff --git a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts index 719341aa29408..6966882c55549 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts +++ b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts @@ -1633,6 +1633,34 @@ describe('unsigned basic auth', () => { }); }); +describe('advanced options', () => { + test('use advanced options', () => { + new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_1, + advancedOptions: { + 'rest.action.multi.allow_explicit_index': 'true', + 'indices.fielddata.cache.size': '50', + }, + }); + + expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', { + AdvancedOptions: { + 'rest.action.multi.allow_explicit_index': 'true', + 'indices.fielddata.cache.size': '50', + }, + }); + }); + + test('advanced options absent by default', () => { + new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_1, + }); + + expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', { + AdvancedOptions: assert.ABSENT, + }); + }); +}); function testGrant( expectedActions: string[], diff --git a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json index 2f43d85055ad1..4299402202092 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json +++ b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json @@ -107,6 +107,11 @@ "Domain19FCBCB91": { "Type": "AWS::Elasticsearch::Domain", "Properties": { + "AdvancedOptions": { + "rest.action.multi.allow_explicit_index": "false", + "indices.fielddata.cache.size": "25", + "indices.query.bool.max_clause_count": "2048" + }, "CognitoOptions": { "Enabled": false }, @@ -206,7 +211,15 @@ { "Ref": "Domain19FCBCB91" }, - "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"*\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", + "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", { "Ref": "Domain19FCBCB91" }, @@ -222,7 +235,15 @@ { "Ref": "Domain19FCBCB91" }, - "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"*\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", + "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", { "Ref": "Domain19FCBCB91" }, @@ -275,7 +296,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" + "Ref": "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3BucketB17E5ABD" }, "S3Key": { "Fn::Join": [ @@ -288,7 +309,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" + "Ref": "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3VersionKey77778F6A" } ] } @@ -301,7 +322,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" + "Ref": "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3VersionKey77778F6A" } ] } @@ -432,6 +453,11 @@ "Domain2644FE48C": { "Type": "AWS::Elasticsearch::Domain", "Properties": { + "AdvancedOptions": { + "rest.action.multi.allow_explicit_index": "false", + "indices.fielddata.cache.size": "25", + "indices.query.bool.max_clause_count": "2048" + }, "CognitoOptions": { "Enabled": false }, @@ -531,7 +557,15 @@ { "Ref": "Domain2644FE48C" }, - "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"*\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", + "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", { "Ref": "Domain2644FE48C" }, @@ -547,7 +581,15 @@ { "Ref": "Domain2644FE48C" }, - "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"*\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", + "\",\"AccessPolicies\":\"{\\\"Statement\\\":[{\\\"Action\\\":\\\"es:ESHttp*\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"AWS\\\":\\\"arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root\\\"},\\\"Resource\\\":\\\"*\\\"}],\\\"Version\\\":\\\"2012-10-17\\\"}\"},\"outputPath\":\"DomainConfig.ElasticsearchClusterConfig.AccessPolicies\",\"physicalResourceId\":{\"id\":\"", { "Ref": "Domain2644FE48C" }, @@ -566,17 +608,17 @@ } }, "Parameters": { - "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { + "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3BucketB17E5ABD": { "Type": "String", - "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" + "Description": "S3 bucket for asset \"5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4\"" }, - "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { + "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3VersionKey77778F6A": { "Type": "String", - "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" + "Description": "S3 key for asset version \"5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4\"" }, - "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { + "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4ArtifactHash580E429C": { "Type": "String", - "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" + "Description": "Artifact hash for asset \"5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.ts b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.ts index d2851bd3d47b9..fb112d26390ee 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.ts +++ b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.ts @@ -24,12 +24,17 @@ class TestStack extends Stack { encryptionAtRest: { enabled: true, }, + advancedOptions: { + 'rest.action.multi.allow_explicit_index': 'false', + 'indices.fielddata.cache.size': '25', + 'indices.query.bool.max_clause_count': '2048', + }, // test the access policies custom resource works accessPolicies: [ new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['es:ESHttp*'], - principals: [new iam.AnyPrincipal()], + principals: [new iam.AccountRootPrincipal()], resources: ['*'], }), ], From a8b1c471b7058bbf739a1d4f5b4860656ebd5432 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 26 Jul 2021 12:13:52 +0100 Subject: [PATCH 17/27] feat(assertions): retrieve matching resources from the template (#15642) Provide API `findResources()` that retrieves the matching resources from the template given its type and optional predicate. For complex assertions that cannot be modeled using the primitives provided by this module, this API allows an 'escape hatch' so that assertions can be written directly into the test case. This is being used in the `aws-cloudwatch` module, specifically to assert widgets in a CloudWatch Dashboard that are modeled as serialized JSON within a property in the resource. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/assertions/README.md | 6 +- packages/@aws-cdk/assertions/lib/index.ts | 2 +- .../{has-resource.ts => private/resource.ts} | 60 +++++++++++++----- .../lib/{assertions.ts => template.ts} | 13 +++- .../{assertions.test.ts => template.test.ts} | 62 +++++++++++++++++++ packages/@aws-cdk/aws-cloudwatch/package.json | 4 +- .../aws-cloudwatch/test/alarm.test.ts | 27 ++++---- .../test/composite-alarm.test.ts | 4 +- .../test/cross-environment.test.ts | 4 +- .../aws-cloudwatch/test/dashboard.test.ts | 44 ++++++------- .../aws-cloudwatch/test/metric-math.test.ts | 4 +- .../aws-cloudwatch/test/metrics.test.ts | 6 +- 12 files changed, 172 insertions(+), 64 deletions(-) rename packages/@aws-cdk/assertions/lib/{has-resource.ts => private/resource.ts} (53%) rename packages/@aws-cdk/assertions/lib/{assertions.ts => template.ts} (86%) rename packages/@aws-cdk/assertions/test/{assertions.test.ts => template.test.ts} (80%) diff --git a/packages/@aws-cdk/assertions/README.md b/packages/@aws-cdk/assertions/README.md index 5d61312a812cb..1a79f8294cf3b 100644 --- a/packages/@aws-cdk/assertions/README.md +++ b/packages/@aws-cdk/assertions/README.md @@ -62,7 +62,7 @@ in a template. assert.resourceCountIs('Foo::Bar', 2); ``` -## Resource Matching +## Resource Matching & Retrieval Beyond resource counting, the module also allows asserting that a resource with specific properties are present. @@ -88,6 +88,10 @@ assert.hasResource('Foo::Bar', { }); ``` +Beyond assertions, the module provides APIs to retrieve matching resources. +The `findResources()` API is complementary to the `hasResource()` API, except, +instead of asserting its presence, it returns the set of matching resources. + By default, the `hasResource()` and `hasResourceProperties()` APIs perform deep partial object matching. This behavior can be configured using matchers. See subsequent section on [special matchers](#special-matchers). diff --git a/packages/@aws-cdk/assertions/lib/index.ts b/packages/@aws-cdk/assertions/lib/index.ts index 937b59823762c..963039f921bc1 100644 --- a/packages/@aws-cdk/assertions/lib/index.ts +++ b/packages/@aws-cdk/assertions/lib/index.ts @@ -1,3 +1,3 @@ -export * from './assertions'; +export * from './template'; export * from './match'; export * from './matcher'; \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/lib/has-resource.ts b/packages/@aws-cdk/assertions/lib/private/resource.ts similarity index 53% rename from packages/@aws-cdk/assertions/lib/has-resource.ts rename to packages/@aws-cdk/assertions/lib/private/resource.ts index e4b6bf3c1eb06..22a2b01734b70 100644 --- a/packages/@aws-cdk/assertions/lib/has-resource.ts +++ b/packages/@aws-cdk/assertions/lib/private/resource.ts @@ -1,6 +1,20 @@ -import { Match } from './match'; -import { Matcher, MatchResult } from './matcher'; -import { StackInspector } from './vendored/assert'; +import { Match } from '../match'; +import { Matcher, MatchResult } from '../matcher'; +import { StackInspector } from '../vendored/assert'; + +export function findResources(inspector: StackInspector, type: string, props: any = {}): { [key: string]: any }[] { + const matcher = Matcher.isMatcher(props) ? props : Match.objectLike(props); + let results: { [key: string]: any }[] = []; + + eachResourceWithType(inspector, type, (resource) => { + const result = matcher.test(resource); + if (!result.hasFailed()) { + results.push(resource); + } + }); + + return results; +} export function hasResource(inspector: StackInspector, type: string, props: any): string | void { const matcher = Matcher.isMatcher(props) ? props : Match.objectLike(props); @@ -8,19 +22,22 @@ export function hasResource(inspector: StackInspector, type: string, props: any) let closestResource: { [key: string]: any } | undefined = undefined; let count: number = 0; - for (const logicalId of Object.keys(inspector.value.Resources ?? {})) { - const resource: { [key: string]: any } = inspector.value.Resources[logicalId]; - if (resource.Type === type) { - count++; - const result = matcher.test(resource); - if (!result.hasFailed()) { - return; - } - if (closestResult === undefined || closestResult.failCount > result.failCount) { - closestResult = result; - closestResource = resource; - } + let match = false; + eachResourceWithType(inspector, type, (resource) => { + if (match) { return; } + count++; + const result = matcher.test(resource); + if (!result.hasFailed()) { + match = true; + } + if (closestResult === undefined || closestResult.failCount > result.failCount) { + closestResult = result; + closestResource = resource; } + }); + + if (match) { + return; } if (closestResult === undefined) { @@ -37,6 +54,19 @@ export function hasResource(inspector: StackInspector, type: string, props: any) ].join('\n'); } +function eachResourceWithType( + inspector: StackInspector, + type: string, + cb: (resource: {[key: string]: any}) => void): void { + + for (const logicalId of Object.keys(inspector.value.Resources ?? {})) { + const resource: { [key: string]: any } = inspector.value.Resources[logicalId]; + if (resource.Type === type) { + cb(resource); + } + } +} + function formatMessage(closestResult: MatchResult, closestResource: {}): string { return [ 'The closest result is:', diff --git a/packages/@aws-cdk/assertions/lib/assertions.ts b/packages/@aws-cdk/assertions/lib/template.ts similarity index 86% rename from packages/@aws-cdk/assertions/lib/assertions.ts rename to packages/@aws-cdk/assertions/lib/template.ts index aa7eaeb0bfd7d..c76f8150292a2 100644 --- a/packages/@aws-cdk/assertions/lib/assertions.ts +++ b/packages/@aws-cdk/assertions/lib/template.ts @@ -1,7 +1,7 @@ import { Stack, Stage } from '@aws-cdk/core'; -import { hasResource } from './has-resource'; import { Match } from './match'; import { Matcher } from './matcher'; +import { findResources, hasResource } from './private/resource'; import * as assert from './vendored/assert'; /** @@ -82,6 +82,17 @@ export class TemplateAssertions { } } + /** + * Get the set of matching resources of a given type and properties in the CloudFormation template. + * @param type the type to match in the CloudFormation template + * @param props by default, matches all resources with the given type. + * When a literal is provided, performs a partial match via `Match.objectLike()`. + * Use the `Match` APIs to configure a different behaviour. + */ + public findResources(type: string, props: any = {}): { [key: string]: any }[] { + return findResources(this.inspector, type, props); + } + /** * Assert that the CloudFormation template matches the given value * @param expected the expected CloudFormation template as key-value pairs. diff --git a/packages/@aws-cdk/assertions/test/assertions.test.ts b/packages/@aws-cdk/assertions/test/template.test.ts similarity index 80% rename from packages/@aws-cdk/assertions/test/assertions.test.ts rename to packages/@aws-cdk/assertions/test/template.test.ts index bd0cdb828f921..70b09b618446c 100644 --- a/packages/@aws-cdk/assertions/test/assertions.test.ts +++ b/packages/@aws-cdk/assertions/test/template.test.ts @@ -221,6 +221,68 @@ describe('TemplateAssertions', () => { })).toThrow(/No resource/); }); }); + + describe('getResources', () => { + test('matching resource type', () => { + const stack = new Stack(); + new CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { baz: 'qux', fred: 'waldo' }, + }); + + const inspect = TemplateAssertions.fromStack(stack); + expect(inspect.findResources('Foo::Bar')).toEqual([{ + Type: 'Foo::Bar', + Properties: { baz: 'qux', fred: 'waldo' }, + }]); + }); + + test('no matching resource type', () => { + const stack = new Stack(); + new CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { baz: 'qux', fred: 'waldo' }, + }); + + const inspect = TemplateAssertions.fromStack(stack); + expect(inspect.findResources('Foo::Baz')).toEqual([]); + }); + + test('matching resource props', () => { + const stack = new Stack(); + new CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { baz: 'qux', fred: 'waldo' }, + }); + + const inspect = TemplateAssertions.fromStack(stack); + expect(inspect.findResources('Foo::Bar', { + Properties: { baz: 'qux' }, + }).length).toEqual(1); + }); + + test('no matching resource props', () => { + const stack = new Stack(); + new CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { baz: 'qux', fred: 'waldo' }, + }); + + const inspect = TemplateAssertions.fromStack(stack); + expect(inspect.findResources('Foo::Bar', { + Properties: { baz: 'waldo' }, + })).toEqual([]); + }); + + test('multiple matching resources', () => { + const stack = new Stack(); + new CfnResource(stack, 'Foo', { type: 'Foo::Bar' }); + new CfnResource(stack, 'Bar', { type: 'Foo::Bar' }); + + const inspect = TemplateAssertions.fromStack(stack); + expect(inspect.findResources('Foo::Bar').length).toEqual(2); + }); + }); }); function expectToThrow(fn: () => void, msgs: (RegExp | string)[], done: jest.DoneCallback): void { diff --git a/packages/@aws-cdk/aws-cloudwatch/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index 246ad8a89ef37..683ca79bde154 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -73,13 +73,13 @@ }, "license": "Apache-2.0", "devDependencies": { + "@aws-cdk/assertions": "0.0.0", "@types/jest": "^26.0.24", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", "jest": "^26.6.3", - "pkglint": "0.0.0", - "@aws-cdk/assert-internal": "0.0.0" + "pkglint": "0.0.0" }, "dependencies": { "@aws-cdk/aws-iam": "0.0.0", diff --git a/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts index bfe3eca913b67..437dbc5df4ab8 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT } from '@aws-cdk/assert-internal'; +import { Match, TemplateAssertions } from '@aws-cdk/assertions'; import { Duration, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { Alarm, IAlarm, IAlarmAction, Metric, MathExpression, IMetric } from '../lib'; @@ -68,7 +67,7 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 3, MetricName: 'Metric', @@ -94,7 +93,7 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 3, MetricName: 'Metric', @@ -120,14 +119,14 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 3, MetricName: 'Metric', Namespace: 'CDK/Test', Period: 300, Statistic: 'Maximum', - ExtendedStatistic: ABSENT, + ExtendedStatistic: Match.absentProperty(), Threshold: 1000, }); @@ -147,13 +146,13 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 3, MetricName: 'Metric', Namespace: 'CDK/Test', Period: 300, - Statistic: ABSENT, + Statistic: Match.absentProperty(), ExtendedStatistic: 'p99', Threshold: 1000, }); @@ -174,7 +173,7 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 3, DatapointsToAlarm: 2, @@ -204,7 +203,7 @@ describe('Alarm', () => { alarm.addOkAction(new TestAlarmAction('C')); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { AlarmActions: ['A'], InsufficientDataActions: ['B'], OKActions: ['C'], @@ -226,7 +225,7 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 2, MetricName: 'Metric', @@ -251,7 +250,7 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ExtendedStatistic: 'p99.9', }); @@ -270,8 +269,8 @@ describe('Alarm', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { - Statistic: ABSENT, + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + Statistic: Match.absentProperty(), ExtendedStatistic: 'tm99.9999999999', }); diff --git a/packages/@aws-cdk/aws-cloudwatch/test/composite-alarm.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/composite-alarm.test.ts index 054f1b21724ee..e77d33a546ca5 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/composite-alarm.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/composite-alarm.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { TemplateAssertions } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { Alarm, AlarmRule, AlarmState, CompositeAlarm, Metric } from '../lib'; @@ -59,7 +59,7 @@ describe('CompositeAlarm', () => { alarmRule, }); - expect(stack).toHaveResource('AWS::CloudWatch::CompositeAlarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::CompositeAlarm', { AlarmName: 'CompositeAlarm', AlarmRule: { 'Fn::Join': [ diff --git a/packages/@aws-cdk/aws-cloudwatch/test/cross-environment.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/cross-environment.test.ts index 959ceafab54fc..fc00cda0753d1 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/cross-environment.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/cross-environment.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { TemplateAssertions } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { Alarm, GraphWidget, IWidget, Metric } from '../lib'; @@ -89,7 +89,7 @@ describe('cross environment', () => { }); // THEN - expect(stack1).toHaveResourceLike('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack1).hasResourceProperties('AWS::CloudWatch::Alarm', { MetricName: 'ACount', Namespace: 'Test', Period: 300, diff --git a/packages/@aws-cdk/aws-cloudwatch/test/dashboard.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/dashboard.test.ts index 5501a47ba5c3a..cf3c4caa96042 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/dashboard.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/dashboard.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { isSuperObject } from '@aws-cdk/assert-internal'; +import { TemplateAssertions } from '@aws-cdk/assertions'; import { App, Stack } from '@aws-cdk/core'; import { Dashboard, GraphWidget, PeriodOverride, TextWidget } from '../lib'; @@ -27,11 +26,13 @@ describe('Dashboard', () => { })); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Dashboard', thatHasWidgets([ + const resources = TemplateAssertions.fromStack(stack).findResources('AWS::CloudWatch::Dashboard'); + expect(resources.length).toEqual(1); + hasWidgets(resources[0].Properties, [ { type: 'text', width: 10, height: 2, x: 0, y: 0, properties: { markdown: 'first' } }, { type: 'text', width: 1, height: 4, x: 0, y: 2, properties: { markdown: 'second' } }, { type: 'text', width: 4, height: 1, x: 0, y: 6, properties: { markdown: 'third' } }, - ])); + ]); }); @@ -61,11 +62,13 @@ describe('Dashboard', () => { ); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Dashboard', thatHasWidgets([ + const resources = TemplateAssertions.fromStack(stack).findResources('AWS::CloudWatch::Dashboard'); + expect(resources.length).toEqual(1); + hasWidgets(resources[0].Properties, [ { type: 'text', width: 10, height: 2, x: 0, y: 0, properties: { markdown: 'first' } }, { type: 'text', width: 1, height: 4, x: 10, y: 0, properties: { markdown: 'second' } }, { type: 'text', width: 4, height: 1, x: 11, y: 0, properties: { markdown: 'third' } }, - ])); + ]); }); @@ -81,7 +84,7 @@ describe('Dashboard', () => { ); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Dashboard', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Dashboard', { DashboardBody: { 'Fn::Join': ['', [ '{"widgets":[{"type":"metric","width":1,"height":1,"x":0,"y":0,"properties":{"view":"timeSeries","region":"', @@ -110,7 +113,7 @@ describe('Dashboard', () => { ); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Dashboard', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Dashboard', { DashboardBody: { 'Fn::Join': ['', [ '{"start":"-9H","end":"2018-12-17T06:00:00.000Z","periodOverride":"inherit",\ @@ -135,7 +138,7 @@ describe('Dashboard', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Dashboard', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Dashboard', { DashboardName: 'MyCustomDashboardName', }); @@ -151,7 +154,7 @@ describe('Dashboard', () => { new Dashboard(stack, 'MyDashboard'); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Dashboard', {}); + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Dashboard', {}); }); @@ -178,15 +181,14 @@ describe('Dashboard', () => { /** * Returns a property predicate that checks that the given Dashboard has the indicated widgets */ -function thatHasWidgets(widgets: any): (props: any) => boolean { - return (props: any) => { - try { - const actualWidgets = JSON.parse(props.DashboardBody).widgets; - return isSuperObject(actualWidgets, widgets); - } catch (e) { - // eslint-disable-next-line no-console - console.error('Error parsing', props); - throw e; - } - }; +function hasWidgets(props: any, widgets: any[]) { + let actualWidgets: any[] = []; + try { + actualWidgets = JSON.parse(props.DashboardBody).widgets; + } catch (e) { + // eslint-disable-next-line no-console + console.error('Error parsing', props); + throw e; + } + expect(actualWidgets).toEqual(expect.arrayContaining(widgets)); } diff --git a/packages/@aws-cdk/aws-cloudwatch/test/metric-math.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/metric-math.test.ts index b9379bbdc360c..52e7803575d05 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/metric-math.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/metric-math.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { TemplateAssertions } from '@aws-cdk/assertions'; import { Duration, Stack } from '@aws-cdk/core'; import { Alarm, GraphWidget, IWidget, MathExpression, Metric } from '../lib'; @@ -638,7 +638,7 @@ function graphMetricsAre(w: IWidget, metrics: any[]) { } function alarmMetricsAre(metrics: any[]) { - expect(stack).toHaveResourceLike('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { Metrics: metrics, }); } diff --git a/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts index ff1620f91ed50..f7b5c69f8018b 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/metrics.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { TemplateAssertions } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Alarm, Metric } from '../lib'; @@ -15,7 +15,7 @@ describe('Metrics', () => { Metric.grantPutMetricData(role); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -188,7 +188,7 @@ describe('Metrics', () => { dimensionA: 'value1', dimensionB: 'value2', }); - expect(stack).toHaveResourceLike('AWS::CloudWatch::Alarm', { + TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { Namespace: 'Test', MetricName: 'Metric', Dimensions: [ From 807351857db014ccda09718f88a8c51732cd0f1a Mon Sep 17 00:00:00 2001 From: Wouter Klijn Date: Mon, 26 Jul 2021 17:26:18 +0200 Subject: [PATCH 18/27] chore(rds): add SQL Server version 14.00.3381.3.v1 (#15752) According to the [documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_SQLServer.html#SQLServer.Concepts.General.VersionSupport) on supported SQL Server versions for AWS RDS, `14.00.3381.3.v1` should be a supported engine version. It wasn't yet available in the CDK; this merge request should change that. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-rds/lib/instance-engine.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts index 2921d828f37ba..7b1c8fad83eaf 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts @@ -1343,6 +1343,8 @@ export class SqlServerEngineVersion { public static readonly VER_14_00_3294_2_V1 = SqlServerEngineVersion.of('14.00.3294.2.v1', '14.00'); /** Version "14.00.3356.20.v1". */ public static readonly VER_14_00_3356_20_V1 = SqlServerEngineVersion.of('14.00.3356.20.v1', '14.00'); + /** Version "14.00.3381.3.v1". */ + public static readonly VER_14_00_3381_3_V1 = SqlServerEngineVersion.of('14.00.3381.3.v1', '14.00'); /** Version "15.00" (only a major version, without a specific minor version). */ public static readonly VER_15 = SqlServerEngineVersion.of('15.00', '15.00'); From d9285cb75745028ede8c36afcee34f7a53d27993 Mon Sep 17 00:00:00 2001 From: Michael Sambol Date: Mon, 26 Jul 2021 12:48:59 -0700 Subject: [PATCH 19/27] feat(stepfunctions): allow intrinsic functions for json path (#15320) `QueryString` can start with an [intrinsic function](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html). Here is an example of a working `QueryString`: ``` "QueryString.$": "States.Format('select contact_id from interactions.conversation where case when \\'{}\\' is not null then bu=\\'{}\\' else 1=1 end and case when \\'{}\\' is not null then channel=\\'{}\\' else 1=1 end and case when {} is not null then year={} else 1=1 end and case when {} is not null then month={} else 1=1 end and case when {} is not null then day={} else 1=1 end limit 1;', States.JsonToString($.completed), $.partitions.bu, $.partitions.bu, $.partitions.channel, $.partitions.channel, $.partitions.year, $.partitions.year, $.partitions.month, $.partitions.month, $.partitions.day, $.partitions.day)" ``` --- .../aws-stepfunctions-tasks/README.md | 22 +++++++++++++++++++ .../@aws-cdk/aws-stepfunctions/lib/fields.ts | 3 ++- .../aws-stepfunctions/test/fields.test.ts | 7 +++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md index bc73260059d4e..0ee1cbaba53f9 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md @@ -205,6 +205,28 @@ const submitJob = new tasks.LambdaInvoke(this, 'Invoke Handler', { }); ``` +You can also use [intrinsic functions](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html) with `JsonPath.stringAt()`. +Here is an example of starting an Athena query that is dynamically created using the task input: + +```ts +const startQueryExecutionJob = new tasks.AthenaStartQueryExecution(this, 'Athena Start Query', { + queryString: sfn.JsonPath.stringAt("States.Format('select contacts where year={};', $.year)"), + queryExecutionContext: { + databaseName: 'interactions', + }, + resultConfiguration: { + encryptionConfiguration: { + encryptionOption: tasks.EncryptionOption.S3_MANAGED, + }, + outputLocation: { + bucketName: 'mybucket', + objectKey: 'myprefix', + }, + }, + integrationPattern: sfn.IntegrationPattern.RUN_JOB, +}); +``` + Each service integration has its own set of parameters that can be supplied. ## Evaluate Expression diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts b/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts index 9064fb93d2b6e..990a2542d4fea 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts @@ -219,8 +219,9 @@ function validateJsonPath(path: string) { && path !== '$$' && !path.startsWith('$$.') && !path.startsWith('$[') + && ['Format', 'StringToJson', 'JsonToString', 'Array'].every(fn => !path.startsWith(`States.${fn}`)) ) { - throw new Error(`JSON path values must be exactly '$', '$$', start with '$.', start with '$$.' or start with '$[' Received: ${path}`); + throw new Error(`JSON path values must be exactly '$', '$$', start with '$.', start with '$$.', start with '$[', or start with an intrinsic function: States.Format, States.StringToJson, States.JsonToString, or States.Array. Received: ${path}`); } } diff --git a/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts index 382ec424177f9..85549614a0538 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts @@ -2,7 +2,7 @@ import '@aws-cdk/assert-internal/jest'; import { FieldUtils, JsonPath, TaskInput } from '../lib'; describe('Fields', () => { - const jsonPathValidationErrorMsg = /exactly '\$', '\$\$', start with '\$.', start with '\$\$.' or start with '\$\['/; + const jsonPathValidationErrorMsg = /exactly '\$', '\$\$', start with '\$.', start with '\$\$.', start with '\$\[', or start with an intrinsic function: States.Format, States.StringToJson, States.JsonToString, or States.Array./; test('deep replace correctly handles fields in arrays', () => { expect( @@ -71,9 +71,14 @@ describe('Fields', () => { }), test('datafield path must be correct', () => { expect(JsonPath.stringAt('$')).toBeDefined(); + expect(JsonPath.stringAt('States.Format')).toBeDefined(); + expect(JsonPath.stringAt('States.StringToJson')).toBeDefined(); + expect(JsonPath.stringAt('States.JsonToString')).toBeDefined(); + expect(JsonPath.stringAt('States.Array')).toBeDefined(); expect(() => JsonPath.stringAt('$hello')).toThrowError(jsonPathValidationErrorMsg); expect(() => JsonPath.stringAt('hello')).toThrowError(jsonPathValidationErrorMsg); + expect(() => JsonPath.stringAt('States.FooBar')).toThrowError(jsonPathValidationErrorMsg); }), test('context path must be correct', () => { expect(JsonPath.stringAt('$$')).toBeDefined(); From 6f2384ddc180e944c9564a543351b8df2f75c1a7 Mon Sep 17 00:00:00 2001 From: Jackie Yuan <32397155+syeehyn@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:27:38 -0400 Subject: [PATCH 20/27] fix(stepfunctions-tasks): instance type cannot be provided to SageMakerCreateTransformJob as input path (#15726) as referred in https://github.com/aws/aws-cdk/issues/11605 in SageMakerCreateTransformJob has the same kind of issue. similar solution can be found at https://github.com/aws/aws-cdk/pull/11749 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/sagemaker/create-transform-job.ts | 3 +- .../sagemaker/create-transform-job.test.ts | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts index e1b5aabfc61b6..cc108c45e4439 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts @@ -227,7 +227,8 @@ export class SageMakerCreateTransformJob extends sfn.TaskStateBase { return { TransformResources: { InstanceCount: resources.instanceCount, - InstanceType: 'ml.' + resources.instanceType, + InstanceType: sfn.JsonPath.isEncodedJsonPath(resources.instanceType.toString()) + ? resources.instanceType.toString() : `ml.${resources.instanceType}`, ...(resources.volumeEncryptionKey ? { VolumeKmsKeyId: resources.volumeEncryptionKey.keyArn } : {}), }, }; diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-transform-job.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-transform-job.test.ts index cf545b3e1defb..16f499e53708c 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-transform-job.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-transform-job.test.ts @@ -249,3 +249,61 @@ test('pass param to transform job', () => { }, }); }); +test('create transform job with instance type supplied as JsonPath', () => { + // WHEN + const task = new SageMakerCreateTransformJob(stack, 'TransformTask', { + transformJobName: 'MyTransformJob', + modelName: 'MyModelName', + transformInput: { + transformDataSource: { + s3DataSource: { + s3Uri: 's3://inputbucket/prefix', + }, + }, + }, + transformOutput: { + s3OutputPath: 's3://outputbucket/prefix', + }, + transformResources: { + instanceCount: 1, + instanceType: new ec2.InstanceType(sfn.JsonPath.stringAt('$.InstanceType')), + }, + }); + + // THEN + expect(stack.resolve(task.toStateJson())).toEqual({ + Type: 'Task', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':states:::sagemaker:createTransformJob', + ], + ], + }, + End: true, + Parameters: { + TransformJobName: 'MyTransformJob', + ModelName: 'MyModelName', + TransformInput: { + DataSource: { + S3DataSource: { + S3Uri: 's3://inputbucket/prefix', + S3DataType: 'S3Prefix', + }, + }, + }, + TransformOutput: { + S3OutputPath: 's3://outputbucket/prefix', + }, + TransformResources: { + 'InstanceCount': 1, + 'InstanceType.$': '$.InstanceType', + }, + }, + }); +}); From 9451b33a65f4d83b5bb706ac03a89386e061d2d9 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 27 Jul 2021 09:25:55 +0100 Subject: [PATCH 21/27] chore(s3): additional documentation for addToResourcePolicy. (#15761) This is to clarify that in some cases the policy will not be added and the result should be checked. Closes #6548 and #7370. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-s3/README.md | 27 ++++++++++++++++++-- packages/@aws-cdk/aws-s3/lib/bucket.ts | 35 ++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 6a266560f65e4..8b60ec35eb766 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -92,13 +92,36 @@ A bucket policy will be automatically created for the bucket upon the first call ```ts const bucket = new Bucket(this, 'MyBucket'); -bucket.addToResourcePolicy(new iam.PolicyStatement({ +const result = bucket.addToResourcePolicy(new iam.PolicyStatement({ actions: ['s3:GetObject'], resources: [bucket.arnForObjects('file.txt')], principals: [new iam.AccountRootPrincipal()], })); ``` +If you try to add a policy statement to an existing bucket, this method will +not do anything: + +```ts +const bucket = Bucket.fromBucketName(this, 'existingBucket', 'bucket-name'); + +// Nothing will change here +const result = bucket.addToResourcePolicy(new iam.PolicyStatement({ + ... +})); +``` + +That's because it's not possible to tell whether the bucket +already has a policy attached, let alone to re-use that policy to add more +statements to it. We recommend that you always check the result of the call: + +```ts +const result = bucket.addToResourcePolicy(...) +if (!result.statementAdded) { + // Uh-oh! Someone probably made a mistake here. +} +``` + The bucket policy can be directly accessed after creation to add statements or adjust the removal policy. @@ -150,7 +173,7 @@ const bucket = Bucket.fromBucketAttributes(this, 'ImportedBucket', { }); // now you can just call methods on the bucket -bucket.grantReadWrite(user); +bucket.addEventNotification(EventType.OBJECT_CREATED, ...); ``` Alternatively, short-hand factories are available as `Bucket.fromBucketName` and diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index b4f549f5feef0..970710c9a3dc4 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -82,9 +82,23 @@ export interface IBucket extends IResource { /** * Adds a statement to the resource policy for a principal (i.e. - * account/role/service) to perform actions on this bucket and/or it's + * account/role/service) to perform actions on this bucket and/or its * contents. Use `bucketArn` and `arnForObjects(keys)` to obtain ARNs for * this bucket or objects. + * + * Note that the policy statement may or may not be added to the policy. + * For example, when an `IBucket` is created from an existing bucket, + * it's not possible to tell whether the bucket already has a policy + * attached, let alone to re-use that policy to add more statements to it. + * So it's safest to do nothing in these cases. + * + * @param permission the policy statement to be added to the bucket's + * policy. + * @returns metadata about the execution of this method. If the policy + * was not added, the value of `statementAdded` will be `false`. You + * should always check this value to make sure that the operation was + * actually carried out. Otherwise, synthesis and deploy will terminate + * silently, which may be confusing. */ addToResourcePolicy(permission: iam.PolicyStatement): iam.AddToResourcePolicyResult; @@ -546,9 +560,23 @@ export abstract class BucketBase extends Resource implements IBucket { /** * Adds a statement to the resource policy for a principal (i.e. - * account/role/service) to perform actions on this bucket and/or it's + * account/role/service) to perform actions on this bucket and/or its * contents. Use `bucketArn` and `arnForObjects(keys)` to obtain ARNs for * this bucket or objects. + * + * Note that the policy statement may or may not be added to the policy. + * For example, when an `IBucket` is created from an existing bucket, + * it's not possible to tell whether the bucket already has a policy + * attached, let alone to re-use that policy to add more statements to it. + * So it's safest to do nothing in these cases. + * + * @param permission the policy statement to be added to the bucket's + * policy. + * @returns metadata about the execution of this method. If the policy + * was not added, the value of `statementAdded` will be `false`. You + * should always check this value to make sure that the operation was + * actually carried out. Otherwise, synthesis and deploy will terminate + * silently, which may be confusing. */ public addToResourcePolicy(permission: iam.PolicyStatement): iam.AddToResourcePolicyResult { if (!this.policy && this.autoCreatePolicy) { @@ -720,6 +748,9 @@ export abstract class BucketBase extends Resource implements IBucket { * const grant = bucket.grantPublicAccess(); * grant.resourceStatement!.addCondition(‘IpAddress’, { “aws:SourceIp”: “54.240.143.0/24” }); * + * Note that if this `IBucket` refers to an existing bucket, possibly not + * managed by CloudFormation, this method will have no effect, since it's + * impossible to modify the policy of an existing bucket. * * @param keyPrefix the prefix of S3 object keys (e.g. `home/*`). Default is "*". * @param allowedActions the set of S3 actions to allow. Default is "s3:GetObject". From c6fa1318b45ed4d3db03766538865260862e50fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jul 2021 13:54:25 +0200 Subject: [PATCH 22/27] chore(deps): bump actions/setup-node from 2.2.0 to 2.3.0 (#15759) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2.2.0 to 2.3.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v2.2.0...v2.3.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/yarn-upgrade.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml index bc42c35967a1d..918b691544287 100644 --- a/.github/workflows/yarn-upgrade.yml +++ b/.github/workflows/yarn-upgrade.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v2 - name: Set up Node - uses: actions/setup-node@v2.2.0 + uses: actions/setup-node@v2.3.0 with: node-version: 10 From 2cefe57835391155aa01962f01fe13fbf7b85c3a Mon Sep 17 00:00:00 2001 From: mokocm <52817048+mokocm@users.noreply.github.com> Date: Wed, 28 Jul 2021 00:50:28 +0900 Subject: [PATCH 23/27] chore(rds): add Aurora Postgres engine versions 10.16 and 11.11 (#15781) Amazon Aurora is available in 10.16 and 11.11, but I added it because it was not available in the CDK. https://aws.amazon.com/about-aws/whats-new/2021/06/amazon-aurora-supports-postgresql-12-6-11-11-10-16-and-9-6-21/ ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-rds/lib/cluster-engine.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts index 2a9c3b378e1fd..f92e738b2f90b 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts @@ -447,6 +447,8 @@ export class AuroraPostgresEngineVersion { public static readonly VER_10_13 = AuroraPostgresEngineVersion.of('10.13', '10', { s3Import: true, s3Export: true }); /** Version "10.14". */ public static readonly VER_10_14 = AuroraPostgresEngineVersion.of('10.14', '10', { s3Import: true, s3Export: true }); + /** Version "10.16". */ + public static readonly VER_10_16 = AuroraPostgresEngineVersion.of('10.16', '10', { s3Import: true, s3Export: true }); /** Version "11.4". */ public static readonly VER_11_4 = AuroraPostgresEngineVersion.of('11.4', '11', { s3Import: true }); /** Version "11.6". */ @@ -457,6 +459,8 @@ export class AuroraPostgresEngineVersion { public static readonly VER_11_8 = AuroraPostgresEngineVersion.of('11.8', '11', { s3Import: true, s3Export: true }); /** Version "11.9". */ public static readonly VER_11_9 = AuroraPostgresEngineVersion.of('11.9', '11', { s3Import: true, s3Export: true }); + /** Version "11.11". */ + public static readonly VER_11_11 = AuroraPostgresEngineVersion.of('11.11', '11', { s3Import: true, s3Export: true }); /** Version "12.4". */ public static readonly VER_12_4 = AuroraPostgresEngineVersion.of('12.4', '12', { s3Import: true, s3Export: true }); /** Version "12.6". */ From 76fb4811bb9f5d5fc1bd340954840032cb23698b Mon Sep 17 00:00:00 2001 From: Michael Neil Date: Tue, 27 Jul 2021 15:36:30 -0700 Subject: [PATCH 24/27] feat(codebuild): add support for setting a BuildEnvironment Certificate (#15738) fixes #15701 Add certificate as optional parameter to codebuild project Environment. The certificate option supports including an additional PEM certificate for on-prem DNS / SSL of codebuild projects. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-codebuild/README.md | 6 ++++ .../@aws-cdk/aws-codebuild/lib/project.ts | 22 ++++++++++++ .../aws-codebuild/test/test.project.ts | 35 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 54ded33d63974..d4b31c1bf91db 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -225,6 +225,7 @@ can use the `environment` property to customize the build environment: * `buildImage` defines the Docker image used. See [Images](#images) below for details on how to define build images. +* `certificate` defines the location of a PEM encoded certificate to import. * `computeType` defines the instance type used for the build. * `privileged` can be set to `true` to allow privileged access. * `environmentVariables` can be set at this level (and also at the project @@ -262,6 +263,11 @@ which can be either `WindowsImageType.STANDARD`, the default, or `WindowsImageTy new codebuild.Project(this, 'Project', { environment: { buildImage: codebuild.WindowsBuildImage.fromEcrRepository(ecrRepository, 'v1.0', codebuild.WindowsImageType.SERVER_2019), + // optional certificate to include in the build image + certificate: { + bucket: s3.Bucket.fromBucketName(this, 'Bucket', 'my-bucket'), + objectKey: 'path/to/cert.pem', + }, }, ... }) diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index efb4896534c60..fdd2e1295f6bb 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -39,6 +39,20 @@ export interface BatchBuildConfig { readonly role: iam.IRole; } +/** + * Location of a PEM certificate on S3 + */ +export interface BuildEnvironmentCertificate { + /** + * The bucket where the certificate is + */ + readonly bucket: s3.IBucket; + /** + * The full path and name of the key file + */ + readonly objectKey: string; +} + /** * Additional options to pass to the notification rule. */ @@ -1312,6 +1326,7 @@ export class Project extends ProjectBase { credential: secret.secretFullArn ?? secret.secretName, } : undefined, + certificate: env.certificate?.bucket.arnForObjects(env.certificate.objectKey), privilegedMode: env.privileged || false, computeType: env.computeType || this.buildImage.defaultComputeType, environmentVariables: hasEnvironmentVars @@ -1542,6 +1557,13 @@ export interface BuildEnvironment { */ readonly privileged?: boolean; + /** + * The location of the PEM-encoded certificate for the build project + * + * @default - No external certificate is added to the project + */ + readonly certificate?: BuildEnvironmentCertificate; + /** * The environment variables that your builds can use. */ diff --git a/packages/@aws-cdk/aws-codebuild/test/test.project.ts b/packages/@aws-cdk/aws-codebuild/test/test.project.ts index 038bb2e0e45cf..2b778c9d843cb 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.project.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.project.ts @@ -797,6 +797,41 @@ export = { test.done(); }, + + 'certificate arn'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const bucket = s3.Bucket.fromBucketName(stack, 'Bucket', 'my-bucket'); // (stack, 'Bucket'); + + // WHEN + new codebuild.Project(stack, 'Project', { + source: codebuild.Source.s3({ + bucket, + path: 'path', + }), + environment: { + certificate: { + bucket, + objectKey: 'path', + }, + }, + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', { + Environment: objectLike({ + Certificate: { + 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':s3:::my-bucket/path', + ]], + }, + }), + })); + + test.done(); + }, }, 'EnvironmentVariables': { From 3f5b9101366cebd656e8b1256ca3b69eb6c01834 Mon Sep 17 00:00:00 2001 From: Daniel Lindberg Date: Wed, 28 Jul 2021 02:16:48 +0200 Subject: [PATCH 25/27] docs: update constructor declaration for enum-like class in "Design Guidelines" (#15520) Fix constructor issue with enum like class examples ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- docs/DESIGN_GUIDELINES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/DESIGN_GUIDELINES.md b/docs/DESIGN_GUIDELINES.md index 0a8566ab9d90c..87e7b2c07072d 100644 --- a/docs/DESIGN_GUIDELINES.md +++ b/docs/DESIGN_GUIDELINES.md @@ -610,14 +610,14 @@ A pattern for an "Enum-like Class" should be used in such cases: ```ts export interface MyProps { - option: MyOption; + readonly option: MyOption; } export class MyOption { public static COMMON_OPTION_1 = new MyOption('common.option-1'); public static COMMON_OPTION_2 = new MyOption('common.option-2'); - public MyOption(public readonly customValue: string) { } + public constructor(public readonly customValue: string) { } } ``` @@ -644,7 +644,7 @@ export class MyOption { // 'protected' iso. 'private' so that someone that really wants to can still // do subclassing. But maybe might as well be private. - protected MyOption(public readonly value: string) { } + protected constructor(public readonly value: string) { } } // Usage From dbe4641666c918c7bba36010fb4656d050ef5556 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 28 Jul 2021 12:43:51 +0300 Subject: [PATCH 26/27] feat(cfnspec): cloudformation spec v39.7.0 (#15796) Co-authored-by: AWS CDK Team --- packages/monocdk/rosetta/with-bucket.ts-fixture | 13 +++++++++++++ .../monocdk/rosetta/with-delivery-stream.ts-fixture | 12 ++++++++++++ .../monocdk/rosetta/with-destination.ts-fixture | 12 ++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 packages/monocdk/rosetta/with-bucket.ts-fixture create mode 100644 packages/monocdk/rosetta/with-delivery-stream.ts-fixture create mode 100644 packages/monocdk/rosetta/with-destination.ts-fixture diff --git a/packages/monocdk/rosetta/with-bucket.ts-fixture b/packages/monocdk/rosetta/with-bucket.ts-fixture new file mode 100644 index 0000000000000..d0851cff49639 --- /dev/null +++ b/packages/monocdk/rosetta/with-bucket.ts-fixture @@ -0,0 +1,13 @@ +// Fixture with a bucket already created +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; +import * as s3 from '@aws-cdk/aws-s3'; +declare const bucket: s3.Bucket; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/monocdk/rosetta/with-delivery-stream.ts-fixture b/packages/monocdk/rosetta/with-delivery-stream.ts-fixture new file mode 100644 index 0000000000000..c7b75b20d2c1b --- /dev/null +++ b/packages/monocdk/rosetta/with-delivery-stream.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with a delivery stream already created +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; +declare const deliveryStream: DeliveryStream; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/monocdk/rosetta/with-destination.ts-fixture b/packages/monocdk/rosetta/with-destination.ts-fixture new file mode 100644 index 0000000000000..37d78bf7a43d3 --- /dev/null +++ b/packages/monocdk/rosetta/with-destination.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with a destination already created +import { Construct, Stack } from '@aws-cdk/core'; +import { DeliveryStream, DestinationBindOptions, DestinationConfig, IDestination } from '@aws-cdk/aws-kinesisfirehose'; +declare const destination: IDestination; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} From 527cbee6129116e627bdc3199f47e6521b2c78be Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Wed, 28 Jul 2021 10:13:53 +0000 Subject: [PATCH 27/27] chore(release): 1.116.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ version.v1.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b7301ca3863..21264b7582645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.116.0](https://github.com/aws/aws-cdk/compare/v1.115.0...v1.116.0) (2021-07-28) + + +### Features + +* **assertions:** retrieve matching resources from the template ([#15642](https://github.com/aws/aws-cdk/issues/15642)) ([a8b1c47](https://github.com/aws/aws-cdk/commit/a8b1c471b7058bbf739a1d4f5b4860656ebd5432)) +* **aws-kinesisfirehose:** DeliveryStream API and basic S3 destination ([#15544](https://github.com/aws/aws-cdk/issues/15544)) ([1b5d525](https://github.com/aws/aws-cdk/commit/1b5d525cef8ef4209074156c56077eebaa38d57c)), closes [#10810](https://github.com/aws/aws-cdk/issues/10810) [#15499](https://github.com/aws/aws-cdk/issues/15499) +* **cfnspec:** cloudformation spec v39.7.0 ([#15719](https://github.com/aws/aws-cdk/issues/15719)) ([2c4ef01](https://github.com/aws/aws-cdk/commit/2c4ef0131893e77d373c52b41c62d31847023446)) +* **cfnspec:** cloudformation spec v39.7.0 ([#15796](https://github.com/aws/aws-cdk/issues/15796)) ([dbe4641](https://github.com/aws/aws-cdk/commit/dbe4641666c918c7bba36010fb4656d050ef5556)) +* **codebuild:** add support for setting a BuildEnvironment Certificate ([#15738](https://github.com/aws/aws-cdk/issues/15738)) ([76fb481](https://github.com/aws/aws-cdk/commit/76fb4811bb9f5d5fc1bd340954840032cb23698b)), closes [#15701](https://github.com/aws/aws-cdk/issues/15701) +* **core:** lazy mappings will only synthesize if keys are unresolved ([#15617](https://github.com/aws/aws-cdk/issues/15617)) ([32ed229](https://github.com/aws/aws-cdk/commit/32ed2290f8efb27bf622998f98808ff18a8cdef1)) +* **pipelines:** CDK Pipelines is now Generally Available ([#15667](https://github.com/aws/aws-cdk/issues/15667)) ([2e4cfae](https://github.com/aws/aws-cdk/commit/2e4cfaeb8612179c79e293ba52a8afcdcfd6ef52)) +* **servicecatalog:** add ability to set launch Role and deploy with StackSets ([#15678](https://github.com/aws/aws-cdk/issues/15678)) ([c92548b](https://github.com/aws/aws-cdk/commit/c92548b2242478d22db030842014e7646715c2ef)) +* **stepfunctions:** allow intrinsic functions for json path ([#15320](https://github.com/aws/aws-cdk/issues/15320)) ([d9285cb](https://github.com/aws/aws-cdk/commit/d9285cb75745028ede8c36afcee34f7a53d27993)) + + +### Bug Fixes + +* **aws-cloudwatch:** unable to use generic extended statistics for cloudwatch alarms ([#15720](https://github.com/aws/aws-cdk/issues/15720)) ([f593311](https://github.com/aws/aws-cdk/commit/f59331193b5a2cc4a33d71d775f6650d66bb1bf8)) +* **elasticsearch:** advancedOptions in domain has no effect ([#15330](https://github.com/aws/aws-cdk/issues/15330)) ([81cbfec](https://github.com/aws/aws-cdk/commit/81cbfec5ddf065aac442d925484a358ee8cd26a1)), closes [#14067](https://github.com/aws/aws-cdk/issues/14067) +* **elasticsearch:** slow logs incorrectly disabled for Elasticsearch versions lower than 5.1 ([#15714](https://github.com/aws/aws-cdk/issues/15714)) ([91cf79b](https://github.com/aws/aws-cdk/commit/91cf79bc55ffd72b1c79e2218eb76921fbac32b4)), closes [#15532](https://github.com/aws/aws-cdk/issues/15532) [#15532](https://github.com/aws/aws-cdk/issues/15532) +* **pipelines:** Secrets Manager permissions not added to asset projects ([#15718](https://github.com/aws/aws-cdk/issues/15718)) ([7668400](https://github.com/aws/aws-cdk/commit/7668400ec8d4e6ee042c05976f95e42147993375)), closes [#15628](https://github.com/aws/aws-cdk/issues/15628) +* **stepfunctions:** non-object arguments to recurseObject are incorrectly treated as objects ([#14631](https://github.com/aws/aws-cdk/issues/14631)) ([e133bca](https://github.com/aws/aws-cdk/commit/e133bca61b95b71d51b509b646ff1720099ee31e)), closes [#12935](https://github.com/aws/aws-cdk/issues/12935) [aws-cdk/aws-stepfunctions/lib/input.ts#L65](https://github.com/aws-cdk/aws-stepfunctions/lib/input.ts/issues/L65) +* **stepfunctions-tasks:** instance type cannot be provided to SageMakerCreateTransformJob as input path ([#15726](https://github.com/aws/aws-cdk/issues/15726)) ([6f2384d](https://github.com/aws/aws-cdk/commit/6f2384ddc180e944c9564a543351b8df2f75c1a7)) + ## [1.115.0](https://github.com/aws/aws-cdk/compare/v1.114.0...v1.115.0) (2021-07-21) diff --git a/version.v1.json b/version.v1.json index 4af54a1130ffd..63c5030d06ef4 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.115.0" + "version": "1.116.0" }