diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index 5ed1048d1bd4d..97e0de9be71ad 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -8,7 +8,7 @@ on: types: [opened, edited] jobs: - test: + issue-triage-manager: permissions: issues: write pull-requests: write @@ -17,16 +17,57 @@ jobs: - uses: aws-github-ops/aws-issue-triage-manager@main with: github-token: "${{ secrets.GITHUB_TOKEN }}" + target: "issues" excluded-expressions: "[CDK CLI Version|TypeScript|Java|Python]" area-is-keyword: true + included-labels: "[needs-triage]" + excluded-labels: "[p1|p2|p0|effort-small|effort-medium|effort-large|guidance]" + default-area: ${{ env.OSDS_DEVS }} + parameters: ${{ env.AREA_PARAMS }} + guidance-triage-manager: + permissions: + issues: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: aws-github-ops/aws-issue-triage-manager@main + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + target: "issues" + excluded-expressions: "[CDK CLI Version|TypeScript|Java|Python]" + area-is-keyword: true + included-labels: "[guidance]" + default-area: ${{ env.OSDS_DEVS }} parameters: > + [{"area":"guidance","keywords":["guidance"]}] + pr-triage-manager: + permissions: + issues: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: aws-github-ops/aws-issue-triage-manager@main + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + target: "pull-requests" + area-is-keyword: true + excluded-labels: "[contribution/core]" + parameters: ${{ env.AREA_PARAMS }} + +env: + OSDS_DEVS: > + { + "assignees":["NGL321","peterwoodworth","ryparker"] + } + + AREA_PARAMS: > [ {"area":"package/tools","keywords":["cli","command line","init","synth","diff","bootstrap"],"labels":["package/tools"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/alexa-ask","keywords":["alexa-ask","alexa", "cfnskill"],"labels":["@aws-cdk/alexa-ask"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/app-delivery","keywords":["app-delivery","PipelineDeployStackAction"],"labels":["@aws-cdk/app-delivery"],"assignees":["skinny85"]}, {"area":"@aws-cdk/assert","keywords":["assert"],"labels":["@aws-cdk/assert"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/assertions","keywords":["assertions"],"labels":["@aws-cdk/assertions"],"assignees":["kaizen3031593"]}, - {"area":"@aws-cdk/assets","keywords":["assets","staging"],"labels":["@aws-cdk/assets"],"assignees":["eladb"]}, + {"area":"@aws-cdk/assets","keywords":["assets","staging"],"labels":["@aws-cdk/assets"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-accessanalyzer","keywords":["aws-accessanalyzer","accessanalyzer","cfnanalyzer"],"labels":["@aws-cdk/aws-accessanalyzer"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-acmpca","keywords":["aws-acmpca","acmpca","certificateauthority"],"labels":["@aws-cdk/aws-acmpca"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-amazonmq","keywords":["aws-amazonmq","amazonmq","cfnbroker"],"labels":["@aws-cdk/aws-amazonmq"],"assignees":["otaviomacedo"]}, @@ -39,12 +80,12 @@ jobs: {"area":"@aws-cdk/aws-appflow","keywords":["aws-appflow","appflow"],"labels":["@aws-cdk/aws-appflow"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-appintegrations","keywords":["(aws-appintegrations)","(appintegrations)"],"labels":["@aws-cdk/aws-appintegrations"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-applicationautoscaling","keywords":["aws-applicationautoscaling","application-autoscaling","scalabletarget"],"labels":["@aws-cdk/aws-applicationautoscaling"],"assignees":["comcalvi"]}, - {"area":"@aws-cdk/aws-applicationinsights","keywords":["aws-applicationinsights","application-insights"],"labels":["@aws-cdk/aws-applicationinsights"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-applicationinsights","keywords":["aws-applicationinsights","application-insights"],"labels":["@aws-cdk/aws-applicationinsights"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-appmesh","keywords":["aws-appmesh","app-mesh","GatewayRoute","VirtualGateway","VirtualNode","VirtualRouter","VirtualService"],"labels":["@aws-cdk/aws-appmesh"],"assignees":["Seiya6329"]}, {"area":"@aws-cdk/aws-appstream","keywords":["aws-appstream","app-stream"],"labels":["@aws-cdk/aws-appstream"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-appsync","keywords":["aws-appsync","app-sync","appsyncfunction","graphqlapi","dynamodbdatasource","lambdadatasource","nonedatasource","rdsdatasource","resolver"],"labels":["@aws-cdk/aws-appsync"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-athena","keywords":["aws-athena","athena","cfndatacatalog","cfnnamedquery","cfnworkgroup"],"labels":["@aws-cdk/aws-athena"],"assignees":["comcalvi"]}, - {"area":"@aws-cdk/aws-auditmanager","keywords":["(aws-auditmanager)","(auditmanager)"],"labels":["@aws-cdk/aws-auditmanager"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-auditmanager","keywords":["(aws-auditmanager)","(auditmanager)"],"labels":["@aws-cdk/aws-auditmanager"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-autoscaling","keywords":["aws-autoscaling","auto-scaling","AutoScalingGroup","LifescycleHook","scheduledaction"],"labels":["@aws-cdk/aws-autoscaling"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-autoscaling-api","keywords":["aws-autoscaling-api","autoscaling-api"],"labels":["@aws-cdk/aws-autoscaling-api"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-autoscaling-common","keywords":["aws-autoscaling-common","autoscaling-common","arbitraryintervals","completescalinginterval","scalinginterval"],"labels":["@aws-cdk/aws-autoscaling-common"],"assignees":["comcalvi"]}, @@ -52,19 +93,19 @@ jobs: {"area":"@aws-cdk/aws-autoscalingplans","keywords":["aws-autoscalingplans","autoscaling-plans","cfnscalingplan"],"labels":["@aws-cdk/aws-autoscalingplans"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-backup","keywords":["aws-backup","backupplan","backupselection","backupvault"],"labels":["@aws-cdk/aws-backup"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-batch","keywords":["aws-batch","batch","computeenvironment","jobdefinition","jobqueue"],"labels":["@aws-cdk/aws-batch"],"assignees":["madeline-k"]}, - {"area":"@aws-cdk/aws-budgets","keywords":["aws-budgets","budgets","cfnbudget"],"labels":["@aws-cdk/aws-budgets"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-budgets","keywords":["aws-budgets","budgets","cfnbudget"],"labels":["@aws-cdk/aws-budgets"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-cassandra","keywords":["aws-cassandra","cassandra","cfnkeyspace"],"labels":["@aws-cdk/aws-cassandra"],"assignees":["otaviomacedo"]}, - {"area":"@aws-cdk/aws-ce","keywords":["aws-ce","cfnanomalymonitor","cfncostcategory"],"labels":["@aws-cdk/aws-ce"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-certificatemanager","keywords":["aws-certificatemanager","certificate-manager","dnsvalidatedcertificate","acm"],"labels":["@aws-cdk/aws-certificatemanager"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-ce","keywords":["aws-ce","cfnanomalymonitor","cfncostcategory"],"labels":["@aws-cdk/aws-ce"],"assignees":["corymhall"]}, + {"area":"@aws-cdk/aws-certificatemanager","keywords":["aws-certificatemanager","certificate-manager","dnsvalidatedcertificate","acm"],"labels":["@aws-cdk/aws-certificatemanager"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-chatbot","keywords":["aws-chatbot","chatbot","slackchannelconfiguration"],"labels":["@aws-cdk/aws-chatbot"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-cloud9","keywords":["aws-cloud9","cloud 9","ec2environment"],"labels":["@aws-cdk/aws-cloud9"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-cloudformation","keywords":["aws-cloudformation","nestedstack"],"labels":["@aws-cdk/aws-cloudformation"],"assignees":["rix0rrr"]}, - {"area":"@aws-cdk/aws-cloudfront","keywords":["aws-cloudfront","cloud front","cachepolicy","cloudfrontwebdistribution"],"labels":["@aws-cdk/aws-cloudfront"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-cloudfront-origins","keywords":["aws-cloudfront-origins","cloudfront origins"],"labels":["@aws-cdk/aws-cloudfront-origins"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-cloudfront","keywords":["aws-cloudfront","cloud front","cachepolicy","cloudfrontwebdistribution"],"labels":["@aws-cdk/aws-cloudfront"],"assignees":["comcalvi"]}, + {"area":"@aws-cdk/aws-cloudfront-origins","keywords":["aws-cloudfront-origins","cloudfront origins"],"labels":["@aws-cdk/aws-cloudfront-origins"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-cloudtrail","keywords":["aws-cloudtrail","cloud trail","trail"],"labels":["@aws-cdk/aws-cloudtrail"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-cloudwatch","keywords":["aws-cloudwatch","cloud watch","compositealarm","dashboard"],"labels":["@aws-cdk/aws-cloudwatch"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-cloudwatch-actions","keywords":["aws-cloudwatch-actions","cloudwatch actions","applicationscalingaction","autoscalingaction","ec2action","snsaction"],"labels":["@aws-cdk/aws-cloudwatch-actions"],"assignees":["madeline-k"]}, - {"area":"@aws-cdk/aws-codeartifact","keywords":["aws-codeartifact","code-artifact"],"labels":["@aws-cdk/aws-codeartifact"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-codeartifact","keywords":["aws-codeartifact","code-artifact"],"labels":["@aws-cdk/aws-codeartifact"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-codebuild","keywords":["aws-codebuild","code-build","bitbucketsourcecredentials","githubenterprisesourcecredentials","githubsourcecredentials","pipelineproject","reportgroup","untrustedcodeboundarypolicy"],"labels":["@aws-cdk/aws-codebuild"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-codecommit","keywords":["aws-codecommit","code-commit"],"labels":["@aws-cdk/aws-codecommit"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-codedeploy","keywords":["aws-codedeploy","code-deploy","customlambdadeploymentconfig","ecsapplication","lambdaapplication","lambdadeploymentgroup","serverapplication"],"labels":["@aws-cdk/aws-codedeploy"],"assignees":["skinny85"]}, @@ -85,14 +126,14 @@ jobs: {"area":"@aws-cdk/aws-detective","keywords":["aws-detective","detective"],"labels":["@aws-cdk/aws-detective"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-devopsguru","keywords":["(aws-devopsguru)","(devopsguru)"],"labels":["@aws-cdk/aws-devopsguru"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-directoryservice","keywords":["aws-directoryservice","directory-service"],"labels":["@aws-cdk/aws-directoryservice"],"assignees":["rix0rrr"]}, - {"area":"@aws-cdk/aws-dlm","keywords":["aws-dlm","dlm"],"labels":["@aws-cdk/aws-dlm"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-dms","keywords":["aws-dms","dms"],"labels":["@aws-cdk/aws-dms"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-dlm","keywords":["aws-dlm","dlm"],"labels":["@aws-cdk/aws-dlm"],"assignees":["madeline-k"]}, + {"area":"@aws-cdk/aws-dms","keywords":["aws-dms","dms"],"labels":["@aws-cdk/aws-dms"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-docdb","keywords":["aws-docdb","doc-db"],"labels":["@aws-cdk/aws-docdb"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-dynamodb","keywords":["aws-dynamodb","dynamo-db"],"labels":["@aws-cdk/aws-dynamodb"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-dynamodb-global","keywords":["aws-dynamodb-global","dynamodb global"],"labels":["@aws-cdk/aws-dynamodb-global"],"assignees":["skinny85"]}, - {"area":"@aws-cdk/aws-ec2","keywords":["aws-ec2","ec2","vpc","privatesubnet","publicsubnet","vpngateway","vpnconnection","networkacl"],"labels":["@aws-cdk/aws-ec2"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-ec2","keywords":["aws-ec2","ec2","vpc","privatesubnet","publicsubnet","vpngateway","vpnconnection","networkacl"],"labels":["@aws-cdk/aws-ec2"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-ecr","keywords":["aws-ecr","ecr"],"labels":["@aws-cdk/aws-ecr"],"assignees":["madeline-k"]}, - {"area":"@aws-cdk/aws-ecr-assets","keywords":["aws-ecr-assets","ecrassets"],"labels":["@aws-cdk/aws-ecr-assets"],"assignees":["eladb"]}, + {"area":"@aws-cdk/aws-ecr-assets","keywords":["aws-ecr-assets","ecrassets"],"labels":["@aws-cdk/aws-ecr-assets"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-ecs","keywords":["(aws-ecs)","(ecs)"],"labels":["@aws-cdk/aws-ecs"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-ecs-patterns","keywords":["(aws-ecs-patterns)","(ecs-patterns)"],"labels":["@aws-cdk/aws-ecs-patterns"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-efs","keywords":["aws-efs","efs","accesspoint"],"labels":["@aws-cdk/aws-efs"],"assignees":["corymhall"]}, @@ -100,20 +141,20 @@ jobs: {"area":"@aws-cdk/aws-eks-legacy","keywords":["(aws-eks-legacy)","(eks-legacy)"],"labels":["@aws-cdk/aws-eks-legacy"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-elasticache","keywords":["aws-elasticache","elastic-cache"],"labels":["@aws-cdk/aws-elasticache"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-elasticbeanstalk","keywords":["aws-elasticbeanstalk","elastic-beanstalk"],"labels":["@aws-cdk/aws-elasticbeanstalk"],"assignees":["skinny85"]}, - {"area":"@aws-cdk/aws-elasticloadbalancing","keywords":["aws-elasticloadbalancing","elastic-loadbalancing","elb"],"labels":["@aws-cdk/aws-elasticloadbalancing"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-elasticloadbalancingv2","keywords":["aws-elasticloadbalancingv2","elastic-loadbalancing-v2","elbv2","applicationlistener","applicationloadbalancer","applicationtargetgroup","networklistener","networkloadbalancer","networktargetgroup"],"labels":["@aws-cdk/aws-elasticloadbalancingv2"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-elasticloadbalancingv2-actions","keywords":["(aws-elasticloadbalancingv2-actions)","(elasticloadbalancingv2-actions)"],"labels":["@aws-cdk/aws-elasticloadbalancingv2-actions"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-elasticloadbalancingv2-targets","keywords":["aws-elasticloadbalancingv2-targets","elbv2 targets"],"labels":["@aws-cdk/aws-elasticloadbalancingv2-targets"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-elasticloadbalancing","keywords":["aws-elasticloadbalancing","elastic-loadbalancing","elb"],"labels":["@aws-cdk/aws-elasticloadbalancing"],"assignees":["corymhall"]}, + {"area":"@aws-cdk/aws-elasticloadbalancingv2","keywords":["aws-elasticloadbalancingv2","elastic-loadbalancing-v2","elbv2","applicationlistener","applicationloadbalancer","applicationtargetgroup","networklistener","networkloadbalancer","networktargetgroup"],"labels":["@aws-cdk/aws-elasticloadbalancingv2"],"assignees":["corymhall"]}, + {"area":"@aws-cdk/aws-elasticloadbalancingv2-actions","keywords":["(aws-elasticloadbalancingv2-actions)","(elasticloadbalancingv2-actions)"],"labels":["@aws-cdk/aws-elasticloadbalancingv2-actions"],"assignees":["corymhall"]}, + {"area":"@aws-cdk/aws-elasticloadbalancingv2-targets","keywords":["aws-elasticloadbalancingv2-targets","elbv2 targets"],"labels":["@aws-cdk/aws-elasticloadbalancingv2-targets"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-elasticsearch","keywords":["aws-elasticsearch","elastic-search"],"labels":["@aws-cdk/aws-elasticsearch"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-emr","keywords":["aws-emr","emr"],"labels":["@aws-cdk/aws-emr"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-emrcontainers","keywords":["(aws-emrcontainers)","(emrcontainers)"],"labels":["@aws-cdk/aws-emrcontainers"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-events","keywords":["aws-events","events","event-bridge","eventbus"],"labels":["@aws-cdk/aws-events"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/aws-events-targets","keywords":["aws-events-targets","events-targets","events targets"],"labels":["@aws-cdk/aws-events-targets"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/aws-eventschemas","keywords":["aws-eventschemas","event schemas"],"labels":["@aws-cdk/aws-eventschemas"],"assignees":["skinny85"]}, - {"area":"@aws-cdk/aws-finspace","keywords":["(aws-finspace)","(finspace)"],"labels":["@aws-cdk/aws-finspace"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-fis","keywords":["(aws-fis)","(fis)"],"labels":["@aws-cdk/aws-fis"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-finspace","keywords":["(aws-finspace)","(finspace)"],"labels":["@aws-cdk/aws-finspace"],"assignees":["madeline-k"]}, + {"area":"@aws-cdk/aws-fis","keywords":["(aws-fis)","(fis)"],"labels":["@aws-cdk/aws-fis"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-fms","keywords":["aws-fms","fms"],"labels":["@aws-cdk/aws-fms"],"assignees":["rix0rrr"]}, - {"area":"@aws-cdk/aws-frauddetector","keywords":["(aws-frauddetector)","(frauddetector)"],"labels":["@aws-cdk/aws-frauddetector"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-frauddetector","keywords":["(aws-frauddetector)","(frauddetector)"],"labels":["@aws-cdk/aws-frauddetector"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-fsx","keywords":["aws-fsx","fsx","lustrefilesystem"],"labels":["@aws-cdk/aws-fsx"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/aws-gamelift","keywords":["aws-gamelift","game lift"],"labels":["@aws-cdk/aws-gamelift"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-globalaccelerator","keywords":["aws-globalaccelerator","global-accelerator"],"labels":["@aws-cdk/aws-globalaccelerator"],"assignees":["rix0rrr"]}, @@ -141,7 +182,7 @@ jobs: {"area":"@aws-cdk/aws-kinesisanalytics","keywords":["aws-kinesisanalytics","kinesisanalytics","kinesis-analytics"],"labels":["@aws-cdk/aws-kinesisanalytics"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-kinesisanalytics-flink","keywords":["(aws-kinesisanalytics-flink)","(kinesisanalytics-flink)"],"labels":["@aws-cdk/aws-kinesisanalytics-flink"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-kinesisfirehose","keywords":["aws-kinesisfirehose","kinesisfirehose"],"labels":["@aws-cdk/aws-kinesisfirehose"],"assignees":["otaviomacedo"]}, - {"area":"@aws-cdk/aws-kms","keywords":["key-management-service","aws-kms","kms"],"labels":["@aws-cdk/aws-kms"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-kms","keywords":["key-management-service","aws-kms","kms"],"labels":["@aws-cdk/aws-kms"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-lakeformation","keywords":["data-lake","aws-lakeformation","lakeformation"],"labels":["@aws-cdk/aws-lakeformation"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-lambda","keywords":["function","layerversion","aws-lambda","lambda"],"labels":["@aws-cdk/aws-lambda"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-lambda-destinations","keywords":["(aws-lambda-destinations)","(lambda-destinations)"],"labels":["@aws-cdk/aws-lambda-destinations"],"assignees":["kaizen3031593"]}, @@ -149,12 +190,12 @@ jobs: {"area":"@aws-cdk/aws-lambda-go","keywords":["(aws-lambda-go)","(lambda-go)"],"labels":["@aws-cdk/aws-lambda-go"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-lambda-nodejs","keywords":["nodejsfunction","aws-lambda-nodejs","lambda-nodejs"],"labels":["@aws-cdk/aws-lambda-nodejs"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-lambda-python","keywords":["aws-lambda-python","lambda-python","pythonfunction"],"labels":["@aws-cdk/aws-lambda-python"],"assignees":["corymhall"]}, - {"area":"@aws-cdk/aws-licensemanager","keywords":["(aws-licensemanager)","(licensemanager)"],"labels":["@aws-cdk/aws-licensemanager"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-licensemanager","keywords":["(aws-licensemanager)","(licensemanager)"],"labels":["@aws-cdk/aws-licensemanager"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-logs","keywords":["loggroup","aws-logs","logs","logretention"],"labels":["@aws-cdk/aws-logs"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-logs-destinations","keywords":["aws-logs-destinations","lambdadestination","kinesisdestination","logs-destinations"],"labels":["@aws-cdk/aws-logs-destinations"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/aws-lookoutmetrics","keywords":["(aws-lookoutmetrics)","(lookoutmetrics)"],"labels":["@aws-cdk/aws-lookoutmetrics"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-lookoutvision","keywords":["(aws-lookoutvision)","(lookoutvision)"],"labels":["@aws-cdk/aws-lookoutvision"],"assignees":["comcalvi"]}, - {"area":"@aws-cdk/aws-macie","keywords":["aws-macie","macie"],"labels":["@aws-cdk/aws-macie"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-macie","keywords":["aws-macie","macie"],"labels":["@aws-cdk/aws-macie"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-managedblockchain","keywords":["aws-managedblockchain","managedblockchain"],"labels":["@aws-cdk/aws-managedblockchain"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-mediaconnect","keywords":["(aws-mediaconnect)","(mediaconnect)"],"labels":["@aws-cdk/aws-mediaconnect"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-mediaconvert","keywords":["aws-mediaconvert","mediaconvert"],"labels":["@aws-cdk/aws-mediaconvert"],"assignees":["skinny85"]}, @@ -163,37 +204,37 @@ jobs: {"area":"@aws-cdk/aws-mediapackage","keywords":["aws-mediapackage","mediapackage"],"labels":["@aws-cdk/aws-mediapackage"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-msk","keywords":["aws-msk","kafka","msk","managed-streaming"],"labels":["@aws-cdk/aws-msk"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-mwaa","keywords":["(aws-mwaa)","(mwaa)"],"labels":["@aws-cdk/aws-mwaa"],"assignees":["rix0rrr"]}, - {"area":"@aws-cdk/aws-neptune","keywords":["aws-neptune","neptune"],"labels":["@aws-cdk/aws-neptune"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-neptune","keywords":["aws-neptune","neptune"],"labels":["@aws-cdk/aws-neptune"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-networkfirewall","keywords":["(aws-networkfirewall)","(networkfirewall)"],"labels":["@aws-cdk/aws-networkfirewall"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-networkmanager","keywords":["aws-networkmanager","networkmanager","globalnetwork"],"labels":["@aws-cdk/aws-networkmanager"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-nimblestudio","keywords":["(aws-nimblestudio)","(nimblestudio)"],"labels":["@aws-cdk/aws-nimblestudio"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-opensearchservice","keywords":["aws-opensearchservice","opensearchservice","aws-opensearch","opensearch"],"labels":["@aws-cdk/aws-opensearch"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-opsworks","keywords":["aws-opsworks","opsworks"],"labels":["@aws-cdk/aws-opsworks"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-opsworkscm","keywords":["aws-opsworkscm","opsworkscm"],"labels":["@aws-cdk/aws-opsworkscm"],"assignees":["madeline-k"]}, - {"area":"@aws-cdk/aws-personalize","keywords":["aws-personalize","personalize"],"labels":["@aws-cdk/aws-personalize"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-personalize","keywords":["aws-personalize","personalize"],"labels":["@aws-cdk/aws-personalize"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-pinpoint","keywords":["aws-pinpoint","pinpoint"],"labels":["@aws-cdk/aws-pinpoint"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-pinpointemail","keywords":["aws-pinpointemail","pinpointemail"],"labels":["@aws-cdk/aws-pinpointemail"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-qldb","keywords":["aws-qldb","qldb"],"labels":["@aws-cdk/aws-qldb"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-quicksight","keywords":["(aws-quicksight)","(quicksight)"],"labels":["@aws-cdk/aws-quicksight"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-ram","keywords":["aws-ram","ram", "resource-access-manager"],"labels":["@aws-cdk/aws-ram"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-rds","keywords":["aws-rds","rds", "database-cluster","database-instance"],"labels":["@aws-cdk/aws-rds"],"assignees":["skinny85"]}, - {"area":"@aws-cdk/aws-redshift","keywords":["aws-redshift","redshift"],"labels":["@aws-cdk/aws-redshift"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-redshift","keywords":["aws-redshift","redshift"],"labels":["@aws-cdk/aws-redshift"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-resourcegroups","keywords":["resourcegroups","aws-resourcegroups"],"labels":["@aws-cdk/aws-resourcegroups"],"assignees":["madeline-k"]}, - {"area":"@aws-cdk/aws-robomaker","keywords":["aws-robomaker","robomaker","robot"],"labels":["@aws-cdk/aws-robomaker"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-route53","keywords":["aws-route53","route53","recordset","record","hostedzone"],"labels":["@aws-cdk/aws-route53"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-route53-patterns","keywords":["aws-route53-patterns","route53-patterns"],"labels":["@aws-cdk/aws-route53-patterns"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-route53-targets","keywords":["aws-route53-targets","route53-targets"],"labels":["@aws-cdk/aws-route53-targets"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-route53resolver","keywords":["aws-route53resolver","route53resolver"],"labels":["@aws-cdk/aws-route53resolver"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-robomaker","keywords":["aws-robomaker","robomaker","robot"],"labels":["@aws-cdk/aws-robomaker"],"assignees":["comcalvi"]}, + {"area":"@aws-cdk/aws-route53","keywords":["aws-route53","route53","recordset","record","hostedzone"],"labels":["@aws-cdk/aws-route53"],"assignees":["comcalvi"]}, + {"area":"@aws-cdk/aws-route53-patterns","keywords":["aws-route53-patterns","route53-patterns"],"labels":["@aws-cdk/aws-route53-patterns"],"assignees":["comcalvi"]}, + {"area":"@aws-cdk/aws-route53-targets","keywords":["aws-route53-targets","route53-targets"],"labels":["@aws-cdk/aws-route53-targets"],"assignees":["comcalvi"]}, + {"area":"@aws-cdk/aws-route53resolver","keywords":["aws-route53resolver","route53resolver"],"labels":["@aws-cdk/aws-route53resolver"],"assignees":["comcalvi"]}, {"area":"@aws-cdk/aws-s3","keywords":["bucket","aws-s3","s3"],"labels":["@aws-cdk/aws-s3"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-s3-assets","keywords":["aws-s3-assets","s3-assets"],"labels":["@aws-cdk/aws-s3-assets"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-s3-deployment","keywords":["aws-s3-deployment","s3-deployment"],"labels":["@aws-cdk/aws-s3-deployment"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-s3-notifications","keywords":["aws-s3-notifications","s3-notifications"],"labels":["@aws-cdk/aws-s3-notifications"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-s3objectlambda","keywords":["(aws-s3objectlambda)","(s3objectlambda)"],"labels":["@aws-cdk/aws-s3objectlambda"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-s3outposts","keywords":["(aws-s3outposts)","(s3outposts)"],"labels":["@aws-cdk/aws-s3outposts"],"assignees":["otaviomacedo"]}, - {"area":"@aws-cdk/aws-sagemaker","keywords":["aws-sagemaker","sagemaker"],"labels":["@aws-cdk/aws-sagemaker"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-sam","keywords":["serverless-application-model","aws-sam","sam"],"labels":["@aws-cdk/aws-sam"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-sdb","keywords":["simpledb","aws-sdb","sdb"],"labels":["@aws-cdk/aws-sdb"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-secretsmanager","keywords":["secret","aws-secretsmanager","secrets-manager"],"labels":["@aws-cdk/aws-secretsmanager"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-sagemaker","keywords":["aws-sagemaker","sagemaker"],"labels":["@aws-cdk/aws-sagemaker"],"assignees":["madeline-k"]}, + {"area":"@aws-cdk/aws-sam","keywords":["serverless-application-model","aws-sam","sam"],"labels":["@aws-cdk/aws-sam"],"assignees":["madeline-k"]}, + {"area":"@aws-cdk/aws-sdb","keywords":["simpledb","aws-sdb","sdb"],"labels":["@aws-cdk/aws-sdb"],"assignees":["skinny85"]}, + {"area":"@aws-cdk/aws-secretsmanager","keywords":["secret","aws-secretsmanager","secrets-manager"],"labels":["@aws-cdk/aws-secretsmanager"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-securityhub","keywords":["aws-securityhub","security-hub"],"labels":["@aws-cdk/aws-securityhub"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-servicecatalog","keywords":["aws-servicecatalog","service-catalog"],"labels":["@aws-cdk/aws-servicecatalog"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-servicecatalogappregistry","keywords":["(aws-servicecatalogappregistry)","(servicecatalogappregistry)"],"labels":["@aws-cdk/aws-servicecatalogappregistry"],"assignees":["skinny85"]}, @@ -201,19 +242,19 @@ jobs: {"area":"@aws-cdk/aws-ses","keywords":["recipet-filter","reciept-rule","aws-ses","ses"],"labels":["@aws-cdk/aws-ses"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-ses-actions","keywords":["aws-ses-actions","ses-actions"],"labels":["@aws-cdk/aws-ses-actions"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/aws-signer","keywords":["aws-signer","signer"],"labels":["@aws-cdk/aws-signer"],"assignees":["corymhall"]}, - {"area":"@aws-cdk/aws-sns","keywords":["simple-notification-service","aws-sns","sns","topic"],"labels":["@aws-cdk/aws-sns"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-sns-subscriptions","keywords":["aws-sns-subscriptions","sns-subscriptions","subscription"],"labels":["@aws-cdk/aws-sns-subscriptions"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-sqs","keywords":["queue","simple-queue-service","aws-sqs","sqs","fifo"],"labels":["@aws-cdk/aws-sqs"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-ssm","keywords":["aws-ssm","ssm","systems-manager","stringparameter","stringlistparameter"],"labels":["@aws-cdk/aws-ssm"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-sns","keywords":["simple-notification-service","aws-sns","sns","topic"],"labels":["@aws-cdk/aws-sns"],"assignees":["kaizen3031593"]}, + {"area":"@aws-cdk/aws-sns-subscriptions","keywords":["aws-sns-subscriptions","sns-subscriptions","subscription"],"labels":["@aws-cdk/aws-sns-subscriptions"],"assignees":["kaizen3031593"]}, + {"area":"@aws-cdk/aws-sqs","keywords":["queue","simple-queue-service","aws-sqs","sqs","fifo"],"labels":["@aws-cdk/aws-sqs"],"assignees":["otaviomacedo"]}, + {"area":"@aws-cdk/aws-ssm","keywords":["aws-ssm","ssm","systems-manager","stringparameter","stringlistparameter"],"labels":["@aws-cdk/aws-ssm"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-sso","keywords":["aws-sso","sso","single-sign-on"],"labels":["@aws-cdk/aws-sso"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-stepfunctions","keywords":["aws-stepfunctions","stepfunctions","state machine", "chain"],"labels":["@aws-cdk/aws-stepfunctions"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-stepfunctions-tasks","keywords":["aws-stepfunctions-tasks","stepfunctions-tasks"],"labels":["@aws-cdk/aws-stepfunctions-tasks"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-synthetics","keywords":["aws-synthetics","synthetics", "canary"],"labels":["@aws-cdk/aws-synthetics"],"assignees":["kaizen3031593"]}, {"area":"@aws-cdk/aws-timestream","keywords":["aws-timestream","timestream"],"labels":["@aws-cdk/aws-timestream"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-transfer","keywords":["aws-transfer","transfer"],"labels":["@aws-cdk/aws-transfer"],"assignees":["otaviomacedo"]}, - {"area":"@aws-cdk/aws-waf","keywords":["waf","web-application-firewall"],"labels":["@aws-cdk/aws-waf"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-wafregional","keywords":["wafregional","cfnwebacl"],"labels":["@aws-cdk/aws-wafregional"],"assignees":["njlynch"]}, - {"area":"@aws-cdk/aws-wafv2","keywords":["wafv2","aws-wafv2"],"labels":["@aws-cdk/aws-wafv2"],"assignees":["njlynch"]}, + {"area":"@aws-cdk/aws-waf","keywords":["waf","web-application-firewall"],"labels":["@aws-cdk/aws-waf"],"assignees":["skinny85"]}, + {"area":"@aws-cdk/aws-wafregional","keywords":["wafregional","cfnwebacl"],"labels":["@aws-cdk/aws-wafregional"],"assignees":["skinny85"]}, + {"area":"@aws-cdk/aws-wafv2","keywords":["wafv2","aws-wafv2"],"labels":["@aws-cdk/aws-wafv2"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-workspaces","keywords":["aws-workspaces","workspaces"],"labels":["@aws-cdk/aws-workspaces"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/aws-xray","keywords":["(aws-xray)","(xray)"],"labels":["@aws-cdk/aws-xray"],"assignees":["corymhall"]}, {"area":"@aws-cdk/cfnspec","keywords":["cfn-spec"],"labels":["@aws-cdk/cfnspec"],"assignees":["rix0rrr"]}, @@ -224,11 +265,11 @@ jobs: {"area":"@aws-cdk/custom-resources","keywords":["custom-resource","provider"],"labels":["@aws-cdk/custom-resources"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/cx-api","keywords":["cx-api","cloudartifact","cloudassembly"],"labels":["@aws-cdk/cx-api"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/aws-lambda-layer-awscli","keywords":["(aws-lambda-layer-awscli)","(lambda-layer-awscli)"],"labels":["@aws-cdk/aws-lambda-layer-awscli"],"assignees":["rix0rrr"]}, - {"area":"@aws-cdk/aws-lambda-layer-kubectl","keywords":["(aws-lambda-layer-kubectl)","(lambda-layer-kubectl)"],"labels":["@aws-cdk/aws-lambda-layer-kubectl"],"assignees":["eladb"]}, + {"area":"@aws-cdk/aws-lambda-layer-kubectl","keywords":["(aws-lambda-layer-kubectl)","(lambda-layer-kubectl)"],"labels":["@aws-cdk/aws-lambda-layer-kubectl"],"assignees":["otaviomacedo"]}, {"area":"@aws-cdk/pipelines","keywords":["pipelines","cdk-pipelines","sourceaction","synthaction"],"labels":["@aws-cdk/pipelines"],"assignees":["rix0rrr"]}, {"area":"@aws-cdk/region-info","keywords":["region-info","fact"],"labels":["@aws-cdk/region-info"],"assignees":["skinny85"]}, - {"area":"aws-cdk-lib","keywords":["aws-cdk-lib","cdk-v2","v2","ubergen"],"labels":["aws-cdk-lib"],"assignees":["njlynch"]}, - {"area":"monocdk","keywords":["monocdk","monocdk-experiment"],"labels":["monocdk"],"assignees":["njlynch"]}, + {"area":"aws-cdk-lib","keywords":["aws-cdk-lib","cdk-v2","v2","ubergen"],"labels":["aws-cdk-lib"],"assignees":["madeline-k"]}, + {"area":"monocdk","keywords":["monocdk","monocdk-experiment"],"labels":["monocdk"],"assignees":["madeline-k"]}, {"area":"@aws-cdk/yaml-cfn","keywords":["(aws-yaml-cfn)","(yaml-cfn)"],"labels":["@aws-cdk/aws-yaml-cfn"],"assignees":["skinny85"]}, {"area":"@aws-cdk/aws-apprunner","keywords":["apprunner","aws-apprunner"],"labels":["@aws-cdk/aws-apprunner"],"assignees":["corymhall"]}, {"area":"@aws-cdk/aws-lightsail","keywords":["lightsail","aws-lightsail"],"labels":["@aws-cdk/aws-lightsail"],"assignees":["corymhall"]}, diff --git a/CHANGELOG.md b/CHANGELOG.md index dc314e3354418..c9cd105471ab4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,63 @@ 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.142.0](https://github.com/aws/aws-cdk/compare/v1.141.0...v1.142.0) (2022-01-28) + + +### Features + +* **cfnspec:** cloudformation spec v53.1.0 ([#18680](https://github.com/aws/aws-cdk/issues/18680)) ([f385059](https://github.com/aws/aws-cdk/commit/f38505911a3e140a9cb6b269bdf22abe9803c515)) +* **cloudfront-origins:** extend `readTimeout` maximum value for `HttpOriginProps` ([#18697](https://github.com/aws/aws-cdk/issues/18697)) ([e64de67](https://github.com/aws/aws-cdk/commit/e64de677cdfc014f68e92b204f4728e60a8bb111)), closes [#18628](https://github.com/aws/aws-cdk/issues/18628) +* **eks:** cluster logging ([#18112](https://github.com/aws/aws-cdk/issues/18112)) ([872277b](https://github.com/aws/aws-cdk/commit/872277b9e853dbf5f2cac84b5afb6d26e0ed5659)), closes [#4159](https://github.com/aws/aws-cdk/issues/4159) +* **iotevents:** allow setting description, evaluation method and key of DetectorModel ([#18644](https://github.com/aws/aws-cdk/issues/18644)) ([2eeaebc](https://github.com/aws/aws-cdk/commit/2eeaebc3cdc9c5c7ef3fa312b3d1abca265dcbb6)) +* **lambda-python:** support setting environment vars for bundling ([#18635](https://github.com/aws/aws-cdk/issues/18635)) ([30e2233](https://github.com/aws/aws-cdk/commit/30e223333fef0b0d7f12287dab170a34e092d7fa)) + + +### Bug Fixes + +* **aws-lambda-nodejs:** pre compilation with tsc is not being run ([#18062](https://github.com/aws/aws-cdk/issues/18062)) ([7ac7221](https://github.com/aws/aws-cdk/commit/7ac7221aff3c612ab80e7812c371b11c56e5db0a)), closes [#18002](https://github.com/aws/aws-cdk/issues/18002) +* **pipelines:** undeployable due to dependency cycle ([#18686](https://github.com/aws/aws-cdk/issues/18686)) ([009d689](https://github.com/aws/aws-cdk/commit/009d68912267de9dcf4136a7d80a652a891b7bb9)), closes [#18492](https://github.com/aws/aws-cdk/issues/18492) [#18673](https://github.com/aws/aws-cdk/issues/18673) + +## [1.141.0](https://github.com/aws/aws-cdk/compare/v1.140.0...v1.141.0) (2022-01-27) + + +### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES + +* **servicecatalog:** `TagOptions` now have `scope` and `props` argument in constructor, and data is now passed via a `allowedValueForTags` field in props + +### Features + +* **assertions:** support assertions on stack messages ([#18521](https://github.com/aws/aws-cdk/issues/18521)) ([cb86e30](https://github.com/aws/aws-cdk/commit/cb86e30391aefdda13e6b0d4b3be2fedf76477c8)), closes [#18347](https://github.com/aws/aws-cdk/issues/18347) +* **assertions:** support for conditions ([#18577](https://github.com/aws/aws-cdk/issues/18577)) ([55ff1b2](https://github.com/aws/aws-cdk/commit/55ff1b2e69f1b42bbbecd9dc95e17f2ffc35f94e)), closes [#18560](https://github.com/aws/aws-cdk/issues/18560) +* **aws-ecs-patterns:** adding support for custom HealthCheck while creating QueueProcessingFargateService ([#18219](https://github.com/aws/aws-cdk/issues/18219)) ([0ca81a1](https://github.com/aws/aws-cdk/commit/0ca81a118d3d54b87d2d05a53fb72e4efe03b591)), closes [#15636](https://github.com/aws/aws-cdk/issues/15636) +* **certificatemanager:** DnsValidatedCertificate DNS record cleanup ([#18311](https://github.com/aws/aws-cdk/issues/18311)) ([36d356d](https://github.com/aws/aws-cdk/commit/36d356d0b3e422f7451f4b0dd2f971aa0378210e)), closes [#3333](https://github.com/aws/aws-cdk/issues/3333) [#7063](https://github.com/aws/aws-cdk/issues/7063) +* **cfnspec:** cloudformation spec v53.1.0 ([#18588](https://github.com/aws/aws-cdk/issues/18588)) ([a283a48](https://github.com/aws/aws-cdk/commit/a283a482dead64e94383ba21cc7908f10c4459a2)) +* **cfnspec:** cloudformation spec v53.1.0 ([#18658](https://github.com/aws/aws-cdk/issues/18658)) ([2eda19e](https://github.com/aws/aws-cdk/commit/2eda19e510374426190531810cff518d582644ad)) +* **ec2:** session timeout and login banner for client vpn endpoint ([#18590](https://github.com/aws/aws-cdk/issues/18590)) ([7294118](https://github.com/aws/aws-cdk/commit/72941180a7188e5560a58f1509554ef038544ec4)) +* **ecs:** add `BaseService.fromServiceArnWithCluster()` for use in CodePipeline ([#18530](https://github.com/aws/aws-cdk/issues/18530)) ([3d192a9](https://github.com/aws/aws-cdk/commit/3d192a9a832857cb246d719a68b4b8f40d807fed)) +* **iotevents:** add DetectorModel L2 Construct ([#18049](https://github.com/aws/aws-cdk/issues/18049)) ([d0960f1](https://github.com/aws/aws-cdk/commit/d0960f181e5f66daa1eb53be2190b7e62bd66030)), closes [#17711](https://github.com/aws/aws-cdk/issues/17711) [#17711](https://github.com/aws/aws-cdk/issues/17711) +* **lambda-nodejs:** Allow setting mainFields for esbuild ([#18569](https://github.com/aws/aws-cdk/issues/18569)) ([0e78aeb](https://github.com/aws/aws-cdk/commit/0e78aeb9ad62226e67f72f23c0008ba749b3a73b)) +* **s3:** custom role for the bucket notifications handler ([#17794](https://github.com/aws/aws-cdk/issues/17794)) ([43f232d](https://github.com/aws/aws-cdk/commit/43f232ddc0a18e9a2fada2fbead758ab3538adc2)), closes [#9918](https://github.com/aws/aws-cdk/issues/9918) [#13241](https://github.com/aws/aws-cdk/issues/13241) +* **servicecatalog:** Create TagOptions Construct ([#18314](https://github.com/aws/aws-cdk/issues/18314)) ([903c4b6](https://github.com/aws/aws-cdk/commit/903c4b6e4adf676fae42265a048dddd0e1386542)), closes [#17753](https://github.com/aws/aws-cdk/issues/17753) + + +### Bug Fixes + +* **apigatewayv2:** websocket api: allow all methods in grant manage connections ([#18544](https://github.com/aws/aws-cdk/issues/18544)) ([41c8a3f](https://github.com/aws/aws-cdk/commit/41c8a3fa6b50a94affb65286d862056050d02e84)), closes [#18410](https://github.com/aws/aws-cdk/issues/18410) +* **aws-apigateway:** cross region authorizer ref ([#18444](https://github.com/aws/aws-cdk/issues/18444)) ([0e0a092](https://github.com/aws/aws-cdk/commit/0e0a0922ba1d538abdfeb61a260c262109115038)) +* **cli:** hotswap should wait for lambda's `updateFunctionCode` to complete ([#18536](https://github.com/aws/aws-cdk/issues/18536)) ([0e08eeb](https://github.com/aws/aws-cdk/commit/0e08eebd2f13ab0da6cac7b91288845cad530192)), closes [#18386](https://github.com/aws/aws-cdk/issues/18386) [#18386](https://github.com/aws/aws-cdk/issues/18386) +* **ecs:** only works in 'aws' partition ([#18496](https://github.com/aws/aws-cdk/issues/18496)) ([525ac07](https://github.com/aws/aws-cdk/commit/525ac07369e33e2f36b7a0eea7913e43649484db)), closes [#18429](https://github.com/aws/aws-cdk/issues/18429) +* **ecs-patterns:** Fix Network Load Balancer Port assignments in ECS Patterns ([#18157](https://github.com/aws/aws-cdk/issues/18157)) ([1393729](https://github.com/aws/aws-cdk/commit/13937299596d0b858d56e9116bf7a7dbe039d4b4)), closes [#18073](https://github.com/aws/aws-cdk/issues/18073) +* **elasticloadbalancingv2:** ApplicationLoadBalancer.logAccessLogs does not grant all necessary permissions ([#18558](https://github.com/aws/aws-cdk/issues/18558)) ([bde1795](https://github.com/aws/aws-cdk/commit/bde17950293309b7449fc412301634770b47111f)), closes [#18367](https://github.com/aws/aws-cdk/issues/18367) +* **pipelines:** CodeBuild projects are hard to tell apart ([#18492](https://github.com/aws/aws-cdk/issues/18492)) ([f6dab8d](https://github.com/aws/aws-cdk/commit/f6dab8d8c5aa4cf56d6846e2d13c1d5641136f72)) +* **region-info:** incorrect codedeploy service principals ([#18505](https://github.com/aws/aws-cdk/issues/18505)) ([16db963](https://github.com/aws/aws-cdk/commit/16db9639e86f1fd6f26a1054f4d6df24801d0f05)) +* **route53:** add RoutingControlArn to HealthCheck patch ([#18645](https://github.com/aws/aws-cdk/issues/18645)) ([c58e8bb](https://github.com/aws/aws-cdk/commit/c58e8bbbcb0a66c37b65cddc1da8d19dfbf26b4f)), closes [#18570](https://github.com/aws/aws-cdk/issues/18570) +* **s3:** add missing safe actions to `grantWrite`, `grantReadWrite` and `grantPut` methods ([#18494](https://github.com/aws/aws-cdk/issues/18494)) ([940d043](https://github.com/aws/aws-cdk/commit/940d0439cd347f06d755f3e3dd0582470749f710)), closes [#13616](https://github.com/aws/aws-cdk/issues/13616) +* **secretsmanager:** SecretRotation for secret imported by name has incorrect permissions ([#18567](https://github.com/aws/aws-cdk/issues/18567)) ([9ed263c](https://github.com/aws/aws-cdk/commit/9ed263cde0b41959ff267720c0978bfe7449337a)), closes [#18424](https://github.com/aws/aws-cdk/issues/18424) +* **stepfunctions:** task token integration cannot be used with API Gateway ([#18595](https://github.com/aws/aws-cdk/issues/18595)) ([678eede](https://github.com/aws/aws-cdk/commit/678eeded5d5631dbacff43ead697ecbd3bd4b27d)), closes [#14184](https://github.com/aws/aws-cdk/issues/14184) [#14181](https://github.com/aws/aws-cdk/issues/14181) +* **stepfunctions-tasks:** cluster creation fails with unresolved release labels ([#18288](https://github.com/aws/aws-cdk/issues/18288)) ([9940952](https://github.com/aws/aws-cdk/commit/9940952d67bdf07f3d737dc88676dc7f7c435a12)) +* **synthetics:** correct getbucketlocation policy ([#13573](https://github.com/aws/aws-cdk/issues/13573)) ([e743525](https://github.com/aws/aws-cdk/commit/e743525b6379371110d737bb360f637c41d30ca1)), closes [#13572](https://github.com/aws/aws-cdk/issues/13572) + ## [1.140.0](https://github.com/aws/aws-cdk/compare/v1.139.0...v1.140.0) (2022-01-20) diff --git a/package.json b/package.json index b99f8690e2171..b29235f97a8dc 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ }, "resolutions": { "colors": "1.4.0", - "string-width": "^4.2.3" + "string-width": "^4.2.3", + "markdown-it": "^12.3.2" }, "repository": { "type": "git", diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/package.json b/packages/@aws-cdk-containers/ecs-service-extensions/package.json index e59612c4d6b3d..016a3d84b427a 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/package.json +++ b/packages/@aws-cdk-containers/ecs-service-extensions/package.json @@ -44,7 +44,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "jest": "^27.4.7", "@aws-cdk/pkglint": "0.0.0", - "@aws-cdk/assert-internal": "0.0.0" + "@aws-cdk/assertions": "0.0.0" }, "dependencies": { "@aws-cdk/aws-applicationautoscaling": "0.0.0", diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/appmesh.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/appmesh.test.ts index 0d54e8e43570f..02a572e9e2ced 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/appmesh.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/appmesh.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as appmesh from '@aws-cdk/aws-appmesh'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; @@ -34,7 +34,7 @@ describe('appmesh', () => { // THEN // Ensure that task has an App Mesh sidecar - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -210,7 +210,7 @@ describe('appmesh', () => { }); // Ensure that the service has the right settings - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { Cluster: { Ref: 'productionenvironmentclusterC6599D2D', }, @@ -256,8 +256,6 @@ describe('appmesh', () => { Ref: 'myservicetaskdefinitionF3E2D86F', }, }); - - }); test('should have the right maximumPercentage at desired count == 1', () => { @@ -288,15 +286,13 @@ describe('appmesh', () => { desiredCount: 1, }); - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 200, MinimumHealthyPercent: 100, }, DesiredCount: 1, }); - - }); test('should have the right maximumPercentage at desired count == 2', () => { @@ -327,15 +323,13 @@ describe('appmesh', () => { desiredCount: 2, }); - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 150, MinimumHealthyPercent: 100, }, DesiredCount: 2, }); - - }); test('should have the right maximumPercentage at desired count == 3', () => { @@ -366,15 +360,13 @@ describe('appmesh', () => { desiredCount: 3, }); - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 150, MinimumHealthyPercent: 100, }, DesiredCount: 3, }); - - }); test('should have the right maximumPercentage at desired count == 4', () => { @@ -405,15 +397,13 @@ describe('appmesh', () => { desiredCount: 4, }); - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 125, MinimumHealthyPercent: 100, }, DesiredCount: 4, }); - - }); test('should have the right maximumPercentage at desired count > 4', () => { @@ -444,15 +434,13 @@ describe('appmesh', () => { desiredCount: 8, }); - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 125, MinimumHealthyPercent: 100, }, DesiredCount: 8, }); - - }); test('should be able to create multiple App Mesh enabled services and connect', () => { @@ -516,9 +504,7 @@ describe('appmesh', () => { greeterService.connectTo(greetingService); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition'); - - + Template.fromStack(stack).hasResource('AWS::ECS::TaskDefinition', Match.anyValue()); }); test('should detect when attempting to connect services from two different envs', () => { @@ -572,7 +558,5 @@ describe('appmesh', () => { expect(() => { developmentNameService.connectTo(productionNameService); }).toThrow(/Unable to connect service 'name-development' in environment 'development' to service 'name-production' in environment 'production' because services can not be connected across environment boundaries/); - - }); }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/assign-public-ip.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/assign-public-ip.test.ts index 173a573b96e78..be8d6e6bcac51 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/assign-public-ip.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/assign-public-ip.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; @@ -29,15 +29,13 @@ describe('assign public ip', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { NetworkConfiguration: { AwsvpcConfiguration: { AssignPublicIp: 'ENABLED', }, }, }); - - }); test('errors when adding a public ip to ec2-backed service', () => { @@ -76,8 +74,6 @@ describe('assign public ip', () => { serviceDescription, }); }).toThrow(/Fargate/i); - - }); test('should not add a task record manager by default', () => { @@ -103,8 +99,6 @@ describe('assign public ip', () => { // THEN expect(service.ecsService.node.tryFindChild('TaskRecordManager')).toBeUndefined(); - - }); test('should add a task record manager when dns is requested', () => { @@ -138,8 +132,6 @@ describe('assign public ip', () => { // THEN expect(service.ecsService.node.tryFindChild('TaskRecordManager')).toBeDefined(); - - }); test('task record manager listens for ecs events', () => { @@ -172,7 +164,7 @@ describe('assign public ip', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventPattern: { 'source': ['aws.ecs'], 'detail-type': [ @@ -185,7 +177,7 @@ describe('assign public ip', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventPattern: { 'source': ['aws.ecs'], 'detail-type': [ @@ -197,7 +189,5 @@ describe('assign public ip', () => { }, }, }); - - }); }); diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/cloudwatch-agent.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/cloudwatch-agent.test.ts index d9ff4f3b50fae..929f94180b3a2 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/cloudwatch-agent.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/cloudwatch-agent.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; import { CloudwatchAgentExtension, Container, Environment, Service, ServiceDescription } from '../lib'; @@ -27,7 +27,7 @@ describe('cloudwatch agent', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -102,8 +102,5 @@ describe('cloudwatch agent', () => { ], }, }); - - }); - }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/container.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/container.test.ts index 3e9b2e79dd9e8..80fbe097b08d0 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/container.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/container.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; @@ -46,9 +46,9 @@ describe('container', () => { }); // THEN - expect(stack).toCountResources('AWS::ECS::Service', 1); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Service', 1); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -86,8 +86,6 @@ describe('container', () => { ], }, }); - - }); test('should be able to enable default logging behavior - with enable default log driver feature flag', () => { @@ -129,12 +127,12 @@ describe('container', () => { }); // THEN - expect(stack).toCountResources('AWS::ECS::Service', 1); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Service', 1); // Ensure that the log group was created - expect(stack).toHaveResource('AWS::Logs::LogGroup'); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 1); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -184,8 +182,6 @@ describe('container', () => { ], }, }); - - }); test('should be able to add user-provided log group in the log driver options', () => { @@ -228,12 +224,12 @@ describe('container', () => { }); // THEN - expect(stack).toCountResources('AWS::ECS::Service', 1); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Service', 1); // Ensure that the log group was created - expect(stack).toHaveResource('AWS::Logs::LogGroup'); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 1); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -283,8 +279,6 @@ describe('container', () => { ], }, }); - - }); test('should error when log group is provided in the container extension and another observability extension is added', () => { @@ -312,5 +306,4 @@ describe('container', () => { }); }).toThrow(/Log configuration already specified. You cannot provide a log group for the application container of service 'my-service' while also adding log configuration separately using service extensions./); }); - }); diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/environment.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/environment.test.ts index 443c9fa9f9f68..dc49c70a1b9d8 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/environment.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/environment.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; @@ -27,9 +27,9 @@ describe('environment', () => { }); // THEN - expect(stack).toCountResources('AWS::ECS::Service', 1); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Service', 1); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -67,8 +67,6 @@ describe('environment', () => { ], }, }); - - }); test('should be able to create a Fargate environment with a given VPC and cluster', () => { @@ -98,9 +96,9 @@ describe('environment', () => { }); // THEN - expect(stack).toCountResources('AWS::ECS::Service', 1); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Service', 1); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -138,8 +136,6 @@ describe('environment', () => { ], }, }); - - }); test('should be able to create an environment for EC2', () => { @@ -177,9 +173,9 @@ describe('environment', () => { }); // THEN - expect(stack).toCountResources('AWS::ECS::Service', 1); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Service', 1); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -217,8 +213,6 @@ describe('environment', () => { ], }, }); - - }); test('should be able to create an environment from attributes', () => { @@ -246,7 +240,5 @@ describe('environment', () => { expect(environment.cluster).toEqual(cluster); expect(environment.vpc).toEqual(vpc); expect(environment.id).toEqual('Environment'); - - }); }); diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/firelens.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/firelens.test.ts index 2f30e6139e33f..6cdd83d07610c 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/firelens.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/firelens.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; import { Container, Environment, FireLensExtension, Service, ServiceDescription } from '../lib'; @@ -29,11 +29,11 @@ describe('firelens', () => { // THEN // Ensure that the log group was created - expect(stack).toHaveResource('AWS::Logs::LogGroup'); + Template.fromStack(stack).hasResource('AWS::Logs::LogGroup', Match.anyValue()); // Ensure that task has a Firelens sidecar and a log configuration // pointing at the sidecar - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -114,8 +114,5 @@ describe('firelens', () => { ], }, }); - - }); - }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/http-load-balancer.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/http-load-balancer.test.ts index e06c1632d93b5..8af4ef5d44ab6 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/http-load-balancer.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/http-load-balancer.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; import { Container, Environment, HttpLoadBalancerExtension, Service, ServiceDescription } from '../lib'; @@ -27,7 +27,7 @@ describe('http load balancer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -66,10 +66,8 @@ describe('http load balancer', () => { }, }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener'); - - + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::Listener', 1); }); test('allows scaling on request count for the HTTP load balancer', () => { @@ -98,12 +96,12 @@ describe('http load balancer', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 5, MinCapacity: 1, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { @@ -139,5 +137,4 @@ describe('http load balancer', () => { }); }).toThrow(/Auto scaling target for the service 'my-service' hasn't been configured. Please use Service construct to configure 'minTaskCount' and 'maxTaskCount'./); }); - }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/injecter.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/injecter.test.ts index 3cfd8f4918f48..e52c498f17455 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/injecter.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/injecter.test.ts @@ -1,4 +1,4 @@ -import { countResources, expect, haveResource } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; @@ -42,10 +42,10 @@ describe('injecter', () => { // THEN // Ensure creation of provided topics - expect(stack).to(countResources('AWS::SNS::Topic', 2)); + Template.fromStack(stack).resourceCountIs('AWS::SNS::Topic', 2); // Ensure the task role is given permissions to publish events to topics - expect(stack).to(haveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -65,10 +65,10 @@ describe('injecter', () => { ], Version: '2012-10-17', }, - })); + }); // Ensure that the topic ARNs have been correctly appended to the environment variables - expect(stack).to(haveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -109,6 +109,6 @@ describe('injecter', () => { ], }, ], - })); + }); }); }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/queue.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/queue.test.ts index 7aa00581361d9..dfc6bd8a9bcdd 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/queue.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/queue.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; @@ -33,11 +33,11 @@ describe('queue', () => { // THEN // Ensure creation of default queue and queue policy allowing SNS Topics to send message to the queue - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { MessageRetentionPeriod: 1209600, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': [ @@ -50,7 +50,7 @@ describe('queue', () => { }); // Ensure the task role is given permissions to consume messages from the queue - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -75,10 +75,10 @@ describe('queue', () => { }); // Ensure there are no SNS Subscriptions created - expect(stack).toCountResources('AWS::SNS::Subscription', 0); + Template.fromStack(stack).resourceCountIs('AWS::SNS::Subscription', 0); // Ensure that the queue URL has been correctly appended to the environment variables - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -114,8 +114,6 @@ describe('queue', () => { }, ], }); - - }); test('should be able to subscribe default events queue created by the extension to given topics', () => { @@ -153,11 +151,11 @@ describe('queue', () => { // THEN // Ensure creation of default queue and queue policy allowing SNS Topics to send message to the queue - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { MessageRetentionPeriod: 1209600, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': [ @@ -169,7 +167,7 @@ describe('queue', () => { }, }); - expect(stack).toHaveResource('AWS::SQS::QueuePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::QueuePolicy', { PolicyDocument: { Statement: [ { @@ -218,7 +216,7 @@ describe('queue', () => { }); // Ensure the task role is given permissions to consume messages from the queue - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -243,7 +241,7 @@ describe('queue', () => { }); // Ensure SNS Subscriptions for given topics - expect(stack).toHaveResource('AWS::SNS::Subscription', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Subscription', { Protocol: 'sqs', TopicArn: { Ref: 'topic152D84A37', @@ -256,7 +254,7 @@ describe('queue', () => { }, }); - expect(stack).toHaveResource('AWS::SNS::Subscription', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Subscription', { Protocol: 'sqs', TopicArn: { Ref: 'topic2A4FB547F', @@ -270,7 +268,7 @@ describe('queue', () => { }); // Ensure that the queue URL has been correctly appended to the environment variables - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -306,8 +304,6 @@ describe('queue', () => { }, ], }); - - }); test('should be able to subscribe user-provided queue to given topics', () => { @@ -346,7 +342,7 @@ describe('queue', () => { // THEN // Ensure queue policy allows SNS Topics to send message to the queue - expect(stack).toHaveResource('AWS::SQS::QueuePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::QueuePolicy', { PolicyDocument: { Statement: [ { @@ -374,7 +370,7 @@ describe('queue', () => { }, }); - expect(stack).toHaveResource('AWS::SQS::QueuePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::QueuePolicy', { PolicyDocument: { Statement: [ { @@ -403,7 +399,7 @@ describe('queue', () => { }); // Ensure the task role is given permissions to consume messages from the queue - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -444,7 +440,7 @@ describe('queue', () => { }); // Ensure SNS Subscriptions for given topics - expect(stack).toHaveResource('AWS::SNS::Subscription', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Subscription', { Protocol: 'sqs', TopicArn: { Ref: 'topic152D84A37', @@ -457,7 +453,7 @@ describe('queue', () => { }, }); - expect(stack).toHaveResource('AWS::SNS::Subscription', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Subscription', { Protocol: 'sqs', TopicArn: { Ref: 'topic2A4FB547F', @@ -471,7 +467,7 @@ describe('queue', () => { }); // Ensure that the queue URL has been correctly added to the environment variables - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -562,12 +558,12 @@ describe('queue', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 10, MinCapacity: 1, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { @@ -639,12 +635,12 @@ describe('queue', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 10, MinCapacity: 1, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { @@ -668,7 +664,7 @@ describe('queue', () => { }, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { @@ -692,7 +688,7 @@ describe('queue', () => { }, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/scale-on-cpu-utilization.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/scale-on-cpu-utilization.test.ts index 561e1f7991a94..bfcf67e394609 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/scale-on-cpu-utilization.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/scale-on-cpu-utilization.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; import { Container, Environment, ScaleOnCpuUtilization, Service, ServiceDescription } from '../lib'; @@ -27,7 +27,7 @@ describe('scale on cpu utilization', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 200, MinimumHealthyPercent: 100, @@ -35,7 +35,7 @@ describe('scale on cpu utilization', () => { DesiredCount: 2, }); - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 8, MinCapacity: 2, ResourceId: { @@ -76,7 +76,7 @@ describe('scale on cpu utilization', () => { ServiceNamespace: 'ecs', }); - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyName: 'myserviceserviceTaskCountTargetmyservicetargetcpuutilization50E6628660', PolicyType: 'TargetTrackingScaling', ScalingTargetId: { @@ -91,8 +91,6 @@ describe('scale on cpu utilization', () => { TargetValue: 50, }, }); - - }); test('should be able to set a custom scaling policy as well', () => { @@ -125,7 +123,7 @@ describe('scale on cpu utilization', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 200, MinimumHealthyPercent: 100, @@ -133,20 +131,18 @@ describe('scale on cpu utilization', () => { DesiredCount: 25, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 30, MinCapacity: 15, }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { TargetTrackingScalingPolicyConfiguration: { ScaleInCooldown: 180, ScaleOutCooldown: 180, TargetValue: 75, }, }); - - }); test('should error if configuring autoscaling target both in the extension and the Service', () => { @@ -176,5 +172,4 @@ describe('scale on cpu utilization', () => { }); }).toThrow('Cannot specify \'autoScaleTaskCount\' in the Service construct and also provide a \'ScaleOnCpuUtilization\' extension. \'ScaleOnCpuUtilization\' is deprecated. Please only provide \'autoScaleTaskCount\'.'); }); - }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/service.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/service.test.ts index 8db284b856e28..9879282f7e254 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/service.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/service.test.ts @@ -1,5 +1,4 @@ -import { ABSENT } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; import { Container, Environment, Service, ServiceDescription } from '../lib'; @@ -20,8 +19,6 @@ describe('service', () => { serviceDescription, }); }).toThrow(/Service 'my-service' must have a Container extension/); - - }); test('allows scaling on a target CPU utilization', () => { @@ -49,16 +46,16 @@ describe('service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 5, MinCapacity: 1, }); - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ECSServiceAverageCPUUtilization' }, @@ -92,16 +89,16 @@ describe('service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), }); - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 5, MinCapacity: 1, }); - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ECSServiceAverageMemoryUtilization' }, @@ -136,5 +133,4 @@ describe('service', () => { }); }).toThrow(/The auto scaling target for the service 'my-service' has been created but no auto scaling policies have been configured./); }); - }); \ No newline at end of file diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/xray.test.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/xray.test.ts index 6fce84bca7338..f8f096369d50b 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/xray.test.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/xray.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ecs from '@aws-cdk/aws-ecs'; import * as cdk from '@aws-cdk/core'; import { Container, Environment, XRayExtension, Service, ServiceDescription } from '../lib'; @@ -27,7 +27,7 @@ describe('xray', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -114,8 +114,5 @@ describe('xray', () => { ], }, }); - - }); - }); \ No newline at end of file diff --git a/packages/@aws-cdk/alexa-ask/package.json b/packages/@aws-cdk/alexa-ask/package.json index 60a4890e26428..ece5934f1136a 100644 --- a/packages/@aws-cdk/alexa-ask/package.json +++ b/packages/@aws-cdk/alexa-ask/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/app-delivery/README.md b/packages/@aws-cdk/app-delivery/README.md index a60f4590dc39f..cd8e80e218bba 100644 --- a/packages/@aws-cdk/app-delivery/README.md +++ b/packages/@aws-cdk/app-delivery/README.md @@ -59,6 +59,10 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; import * as cdk from '@aws-cdk/core'; import * as cicd from '@aws-cdk/app-delivery'; +import * as iam from '@aws-cdk/aws-iam'; + +class MyServiceStackA extends cdk.Stack {} +class MyServiceStackB extends cdk.Stack {} const app = new cdk.App(); @@ -77,7 +81,9 @@ const sourceOutput = new codepipeline.Artifact(); const source = new codepipeline_actions.GitHubSourceAction({ actionName: 'GitHub', output: sourceOutput, - /* ... */ + owner: 'myName', + repo: 'myRepo', + oauthToken: cdk.SecretValue.plainText('secret'), }); pipeline.addStage({ stageName: 'source', @@ -129,10 +135,11 @@ deployStage.addAction(deployServiceAAction); // is passed to CloudFormation and needs the permissions necessary to deploy // stack. Alternatively you can enable [Administrator](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator) permissions above, // users should understand the privileged nature of this role. -deployServiceAAction.addToRolePolicy(new iam.PolicyStatement({ - actions: ['service:SomeAction'], - resources: [myResource.myResourceArn], - // add more Action(s) and/or Resource(s) here, as needed +const myResourceArn = 'arn:partition:service:region:account-id:resource-id'; +deployServiceAAction.addToDeploymentRolePolicy(new iam.PolicyStatement({ + actions: ['service:SomeAction'], + resources: [myResourceArn], + // add more Action(s) and/or Resource(s) here, as needed })); const serviceStackB = new MyServiceStackB(app, 'ServiceStackB', { /* ... */ }); diff --git a/packages/@aws-cdk/app-delivery/package.json b/packages/@aws-cdk/app-delivery/package.json index 7f57bf73f19d3..8d3da28ad60ba 100644 --- a/packages/@aws-cdk/app-delivery/package.json +++ b/packages/@aws-cdk/app-delivery/package.json @@ -29,7 +29,14 @@ } }, "outdir": "dist", - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "scripts": { "build": "cdk-build", diff --git a/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json b/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json index 9b2ed51da9ff2..9d53a0d8b915c 100644 --- a/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json +++ b/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/assert-internal/README.md b/packages/@aws-cdk/assert-internal/README.md index a74f9304f60d6..60490bbb7108f 100644 --- a/packages/@aws-cdk/assert-internal/README.md +++ b/packages/@aws-cdk/assert-internal/README.md @@ -3,15 +3,14 @@ --- -![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) +![Deprecated](https://img.shields.io/badge/deprecated-critical.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 API may emit warnings. Backward compatibility is not guaranteed. -If using monocdk, use [@monocdk-experiment/assert](https://www.npmjs.com/package/@monocdk-experiment/assert) instead. +## Replacement recommended + +This library has been deprecated. We recommend you use the +[@aws-cdk/assertions](https://docs.aws.amazon.com/cdk/api/v1/docs/assertions-readme.html) module instead. --- diff --git a/packages/@aws-cdk/assert-internal/package.json b/packages/@aws-cdk/assert-internal/package.json index 99b32de7a66c9..c263b915edd4f 100644 --- a/packages/@aws-cdk/assert-internal/package.json +++ b/packages/@aws-cdk/assert-internal/package.json @@ -55,8 +55,8 @@ "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, - "stability": "experimental", - "maturity": "experimental", + "stability": "deprecated", + "maturity": "deprecated", "publishConfig": { "tag": "latest" }, diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index 1a66fcf3a6d99..b462bcbbab2db 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -71,8 +71,8 @@ "exclude": true }, "nozem": false, - "stability": "experimental", - "maturity": "developer-preview", + "stability": "deprecated", + "maturity": "deprecated", "publishConfig": { "tag": "latest-1" } diff --git a/packages/@aws-cdk/assertions/MIGRATING.md b/packages/@aws-cdk/assertions/MIGRATING.md new file mode 100644 index 0000000000000..afa40bf9b1c5f --- /dev/null +++ b/packages/@aws-cdk/assertions/MIGRATING.md @@ -0,0 +1,123 @@ +# Migrating to Assertions + +Most of the APIs in the old `assert` module has a corresponding API in `assertions`. +Make the following modifications to your CDK test files to migrate to the +`@aws-cdk/assertions` module. + +For a migration script that handles most common use cases for you, see +[Migration Script](migration-script). + +## Translation Guide + +- Rewrite module imports that use `@aws-cdk/aws-assert` to `@aws-cdk/aws-assertions`. + For example: + + ```ts + import '@aws-cdk/assert/jest'; + import { ABSENT, SynthUtils, ResourcePart } from '@aws-cdk/assert'; + ``` + + ...becomes... + + ```ts + import { Template } from '@aws-cdk/assertions'; + import { Match, Template } from '@aws-cdk/assertions'; + ``` + +- Replace instances of `toHaveResource()` with `hasResourceProperties()` or `hasResource()`. + For example: + + ```ts + expect(stack).toHaveResource('FOO::BAR', {/*...*/}); + expect(stack).toHaveResource('FOO::BAR', {/*...*/}, ResourcePart.CompleteDefinition); + ``` + + ...becomes... + + ```ts + Template.fromStack(stack).hasResourceProperties('FOO::BAR', {/*...*/}); + Template.fromStack(stacK).hasResource('FOO::BAR', {/*...*/}); + ``` + +- Replace instances of `toCountResources()` with `resourceCountIs`. For example: + + ```ts + expect(stack).toCountResources('FOO::BAR', 1); + ``` + + ...becomes... + + ```ts + Template.fromStack(stack).resourceCountIs('FOO::BAR', 1); + ``` +- Replace instances of `toMatchTemplate()` with `templateMatches()`. For example: + + ```ts + expect(stack).toMatchTemplate({/*...*/}); + ``` + + ...becomes... + + ```ts + Template.fromStack(stack).templateMatches({/*...*/}); + ``` + +- Replace `arrayWith()` with `Match.arrayWith()`, `objectLike()` with `Match.objectLike()`, and + `ABSENT` with `Match.absent()`. + +- `not` can be replaced with `Match.not()` _or_ `resourceCountIs()` depending on the use case. + + ```ts + // asserting that the stack does not have a particular resource. + expect(stack).not.toHaveResource('FOO::BAR'); + ``` + + ...becomes... + + ```ts + Template.fromStack(stack).resourceCountIs('FOO::BAR', 0); + ``` + + ```ts + // asserting that the stack does not have a resource with these properties + expect(stack).not.toHaveResource('FOO::BAR', { + prop: 'does not exist', + }); + ``` + + ...becomes... + + ```ts + Template.fromStack(stack).hasResourceProperties('FOO::BAR', Match.not({ + prop: 'does not exist', + })); + ``` + +- `SynthUtils.synthesize(stack)` can be replaced as well. For example: + + ```ts + expect(SynthUtils.synthesize(stack).template).toEqual(/*...*/); + SynthUtils.syntesize(stack); + ``` + + ...becomes... + + ```ts + expect(Template.fromStack(stack).toJSON()).toEqual(/*...*/); + App.of(stack).synth(); + ``` + +## Migration Script + +> NOTE: We have some code rewrite rules that will make it easier to migrate from one library +> to the other. This tool will not do a complete rewrite and is not guaranteed to produce +> compilable code! It will just save you the effort of performing a lot of code substitutions +> you would otherwise have to do by hand. + +Comby is a tool used to do structured code rewriting. You can install it +[here](https://comby.dev/). Download the [rewrite.toml](rewrite.toml) file from our GitHub +repository, and run the following command in the root directory of your project: + +```bash +comby -config ~/rewrite.toml -f .ts -d test -in-place -timeout 10 +``` \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/README.md b/packages/@aws-cdk/assertions/README.md index 3d1275d2b4a3e..d23c37a08b099 100644 --- a/packages/@aws-cdk/assertions/README.md +++ b/packages/@aws-cdk/assertions/README.md @@ -9,6 +9,9 @@ +If you're migrating from the old `assert` library, the migration guide can be found in +[our GitHub repository](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assertions/MIGRATING.md). + Functions for writing test asserting against CDK applications, with focus on CloudFormation templates. The `Template` class includes a set of methods for writing assertions against CloudFormation templates. Use one of the `Template.fromXxx()` static methods to create an instance of this class. @@ -251,7 +254,7 @@ This matcher can be combined with any of the other matchers. // The following will NOT throw an assertion error template.hasResourceProperties('Foo::Bar', { Fred: { - Wobble: [ Match.anyValue(), "Flip" ], + Wobble: [ Match.anyValue(), Match.anyValue() ], }, }); @@ -400,7 +403,7 @@ template.hasResourceProperties('Foo::Bar', { ## Capturing Values -This matcher APIs documented above allow capturing values in the matching entry +The matcher APIs documented above allow capturing values in the matching entry (Resource, Output, Mapping, etc.). The following code captures a string from a matching resource. @@ -492,3 +495,74 @@ fredCapture.asString(); // returns "Flob" fredCapture.next(); // returns true fredCapture.asString(); // returns "Quib" ``` + +## Asserting Annotations + +In addition to template matching, we provide an API for annotation matching. +[Annotations](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Annotations.html) +can be added via the [Aspects](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Aspects.html) +API. You can learn more about Aspects [here](https://docs.aws.amazon.com/cdk/v2/guide/aspects.html). + +Say you have a `MyAspect` and a `MyStack` that uses `MyAspect`: + +```ts nofixture +import * as cdk from '@aws-cdk/core'; +import { Construct, IConstruct } from 'constructs'; + +class MyAspect implements cdk.IAspect { + public visit(node: IConstruct): void { + if (node instanceof cdk.CfnResource && node.cfnResourceType === 'Foo::Bar') { + this.error(node, 'we do not want a Foo::Bar resource'); + } + } + + protected error(node: IConstruct, message: string): void { + cdk.Annotations.of(node).addError(message); + } +} + +class MyStack extends cdk.Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const stack = new cdk.Stack(); + new cdk.CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { + Fred: 'Thud', + }, + }); + cdk.Aspects.of(stack).add(new MyAspect()); + } +} +``` + +We can then assert that the stack contains the expected Error: + +```ts +// import { Annotations } from '@aws-cdk/assertions'; + +Annotations.fromStack(stack).hasError( + '/Default/Foo', + 'we do not want a Foo::Bar resource', +); +``` + +Here are the available APIs for `Annotations`: + +- `hasError()` and `findError()` +- `hasWarning()` and `findWarning()` +- `hasInfo()` and `findInfo()` + +The corresponding `findXxx()` API is complementary to the `hasXxx()` API, except instead +of asserting its presence, it returns the set of matching messages. + +In addition, this suite of APIs is compatable with `Matchers` for more fine-grained control. +For example, the following assertion works as well: + +```ts +Annotations.fromStack(stack).hasError( + '/Default/Foo', + Match.stringLikeRegexp('.*Foo::Bar.*'), +); +``` diff --git a/packages/@aws-cdk/assertions/lib/annotations.ts b/packages/@aws-cdk/assertions/lib/annotations.ts new file mode 100644 index 0000000000000..c656b15d6bab8 --- /dev/null +++ b/packages/@aws-cdk/assertions/lib/annotations.ts @@ -0,0 +1,129 @@ +import { Stack, Stage } from '@aws-cdk/core'; +import { SynthesisMessage } from '@aws-cdk/cx-api'; +import { Messages } from './private/message'; +import { findMessage, hasMessage } from './private/messages'; + +/** + * Suite of assertions that can be run on a CDK Stack. + * Focused on asserting annotations. + */ +export class Annotations { + /** + * Base your assertions on the messages returned by a synthesized CDK `Stack`. + * @param stack the CDK Stack to run assertions on + */ + public static fromStack(stack: Stack): Annotations { + return new Annotations(toMessages(stack)); + } + + private readonly _messages: Messages; + + private constructor(messages: SynthesisMessage[]) { + this._messages = convertArrayToMessagesType(messages); + } + + /** + * Assert that an error with the given message exists in the synthesized CDK `Stack`. + * + * @param constructPath the construct path to the error. Provide `'*'` to match all errors in the template. + * @param message the error message as should be expected. This should be a string or Matcher object. + */ + public hasError(constructPath: string, message: any): void { + const matchError = hasMessage(this._messages, constructPath, constructMessage('error', message)); + if (matchError) { + throw new Error(matchError); + } + } + + /** + * Get the set of matching errors of a given construct path and message. + * + * @param constructPath the construct path to the error. Provide `'*'` to match all errors in the template. + * @param message the error message as should be expected. This should be a string or Matcher object. + */ + public findError(constructPath: string, message: any): SynthesisMessage[] { + return convertMessagesTypeToArray(findMessage(this._messages, constructPath, constructMessage('error', message)) as Messages); + } + + /** + * Assert that an warning with the given message exists in the synthesized CDK `Stack`. + * + * @param constructPath the construct path to the warning. Provide `'*'` to match all warnings in the template. + * @param message the warning message as should be expected. This should be a string or Matcher object. + */ + public hasWarning(constructPath: string, message: any): void { + const matchError = hasMessage(this._messages, constructPath, constructMessage('warning', message)); + if (matchError) { + throw new Error(matchError); + } + } + + /** + * Get the set of matching warning of a given construct path and message. + * + * @param constructPath the construct path to the warning. Provide `'*'` to match all warnings in the template. + * @param message the warning message as should be expected. This should be a string or Matcher object. + */ + public findWarning(constructPath: string, message: any): SynthesisMessage[] { + return convertMessagesTypeToArray(findMessage(this._messages, constructPath, constructMessage('warning', message)) as Messages); + } + + /** + * Assert that an info with the given message exists in the synthesized CDK `Stack`. + * + * @param constructPath the construct path to the info. Provide `'*'` to match all info in the template. + * @param message the info message as should be expected. This should be a string or Matcher object. + */ + public hasInfo(constructPath: string, message: any): void { + const matchError = hasMessage(this._messages, constructPath, constructMessage('info', message)); + if (matchError) { + throw new Error(matchError); + } + } + + /** + * Get the set of matching infos of a given construct path and message. + * + * @param constructPath the construct path to the info. Provide `'*'` to match all infos in the template. + * @param message the info message as should be expected. This should be a string or Matcher object. + */ + public findInfo(constructPath: string, message: any): SynthesisMessage[] { + return convertMessagesTypeToArray(findMessage(this._messages, constructPath, constructMessage('info', message)) as Messages); + } +} + +function constructMessage(type: 'info' | 'warning' | 'error', message: any): {[key:string]: any } { + return { + level: type, + entry: { + data: message, + }, + }; +} + +function convertArrayToMessagesType(messages: SynthesisMessage[]): Messages { + return messages.reduce((obj, item) => { + return { + ...obj, + [item.id]: item, + }; + }, {}) as Messages; +} + +function convertMessagesTypeToArray(messages: Messages): SynthesisMessage[] { + return Object.values(messages) as SynthesisMessage[]; +} + +function toMessages(stack: Stack): any { + const root = stack.node.root; + if (!Stage.isStage(root)) { + throw new Error('unexpected: all stacks must be part of a Stage or an App'); + } + + // to support incremental assertions (i.e. "expect(stack).toNotContainSomething(); doSomething(); expect(stack).toContainSomthing()") + const force = true; + + const assembly = root.synth({ force }); + + return assembly.getStackArtifact(stack.artifactId).messages; +} diff --git a/packages/@aws-cdk/assertions/lib/index.ts b/packages/@aws-cdk/assertions/lib/index.ts index 492fad1227af3..eccbfac38637f 100644 --- a/packages/@aws-cdk/assertions/lib/index.ts +++ b/packages/@aws-cdk/assertions/lib/index.ts @@ -1,4 +1,5 @@ export * from './capture'; export * from './template'; export * from './match'; -export * from './matcher'; \ No newline at end of file +export * from './matcher'; +export * from './annotations'; \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/lib/private/conditions.ts b/packages/@aws-cdk/assertions/lib/private/conditions.ts index e7c4665dee219..6ed10379dea9e 100644 --- a/packages/@aws-cdk/assertions/lib/private/conditions.ts +++ b/packages/@aws-cdk/assertions/lib/private/conditions.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findConditions(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section: { [key: string] : {} } = template.Conditions; + const section: { [key: string] : {} } = template.Conditions ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findConditions(template: Template, logicalId: string, props: any } export function hasCondition(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string] : {} } = template.Conditions; + const section: { [key: string] : {} } = template.Conditions ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { return; diff --git a/packages/@aws-cdk/assertions/lib/private/cyclic.ts b/packages/@aws-cdk/assertions/lib/private/cyclic.ts new file mode 100644 index 0000000000000..07451a126ad28 --- /dev/null +++ b/packages/@aws-cdk/assertions/lib/private/cyclic.ts @@ -0,0 +1,175 @@ +import { Resource, Template } from './template'; + +/** + * Check a template for cyclic dependencies + * + * This will make sure that we don't happily validate templates + * in unit tests that wouldn't deploy to CloudFormation anyway. + */ +export function checkTemplateForCyclicDependencies(template: Template): void { + const logicalIds = new Set(Object.keys(template.Resources ?? {})); + + const dependencies = new Map>(); + for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) { + dependencies.set(logicalId, intersect(findResourceDependencies(resource), logicalIds)); + } + + // We will now progressively remove entries from the map of 'dependencies' that have + // 0 elements in them. If we can't do that anymore and the map isn't empty, we + // have a cyclic dependency. + while (dependencies.size > 0) { + const free = Array.from(dependencies.entries()).filter(([_, deps]) => deps.size === 0); + if (free.length === 0) { + // Oops! + const cycle = findCycle(dependencies); + + const cycleResources: any = {}; + for (const logicalId of cycle) { + cycleResources[logicalId] = template.Resources?.[logicalId]; + } + + throw new Error(`Template is undeployable, these resources have a dependency cycle: ${cycle.join(' -> ')}:\n\n${JSON.stringify(cycleResources, undefined, 2)}`); + } + + for (const [logicalId, _] of free) { + for (const deps of dependencies.values()) { + deps.delete(logicalId); + } + dependencies.delete(logicalId); + } + } +} + +function findResourceDependencies(res: Resource): Set { + return new Set([ + ...toArray(res.DependsOn ?? []), + ...findExpressionDependencies(res.Properties), + ]); +} + +function toArray(x: A | A[]): A[] { + return Array.isArray(x) ? x : [x]; +} + +function findExpressionDependencies(obj: any): Set { + const ret = new Set(); + recurse(obj); + return ret; + + function recurse(x: any): void { + if (!x) { return; } + if (Array.isArray(x)) { + x.forEach(recurse); + } + if (typeof x === 'object') { + const keys = Object.keys(x); + if (keys.length === 1 && keys[0] === 'Ref') { + ret.add(x[keys[0]]); + } else if (keys.length === 1 && keys[0] === 'Fn::GetAtt') { + ret.add(x[keys[0]][0]); + } else if (keys.length === 1 && keys[0] === 'Fn::Sub') { + const argument = x[keys[0]]; + const pattern = Array.isArray(argument) ? argument[0] : argument; + for (const logId of logicalIdsInSubString(pattern)) { + ret.add(logId); + } + const contextDict = Array.isArray(argument) ? argument[1] : undefined; + if (contextDict) { + Object.values(contextDict).forEach(recurse); + } + } else { + Object.values(x).forEach(recurse); + } + } + } +} + +/** + * Return the logical IDs found in a {Fn::Sub} format string + */ +function logicalIdsInSubString(x: string): string[] { + return analyzeSubPattern(x).flatMap((fragment) => { + switch (fragment.type) { + case 'getatt': + case 'ref': + return [fragment.logicalId]; + case 'literal': + return []; + } + }); +} + + +function analyzeSubPattern(pattern: string): SubFragment[] { + const ret: SubFragment[] = []; + let start = 0; + + let ph0 = pattern.indexOf('${', start); + while (ph0 > -1) { + if (pattern[ph0 + 2] === '!') { + // "${!" means "don't actually substitute" + start = ph0 + 3; + ph0 = pattern.indexOf('${', start); + continue; + } + + const ph1 = pattern.indexOf('}', ph0 + 2); + if (ph1 === -1) { + break; + } + const placeholder = pattern.substring(ph0 + 2, ph1); + + if (ph0 > start) { + ret.push({ type: 'literal', content: pattern.substring(start, ph0) }); + } + if (placeholder.includes('.')) { + const [logicalId, attr] = placeholder.split('.'); + ret.push({ type: 'getatt', logicalId: logicalId!, attr: attr! }); + } else { + ret.push({ type: 'ref', logicalId: placeholder }); + } + + start = ph1 + 1; + ph0 = pattern.indexOf('${', start); + } + + if (start < pattern.length - 1) { + ret.push({ type: 'literal', content: pattern.substr(start) }); + } + + return ret; +} + +type SubFragment = + | { readonly type: 'literal'; readonly content: string } + | { readonly type: 'ref'; readonly logicalId: string } + | { readonly type: 'getatt'; readonly logicalId: string; readonly attr: string }; + + +function intersect(xs: Set, ys: Set): Set { + return new Set(Array.from(xs).filter(x => ys.has(x))); +} + +/** + * Find cycles in a graph + * + * Not the fastest, but effective and should be rare + */ +function findCycle(deps: ReadonlyMap>): string[] { + for (const node of deps.keys()) { + const cycle = recurse(node, [node]); + if (cycle) { return cycle; } + } + throw new Error('No cycle found. Assertion failure!'); + + function recurse(node: string, path: string[]): string[] | undefined { + for (const dep of deps.get(node) ?? []) { + if (dep === path[0]) { return [...path, dep]; } + + const cycle = recurse(dep, [...path, dep]); + if (cycle) { return cycle; } + } + + return undefined; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/lib/private/mappings.ts b/packages/@aws-cdk/assertions/lib/private/mappings.ts index e080843dd87f8..e8788fb2ef112 100644 --- a/packages/@aws-cdk/assertions/lib/private/mappings.ts +++ b/packages/@aws-cdk/assertions/lib/private/mappings.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findMappings(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section: { [key: string] : {} } = template.Mappings; + const section: { [key: string] : {} } = template.Mappings ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findMappings(template: Template, logicalId: string, props: any = } export function hasMapping(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string]: {} } = template.Mappings; + const section: { [key: string]: {} } = template.Mappings ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { diff --git a/packages/@aws-cdk/assertions/lib/private/message.ts b/packages/@aws-cdk/assertions/lib/private/message.ts new file mode 100644 index 0000000000000..9657a5d90ad99 --- /dev/null +++ b/packages/@aws-cdk/assertions/lib/private/message.ts @@ -0,0 +1,5 @@ +import { SynthesisMessage } from '@aws-cdk/cx-api'; + +export type Messages = { + [logicalId: string]: SynthesisMessage; +} diff --git a/packages/@aws-cdk/assertions/lib/private/messages.ts b/packages/@aws-cdk/assertions/lib/private/messages.ts new file mode 100644 index 0000000000000..75c6fe3ae50b1 --- /dev/null +++ b/packages/@aws-cdk/assertions/lib/private/messages.ts @@ -0,0 +1,41 @@ +import { MatchResult } from '../matcher'; +import { Messages } from './message'; +import { filterLogicalId, formatFailure, matchSection } from './section'; + +export function findMessage(messages: Messages, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { + const section: { [key: string]: {} } = messages; + const result = matchSection(filterLogicalId(section, logicalId), props); + + if (!result.match) { + return {}; + } + + return result.matches; +} + +export function hasMessage(messages: Messages, logicalId: string, props: any): string | void { + const section: { [key: string]: {} } = messages; + const result = matchSection(filterLogicalId(section, logicalId), props); + + if (result.match) { + return; + } + + if (result.closestResult === undefined) { + return 'No messages found in the stack'; + } + + return [ + `Stack has ${result.analyzedCount} messages, but none match as expected.`, + formatFailure(formatMessage(result.closestResult)), + ].join('\n'); +} + +// We redact the stack trace by default because it is unnecessarily long and unintelligible. +// If there is a use case for rendering the trace, we can add it later. +function formatMessage(match: MatchResult, renderTrace: boolean = false): MatchResult { + if (!renderTrace) { + match.target.entry.trace = 'redacted'; + } + return match; +} diff --git a/packages/@aws-cdk/assertions/lib/private/outputs.ts b/packages/@aws-cdk/assertions/lib/private/outputs.ts index f00f05bc9bb0f..39509698d0e43 100644 --- a/packages/@aws-cdk/assertions/lib/private/outputs.ts +++ b/packages/@aws-cdk/assertions/lib/private/outputs.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findOutputs(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section = template.Outputs; + const section = template.Outputs ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findOutputs(template: Template, logicalId: string, props: any = } export function hasOutput(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string]: {} } = template.Outputs; + const section: { [key: string]: {} } = template.Outputs ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { return; diff --git a/packages/@aws-cdk/assertions/lib/private/parameters.ts b/packages/@aws-cdk/assertions/lib/private/parameters.ts index b708460caf399..0e73160ea5a75 100644 --- a/packages/@aws-cdk/assertions/lib/private/parameters.ts +++ b/packages/@aws-cdk/assertions/lib/private/parameters.ts @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section'; import { Template } from './template'; export function findParameters(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section: { [key: string] : {} } = template.Parameters; + const section: { [key: string] : {} } = template.Parameters ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (!result.match) { @@ -13,7 +13,7 @@ export function findParameters(template: Template, logicalId: string, props: any } export function hasParameter(template: Template, logicalId: string, props: any): string | void { - const section: { [key: string] : {} } = template.Parameters; + const section: { [key: string] : {} } = template.Parameters ?? {}; const result = matchSection(filterLogicalId(section, logicalId), props); if (result.match) { return; diff --git a/packages/@aws-cdk/assertions/lib/private/resources.ts b/packages/@aws-cdk/assertions/lib/private/resources.ts index 68e8e6c2ddff8..00a57c05f9b26 100644 --- a/packages/@aws-cdk/assertions/lib/private/resources.ts +++ b/packages/@aws-cdk/assertions/lib/private/resources.ts @@ -4,7 +4,7 @@ import { formatFailure, matchSection } from './section'; import { Resource, Template } from './template'; export function findResources(template: Template, type: string, props: any = {}): { [key: string]: { [key: string]: any } } { - const section = template.Resources; + const section = template.Resources ?? {}; const result = matchSection(filterType(section, type), props); if (!result.match) { @@ -15,7 +15,7 @@ export function findResources(template: Template, type: string, props: any = {}) } export function hasResource(template: Template, type: string, props: any): string | void { - const section = template.Resources; + const section = template.Resources ?? {}; const result = matchSection(filterType(section, type), props); if (result.match) { return; @@ -46,14 +46,14 @@ export function hasResourceProperties(template: Template, type: string, props: a } export function countResources(template: Template, type: string): number { - const section = template.Resources; + const section = template.Resources ?? {}; const types = filterType(section, type); return Object.entries(types).length; } function addEmptyProperties(template: Template): Template { - let section = template.Resources; + let section = template.Resources ?? {}; Object.keys(section).map((key) => { if (!section[key].hasOwnProperty('Properties')) { diff --git a/packages/@aws-cdk/assertions/lib/private/template.ts b/packages/@aws-cdk/assertions/lib/private/template.ts index fc5d0cb6b1e01..4aea34a2b5132 100644 --- a/packages/@aws-cdk/assertions/lib/private/template.ts +++ b/packages/@aws-cdk/assertions/lib/private/template.ts @@ -1,15 +1,19 @@ // Partial types for CloudFormation Template export type Template = { - Resources: { [logicalId: string]: Resource }, - Outputs: { [logicalId: string]: Output }, - Mappings: { [logicalId: string]: Mapping }, - Parameters: { [logicalId: string]: Parameter }, - Conditions: { [logicalId: string]: Condition }, + // In actuality this is not optional, but we sometimes don't generate it so we + // need to account for that. + Resources?: { [logicalId: string]: Resource }, + Outputs?: { [logicalId: string]: Output }, + Mappings?: { [logicalId: string]: Mapping }, + Parameters?: { [logicalId: string]: Parameter }, + Conditions?: { [logicalId: string]: Condition }, } export type Resource = { Type: string; + DependsOn?: string | string[]; + Properties?: { [key: string]: any }; [key: string]: any; } diff --git a/packages/@aws-cdk/assertions/lib/template.ts b/packages/@aws-cdk/assertions/lib/template.ts index 8875a91d0ac9c..6399d3a971897 100644 --- a/packages/@aws-cdk/assertions/lib/template.ts +++ b/packages/@aws-cdk/assertions/lib/template.ts @@ -4,6 +4,7 @@ import * as fs from 'fs-extra'; import { Match } from './match'; import { Matcher } from './matcher'; import { findConditions, hasCondition } from './private/conditions'; +import { checkTemplateForCyclicDependencies } from './private/cyclic'; import { findMappings, hasMapping } from './private/mappings'; import { findOutputs, hasOutput } from './private/outputs'; import { findParameters, hasParameter } from './private/parameters'; @@ -47,6 +48,7 @@ export class Template { private constructor(template: { [key: string]: any }) { this.template = template as TemplateType; + checkTemplateForCyclicDependencies(this.template); } /** @@ -129,7 +131,8 @@ export class Template { * @param logicalId the name of the parameter. Provide `'*'` to match all parameters in the template. * @param props by default, matches all Parameters in the template. * When a literal object is provided, performs a partial match via `Match.objectLike()`. - * Use the `Match` APIs to configure a different behaviour. */ + * Use the `Match` APIs to configure a different behaviour. + */ public findParameters(logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } { return findParameters(this.template, logicalId, props); } diff --git a/packages/@aws-cdk/assertions/rewrite.toml b/packages/@aws-cdk/assertions/rewrite.toml new file mode 100644 index 0000000000000..66459d1a6991f --- /dev/null +++ b/packages/@aws-cdk/assertions/rewrite.toml @@ -0,0 +1,115 @@ +# comby -config ~/rewrite.toml -f .ts -d test -in-place -timeout 10 + +[000_import] +match="import '@aws-cdk/assert-internal/jest'" +rewrite="import { Template } from '@aws-cdk/assertions'" + +[000_import2] +match="import :[_] from '@aws-cdk/assert-internal'" +rewrite="import { Template } from '@aws-cdk/assertions'" + +[100_jest_toHaveResourceLike_CompleteDefinition] +match="expect(:[stack]).toHaveResourceLike(:[args], ResourcePart.CompleteDefinition)" +rewrite="Template.fromStack(:[stack]).hasResource(:[args])" + +[100_assert_toHaveResourceLike_CompleteDefinition] +match=":[[expect]](:[stack]).to(haveResourceLike(:[args], ResourcePart.CompleteDefinition))" +rewrite="Template.fromStack(:[stack]).hasResource(:[args])" +rule='''where match :[expect] { + | "expect" -> true + | "cdkExpect" -> true + | ":[_]" -> false +}''' + +[100_jest_toHaveResource_CompleteDefinition] +match="expect(:[stack]).toHaveResource(:[args], ResourcePart.CompleteDefinition)" +rewrite="Template.fromStack(:[stack]).hasResource(:[args])" + +[100_assert_toHaveResource_CompleteDefinition] +match=":[[expect]](:[stack]).to(haveResource(:[args], ResourcePart.CompleteDefinition))" +rewrite="Template.fromStack(:[stack]).hasResource(:[args])" +rule='''where match :[expect] { + | "expect" -> true + | "cdkExpect" -> true + | ":[_]" -> false +}''' + +[200_jest_toHaveResourceLike] +match="expect(:[stack]).toHaveResourceLike(:[args])" +rewrite="Template.fromStack(:[stack]).hasResourceProperties(:[args])" + +[200_assert_toHaveResourceLike] +match=":[[expect]](:[stack]).to(haveResourceLike(:[args]))" +rewrite="Template.fromStack(:[stack]).hasResourceProperties(:[args])" +rule='''where match :[expect] { + | "expect" -> true + | "cdkExpect" -> true + | ":[_]" -> false +}''' + +[200_jest_toHaveResource] +match="expect(:[stack]).toHaveResource(:[args])" +rewrite="Template.fromStack(:[stack]).hasResourceProperties(:[args])" + +[200_assert_toHaveResource] +match=":[[expect]](:[stack]).to(haveResource(:[args]))" +rewrite="Template.fromStack(:[stack]).hasResourceProperties(:[args])" +rule='''where match :[expect] { + | "expect" -> true + | "cdkExpect" -> true + | ":[_]" -> false +}''' + +[200_jest_toCountResources] +match="expect(:[stack]).toCountResources" +rewrite="Template.fromStack(:[stack]).resourceCountIs" + +[200_assert_toCountResources2] +match=":[[expect]](:[stack]).to(countResources(:[args]))" +rewrite="Template.fromStack(:[stack]).resourceCountIs(:[args])" +rule='''where match :[expect] { + | "expect" -> true + | "cdkExpect" -> true + | ":[_]" -> false +}''' + +[200_jest_toMatchTemplate] +match="expect(:[stack]).toMatchTemplate" +rewrite="Template.fromStack(:[stack]).templateMatches" + +[200_assert_toMatchTemplate] +match=":[[expect]](:[stack]).toMatchTemplate" +rewrite="Template.fromStack(:[stack]).templateMatches" +rule='''where match :[expect] { + | "expect" -> true + | "cdkExpect" -> true + | ":[_]" -> false +}''' + +[300_notToHaveResourceLike] +match="expect(:[stack]).not.toHaveResourceLike(:[args])" +rewrite="Template.fromStack(:[stack]).resourceCountIs(:[args], 0)" + +[300_notToHaveResource] +match="expect(:[stack]).not.toHaveResource(:[args])" +rewrite="Template.fromStack(:[stack]).resourceCountIs(:[args], 0)" + +[arrayWith] +match="arrayWith(:[args])" +rewrite="Match.arrayWith([:[args]])" + +[objectLike] +match="objectLike" +rewrite="Match.objectLike" + +[absent] +match="ABSENT" +rewrite="Match.absent()" + +[400_synthutils_template] +match="SynthUtils.synthesize(:[stack]).template" +rewrite="Template.fromStack(:[stack]).toJSON()" + +[401_synthutils_assembly] +match="SynthUtils.synthesize(:[stack])" +rewrite="App.of(:[stack]).synth()" \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/rosetta/default.ts-fixture b/packages/@aws-cdk/assertions/rosetta/default.ts-fixture index fa862da5c609a..32d751f8c38fe 100644 --- a/packages/@aws-cdk/assertions/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/assertions/rosetta/default.ts-fixture @@ -1,7 +1,7 @@ // Fixture with packages imported, but nothing else import { Construct } from 'constructs'; -import { Stack } from '@aws-cdk/core'; -import { Capture, Match, Template } from '@aws-cdk/assertions'; +import { Aspects, CfnResource, Stack } from '@aws-cdk/core'; +import { Annotations, Capture, Match, Template } from '@aws-cdk/assertions'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/assertions/test/annotations.ts b/packages/@aws-cdk/assertions/test/annotations.ts new file mode 100644 index 0000000000000..8275e52d39dff --- /dev/null +++ b/packages/@aws-cdk/assertions/test/annotations.ts @@ -0,0 +1,150 @@ +import { Annotations, Aspects, CfnResource, IAspect, Stack } from '@aws-cdk/core'; +import { IConstruct } from 'constructs'; +import { Annotations as _Annotations, Match } from '../lib'; + +describe('Messages', () => { + let stack: Stack; + let annotations: _Annotations; + beforeAll(() => { + stack = new Stack(); + new CfnResource(stack, 'Foo', { + type: 'Foo::Bar', + properties: { + Fred: 'Thud', + }, + }); + + new CfnResource(stack, 'Bar', { + type: 'Foo::Bar', + properties: { + Baz: 'Qux', + }, + }); + + new CfnResource(stack, 'Fred', { + type: 'Baz::Qux', + properties: { + Foo: 'Bar', + }, + }); + + new CfnResource(stack, 'Qux', { + type: 'Fred::Thud', + properties: { + Fred: 'Bar', + }, + }); + + Aspects.of(stack).add(new MyAspect()); + annotations = _Annotations.fromStack(stack); + }); + + describe('hasError', () => { + test('match', () => { + annotations.hasError('/Default/Foo', 'this is an error'); + }); + + test('no match', () => { + expect(() => annotations.hasError('/Default/Fred', Match.anyValue())) + .toThrowError(/Stack has 1 messages, but none match as expected./); + }); + }); + + describe('findError', () => { + test('match', () => { + const result = annotations.findError('*', Match.anyValue()); + expect(Object.keys(result).length).toEqual(2); + }); + + test('no match', () => { + const result = annotations.findError('*', 'no message looks like this'); + expect(Object.keys(result).length).toEqual(0); + }); + }); + + describe('hasWarning', () => { + test('match', () => { + annotations.hasWarning('/Default/Fred', 'this is a warning'); + }); + + test('no match', () => { + expect(() => annotations.hasWarning('/Default/Foo', Match.anyValue())).toThrowError(/Stack has 1 messages, but none match as expected./); + }); + }); + + describe('findWarning', () => { + test('match', () => { + const result = annotations.findWarning('*', Match.anyValue()); + expect(Object.keys(result).length).toEqual(1); + }); + + test('no match', () => { + const result = annotations.findWarning('*', 'no message looks like this'); + expect(Object.keys(result).length).toEqual(0); + }); + }); + + describe('hasInfo', () => { + test('match', () => { + annotations.hasInfo('/Default/Qux', 'this is an info'); + }); + + test('no match', () => { + expect(() => annotations.hasInfo('/Default/Qux', 'this info is incorrect')).toThrowError(/Stack has 1 messages, but none match as expected./); + }); + }); + + describe('findInfo', () => { + test('match', () => { + const result = annotations.findInfo('/Default/Qux', 'this is an info'); + expect(Object.keys(result).length).toEqual(1); + }); + + test('no match', () => { + const result = annotations.findInfo('*', 'no message looks like this'); + expect(Object.keys(result).length).toEqual(0); + }); + }); + + describe('with matchers', () => { + test('anyValue', () => { + const result = annotations.findError('*', Match.anyValue()); + expect(Object.keys(result).length).toEqual(2); + }); + + test('not', () => { + expect(() => annotations.hasError('/Default/Foo', Match.not('this is an error'))) + .toThrowError(/Found unexpected match: "this is an error" at \/entry\/data/); + }); + + test('stringLikeRegEx', () => { + annotations.hasError('/Default/Foo', Match.stringLikeRegexp('.*error')); + }); + }); +}); + +class MyAspect implements IAspect { + public visit(node: IConstruct): void { + if (node instanceof CfnResource) { + if (node.cfnResourceType === 'Foo::Bar') { + this.error(node, 'this is an error'); + } else if (node.cfnResourceType === 'Baz::Qux') { + this.warn(node, 'this is a warning'); + } else { + this.info(node, 'this is an info'); + } + } + }; + + protected warn(node: IConstruct, message: string): void { + Annotations.of(node).addWarning(message); + } + + protected error(node: IConstruct, message: string): void { + Annotations.of(node).addError(message); + } + + protected info(node: IConstruct, message: string): void { + Annotations.of(node).addInfo(message); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/assertions/test/template.test.ts b/packages/@aws-cdk/assertions/test/template.test.ts index 92bdb405ab9ce..dcdb73e61da71 100644 --- a/packages/@aws-cdk/assertions/test/template.test.ts +++ b/packages/@aws-cdk/assertions/test/template.test.ts @@ -5,11 +5,11 @@ import { Capture, Match, Template } from '../lib'; describe('Template', () => { test('fromString', () => { const template = Template.fromString(`{ - "Resources": { - "Foo": { + "Resources": { + "Foo": { "Type": "Baz::Qux", "Properties": { "Fred": "Waldo" } - } + } } }`); @@ -79,11 +79,11 @@ describe('Template', () => { describe('fromString', () => { test('default', () => { const assertions = Template.fromString(`{ - "Resources": { - "Foo": { + "Resources": { + "Foo": { "Type": "Baz::Qux", "Properties": { "Fred": "Waldo" } - } + } } }`); assertions.resourceCountIs('Baz::Qux', 1); @@ -1084,6 +1084,25 @@ describe('Template', () => { expect(Object.keys(result).length).toEqual(0); }); }); + + test('throws when given a template with cyclic dependencies', () => { + expect(() => { + Template.fromJSON({ + Resources: { + Res1: { + Type: 'Foo', + Properties: { + Thing: { Ref: 'Res2' }, + }, + }, + Res2: { + Type: 'Foo', + DependsOn: ['Res1'], + }, + }, + }); + }).toThrow(/dependency cycle/); + }); }); function expectToThrow(fn: () => void, msgs: (RegExp | string)[], done: jest.DoneCallback): void { diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index 7bac0d2b66d81..13efc124c74d0 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-accessanalyzer/package.json b/packages/@aws-cdk/aws-accessanalyzer/package.json index ae74b6abb8130..faf809becc93b 100644 --- a/packages/@aws-cdk/aws-accessanalyzer/package.json +++ b/packages/@aws-cdk/aws-accessanalyzer/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-acmpca/README.md b/packages/@aws-cdk/aws-acmpca/README.md index 66f15be5be271..d6f2c754f808e 100644 --- a/packages/@aws-cdk/aws-acmpca/README.md +++ b/packages/@aws-cdk/aws-acmpca/README.md @@ -14,7 +14,7 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. -```ts +```ts nofixture import * as acmpca from '@aws-cdk/aws-acmpca'; ``` @@ -62,6 +62,8 @@ If you need to pass the higher-level `ICertificateAuthority` somewhere, you can get it from the lower-level `CfnCertificateAuthority` using the same `fromCertificateAuthorityArn` method: ```ts +declare const cfnCertificateAuthority: acmpca.CfnCertificateAuthority; + const certificateAuthority = acmpca.CertificateAuthority.fromCertificateAuthorityArn(this, 'CertificateAuthority', cfnCertificateAuthority.attrArn); ``` diff --git a/packages/@aws-cdk/aws-acmpca/package.json b/packages/@aws-cdk/aws-acmpca/package.json index 360332a20d54e..a4a7a79a1e8e6 100644 --- a/packages/@aws-cdk/aws-acmpca/package.json +++ b/packages/@aws-cdk/aws-acmpca/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-acmpca/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-acmpca/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..16526213b2c62 --- /dev/null +++ b/packages/@aws-cdk/aws-acmpca/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as acmpca from '@aws-cdk/aws-acmpca'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-amazonmq/package.json b/packages/@aws-cdk/aws-amazonmq/package.json index 4d98d24d7ee73..c7050a78a44e4 100644 --- a/packages/@aws-cdk/aws-amazonmq/package.json +++ b/packages/@aws-cdk/aws-amazonmq/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-amplify/README.md b/packages/@aws-cdk/aws-amplify/README.md index a08563de5dfb1..fffaf2ac97bcf 100644 --- a/packages/@aws-cdk/aws-amplify/README.md +++ b/packages/@aws-cdk/aws-amplify/README.md @@ -29,37 +29,36 @@ To set up an Amplify Console app, define an `App`: ```ts import * as codebuild from '@aws-cdk/aws-codebuild'; -import * as amplify from '@aws-cdk/aws-amplify'; -import * as cdk from '@aws-cdk/core'; const amplifyApp = new amplify.App(this, 'MyApp', { sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ owner: '', repository: '', - oauthToken: cdk.SecretValue.secretsManager('my-github-token') + oauthToken: SecretValue.secretsManager('my-github-token'), }), - buildSpec: codebuild.BuildSpec.fromObjectToYaml({ // Alternatively add a `amplify.yml` to the repo + buildSpec: codebuild.BuildSpec.fromObjectToYaml({ + // Alternatively add a `amplify.yml` to the repo version: '1.0', frontend: { phases: { preBuild: { commands: [ - 'yarn' - ] + 'yarn', + ], }, build: { commands: [ - 'yarn build' - ] - } + 'yarn build', + ], + }, }, artifacts: { baseDirectory: 'public', files: - - '**/*' - } - } - }) + - '**/*', + }, + }, + }), }); ``` @@ -70,20 +69,22 @@ const amplifyApp = new amplify.App(this, 'MyApp', { sourceCodeProvider: new amplify.GitLabSourceCodeProvider({ owner: '', repository: '', - oauthToken: cdk.SecretValue.secretsManager('my-gitlab-token') - }) + oauthToken: SecretValue.secretsManager('my-gitlab-token'), + }), }); ``` To connect your `App` to CodeCommit, use the `CodeCommitSourceCodeProvider`: ```ts +import * as codecommit from '@aws-cdk/aws-codecommit'; + const repository = new codecommit.Repository(this, 'Repo', { - repositoryName: 'my-repo' + repositoryName: 'my-repo', }); const amplifyApp = new amplify.App(this, 'App', { - sourceCodeProvider: new amplify.CodeCommitSourceCodeProvider({ repository }) + sourceCodeProvider: new amplify.CodeCommitSourceCodeProvider({ repository }), }); ``` @@ -93,6 +94,8 @@ to pull the CodeCommit repository. Add branches: ```ts +declare const amplifyApp: amplify.App; + const master = amplifyApp.addBranch('master'); // `id` will be used as repo branch name const dev = amplifyApp.addBranch('dev', { performanceMode: true, // optional, enables performance mode @@ -105,10 +108,11 @@ Auto build and pull request preview are enabled by default. Add custom rules for redirection: ```ts +declare const amplifyApp: amplify.App; amplifyApp.addCustomRule({ source: '/docs/specific-filename.html', target: '/documents/different-filename.html', - status: amplify.RedirectStatus.TEMPORARY_REDIRECT + status: amplify.RedirectStatus.TEMPORARY_REDIRECT, }); ``` @@ -119,12 +123,18 @@ file extensions: css, gif, ico, jpg, js, png, txt, svg, woff, ttf, map, json, webmanifest. ```ts +declare const mySinglePageApp: amplify.App; + mySinglePageApp.addCustomRule(amplify.CustomRule.SINGLE_PAGE_APPLICATION_REDIRECT); ``` Add a domain and map sub domains to branches: ```ts +declare const amplifyApp: amplify.App; +declare const master: amplify.Branch; +declare const dev: amplify.Branch; + const domain = amplifyApp.addDomain('example.com', { enableAutoSubdomain: true, // in case subdomains should be auto registered for branches autoSubdomainCreationPatterns: ['*', 'pr*'], // regex for branches that should auto register subdomains @@ -142,9 +152,12 @@ Use `BasicAuth.fromCredentials` when referencing an existing secret: ```ts const amplifyApp = new amplify.App(this, 'MyApp', { - repository: 'https://github.com//', - oauthToken: cdk.SecretValue.secretsManager('my-github-token'), - basicAuth: amplify.BasicAuth.fromCredentials('username', cdk.SecretValue.secretsManager('my-github-token')) + sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ + owner: '', + repository: '', + oauthToken: SecretValue.secretsManager('my-github-token'), + }), + basicAuth: amplify.BasicAuth.fromCredentials('username', SecretValue.secretsManager('my-github-token')), }); ``` @@ -152,17 +165,21 @@ Use `BasicAuth.fromGeneratedPassword` to generate a password in Secrets Manager: ```ts const amplifyApp = new amplify.App(this, 'MyApp', { - repository: 'https://github.com//', - oauthToken: cdk.SecretValue.secretsManager('my-github-token'), - basicAuth: amplify.BasicAuth.fromGeneratedPassword('username') + sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ + owner: '', + repository: '', + oauthToken: SecretValue.secretsManager('my-github-token'), + }), + basicAuth: amplify.BasicAuth.fromGeneratedPassword('username'), }); ``` Basic auth can be added to specific branches: ```ts -app.addBranch('feature/next', { - basicAuth: amplify.BasicAuth.fromGeneratedPassword('username') +declare const amplifyApp: amplify.App; +amplifyApp.addBranch('feature/next', { + basicAuth: amplify.BasicAuth.fromGeneratedPassword('username'), }); ``` @@ -173,11 +190,14 @@ of branches: ```ts const amplifyApp = new amplify.App(this, 'MyApp', { - repository: 'https://github.com//', - oauthToken: cdk.SecretValue.secretsManager('my-github-token'), + sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ + owner: '', + repository: '', + oauthToken: SecretValue.secretsManager('my-github-token'), + }), autoBranchCreation: { // Automatically connect branches that match a pattern set - patterns: ['feature/*', 'test/*'] - } + patterns: ['feature/*', 'test/*'], + }, autoBranchDeletion: true, // Automatically disconnect a branch when you delete a branch from your repository }); ``` @@ -187,11 +207,11 @@ const amplifyApp = new amplify.App(this, 'MyApp', { Use the `customResponseHeaders` prop to configure custom response headers for an Amplify app: ```ts -const amplifyApp = new amplify.App(stack, 'App', { +const amplifyApp = new amplify.App(this, 'App', { sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ owner: '', repository: '', - oauthToken: cdk.SecretValue.secretsManager('my-github-token') + oauthToken: SecretValue.secretsManager('my-github-token'), }), customResponseHeaders: [ { @@ -216,7 +236,9 @@ const amplifyApp = new amplify.App(stack, 'App', { `sourceCodeProvider` is optional; when this is not specified the Amplify app can be deployed to using `.zip` packages. The `asset` property can be used to deploy S3 assets to Amplify as part of the CDK: ```ts -const asset = new assets.Asset(this, "SampleAsset", {}); -const amplifyApp = new amplify.App(this, 'MyApp', {}); +import * as assets from '@aws-cdk/aws-s3-assets'; + +declare const asset: assets.Asset; +declare const amplifyApp: amplify.App; const branch = amplifyApp.addBranch("dev", { asset: asset }); ``` diff --git a/packages/@aws-cdk/aws-amplify/lib/app.ts b/packages/@aws-cdk/aws-amplify/lib/app.ts index 030dc58059a6e..bedec6e9e58ac 100644 --- a/packages/@aws-cdk/aws-amplify/lib/app.ts +++ b/packages/@aws-cdk/aws-amplify/lib/app.ts @@ -28,7 +28,7 @@ export interface SourceCodeProviderConfig { /** * The repository for the application. Must use the `HTTPS` protocol. * - * @example https://github.com/aws/aws-cdk + * For example, `https://github.com/aws/aws-cdk`. */ readonly repository: string; diff --git a/packages/@aws-cdk/aws-amplify/package.json b/packages/@aws-cdk/aws-amplify/package.json index 7360eadcffaf4..e546c68ed28c4 100644 --- a/packages/@aws-cdk/aws-amplify/package.json +++ b/packages/@aws-cdk/aws-amplify/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-amplify/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-amplify/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..922b7902f805d --- /dev/null +++ b/packages/@aws-cdk/aws-amplify/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { SecretValue, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as amplify from '@aws-cdk/aws-amplify'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index dcab23ccc050e..cff64a652688b 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-apigateway/test/access-log.test.ts b/packages/@aws-cdk/aws-apigateway/test/access-log.test.ts index 6509cc24b614b..3262207317c38 100644 --- a/packages/@aws-cdk/aws-apigateway/test/access-log.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/access-log.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import * as apigateway from '../lib'; describe('access log', () => { diff --git a/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts b/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts index c4af056038451..a74b3664dae26 100644 --- a/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/api-definition.test.ts @@ -1,6 +1,5 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as path from 'path'; -import { ResourcePart } from '@aws-cdk/assert-internal'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; @@ -84,12 +83,12 @@ describe('api definition', () => { apiDefinition: assetApiDefinition, }); - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResource('AWS::ApiGateway::RestApi', { Metadata: { 'aws:asset:path': 'asset.68497ac876de4e963fc8f7b5f1b28844c18ecc95e3f7c6e9e0bf250e03c037fb.yaml', 'aws:asset:property': 'BodyS3Location', }, - }, ResourcePart.CompleteDefinition); + }); }); }); diff --git a/packages/@aws-cdk/aws-apigateway/test/api-key.test.ts b/packages/@aws-cdk/aws-apigateway/test/api-key.test.ts index 3008568ce2de4..46620128f7977 100644 --- a/packages/@aws-cdk/aws-apigateway/test/api-key.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/api-key.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as apigateway from '../lib'; @@ -13,8 +12,9 @@ describe('api key', () => { new apigateway.ApiKey(stack, 'my-api-key'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', undefined, ResourcePart.CompleteDefinition); - // should have an api key with no props defined. + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { + Enabled: true, + }); }); @@ -29,7 +29,7 @@ describe('api key', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { Enabled: false, Value: 'arandomstringwithmorethantwentycharacters', }); @@ -53,7 +53,7 @@ describe('api key', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { CustomerId: 'test-customer', StageKeys: [ { @@ -76,7 +76,7 @@ describe('api key', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { Description: 'The most secret api key', }); }); @@ -97,7 +97,7 @@ describe('api key', () => { usagePlan.addApiKey(importedKey); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::UsagePlanKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlanKey', { KeyId: 'KeyIdabc', KeyType: 'API_KEY', UsagePlanId: { @@ -125,7 +125,7 @@ describe('api key', () => { apiKey.grantRead(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -176,7 +176,7 @@ describe('api key', () => { apiKey.grantWrite(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -232,7 +232,7 @@ describe('api key', () => { apiKey.grantReadWrite(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -282,11 +282,11 @@ describe('api key', () => { // THEN // should have an api key with no props defined. - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', undefined, ResourcePart.CompleteDefinition); + Template.fromStack(stack).hasResource('AWS::ApiGateway::ApiKey', Match.anyValue()); // should not have a usage plan. - expect(stack).not.toHaveResource('AWS::ApiGateway::UsagePlan'); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::UsagePlan', 0); // should not have a usage plan key. - expect(stack).not.toHaveResource('AWS::ApiGateway::UsagePlanKey'); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::UsagePlanKey', 0); }); test('only api key is created when rate limiting properties are not provided', () => { @@ -306,7 +306,7 @@ describe('api key', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { CustomerId: 'test-customer', StageKeys: [ { @@ -316,9 +316,9 @@ describe('api key', () => { ], }); // should not have a usage plan. - expect(stack).not.toHaveResource('AWS::ApiGateway::UsagePlan'); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::UsagePlan', 0); // should not have a usage plan key. - expect(stack).not.toHaveResource('AWS::ApiGateway::UsagePlanKey'); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::UsagePlanKey', 0); }); test('api key and usage plan are created and linked when rate limiting properties are provided', () => { @@ -343,7 +343,7 @@ describe('api key', () => { // THEN // should have an api key - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { CustomerId: 'test-customer', StageKeys: [ { @@ -353,14 +353,14 @@ describe('api key', () => { ], }); // should have a usage plan with specified quota. - expect(stack).toHaveResource('AWS::ApiGateway::UsagePlan', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlan', { Quota: { Limit: 10000, Period: 'MONTH', }, - }, ResourcePart.Properties); + }); // should have a usage plan key linking the api key and usage plan - expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlanKey', { KeyId: { Ref: 'testapikey998028B6', }, @@ -368,7 +368,7 @@ describe('api key', () => { UsagePlanId: { Ref: 'testapikeyUsagePlanResource66DB63D6', }, - }, ResourcePart.Properties); + }); }); }); }); diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/cognito.test.ts b/packages/@aws-cdk/aws-apigateway/test/authorizers/cognito.test.ts index 1956c69dbb149..906f772a8505b 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/cognito.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/cognito.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cognito from '@aws-cdk/aws-cognito'; import { Duration, Stack } from '@aws-cdk/core'; import { AuthorizationType, CognitoUserPoolsAuthorizer, RestApi } from '../../lib'; @@ -21,7 +21,7 @@ describe('Cognito Authorizer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'COGNITO_USER_POOLS', RestApiId: stack.resolve(restApi.restApiId), IdentitySource: 'method.request.header.Authorization', @@ -52,7 +52,7 @@ describe('Cognito Authorizer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'COGNITO_USER_POOLS', Name: 'myauthorizer', RestApiId: stack.resolve(restApi.restApiId), diff --git a/packages/@aws-cdk/aws-apigateway/test/authorizers/lambda.test.ts b/packages/@aws-cdk/aws-apigateway/test/authorizers/lambda.test.ts index 6728e0204ed37..a4eea0f56892d 100644 --- a/packages/@aws-cdk/aws-apigateway/test/authorizers/lambda.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/authorizers/lambda.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Stack } from '@aws-cdk/core'; @@ -25,7 +24,7 @@ describe('lambda authorizer', () => { authorizationType: AuthorizationType.CUSTOM, }); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'TOKEN', RestApiId: stack.resolve(restApi.restApiId), IdentitySource: 'method.request.header.Authorization', @@ -71,7 +70,7 @@ describe('lambda authorizer', () => { }, }); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', Principal: 'apigateway.amazonaws.com', }); @@ -100,7 +99,7 @@ describe('lambda authorizer', () => { authorizationType: AuthorizationType.CUSTOM, }); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'REQUEST', RestApiId: stack.resolve(restApi.restApiId), AuthorizerUri: { @@ -145,7 +144,7 @@ describe('lambda authorizer', () => { }, }); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', Principal: 'apigateway.amazonaws.com', }); @@ -194,7 +193,7 @@ describe('lambda authorizer', () => { authorizationType: AuthorizationType.CUSTOM, }); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'TOKEN', RestApiId: stack.resolve(restApi.restApiId), IdentitySource: 'method.request.header.whoami', @@ -266,7 +265,7 @@ describe('lambda authorizer', () => { authorizationType: AuthorizationType.CUSTOM, }); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'REQUEST', RestApiId: stack.resolve(restApi.restApiId), IdentitySource: 'method.request.header.whoami', @@ -340,7 +339,7 @@ describe('lambda authorizer', () => { authorizationType: AuthorizationType.CUSTOM, }); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'TOKEN', RestApiId: stack.resolve(restApi.restApiId), AuthorizerUri: { @@ -385,9 +384,9 @@ describe('lambda authorizer', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Role'); + Template.fromStack(stack).hasResource('AWS::IAM::Role', Match.anyValue()); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { Roles: [ stack.resolve(role.roleName), ], @@ -400,9 +399,9 @@ describe('lambda authorizer', () => { }, ], }, - }, ResourcePart.Properties); + }); - expect(stack).not.toHaveResource('AWS::Lambda::Permission'); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Permission', 0); }); test('request authorizer with assume role', () => { @@ -432,7 +431,7 @@ describe('lambda authorizer', () => { authorizationType: AuthorizationType.CUSTOM, }); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Type: 'REQUEST', RestApiId: stack.resolve(restApi.restApiId), AuthorizerUri: { @@ -477,9 +476,9 @@ describe('lambda authorizer', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Role'); + Template.fromStack(stack).hasResource('AWS::IAM::Role', Match.anyValue()); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { Roles: [ stack.resolve(role.roleName), ], @@ -492,9 +491,9 @@ describe('lambda authorizer', () => { }, ], }, - }, ResourcePart.Properties); + }); - expect(stack).not.toHaveResource('AWS::Lambda::Permission'); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Permission', 0); }); test('token authorizer throws when not attached to a rest api', () => { diff --git a/packages/@aws-cdk/aws-apigateway/test/base-path-mapping.test.ts b/packages/@aws-cdk/aws-apigateway/test/base-path-mapping.test.ts index 6e57ce68d1394..a886b3c957dad 100644 --- a/packages/@aws-cdk/aws-apigateway/test/base-path-mapping.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/base-path-mapping.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as acm from '@aws-cdk/aws-certificatemanager'; import * as cdk from '@aws-cdk/core'; import * as apigw from '../lib'; @@ -22,7 +22,7 @@ describe('BasePathMapping', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { DomainName: { Ref: 'MyDomainE4943FBC' }, RestApiId: { Ref: 'MyApi49610EDF' }, }); @@ -47,7 +47,7 @@ describe('BasePathMapping', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { BasePath: 'My_B45E-P4th', }); }); @@ -100,7 +100,7 @@ describe('BasePathMapping', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { Stage: { Ref: 'MyStage572B0482' }, }); }); diff --git a/packages/@aws-cdk/aws-apigateway/test/cors.test.ts b/packages/@aws-cdk/aws-apigateway/test/cors.test.ts index 42879b562df18..581b8af3e6c53 100644 --- a/packages/@aws-cdk/aws-apigateway/test/cors.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/cors.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Stack } from '@aws-cdk/core'; import * as apigw from '../lib'; @@ -16,7 +16,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -63,7 +63,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -112,7 +112,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -159,7 +159,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -219,7 +219,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -277,7 +277,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -327,7 +327,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -376,7 +376,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -439,7 +439,7 @@ describe('cors', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, Integration: { @@ -489,12 +489,12 @@ describe('cors', () => { resource.addResource('MyChildResource'); // THEN - expect(stack).toCountResources('AWS::ApiGateway::Method', 2); // on both resources - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::Method', 2); // on both resources + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceD5CDB490' }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apiMyResourceMyChildResource2DC010C5' }, }); @@ -515,15 +515,15 @@ describe('cors', () => { child1.addResource('child2'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { 'Fn::GetAtt': ['apiC8550315', 'RootResourceId'] }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apichild1841A5840' }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'apichild1child26A9A7C47' }, }); @@ -548,7 +548,7 @@ describe('cors', () => { }); // THENB - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ResourceId: { Ref: 'apiAllowAll2F5BC564', }, @@ -579,7 +579,7 @@ describe('cors', () => { }, ], }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ResourceId: { Ref: 'apiAllowSpecific77DD8AF1', }, @@ -646,8 +646,8 @@ describe('cors', () => { }); // THEN - expect(stack).toCountResources('AWS::ApiGateway::Method', 4); // two ANY and two OPTIONS resources - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::Method', 4); // two ANY and two OPTIONS resources + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { 'Fn::GetAtt': [ @@ -656,7 +656,7 @@ describe('cors', () => { ], }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'lambdarestapiproxyE3AE07E3', @@ -676,7 +676,7 @@ describe('cors', () => { api.root.addProxy(); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', }); }); diff --git a/packages/@aws-cdk/aws-apigateway/test/deployment.test.ts b/packages/@aws-cdk/aws-apigateway/test/deployment.test.ts index 3f431bfb29f2a..ef537a848c070 100644 --- a/packages/@aws-cdk/aws-apigateway/test/deployment.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/deployment.test.ts @@ -1,6 +1,5 @@ import * as path from 'path'; -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart, SynthUtils } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import { CfnResource, Lazy, Stack } from '@aws-cdk/core'; import * as apigateway from '../lib'; @@ -16,7 +15,7 @@ describe('deployment', () => { new apigateway.Deployment(stack, 'deployment', { api }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { apiGETECF0BD67: { Type: 'AWS::ApiGateway::Method', @@ -68,7 +67,7 @@ describe('deployment', () => { new apigateway.Deployment(stack, 'deployment', { api, retainDeployments: true }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { apiGETECF0BD67: { Type: 'AWS::ApiGateway::Method', @@ -122,39 +121,54 @@ describe('deployment', () => { new apigateway.Deployment(stack, 'deployment', { api, description: 'this is my deployment' }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Deployment', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Deployment', { Description: 'this is my deployment', }); }); - test('logical ID of the deployment resource is salted', () => { - // GIVEN - const stack = new Stack(); - const api = new apigateway.RestApi(stack, 'api', { deploy: false, cloudWatchRole: false }); - const deployment = new apigateway.Deployment(stack, 'deployment', { api }); - api.root.addMethod('GET'); + describe('logical ID of the deployment resource is salted', () => { + test('before salting', () => { + // GIVEN + const stack = new Stack(); + const api = new apigateway.RestApi(stack, 'api', { deploy: false, cloudWatchRole: false }); + new apigateway.Deployment(stack, 'deployment', { api }); + api.root.addMethod('GET'); + + const resources = Template.fromStack(stack).findResources('AWS::ApiGateway::Deployment'); + expect(resources.deployment33381975bba46c5132329b81e7befcbbba5a0e75).toBeDefined(); + }); + + test('after salting with a resolved value', () => { + const stack = new Stack(); + const api = new apigateway.RestApi(stack, 'api', { deploy: false, cloudWatchRole: false }); + const deployment = new apigateway.Deployment(stack, 'deployment', { api }); + api.root.addMethod('GET'); - const resources = synthesize().Resources; - expect(resources.deployment33381975bba46c5132329b81e7befcbbba5a0e75).toBeDefined(); + // adding some salt + deployment.addToLogicalId({ foo: 123 }); // add some data to the logical ID - // adding some salt - deployment.addToLogicalId({ foo: 123 }); // add some data to the logical ID + // the logical ID changed + const template = Template.fromStack(stack).findResources('AWS::ApiGateway::Deployment'); + expect(template.deployment33381975bba46c5132329b81e7befcbbba5a0e75).toBeUndefined(); + expect(template.deployment333819758aa4cdb9d204502b959c4903f4d5d29f).toBeDefined(); + }); - // the logical ID changed - const template = synthesize(); - expect(template.Resources.deployment33381975bba46c5132329b81e7befcbbba5a0e75).toBeUndefined(); - expect(template.Resources.deployment333819758aa4cdb9d204502b959c4903f4d5d29f).toBeDefined(); + test('after salting with a resolved value and a token', () => { + const stack = new Stack(); + const api = new apigateway.RestApi(stack, 'api', { deploy: false, cloudWatchRole: false }); + const deployment = new apigateway.Deployment(stack, 'deployment', { api }); + api.root.addMethod('GET'); - // tokens supported, and are resolved upon synthesis - const value = 'hello hello'; - deployment.addToLogicalId({ foo: Lazy.string({ produce: () => value }) }); + // adding some salt + deployment.addToLogicalId({ foo: 123 }); // add some data to the logical ID - const template2 = synthesize(); - expect(template2.Resources.deployment333819758d91bed959c6bd6268ba84f6d33e888e).toBeDefined(); + // tokens supported, and are resolved upon synthesis + const value = 'hello hello'; + deployment.addToLogicalId({ foo: Lazy.string({ produce: () => value }) }); - function synthesize() { - return SynthUtils.synthesize(stack).template; - } + const template = Template.fromStack(stack).findResources('AWS::ApiGateway::Deployment'); + expect(template.deployment333819758d91bed959c6bd6268ba84f6d33e888e).toBeDefined(); + }); }); test('"addDependency" can be used to add a resource as a dependency', () => { @@ -169,12 +183,12 @@ describe('deployment', () => { // WHEN deployment.node.addDependency(dep); - expect(stack).toHaveResource('AWS::ApiGateway::Deployment', { + Template.fromStack(stack).hasResource('AWS::ApiGateway::Deployment', { DependsOn: [ 'apiGETECF0BD67', 'MyResource', ], - }, ResourcePart.CompleteDefinition); + }); }); @@ -205,10 +219,10 @@ describe('deployment', () => { api2.root.addMethod('GET'); // THEN - expect(stack1).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack1).hasResourceProperties('AWS::ApiGateway::Stage', { DeploymentId: { Ref: 'myapiDeploymentB7EF8EB74c5295c27fa87ff13f4d04e13f67662d' }, }); - expect(stack2).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack2).hasResourceProperties('AWS::ApiGateway::Stage', { DeploymentId: { Ref: 'myapiDeploymentB7EF8EB7b50d305057ba109c118e4aafd4509355' }, }); @@ -233,12 +247,12 @@ describe('deployment', () => { const resource = restapi.root.addResource('myresource'); resource.addMethod('GET'); - expect(stack).toHaveResource('AWS::ApiGateway::Deployment', { + Template.fromStack(stack).hasResource('AWS::ApiGateway::Deployment', { DependsOn: [ 'myapiGET9B7CD29E', 'myapimyresourceGET732851A5', 'myapiPOST23417BD2', ], - }, ResourcePart.CompleteDefinition); + }); }); }); diff --git a/packages/@aws-cdk/aws-apigateway/test/domains.test.ts b/packages/@aws-cdk/aws-apigateway/test/domains.test.ts index 31c553ad9c973..7e054c83d7102 100644 --- a/packages/@aws-cdk/aws-apigateway/test/domains.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/domains.test.ts @@ -1,5 +1,4 @@ -import { ABSENT } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as acm from '@aws-cdk/aws-certificatemanager'; import { Bucket } from '@aws-cdk/aws-s3'; import { Stack } from '@aws-cdk/core'; @@ -27,13 +26,13 @@ describe('domains', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': { 'Ref': 'Cert5C9FAEC1' }, }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'example.com', 'EndpointConfiguration': { 'Types': ['EDGE'] }, 'CertificateArn': { 'Ref': 'Cert5C9FAEC1' }, @@ -57,7 +56,7 @@ describe('domains', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': { 'Ref': 'Cert5C9FAEC1' }, @@ -88,25 +87,25 @@ describe('domains', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'old.example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': { 'Ref': 'Cert5C9FAEC1' }, 'SecurityPolicy': 'TLS_1_0', }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'new.example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': { 'Ref': 'Cert5C9FAEC1' }, 'SecurityPolicy': 'TLS_1_2', }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'default.example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': { 'Ref': 'Cert5C9FAEC1' }, - 'SecurityPolicy': ABSENT, + 'SecurityPolicy': Match.absent(), }); }); @@ -125,7 +124,7 @@ describe('domains', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'Domain66AC69E0', }, @@ -156,7 +155,7 @@ describe('domains', () => { domain.addBasePathMapping(api2, { basePath: 'api2' }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'mydomain592C948B', }, @@ -169,7 +168,7 @@ describe('domains', () => { }, }); - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'mydomain592C948B', }, @@ -197,7 +196,7 @@ describe('domains', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'my.domain.com', 'EndpointConfiguration': { 'Types': [ @@ -208,7 +207,7 @@ describe('domains', () => { 'Ref': 'cert56CA94EB', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'apiCustomDomain64773C4F', }, @@ -235,7 +234,7 @@ describe('domains', () => { api.addDomainName('domainId', { domainName, certificate }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': domainName, 'EndpointConfiguration': { 'Types': [ @@ -246,7 +245,7 @@ describe('domains', () => { 'Ref': 'cert56CA94EB', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'apidomainId102F8DAA', }, @@ -274,7 +273,7 @@ describe('domains', () => { api.addDomainName('domainId', { domainName, certificate, basePath }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'BasePath': 'users', 'RestApiId': { 'Ref': 'apiC8550315', @@ -300,13 +299,13 @@ describe('domains', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'BasePath': 'users', 'RestApiId': { 'Ref': 'apiC8550315', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'BasePath': 'books', 'RestApiId': { 'Ref': 'apiC8550315', @@ -361,7 +360,7 @@ describe('domains', () => { expect(api.domainName).toEqual(domainName1); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'my.domain.com', 'EndpointConfiguration': { 'Types': [ @@ -372,7 +371,7 @@ describe('domains', () => { 'Ref': 'cert56CA94EB', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'your.domain.com', 'EndpointConfiguration': { 'Types': [ @@ -383,7 +382,7 @@ describe('domains', () => { 'Ref': 'cert56CA94EB', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'our.domain.com', 'EndpointConfiguration': { 'Types': [ @@ -394,7 +393,7 @@ describe('domains', () => { 'Ref': 'cert56CA94EB', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'apidomainId102F8DAA', }, @@ -433,7 +432,7 @@ describe('domains', () => { domain.addBasePathMapping(api2, { basePath: 'api2' }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'mydomain592C948B', }, @@ -444,7 +443,7 @@ describe('domains', () => { 'Stage': stack.resolve(testStage.stageName), }); - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'mydomain592C948B', }, @@ -471,7 +470,7 @@ describe('domains', () => { certificate: acm.Certificate.fromCertificateArn(stack, 'cert', 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d'), }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d', @@ -492,7 +491,7 @@ describe('domains', () => { version: 'version', }, }); - expect(stack).toHaveResource('AWS::ApiGateway::DomainName', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::DomainName', { 'DomainName': 'example.com', 'EndpointConfiguration': { 'Types': ['REGIONAL'] }, 'RegionalCertificateArn': 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d', @@ -512,7 +511,7 @@ describe('domains', () => { }).root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'restApiWithStageCustomDomainC4749625', }, @@ -543,7 +542,7 @@ describe('domains', () => { }).root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::BasePathMapping', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::BasePathMapping', { 'DomainName': { 'Ref': 'specRestApiWithStageCustomDomain8A36A5C9', }, diff --git a/packages/@aws-cdk/aws-apigateway/test/gateway-response.test.ts b/packages/@aws-cdk/aws-apigateway/test/gateway-response.test.ts index 7a02ca17b6ca0..80093aba04de1 100644 --- a/packages/@aws-cdk/aws-apigateway/test/gateway-response.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/gateway-response.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { ResponseType, RestApi } from '../lib'; @@ -20,12 +19,12 @@ describe('gateway response', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::GatewayResponse', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::GatewayResponse', { ResponseType: 'ACCESS_DENIED', RestApiId: stack.resolve(api.restApiId), - StatusCode: ABSENT, - ResponseParameters: ABSENT, - ResponseTemplates: ABSENT, + StatusCode: Match.absent(), + ResponseParameters: Match.absent(), + ResponseTemplates: Match.absent(), }); }); @@ -50,7 +49,7 @@ describe('gateway response', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::GatewayResponse', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::GatewayResponse', { ResponseType: 'AUTHORIZER_FAILURE', RestApiId: stack.resolve(api.restApiId), StatusCode: '500', @@ -58,7 +57,7 @@ describe('gateway response', () => { 'gatewayresponse.header.Access-Control-Allow-Origin': 'test.com', 'gatewayresponse.header.test-key': 'test-value', }, - ResponseTemplates: ABSENT, + ResponseTemplates: Match.absent(), }); }); @@ -82,11 +81,11 @@ describe('gateway response', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::GatewayResponse', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::GatewayResponse', { ResponseType: 'AUTHORIZER_FAILURE', RestApiId: stack.resolve(api.restApiId), StatusCode: '500', - ResponseParameters: ABSENT, + ResponseParameters: Match.absent(), ResponseTemplates: { 'application/json': '{ "message": $context.error.messageString, "statusCode": "488" }', }, diff --git a/packages/@aws-cdk/aws-apigateway/test/http.test.ts b/packages/@aws-cdk/aws-apigateway/test/http.test.ts index 15714ca988087..4d687da1546b7 100644 --- a/packages/@aws-cdk/aws-apigateway/test/http.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/http.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as apigateway from '../lib'; @@ -14,7 +14,7 @@ describe('http integration', () => { api.root.addMethod('GET', integ); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { IntegrationHttpMethod: 'GET', Type: 'HTTP_PROXY', @@ -40,7 +40,7 @@ describe('http integration', () => { api.root.addMethod('GET', integ); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { CacheNamespace: 'hey', IntegrationHttpMethod: 'POST', diff --git a/packages/@aws-cdk/aws-apigateway/test/integration.test.ts b/packages/@aws-cdk/aws-apigateway/test/integration.test.ts index f89a1fdf042ed..0ae46986bcc93 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integration.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integration.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as iam from '@aws-cdk/aws-iam'; @@ -52,7 +51,7 @@ describe('integration', () => { }); api.root.addMethod('GET', integration); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { Uri: { 'Fn::Join': [ @@ -133,7 +132,7 @@ describe('integration', () => { })).toThrow(/cannot set 'vpcLink' where 'connectionType' is INTERNET/); }); - test('connectionType is ABSENT when vpcLink is not specified', () => { + test('connectionType is Match.absent() when vpcLink is not specified', () => { // GIVEN const stack = new cdk.Stack(); const api = new apigw.RestApi(stack, 'restapi'); @@ -146,10 +145,10 @@ describe('integration', () => { api.root.addMethod('ANY', integration); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', Integration: { - ConnectionType: ABSENT, + ConnectionType: Match.absent(), }, }); }); @@ -177,7 +176,7 @@ describe('integration', () => { api.root.addMethod('ANY', integration); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', Integration: { ConnectionType: 'VPC_LINK', @@ -221,7 +220,7 @@ describe('integration', () => { api.root.addMethod('ANY', integration); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', Integration: { TimeoutInMillis: 1000, diff --git a/packages/@aws-cdk/aws-apigateway/test/integrations/lambda.test.ts b/packages/@aws-cdk/aws-apigateway/test/integrations/lambda.test.ts index 7c5b9e60e85d6..187e15e2b09cf 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integrations/lambda.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integrations/lambda.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as apigateway from '../../lib'; @@ -19,7 +19,7 @@ describe('lambda', () => { api.root.addMethod('GET', integ); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { IntegrationHttpMethod: 'POST', Type: 'AWS_PROXY', @@ -66,7 +66,7 @@ describe('lambda', () => { api.root.addMethod('GET', integ); // THEN - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { SourceArn: { 'Fn::Join': [ '', @@ -78,7 +78,7 @@ describe('lambda', () => { }, }); - expect(stack).not.toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', Match.not({ SourceArn: { 'Fn::Join': [ '', @@ -95,7 +95,7 @@ describe('lambda', () => { ], ], }, - }); + })); }); test('"allowTestInvoke" set to true allows calling the API from the test UI', () => { @@ -114,7 +114,7 @@ describe('lambda', () => { api.root.addMethod('GET', integ); // THEN - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { SourceArn: { 'Fn::Join': [ '', @@ -150,7 +150,7 @@ describe('lambda', () => { api.root.addMethod('GET', integ); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { Type: 'AWS', }, @@ -171,7 +171,7 @@ describe('lambda', () => { api.root.addMethod('ANY', target); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { SourceArn: { 'Fn::Join': [ '', @@ -190,7 +190,7 @@ describe('lambda', () => { }, }); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { SourceArn: { 'Fn::Join': [ '', @@ -235,7 +235,7 @@ describe('lambda', () => { api.root.addMethod('ANY', new apigateway.LambdaIntegration(handler)); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { RestApiId: 'imported-rest-api-id', ResourceId: 'imported-root-resource-id', HttpMethod: 'ANY', diff --git a/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts b/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts index c803fa974f7f6..830ca2a8e2000 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { stringLike, anything } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import { StateMachine, StateMachineType } from '@aws-cdk/aws-stepfunctions'; import * as cdk from '@aws-cdk/core'; @@ -16,7 +15,7 @@ describe('StepFunctionsIntegration', () => { api.root.addMethod('GET', integ); //THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ResourceId: { 'Fn::GetAtt': [ 'myrestapiBAC2BF45', @@ -74,16 +73,16 @@ describe('StepFunctionsIntegration', () => { const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine); api.root.addMethod('GET', integ); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - stringLike('*includeHeaders = false*'), + Match.stringLikeRegexp('includeHeaders = false'), { Ref: 'StateMachine2E01A3A5' }, - anything(), + Match.anyValue(), ], ], }, @@ -102,16 +101,16 @@ describe('StepFunctionsIntegration', () => { }); api.root.addMethod('GET', integ); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - stringLike('*#set($includeHeaders = true)*'), + Match.stringLikeRegexp('#set\\(\\$includeHeaders = true\\)'), { Ref: 'StateMachine2E01A3A5' }, - anything(), + Match.anyValue(), ], ], }, @@ -128,16 +127,16 @@ describe('StepFunctionsIntegration', () => { const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine); api.root.addMethod('GET', integ); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - stringLike('*#set($includeQueryString = true)*'), + Match.stringLikeRegexp('#set\\(\\$includeQueryString = true\\)'), { Ref: 'StateMachine2E01A3A5' }, - anything(), + Match.anyValue(), ], ], }, @@ -145,16 +144,16 @@ describe('StepFunctionsIntegration', () => { }, }); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - stringLike('*#set($includePath = true)*'), + Match.stringLikeRegexp('#set\\(\\$includePath = true\\)'), { Ref: 'StateMachine2E01A3A5' }, - anything(), + Match.anyValue(), ], ], }, @@ -174,16 +173,16 @@ describe('StepFunctionsIntegration', () => { }); api.root.addMethod('GET', integ); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - stringLike('*#set($includeQueryString = false)*'), + Match.stringLikeRegexp('#set\\(\\$includeQueryString = false\\)'), { Ref: 'StateMachine2E01A3A5' }, - anything(), + Match.anyValue(), ], ], }, @@ -191,16 +190,16 @@ describe('StepFunctionsIntegration', () => { }, }); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - stringLike('*#set($includePath = false)*'), + Match.stringLikeRegexp('#set\\(\\$includePath = false\\)'), { Ref: 'StateMachine2E01A3A5' }, - anything(), + Match.anyValue(), ], ], }, @@ -217,16 +216,16 @@ describe('StepFunctionsIntegration', () => { const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine, {}); api.root.addMethod('GET', integ); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - anything(), + Match.anyValue(), { Ref: 'StateMachine2E01A3A5' }, - stringLike('*#set($requestContext = \"\")*'), + Match.stringLikeRegexp('#set\\(\\$requestContext = \"\"\\)'), ], ], }, @@ -247,16 +246,16 @@ describe('StepFunctionsIntegration', () => { }); api.root.addMethod('GET', integ); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { RequestTemplates: { 'application/json': { 'Fn::Join': [ '', [ - anything(), + Match.anyValue(), { Ref: 'StateMachine2E01A3A5' }, - stringLike('*#set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@}\"*'), + Match.stringLikeRegexp('#set\\(\\$requestContext = \"{@@accountId@@:@@\\$context.identity.accountId@@}\"'), ], ], }, @@ -283,7 +282,7 @@ describe('StepFunctionsIntegration', () => { api.root.addMethod('ANY', apigw.StepFunctionsIntegration.startExecution(stateMachine)); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ResourceId: 'imported-root-resource-id', RestApiId: 'imported-rest-api-id', }); diff --git a/packages/@aws-cdk/aws-apigateway/test/lambda-api.test.ts b/packages/@aws-cdk/aws-apigateway/test/lambda-api.test.ts index 7e412c549e897..314225d3afe34 100644 --- a/packages/@aws-cdk/aws-apigateway/test/lambda-api.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/lambda-api.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as apigw from '../lib'; @@ -23,11 +23,11 @@ describe('lambda api', () => { }).toThrow(); // THEN -- template proxies everything - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: '{proxy+}', }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', ResourceId: { Ref: 'lambdarestapiproxyE3AE07E3', @@ -91,11 +91,11 @@ describe('lambda api', () => { }).toThrow(); // THEN -- template proxies everything - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: '{proxy+}', }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', ResourceId: { Ref: 'lambdarestapiproxyE3AE07E3', @@ -149,20 +149,20 @@ describe('lambda api', () => { tasks.addMethod('POST'); // THEN - expect(stack).not.toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', Match.not({ PathPart: '{proxy+}', - }); + })); - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: 'tasks', }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', ResourceId: { Ref: 'lambdarestapitasks224418C8' }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'POST', ResourceId: { Ref: 'lambdarestapitasks224418C8' }, }); @@ -209,7 +209,7 @@ describe('lambda api', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'OPTIONS', ResourceId: { Ref: 'lambdarestapiproxyE3AE07E3' }, Integration: { diff --git a/packages/@aws-cdk/aws-apigateway/test/method.test.ts b/packages/@aws-cdk/aws-apigateway/test/method.test.ts index f1037e550e23a..de41b0dfe363f 100644 --- a/packages/@aws-cdk/aws-apigateway/test/method.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/method.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; @@ -24,7 +23,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'POST', AuthorizationType: 'NONE', Integration: { @@ -51,7 +50,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ApiKeyRequired: true, OperationName: 'MyOperation', }); @@ -72,7 +71,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { IntegrationHttpMethod: 'POST', Type: 'AWS', @@ -104,7 +103,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { IntegrationHttpMethod: 'POST', Type: 'AWS', @@ -133,7 +132,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { IntegrationHttpMethod: 'GET', }, @@ -159,7 +158,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { Type: 'HTTP_PROXY', Uri: 'https://amazon.com', @@ -325,7 +324,7 @@ describe('method', () => { })); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { Credentials: { 'Fn::GetAtt': ['MyRoleF48FFE04', 'Arn'] }, }, @@ -347,7 +346,7 @@ describe('method', () => { })); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { Credentials: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::*:user/*']] }, }, @@ -386,7 +385,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', MethodResponses: [{ StatusCode: '200', @@ -435,7 +434,7 @@ describe('method', () => { })); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { Integration: { IntegrationHttpMethod: 'POST', IntegrationResponses: [ @@ -467,9 +466,9 @@ describe('method', () => { api.root.addMethod('PUT'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { HttpMethod: 'POST' }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { HttpMethod: 'GET' }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { HttpMethod: 'PUT' }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'POST' }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET' }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'PUT' }); }); @@ -499,7 +498,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', RequestModels: { 'application/json': { Ref: stack.getLogicalId(model.node.findChild('Resource') as cdk.CfnElement) }, @@ -549,7 +548,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', MethodResponses: [{ StatusCode: '200', @@ -593,10 +592,10 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { RequestValidatorId: { Ref: stack.getLogicalId(validator.node.findChild('Resource') as cdk.CfnElement) }, }); - expect(stack).toHaveResource('AWS::ApiGateway::RequestValidator', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RequestValidator', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as cdk.CfnElement) }, ValidateRequestBody: true, ValidateRequestParameters: false, @@ -626,7 +625,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { OperationName: 'defaultRequestParameters', RequestParameters: { 'method.request.path.proxy': true, @@ -644,7 +643,7 @@ describe('method', () => { authorizer: DUMMY_AUTHORIZER, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', AuthorizationType: 'CUSTOM', AuthorizerId: DUMMY_AUTHORIZER.authorizerId, @@ -674,7 +673,7 @@ describe('method', () => { }); restApi.root.addMethod('ANY'); - expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Authorizer', { Name: 'myauthorizer1', Type: 'TOKEN', RestApiId: stack.resolve(restApi.restApiId), @@ -732,7 +731,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ApiKeyRequired: true, AuthorizationScopes: ['AuthScope1', 'AuthScope2'], }); @@ -761,7 +760,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { OperationName: 'defaultAuthScopes', AuthorizationScopes: ['DefaultAuth'], }); @@ -791,7 +790,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { ApiKeyRequired: true, AuthorizationScopes: ['MethodAuthScope'], }); @@ -817,9 +816,9 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { OperationName: 'authScopesAbsent', - AuthorizationScopes: ABSENT, + AuthorizationScopes: Match.absent(), }); @@ -844,7 +843,7 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RequestValidator', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RequestValidator', { RestApiId: stack.resolve(api.restApiId), ValidateRequestBody: true, ValidateRequestParameters: false, @@ -866,8 +865,8 @@ describe('method', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { - RequestValidatorId: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { + RequestValidatorId: Match.absent(), }); diff --git a/packages/@aws-cdk/aws-apigateway/test/model.test.ts b/packages/@aws-cdk/aws-apigateway/test/model.test.ts index 62602ef353959..4318f3c5b0093 100644 --- a/packages/@aws-cdk/aws-apigateway/test/model.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/model.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as apigw from '../lib'; @@ -24,7 +24,7 @@ describe('model', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Model', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Model', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as cdk.CfnElement) }, Schema: { $schema: 'http://json-schema.org/draft-04/schema#', @@ -57,7 +57,7 @@ describe('model', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Model', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Model', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as cdk.CfnElement) }, Schema: { $schema: 'http://json-schema.org/draft-04/schema#', diff --git a/packages/@aws-cdk/aws-apigateway/test/requestvalidator.test.ts b/packages/@aws-cdk/aws-apigateway/test/requestvalidator.test.ts index ec29752b47b6a..061c7853d3392 100644 --- a/packages/@aws-cdk/aws-apigateway/test/requestvalidator.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/requestvalidator.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as apigateway from '../lib'; @@ -20,7 +20,7 @@ describe('request validator', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RequestValidator', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RequestValidator', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as cdk.CfnElement) }, ValidateRequestBody: true, ValidateRequestParameters: false, @@ -45,7 +45,7 @@ describe('request validator', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RequestValidator', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RequestValidator', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as cdk.CfnElement) }, Name: 'my-model', ValidateRequestBody: false, diff --git a/packages/@aws-cdk/aws-apigateway/test/resource.test.ts b/packages/@aws-cdk/aws-apigateway/test/resource.test.ts index b118c328073cb..3c97221b56339 100644 --- a/packages/@aws-cdk/aws-apigateway/test/resource.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/resource.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import * as apigw from '../lib'; @@ -16,7 +16,7 @@ describe('resource', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { 'ParentId': { 'Fn::GetAtt': [ 'apiC8550315', @@ -29,7 +29,7 @@ describe('resource', () => { }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { 'HttpMethod': 'ANY', 'ResourceId': { 'Ref': 'proxy3A1DA9C7', @@ -60,9 +60,9 @@ describe('resource', () => { proxy.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Resource'); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { 'HttpMethod': 'GET' }); - expect(stack).not.toHaveResource('AWS::ApiGateway::Method', { 'HttpMethod': 'ANY' }); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::Resource', 1); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { 'HttpMethod': 'GET' }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', Match.not({ 'HttpMethod': 'ANY' })); }); @@ -78,7 +78,7 @@ describe('resource', () => { const v2 = api.root.addResource('v2'); v2.addProxy(); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'apiC8550315': { 'Type': 'AWS::ApiGateway::RestApi', @@ -154,7 +154,7 @@ describe('resource', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'DELETE', ResourceId: { Ref: 'apiproxy4EA44110' }, Integration: { @@ -164,7 +164,7 @@ describe('resource', () => { OperationName: 'DeleteMe', }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'DELETE', ResourceId: { 'Fn::GetAtt': ['apiC8550315', 'RootResourceId'] }, Integration: { @@ -251,7 +251,7 @@ describe('resource', () => { imported.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', ResourceId: resourceId, }); diff --git a/packages/@aws-cdk/aws-apigateway/test/restapi.test.ts b/packages/@aws-cdk/aws-apigateway/test/restapi.test.ts index 7a9f117afe126..f873c6c643f5d 100644 --- a/packages/@aws-cdk/aws-apigateway/test/restapi.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/restapi.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart, SynthUtils } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import { GatewayVpcEndpoint } from '@aws-cdk/aws-ec2'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { App, CfnElement, CfnResource, Stack } from '@aws-cdk/core'; @@ -15,7 +14,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // must have at least one method or an API definition // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { myapi4C7BF186: { Type: 'AWS::ApiGateway::RestApi', @@ -132,7 +131,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { Name: 'restapi', }); }); @@ -168,17 +167,17 @@ describe('restapi', () => { foo.addResource('{hello}'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: 'foo', ParentId: { 'Fn::GetAtt': ['restapiC5611D27', 'RootResourceId'] }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: 'bar', ParentId: { 'Fn::GetAtt': ['restapiC5611D27', 'RootResourceId'] }, }); - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: '{hello}', ParentId: { Ref: 'restapifooF697E056' }, }); @@ -198,7 +197,7 @@ describe('restapi', () => { proxy.addMethod('ANY'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: '{proxy+}', ParentId: { 'Fn::GetAtt': ['restapiC5611D27', 'RootResourceId'] }, }); @@ -216,7 +215,7 @@ describe('restapi', () => { r1.addMethod('POST'); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { restapiC5611D27: { Type: 'AWS::ApiGateway::RestApi', @@ -330,8 +329,8 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::IAM::Role'); - expect(stack).toHaveResource('AWS::ApiGateway::Account'); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 1); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::Account', 1); }); test('"url" and "urlForPath" return the URL endpoints of the deployed API', () => { @@ -462,7 +461,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { EndpointConfiguration: { Types: [ 'EDGE', @@ -486,7 +485,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { EndpointConfiguration: { Types: ['EDGE', 'PRIVATE'], }, @@ -511,7 +510,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { EndpointConfiguration: { Types: [ 'EDGE', @@ -551,7 +550,7 @@ describe('restapi', () => { api.root.addMethod('GET'); - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { CloneFrom: 'foobar', Name: 'api', }); @@ -568,7 +567,7 @@ describe('restapi', () => { resource.node.addDependency(api); // THEN - expect(stack).toHaveResource('My::Resource', { + Template.fromStack(stack).hasResource('My::Resource', { DependsOn: [ 'myapiAccountC3A4750C', 'myapiCloudWatchRoleEB425128', @@ -577,7 +576,7 @@ describe('restapi', () => { 'myapiDeploymentStageprod329F21FF', 'myapi162F20B8', ], - }, ResourcePart.CompleteDefinition); + }); }); test('defaultIntegration and defaultMethodOptions can be used at any level', () => { @@ -624,7 +623,7 @@ describe('restapi', () => { // THEN // CASE #1 - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', ResourceId: { 'Fn::GetAtt': ['myapi162F20B8', 'RootResourceId'] }, Integration: { Type: 'AWS' }, @@ -633,7 +632,7 @@ describe('restapi', () => { }); // CASE #2 - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'POST', ResourceId: { Ref: 'myapichildA0A65412' }, Integration: { Type: 'AWS' }, @@ -642,7 +641,7 @@ describe('restapi', () => { }); // CASE #3 - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'DELETE', Integration: { Type: 'MOCK' }, AuthorizerId: 'AUTHID2', @@ -650,7 +649,7 @@ describe('restapi', () => { }); // CASE #4 - expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'PUT', Integration: { Type: 'AWS' }, AuthorizerId: 'AUTHID2', @@ -671,7 +670,7 @@ describe('restapi', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { Enabled: true, Name: 'myApiKey1', StageKeys: [ @@ -701,7 +700,7 @@ describe('restapi', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Model', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Model', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as CfnElement) }, Schema: { $schema: 'http://json-schema.org/draft-04/schema#', @@ -731,14 +730,14 @@ describe('restapi', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RequestValidator', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RequestValidator', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as CfnElement) }, Name: 'Parameters', ValidateRequestBody: false, ValidateRequestParameters: true, }); - expect(stack).toHaveResource('AWS::ApiGateway::RequestValidator', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RequestValidator', { RestApiId: { Ref: stack.getLogicalId(api.node.findChild('Resource') as CfnElement) }, Name: 'Body', ValidateRequestBody: true, @@ -755,7 +754,8 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(SynthUtils.toCloudFormation(stack).Outputs).toEqual({ + const outputs = Template.fromStack(stack).findOutputs('myapiEndpoint8EB17201'); + expect(outputs).toEqual({ myapiEndpoint8EB17201: { Value: { 'Fn::Join': [ @@ -787,7 +787,8 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(SynthUtils.toCloudFormation(stack).Outputs).toEqual({ + const outputs = Template.fromStack(stack).findOutputs('myapiEndpoint8EB17201'); + expect(outputs).toEqual({ myapiEndpoint8EB17201: { Value: { 'Fn::Join': [ @@ -864,11 +865,11 @@ describe('restapi', () => { resource.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: 'pets', ParentId: stack.resolve(imported.restApiRootResourceId), }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', ResourceId: stack.resolve(resource.resourceId), }); @@ -888,11 +889,11 @@ describe('restapi', () => { resource.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Resource', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Resource', { PathPart: 'pets', ParentId: stack.resolve(api.restApiRootResourceId), }); - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'GET', ResourceId: stack.resolve(resource.resourceId), }); @@ -911,7 +912,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { EndpointConfiguration: { Types: [ 'EDGE', @@ -936,7 +937,7 @@ describe('restapi', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { Enabled: true, Name: 'myApiKey1', StageKeys: [ @@ -1081,7 +1082,7 @@ describe('restapi', () => { api.root.addMethod('GET'); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::RestApi', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::RestApi', { DisableExecuteApiEndpoint: true, }); }); diff --git a/packages/@aws-cdk/aws-apigateway/test/stage.test.ts b/packages/@aws-cdk/aws-apigateway/test/stage.test.ts index a9b030ebba07d..76d07c6e2b4b3 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stage.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/stage.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; import * as apigateway from '../lib'; @@ -16,7 +15,7 @@ describe('stage', () => { new apigateway.Stage(stack, 'my-stage', { deployment }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { testapiD6451F70: { Type: 'AWS::ApiGateway::RestApi', @@ -80,9 +79,9 @@ describe('stage', () => { // WHEN new apigateway.Stage(stack, 'my-stage', { deployment }); - expect(stack).toHaveResourceLike('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResource('AWS::ApiGateway::Stage', { DependsOn: ['testapiAccount9B907665'], - }, ResourcePart.CompleteDefinition); + }); }); test('common method settings can be set at the stage level', () => { @@ -100,7 +99,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { MethodSettings: [ { DataTraceEnabled: false, @@ -165,7 +164,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { MethodSettings: [ { DataTraceEnabled: false, @@ -198,7 +197,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { CacheClusterEnabled: true, CacheClusterSize: '0.5', }); @@ -218,7 +217,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { CacheClusterEnabled: true, CacheClusterSize: '0.5', }); @@ -253,7 +252,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { CacheClusterEnabled: true, CacheClusterSize: '0.5', MethodSettings: [ @@ -298,7 +297,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { AccessLogSetting: { DestinationArn: { 'Fn::GetAtt': [ @@ -329,7 +328,7 @@ describe('stage', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::Stage', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Stage', { AccessLogSetting: { DestinationArn: { 'Fn::GetAtt': [ diff --git a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts index 30a1769b8965b..51426a4ffe6c3 100644 --- a/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/stepfunctions-api.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import { StateMachine } from '@aws-cdk/aws-stepfunctions'; import * as cdk from '@aws-cdk/core'; @@ -17,7 +17,7 @@ describe('Step Functions api', () => { }).toThrow(); //THEN - expect(stack).toHaveResource('AWS::ApiGateway::Method', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', { HttpMethod: 'ANY', MethodResponses: getMethodResponse(), AuthorizationType: 'NONE', diff --git a/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts b/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts index 195e17a0b7fbf..b31e3c90b256b 100644 --- a/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { testFutureBehavior } from '@aws-cdk/cdk-build-tools/lib/feature-flag'; @@ -22,10 +21,10 @@ describe('usage plan', () => { }); // THEN - expect(stack).toHaveResource(RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(RESOURCE_TYPE, { UsagePlanName: usagePlanName, Description: usagePlanDescription, - }, ResourcePart.Properties); + }); }); test('usage plan with throttling limits', () => { @@ -57,7 +56,7 @@ describe('usage plan', () => { }); // THEN - expect(stack).toHaveResource(RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(RESOURCE_TYPE, { UsagePlanName: usagePlanName, Description: usagePlanDescription, ApiStages: [ @@ -76,7 +75,7 @@ describe('usage plan', () => { }, }, ], - }, ResourcePart.Properties); + }); }); test('usage plan with blocked methods', () => { @@ -108,7 +107,7 @@ describe('usage plan', () => { }); // THEN - expect(stack).toHaveResource(RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(RESOURCE_TYPE, { UsagePlanName: usagePlanName, Description: usagePlanDescription, ApiStages: [ @@ -127,7 +126,7 @@ describe('usage plan', () => { }, }, ], - }, ResourcePart.Properties); + }); }); test('usage plan with quota limits', () => { @@ -143,12 +142,12 @@ describe('usage plan', () => { }); // THEN - expect(stack).toHaveResource(RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(RESOURCE_TYPE, { Quota: { Limit: 10000, Period: 'MONTH', }, - }, ResourcePart.Properties); + }); }); describe('UsagePlanKey', () => { @@ -165,7 +164,7 @@ describe('usage plan', () => { usagePlan.addApiKey(apiKey); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlanKey', { KeyId: { Ref: 'myapikey1B052F70', }, @@ -173,7 +172,7 @@ describe('usage plan', () => { UsagePlanId: { Ref: 'myusageplan23AA1E32', }, - }, ResourcePart.Properties); + }); }); @@ -187,13 +186,13 @@ describe('usage plan', () => { usagePlan.addApiKey(apiKey); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlanKey', { KeyId: { Ref: 'myapikey1B052F70', }, KeyType: 'API_KEY', UsagePlanId: 'imported-id', - }, ResourcePart.Properties); + }); }); test('multiple keys', () => { @@ -212,22 +211,22 @@ describe('usage plan', () => { usagePlan.addApiKey(apiKey2); // THEN - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { Name: 'my-api-key-1', - }, ResourcePart.Properties); - expect(stack).toHaveResource('AWS::ApiGateway::ApiKey', { + }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::ApiKey', { Name: 'my-api-key-2', - }, ResourcePart.Properties); - expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', { + }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlanKey', { KeyId: { Ref: 'myapikey11F723FC7', }, - }, ResourcePart.Properties); - expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', { + }); + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::UsagePlanKey', { KeyId: { Ref: 'myapikey2ABDEF012', }, - }, ResourcePart.Properties); + }); }); test('overrideLogicalId', () => { diff --git a/packages/@aws-cdk/aws-apigateway/test/util.test.ts b/packages/@aws-cdk/aws-apigateway/test/util.test.ts index 07c8a2cef379d..30861de05dad1 100644 --- a/packages/@aws-cdk/aws-apigateway/test/util.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/util.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import { JsonSchema, JsonSchemaType } from '../lib'; import { JsonSchemaMapper, parseAwsApiCall, parseMethodOptionsPath } from '../lib/util'; diff --git a/packages/@aws-cdk/aws-apigateway/test/vpc-link.test.ts b/packages/@aws-cdk/aws-apigateway/test/vpc-link.test.ts index ba2b82464141d..b41e1a4d19471 100644 --- a/packages/@aws-cdk/aws-apigateway/test/vpc-link.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/vpc-link.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as cdk from '@aws-cdk/core'; @@ -20,7 +20,7 @@ describe('vpc link', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::VpcLink', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::VpcLink', { Name: 'MyLink', TargetArns: [{ Ref: 'NLB55158F82' }], }); @@ -43,7 +43,7 @@ describe('vpc link', () => { link.addTargets(nlb3); // THEN - expect(stack).toHaveResourceLike('AWS::ApiGateway::VpcLink', { + Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::VpcLink', { Name: 'VpcLink', TargetArns: [ { Ref: 'NLB03D178991' }, @@ -62,7 +62,7 @@ describe('vpc link', () => { apigateway.VpcLink.fromVpcLinkId(stack, 'ImportedVpcLink', 'vpclink-id'); // THEN - expect(stack).not.toHaveResource('AWS::ApiGateway::VpcLink'); + Template.fromStack(stack).resourceCountIs('AWS::ApiGateway::VpcLink', 0); }); test('validation error if vpc link is created and no targets are added', () => { diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index e1f2183ab9e43..084eb7c3dd027 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -85,7 +85,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index ccecf1546466f..5e0a110082ab9 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -79,7 +79,7 @@ configures all other HTTP method calls to `/books` to a lambda proxy. ```ts import { HttpUrlIntegration, HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; -const getBooksIntegration = new HttpUrlIntegration('GetBooksIntegration' 'https://get-books-proxy.myproxy.internal'); +const getBooksIntegration = new HttpUrlIntegration('GetBooksIntegration', 'https://get-books-proxy.myproxy.internal'); declare const booksDefaultFn: lambda.Function; const booksDefaultIntegration = new HttpLambdaIntegration('BooksIntegration', booksDefaultFn); @@ -261,19 +261,21 @@ Mutual TLS can be configured to limit access to your API based by using client c ```ts import * as s3 from '@aws-cdk/aws-s3'; +import * as acm from '@aws-cdk/aws-certificatemanager'; + const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate'; const domainName = 'example.com'; -const bucket = new s3.Bucket.fromBucketName(stack, 'TrustStoreBucket', ...); +declare const bucket: s3.Bucket; -new DomainName(stack, 'DomainName', { +new apigwv2.DomainName(this, 'DomainName', { domainName, - certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + certificate: acm.Certificate.fromCertificateArn(this, 'cert', certArn), mtls: { bucket, key: 'someca.pem', version: 'version', }, -}) +}); ``` Instructions for configuring your trust store can be found [here](https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/) @@ -408,17 +410,17 @@ Grant permission to use API Gateway Management API of a WebSocket API by calling You can use Management API to send a callback message to a connected client, get connection information, or disconnect the client. Learn more at [Use @connections commands in your backend service](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-how-to-call-websocket-api-connections.html). ```ts -const lambda = new lambda.Function(this, 'lambda', { /* ... */ }); +declare const fn: lambda.Function; -const webSocketApi = new WebSocketApi(stack, 'mywsapi'); -const stage = new WebSocketStage(stack, 'mystage', { +const webSocketApi = new apigwv2.WebSocketApi(this, 'mywsapi'); +const stage = new apigwv2.WebSocketStage(this, 'mystage', { webSocketApi, stageName: 'dev', }); // per stage permission -stage.grantManageConnections(lambda); +stage.grantManagementApiAccess(fn); // for all the stages permission -webSocketApi.grantManageConnections(lambda); +webSocketApi.grantManageConnections(fn); ``` ### Managing access to WebSocket APIs @@ -434,9 +436,8 @@ Websocket APIs also support usage of API Keys. An API Key is a key that is used To require an API Key when accessing the Websocket API: ```ts -const webSocketApi = new WebSocketApi(stack, 'mywsapi',{ - apiKeySelectionExpression: WebSocketApiKeySelectionExpression.HEADER_X_API_KEY, - }); -... +const webSocketApi = new apigwv2.WebSocketApi(this, 'mywsapi',{ + apiKeySelectionExpression: apigwv2.WebSocketApiKeySelectionExpression.HEADER_X_API_KEY, +}); ``` diff --git a/packages/@aws-cdk/aws-apigatewayv2/package.json b/packages/@aws-cdk/aws-apigatewayv2/package.json index c35e477c574e4..31ec578d85b51 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2/package.json @@ -32,7 +32,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-appconfig/package.json b/packages/@aws-cdk/aws-appconfig/package.json index e0a11eb7a7cce..4f4abaaf6dc40 100644 --- a/packages/@aws-cdk/aws-appconfig/package.json +++ b/packages/@aws-cdk/aws-appconfig/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-appflow/package.json b/packages/@aws-cdk/aws-appflow/package.json index 093c09ccbc347..fd0648da5f582 100644 --- a/packages/@aws-cdk/aws-appflow/package.json +++ b/packages/@aws-cdk/aws-appflow/package.json @@ -28,6 +28,13 @@ "Framework :: AWS CDK :: 1" ] } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-appintegrations/package.json b/packages/@aws-cdk/aws-appintegrations/package.json index 6bafcc70147e5..cd4ecf81dd7c5 100644 --- a/packages/@aws-cdk/aws-appintegrations/package.json +++ b/packages/@aws-cdk/aws-appintegrations/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-appintegrations", "module": "aws_cdk.aws_appintegrations" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-applicationinsights/package.json b/packages/@aws-cdk/aws-applicationinsights/package.json index 84d95622107e3..38d8d1d6311c1 100644 --- a/packages/@aws-cdk/aws-applicationinsights/package.json +++ b/packages/@aws-cdk/aws-applicationinsights/package.json @@ -28,6 +28,13 @@ "Framework :: AWS CDK :: 1" ] } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-apprunner/package.json b/packages/@aws-cdk/aws-apprunner/package.json index a9c982a6cd9b0..0627cfbcc38fa 100644 --- a/packages/@aws-cdk/aws-apprunner/package.json +++ b/packages/@aws-cdk/aws-apprunner/package.json @@ -83,7 +83,6 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-appstream/package.json b/packages/@aws-cdk/aws-appstream/package.json index 774a9b54ca4c0..059821e8a6b4b 100644 --- a/packages/@aws-cdk/aws-appstream/package.json +++ b/packages/@aws-cdk/aws-appstream/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-aps/package.json b/packages/@aws-cdk/aws-aps/package.json index aa6d725f1025d..a63226c0a8148 100644 --- a/packages/@aws-cdk/aws-aps/package.json +++ b/packages/@aws-cdk/aws-aps/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-aps", "module": "aws_cdk.aws_aps" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-athena/package.json b/packages/@aws-cdk/aws-athena/package.json index b96b7559fc447..343d9eb7f34eb 100644 --- a/packages/@aws-cdk/aws-athena/package.json +++ b/packages/@aws-cdk/aws-athena/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "cdk-build": { "cloudformation": "AWS::Athena", diff --git a/packages/@aws-cdk/aws-auditmanager/package.json b/packages/@aws-cdk/aws-auditmanager/package.json index 9b9347689509b..a9a3738209a4d 100644 --- a/packages/@aws-cdk/aws-auditmanager/package.json +++ b/packages/@aws-cdk/aws-auditmanager/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-auditmanager", "module": "aws_cdk.aws_auditmanager" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-autoscaling-common/package.json b/packages/@aws-cdk/aws-autoscaling-common/package.json index 6a0a7fa6efafd..64b399fd336ba 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/package.json +++ b/packages/@aws-cdk/aws-autoscaling-common/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json b/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json index 89be3fc475a6b..eb1dab85bfe70 100644 --- a/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json +++ b/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", @@ -64,7 +71,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-autoscaling-hooktargets/test/hooks.test.ts b/packages/@aws-cdk/aws-autoscaling-hooktargets/test/hooks.test.ts index 4615c45913b44..413142423f7b5 100644 --- a/packages/@aws-cdk/aws-autoscaling-hooktargets/test/hooks.test.ts +++ b/packages/@aws-cdk/aws-autoscaling-hooktargets/test/hooks.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { arrayWith } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; @@ -27,7 +26,7 @@ describe('given an AutoScalingGroup and no role', () => { }); afterEach(() => { - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [ @@ -54,8 +53,8 @@ describe('given an AutoScalingGroup and no role', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { 'Fn::GetAtt': ['Queue4A7E3555', 'Arn'] } }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { 'Fn::GetAtt': ['Queue4A7E3555', 'Arn'] } }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -95,8 +94,8 @@ describe('given an AutoScalingGroup and no role', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'TopicBFC7AF6E' } }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'TopicBFC7AF6E' } }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -133,13 +132,13 @@ describe('given an AutoScalingGroup and no role', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'ASGLifecycleHookTransTopic9B0D4842' } }); - expect(stack).toHaveResource('AWS::SNS::Subscription', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'ASGLifecycleHookTransTopic9B0D4842' } }); + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Subscription', { Protocol: 'lambda', TopicArn: { Ref: 'ASGLifecycleHookTransTopic9B0D4842' }, Endpoint: { 'Fn::GetAtt': ['Fn9270CBC0', 'Arn'] }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -177,7 +176,7 @@ describe('given an AutoScalingGroup and no role', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SNS::Topic', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Topic', { KmsMasterKeyId: { 'Fn::GetAtt': [ 'keyFEDD6EC0', @@ -185,9 +184,9 @@ describe('given an AutoScalingGroup and no role', () => { ], }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith( + Statement: Match.arrayWith([ { Effect: 'Allow', Action: [ @@ -201,7 +200,7 @@ describe('given an AutoScalingGroup and no role', () => { ], }, }, - ), + ]), }, }); }); @@ -223,7 +222,7 @@ describe('given an AutoScalingGroup and a role', () => { }); afterEach(() => { - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [ @@ -253,7 +252,7 @@ describe('given an AutoScalingGroup and a role', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { 'Fn::GetAtt': ['Queue4A7E3555', 'Arn'] } }); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { 'Fn::GetAtt': ['Queue4A7E3555', 'Arn'] } }); }); test('can use topic as hook target with a role', () => { @@ -271,8 +270,8 @@ describe('given an AutoScalingGroup and a role', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'TopicBFC7AF6E' } }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'TopicBFC7AF6E' } }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -313,13 +312,13 @@ describe('given an AutoScalingGroup and a role', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'ASGLifecycleHookTransTopic9B0D4842' } }); - expect(stack).toHaveResource('AWS::SNS::Subscription', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: { Ref: 'ASGLifecycleHookTransTopic9B0D4842' } }); + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Subscription', { Protocol: 'lambda', TopicArn: { Ref: 'ASGLifecycleHookTransTopic9B0D4842' }, Endpoint: { 'Fn::GetAtt': ['Fn9270CBC0', 'Arn'] }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index bd58a5c1c288b..cc9037521f731 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts b/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts index 22a58f097a98b..5fa4210d2a6f5 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts @@ -1,8 +1,4 @@ -import { - expect as expectCDK, - haveResourceLike, -} from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import { @@ -37,11 +33,12 @@ describe('AutoScalingGroupRequireImdsv2Aspect', () => { cdk.Aspects.of(stack).add(aspect); // THEN - expectCDK(stack).notTo(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', Match.not({ MetadataOptions: { HttpTokens: 'required', }, })); + expect(asg.node.metadataEntry).toContainEqual({ data: expect.stringContaining('CfnLaunchConfiguration.MetadataOptions field is a CDK token.'), type: 'aws:cdk:warning', @@ -62,11 +59,11 @@ describe('AutoScalingGroupRequireImdsv2Aspect', () => { cdk.Aspects.of(stack).add(aspect); // THEN - expectCDK(stack).to(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { MetadataOptions: { HttpTokens: 'required', }, - })); + }); }); }); diff --git a/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts index 8ca98c19deeb8..945339ca86f5b 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, InspectionFailure, ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; @@ -22,7 +21,7 @@ describe('auto scaling group', () => { vpc, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Parameters': { 'SsmParameterValueawsserviceamiamazonlinuxlatestamznamihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter': { 'Type': 'AWS::SSM::Parameter::Value', @@ -136,8 +135,6 @@ describe('auto scaling group', () => { }, }, }); - - }); test('can set minCapacity, maxCapacity, desiredCapacity to 0', () => { @@ -153,14 +150,11 @@ describe('auto scaling group', () => { desiredCapacity: 0, }); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MinSize: '0', MaxSize: '0', DesiredCapacity: '0', - }, - ); - - + }); }); test('validation is not performed when using Tokens', () => { @@ -177,14 +171,11 @@ describe('auto scaling group', () => { }); // THEN: no exception - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MinSize: '5', MaxSize: '1', DesiredCapacity: '20', - }, - ); - - + }); }); test('userdata can be overridden by image', () => { @@ -206,8 +197,6 @@ describe('auto scaling group', () => { // THEN expect(asg.userData.render()).toEqual('#!/bin/bash\nit me!'); - - }); test('userdata can be overridden at ASG directly', () => { @@ -233,8 +222,6 @@ describe('auto scaling group', () => { // THEN expect(asg.userData.render()).toEqual('#!/bin/bash\nno me!'); - - }); test('can specify only min capacity', () => { @@ -251,13 +238,10 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MinSize: '10', MaxSize: '10', - }, - ); - - + }); }); test('can specify only max capacity', () => { @@ -274,13 +258,10 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MinSize: '1', MaxSize: '10', - }, - ); - - + }); }); test('can specify only desiredCount', () => { @@ -297,14 +278,11 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MinSize: '1', MaxSize: '10', DesiredCapacity: '10', - }, - ); - - + }); }); test('addToRolePolicy can be used to add statements to the role policy', () => { @@ -322,7 +300,7 @@ describe('auto scaling group', () => { resources: ['*'], })); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -352,7 +330,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { UpdatePolicy: { AutoScalingReplacingUpdate: { WillReplace: true, @@ -363,9 +341,7 @@ describe('auto scaling group', () => { MinSuccessfulInstancesPercent: 50, }, }, - }, ResourcePart.CompleteDefinition); - - + }); }); testDeprecated('can configure rolling update', () => { @@ -386,7 +362,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { UpdatePolicy: { 'AutoScalingRollingUpdate': { 'MinSuccessfulInstancesPercent': 50, @@ -395,9 +371,7 @@ describe('auto scaling group', () => { 'SuspendProcesses': ['HealthCheck', 'ReplaceUnhealthy', 'AZRebalance', 'AlarmNotification', 'ScheduledActions'], }, }, - }, ResourcePart.CompleteDefinition); - - + }); }); testDeprecated('can configure resource signals', () => { @@ -415,16 +389,14 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { CreationPolicy: { ResourceSignal: { Count: 5, Timeout: 'PT11M6S', }, }, - }, ResourcePart.CompleteDefinition); - - + }); }); test('can configure EC2 health check', () => { @@ -441,11 +413,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { HealthCheckType: 'EC2', }); - - }); test('can configure EBS health check', () => { @@ -462,12 +432,10 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { HealthCheckType: 'ELB', HealthCheckGracePeriod: 900, }); - - }); test('can add Security Group to Fleet', () => { @@ -482,7 +450,7 @@ describe('auto scaling group', () => { vpc, }); asg.addSecurityGroup(mockSecurityGroup(stack)); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { SecurityGroups: [ { 'Fn::GetAtt': [ @@ -493,7 +461,6 @@ describe('auto scaling group', () => { 'most-secure', ], }); - }); test('can set tags', () => { @@ -514,7 +481,7 @@ describe('auto scaling group', () => { cdk.Tags.of(asg).add('notsuper', 'caramel', { applyToLaunchedInstances: false }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { Tags: [ { Key: 'Name', @@ -533,7 +500,6 @@ describe('auto scaling group', () => { }, ], }); - }); test('allows setting spot price', () => { @@ -552,11 +518,9 @@ describe('auto scaling group', () => { // THEN expect(asg.spotPrice).toEqual('0.05'); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { SpotPrice: '0.05', }); - - }); test('allows association of public IP address', () => { @@ -578,11 +542,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { AssociatePublicIpAddress: true, - }, - ); - + }); }); test('association of public IP address requires public subnet', () => { @@ -602,7 +564,6 @@ describe('auto scaling group', () => { associatePublicIpAddress: true, }); }).toThrow(); - }); test('allows disassociation of public IP address', () => { @@ -622,11 +583,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { AssociatePublicIpAddress: false, - }, - ); - + }); }); test('does not specify public IP address association by default', () => { @@ -645,16 +604,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', (resource: any, errors: InspectionFailure) => { - for (const key of Object.keys(resource)) { - if (key === 'AssociatePublicIpAddress') { - errors.failureReason = 'Has AssociatePublicIpAddress'; - return false; - } - } - return true; + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { + AssociatePublicIpAddress: Match.absent(), }); - }); test('an existing security group can be specified instead of auto-created', () => { @@ -672,11 +624,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { SecurityGroups: ['most-secure'], - }, - ); - + }); }); test('an existing role can be specified instead of auto-created', () => { @@ -695,10 +645,9 @@ describe('auto scaling group', () => { // THEN expect(asg.role).toEqual(importedRole); - expect(stack).toHaveResource('AWS::IAM::InstanceProfile', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::InstanceProfile', { 'Roles': ['HelloDude'], }); - }); test('defaultChild is available on an ASG', () => { @@ -713,8 +662,6 @@ describe('auto scaling group', () => { // THEN expect(asg.node.defaultChild instanceof autoscaling.CfnAutoScalingGroup).toEqual(true); - - }); test('can set blockDeviceMappings', () => { @@ -755,7 +702,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { BlockDeviceMappings: [ { DeviceName: 'ebs', @@ -766,7 +713,7 @@ describe('auto scaling group', () => { VolumeSize: 15, VolumeType: 'io1', }, - NoDevice: ABSENT, + NoDevice: Match.absent(), }, { DeviceName: 'ebs-snapshot', @@ -776,12 +723,12 @@ describe('auto scaling group', () => { VolumeSize: 500, VolumeType: 'sc1', }, - NoDevice: ABSENT, + NoDevice: Match.absent(), }, { DeviceName: 'ephemeral', VirtualName: 'ephemeral0', - NoDevice: ABSENT, + NoDevice: Match.absent(), }, { DeviceName: 'disabled', @@ -793,8 +740,6 @@ describe('auto scaling group', () => { }, ], }); - - }); test('can configure maxInstanceLifetime', () => { @@ -809,11 +754,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { 'MaxInstanceLifetime': 604800, }); - - }); test('throws if maxInstanceLifetime < 7 days', () => { @@ -830,8 +773,6 @@ describe('auto scaling group', () => { maxInstanceLifetime: cdk.Duration.days(6), }); }).toThrow(/maxInstanceLifetime must be between 7 and 365 days \(inclusive\)/); - - }); test('throws if maxInstanceLifetime > 365 days', () => { @@ -848,8 +789,6 @@ describe('auto scaling group', () => { maxInstanceLifetime: cdk.Duration.days(366), }); }).toThrow(/maxInstanceLifetime must be between 7 and 365 days \(inclusive\)/); - - }); test('can configure instance monitoring', () => { @@ -866,10 +805,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { InstanceMonitoring: false, }); - }); test('instance monitoring defaults to absent', () => { @@ -885,10 +823,9 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { - InstanceMonitoring: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { + InstanceMonitoring: Match.absent(), }); - }); test('throws if ephemeral volumeIndex < 0', () => { @@ -908,8 +845,6 @@ describe('auto scaling group', () => { }], }); }).toThrow(/volumeIndex must be a number starting from 0/); - - }); test('throws if volumeType === IO1 without iops', () => { @@ -933,8 +868,6 @@ describe('auto scaling group', () => { }], }); }).toThrow(/ops property is required with volumeType: EbsDeviceVolumeType.IO1/); - - }); test('warning if iops without volumeType', () => { @@ -959,8 +892,6 @@ describe('auto scaling group', () => { // THEN expect(asg.node.metadataEntry[0].type).toEqual(cxschema.ArtifactMetadataEntryType.WARN); expect(asg.node.metadataEntry[0].data).toEqual('iops will be ignored without volumeType: EbsDeviceVolumeType.IO1'); - - }); test('warning if iops and volumeType !== IO1', () => { @@ -986,8 +917,6 @@ describe('auto scaling group', () => { // THEN expect(asg.node.metadataEntry[0].type).toEqual(cxschema.ArtifactMetadataEntryType.WARN); expect(asg.node.metadataEntry[0].data).toEqual('iops will be ignored without volumeType: EbsDeviceVolumeType.IO1'); - - }); test('step scaling on metric', () => { @@ -1015,15 +944,13 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'LessThanOrEqualToThreshold', EvaluationPeriods: 1, MetricName: 'Metric', Namespace: 'Test', Period: 300, }); - - }); test('step scaling on MathExpression', () => { @@ -1056,11 +983,11 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', Match.not({ Period: 60, - }); + })); - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { 'ComparisonOperator': 'LessThanOrEqualToThreshold', 'EvaluationPeriods': 1, 'Metrics': [ @@ -1083,8 +1010,6 @@ describe('auto scaling group', () => { ], 'Threshold': 49, }); - - }); test('test GroupMetrics.all(), adds a single MetricsCollection with no Metrics specified', () => { @@ -1100,15 +1025,14 @@ describe('auto scaling group', () => { }); // Then - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MetricsCollection: [ { Granularity: '1Minute', - Metrics: ABSENT, + Metrics: Match.absent(), }, ], }); - }); test('test can specify a subset of group metrics', () => { @@ -1135,7 +1059,7 @@ describe('auto scaling group', () => { }); // Then - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MetricsCollection: [ { Granularity: '1Minute', @@ -1146,7 +1070,6 @@ describe('auto scaling group', () => { }, ], }); - }); test('test deduplication of group metrics ', () => { @@ -1165,7 +1088,7 @@ describe('auto scaling group', () => { }); // Then - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MetricsCollection: [ { Granularity: '1Minute', @@ -1173,7 +1096,6 @@ describe('auto scaling group', () => { }, ], }); - }); test('allow configuring notifications', () => { @@ -1200,7 +1122,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { NotificationConfigurations: [ { TopicARN: { Ref: 'MyTopic86869434' }, @@ -1216,10 +1138,7 @@ describe('auto scaling group', () => { ], }, ], - }, - ); - - + }); }); testDeprecated('throw if notification and notificationsTopics are both configured', () => { @@ -1240,7 +1159,6 @@ describe('auto scaling group', () => { }], }); }).toThrow('Cannot set \'notificationsTopic\' and \'notifications\', \'notificationsTopic\' is deprecated use \'notifications\' instead'); - }); test('notificationTypes default includes all non test NotificationType', () => { @@ -1262,7 +1180,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { NotificationConfigurations: [ { TopicARN: { Ref: 'MyTopic86869434' }, @@ -1274,10 +1192,7 @@ describe('auto scaling group', () => { ], }, ], - }, - ); - - + }); }); testDeprecated('setting notificationTopic configures all non test NotificationType', () => { @@ -1295,7 +1210,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { NotificationConfigurations: [ { TopicARN: { Ref: 'MyTopic86869434' }, @@ -1307,10 +1222,7 @@ describe('auto scaling group', () => { ], }, ], - }, - ); - - + }); }); test('NotificationTypes.ALL includes all non test NotificationType', () => { @@ -1333,11 +1245,9 @@ describe('auto scaling group', () => { // THEN expect(asg.areNewInstancesProtectedFromScaleIn()).toEqual(true); - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { NewInstancesProtectedFromScaleIn: true, }); - - }); test('Can protect new instances from scale-in via setter', () => { @@ -1355,11 +1265,9 @@ describe('auto scaling group', () => { // THEN expect(asg.areNewInstancesProtectedFromScaleIn()).toEqual(true); - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { NewInstancesProtectedFromScaleIn: true, }); - - }); test('requires imdsv2', () => { @@ -1376,7 +1284,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { MetadataOptions: { HttpTokens: 'required', }, @@ -1400,7 +1308,7 @@ describe('auto scaling group', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { TerminationPolicies: [ 'OldestInstance', 'Default', @@ -1433,7 +1341,7 @@ test('Can set autoScalingGroupName', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { AutoScalingGroupName: 'MyAsg', }); }); @@ -1470,7 +1378,7 @@ test('can use Vpc imported from unparseable list tokens', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { VPCZoneIdentifier: { 'Fn::Split': [',', { 'Fn::ImportValue': 'myPrivateSubnetIds' }], }, diff --git a/packages/@aws-cdk/aws-autoscaling/test/cfn-init.test.ts b/packages/@aws-cdk/aws-autoscaling/test/cfn-init.test.ts index 2fd252e5e459d..1e34f43260c62 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/cfn-init.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/cfn-init.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { anything, arrayWith, expect, haveResourceLike, ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import { Duration, Stack } from '@aws-cdk/core'; import * as autoscaling from '../lib'; @@ -37,13 +36,13 @@ test('Signals.waitForAll uses desiredCapacity if available', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { CreationPolicy: { ResourceSignal: { Count: 5, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('Signals.waitForAll uses minCapacity if desiredCapacity is not available', () => { @@ -55,13 +54,13 @@ test('Signals.waitForAll uses minCapacity if desiredCapacity is not available', }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { CreationPolicy: { ResourceSignal: { Count: 2, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('Signals.waitForMinCapacity uses minCapacity', () => { @@ -72,13 +71,13 @@ test('Signals.waitForMinCapacity uses minCapacity', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { CreationPolicy: { ResourceSignal: { Count: 2, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('Signals.waitForCount uses given number', () => { @@ -89,13 +88,13 @@ test('Signals.waitForCount uses given number', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { CreationPolicy: { ResourceSignal: { Count: 10, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('When signals are given appropriate IAM policy is added', () => { @@ -106,15 +105,15 @@ test('When signals are given appropriate IAM policy is added', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: 'cloudformation:SignalResource', Effect: 'Allow', Resource: { Ref: 'AWS::StackId' }, - }), + }]), }, - })); + }); }); test('UpdatePolicy.rollingUpdate() still correctly inserts IgnoreUnmodifiedGroupSizeProperties', () => { @@ -125,13 +124,13 @@ test('UpdatePolicy.rollingUpdate() still correctly inserts IgnoreUnmodifiedGroup }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { UpdatePolicy: { AutoScalingScheduledAction: { IgnoreUnmodifiedGroupSizeProperties: true, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('UpdatePolicy.rollingUpdate() with Signals uses those defaults', () => { @@ -146,7 +145,7 @@ test('UpdatePolicy.rollingUpdate() with Signals uses those defaults', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { CreationPolicy: { AutoScalingCreationPolicy: { MinSuccessfulInstancesPercent: 50, @@ -163,7 +162,7 @@ test('UpdatePolicy.rollingUpdate() with Signals uses those defaults', () => { WaitOnResourceSignals: true, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('UpdatePolicy.rollingUpdate() without Signals', () => { @@ -174,12 +173,12 @@ test('UpdatePolicy.rollingUpdate() without Signals', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { UpdatePolicy: { AutoScalingRollingUpdate: { }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('UpdatePolicy.replacingUpdate() renders correct UpdatePolicy', () => { @@ -190,13 +189,13 @@ test('UpdatePolicy.replacingUpdate() renders correct UpdatePolicy', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { UpdatePolicy: { AutoScalingReplacingUpdate: { WillReplace: true, }, }, - }, ResourcePart.CompleteDefinition)); + }); }); test('Using init config in ASG leads to default updatepolicy', () => { @@ -210,11 +209,11 @@ test('Using init config in ASG leads to default updatepolicy', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { UpdatePolicy: { - AutoScalingRollingUpdate: anything(), + AutoScalingRollingUpdate: Match.anyValue(), }, - }, ResourcePart.CompleteDefinition)); + }); }); test('Using init config in ASG leads to correct UserData and permissions', () => { @@ -228,7 +227,7 @@ test('Using init config in ASG leads to correct UserData and permissions', () => }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { UserData: { 'Fn::Base64': { 'Fn::Join': ['', [ @@ -244,14 +243,15 @@ test('Using init config in ASG leads to correct UserData and permissions', () => ]], }, }, - })); - expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: ['cloudformation:DescribeStackResource', 'cloudformation:SignalResource'], Effect: 'Allow', Resource: { Ref: 'AWS::StackId' }, - }), + }]), }, - })); + }); }); diff --git a/packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts b/packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts index a1f3717f91042..2cecb8b227107 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/lifecyclehooks.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; @@ -20,22 +19,22 @@ describe('lifecycle hooks', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { LifecycleTransition: 'autoscaling:EC2_INSTANCE_LAUNCHING', DefaultResult: 'ABANDON', NotificationTargetARN: 'target:arn', }); // Lifecycle Hook has a dependency on the policy object - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { + Template.fromStack(stack).hasResource('AWS::AutoScaling::LifecycleHook', { DependsOn: [ 'ASGLifecycleHookTransitionRoleDefaultPolicy2E50C7DB', 'ASGLifecycleHookTransitionRole3AAA6BB7', ], - }, ResourcePart.CompleteDefinition); + }); // A default role is provided - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [ @@ -51,7 +50,7 @@ describe('lifecycle hooks', () => { }); // FakeNotificationTarget.bind() was executed - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -78,13 +77,13 @@ test('we can add a lifecycle hook to an ASG with no role and with no notificatio }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { LifecycleTransition: 'autoscaling:EC2_INSTANCE_LAUNCHING', DefaultResult: 'ABANDON', }); // A default role is NOT provided - expect(stack).not.toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', Match.not({ AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [ @@ -97,21 +96,10 @@ test('we can add a lifecycle hook to an ASG with no role and with no notificatio }, ], }, - }); + })); // FakeNotificationTarget.bind() was NOT executed - expect(stack).not.toHaveResource('AWS::IAM::Policy', { - PolicyDocument: { - Version: '2012-10-17', - Statement: [ - { - Action: 'action:Work', - Effect: 'Allow', - Resource: '*', - }, - ], - }, - }); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); }); test('we can add a lifecycle hook to an ASG with a role and with a notificationTargetArn', () => { @@ -131,14 +119,14 @@ test('we can add a lifecycle hook to an ASG with a role and with a notificationT }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { NotificationTargetARN: 'target:arn', LifecycleTransition: 'autoscaling:EC2_INSTANCE_LAUNCHING', DefaultResult: 'ABANDON', }); // the provided role (myrole), not the default role, is used - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [ diff --git a/packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts b/packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts index a497efab73135..d427b6afa1ff8 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/scaling.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; @@ -24,15 +23,13 @@ describe('scaling', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ASGAverageCPUUtilization' }, TargetValue: 30, }, - })); - - + }); }); test('network ingress', () => { @@ -46,15 +43,13 @@ describe('scaling', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ASGAverageNetworkIn' }, TargetValue: 100, }, - })); - - + }); }); test('network egress', () => { @@ -68,15 +63,13 @@ describe('scaling', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ASGAverageNetworkOut' }, TargetValue: 100, }, - })); - - + }); }); test('request count per second', () => { @@ -103,7 +96,7 @@ describe('scaling', () => { ], }; - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingConfiguration: { TargetValue: 600, @@ -122,9 +115,7 @@ describe('scaling', () => { }, }, }, - })); - - + }); }); test('request count per minute', () => { @@ -151,7 +142,7 @@ describe('scaling', () => { ], }; - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingConfiguration: { TargetValue: 10, @@ -170,9 +161,7 @@ describe('scaling', () => { }, }, }, - })); - - + }); }); test('custom metric', () => { @@ -193,7 +182,7 @@ describe('scaling', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingConfiguration: { CustomizedMetricSpecification: { @@ -204,9 +193,7 @@ describe('scaling', () => { }, TargetValue: 2, }, - })); - - + }); }); }); @@ -231,7 +218,7 @@ describe('scaling', () => { }); // THEN: scaling in policy - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { MetricAggregationType: 'Average', PolicyType: 'StepScaling', StepAdjustments: [ @@ -245,17 +232,17 @@ describe('scaling', () => { ScalingAdjustment: -2, }, ], - })); + }); - expect(stack).to(haveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', Threshold: 3, AlarmActions: [{ Ref: 'FixtureASGMetricUpperPolicyC464CAFB' }], AlarmDescription: 'Upper threshold scaling alarm', - })); + }); // THEN: scaling out policy - expect(stack).to(haveResource('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { MetricAggregationType: 'Average', PolicyType: 'StepScaling', StepAdjustments: [ @@ -264,16 +251,14 @@ describe('scaling', () => { ScalingAdjustment: 1, }, ], - })); + }); - expect(stack).to(haveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'LessThanOrEqualToThreshold', Threshold: 2, AlarmActions: [{ Ref: 'FixtureASGMetricLowerPolicy4A1CDE42' }], AlarmDescription: 'Lower threshold scaling alarm', - })); - - + }); }); }); @@ -293,11 +278,12 @@ test('step scaling from percentile metric', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', MetricAggregationType: 'Average', - })); - expect(stack).to(haveResource('AWS::CloudWatch::Alarm', { + }); + + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 1, AlarmActions: [ @@ -307,7 +293,7 @@ test('step scaling from percentile metric', () => { MetricName: 'Metric', Namespace: 'Test', Threshold: 100, - })); + }); }); test('step scaling with evaluation period configured', () => { @@ -328,18 +314,19 @@ test('step scaling with evaluation period configured', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::AutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', MetricAggregationType: 'Maximum', - })); - expect(stack).to(haveResource('AWS::CloudWatch::Alarm', { + }); + + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 10, ExtendedStatistic: 'p99', MetricName: 'Metric', Namespace: 'Test', Threshold: 100, - })); + }); }); class ASGFixture extends Construct { diff --git a/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts b/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts index 57789d5ae36fe..8579099ffd6cf 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/scheduled-action.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { expect, haveResource, MatchStyle } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cdk from '@aws-cdk/core'; @@ -19,12 +18,10 @@ describeDeprecated('scheduled action', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScheduledAction', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScheduledAction', { Recurrence: '0 8 * * *', MinSize: 10, - })); - - + }); }); test('correctly formats date objects', () => { @@ -40,11 +37,9 @@ describeDeprecated('scheduled action', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScheduledAction', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScheduledAction', { StartTime: '2033-09-10T12:00:00Z', - })); - - + }); }); test('have timezone property', () => { @@ -60,12 +55,11 @@ describeDeprecated('scheduled action', () => { }); // THEN - expect(stack).to(haveResource('AWS::AutoScaling::ScheduledAction', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::ScheduledAction', { MinSize: 12, Recurrence: '0 12 * * *', TimeZone: 'Asia/Seoul', - })); - + }); }); test('autoscaling group has recommended updatepolicy for scheduled actions', () => { @@ -80,7 +74,7 @@ describeDeprecated('scheduled action', () => { }); // THEN - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ Resources: { ASG46ED3070: { Type: 'AWS::AutoScaling::AutoScalingGroup', @@ -124,9 +118,7 @@ describeDeprecated('scheduled action', () => { Default: '/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2', }, }, - }, MatchStyle.SUPERSET); - - + }); }); }); diff --git a/packages/@aws-cdk/aws-autoscalingplans/package.json b/packages/@aws-cdk/aws-autoscalingplans/package.json index fac3bc7d1fd91..6de1130ba56bb 100644 --- a/packages/@aws-cdk/aws-autoscalingplans/package.json +++ b/packages/@aws-cdk/aws-autoscalingplans/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-budgets/package.json b/packages/@aws-cdk/aws-budgets/package.json index bdb97890149aa..7b0506935bdb3 100644 --- a/packages/@aws-cdk/aws-budgets/package.json +++ b/packages/@aws-cdk/aws-budgets/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-cassandra/package.json b/packages/@aws-cdk/aws-cassandra/package.json index a9d233869e092..70e21b61df3e3 100644 --- a/packages/@aws-cdk/aws-cassandra/package.json +++ b/packages/@aws-cdk/aws-cassandra/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ce/package.json b/packages/@aws-cdk/aws-ce/package.json index b0786e2c47636..6332fc8cffd14 100644 --- a/packages/@aws-cdk/aws-ce/package.json +++ b/packages/@aws-cdk/aws-ce/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/lib/index.js b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/lib/index.js index 528dd55eb460a..672b5762dbc15 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/lib/index.js +++ b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/lib/index.js @@ -110,26 +110,14 @@ const requestCertificate = async function (requestId, domainName, subjectAlterna console.log('Waiting for ACM to provide DNS records for validation...'); - let records; - for (let attempt = 0; attempt < maxAttempts && !records; attempt++) { + let records = []; + for (let attempt = 0; attempt < maxAttempts && !records.length; attempt++) { const { Certificate } = await acm.describeCertificate({ CertificateArn: reqCertResponse.CertificateArn }).promise(); - const options = Certificate.DomainValidationOptions || []; - // Ensure all records are ready; there is (at least a theory there's) a chance of a partial response here in rare cases. - if (options.length > 0 && options.every(opt => opt && !!opt.ResourceRecord)) { - // some alternative names will produce the same validation record - // as the main domain (eg. example.com + *.example.com) - // filtering duplicates to avoid errors with adding the same record - // to the route53 zone twice - const unique = options - .map((val) => val.ResourceRecord) - .reduce((acc, cur) => { - acc[cur.Name] = cur; - return acc; - }, {}); - records = Object.keys(unique).sort().map(key => unique[key]); - } else { + + records = getDomainValidationRecords(Certificate); + if (!records.length) { // Exponential backoff with jitter based on 200ms base // component of backoff fixed to ensure minimum total wait time on // slow targets. @@ -137,42 +125,13 @@ const requestCertificate = async function (requestId, domainName, subjectAlterna await sleep(random() * base * 50 + base * 150); } } - if (!records) { + if (!records.length) { throw new Error(`Response from describeCertificate did not contain DomainValidationOptions after ${maxAttempts} attempts.`) } - console.log(`Upserting ${records.length} DNS records into zone ${hostedZoneId}:`); - const changeBatch = await route53.changeResourceRecordSets({ - ChangeBatch: { - Changes: records.map((record) => { - console.log(`${record.Name} ${record.Type} ${record.Value}`) - return { - Action: 'UPSERT', - ResourceRecordSet: { - Name: record.Name, - Type: record.Type, - TTL: 60, - ResourceRecords: [{ - Value: record.Value - }] - } - }; - }), - }, - HostedZoneId: hostedZoneId - }).promise(); - - console.log('Waiting for DNS records to commit...'); - await route53.waitFor('resourceRecordSetsChanged', { - // Wait up to 5 minutes - $waiter: { - delay: 30, - maxAttempts: 10 - }, - Id: changeBatch.ChangeInfo.Id - }).promise(); + await commitRoute53Records(route53, records, hostedZoneId); console.log('Waiting for validation...'); await acm.waitFor('certificateValidated', { @@ -193,40 +152,59 @@ const requestCertificate = async function (requestId, domainName, subjectAlterna * * @param {string} arn The certificate ARN */ -const deleteCertificate = async function (arn, region) { +const deleteCertificate = async function (arn, region, hostedZoneId, route53Endpoint, cleanupRecords) { const acm = new aws.ACM({ region }); + const route53 = route53Endpoint ? new aws.Route53({ endpoint: route53Endpoint }) : new aws.Route53(); + if (waiter) { + // Used by the test suite, since waiters aren't mockable yet + route53.waitFor = acm.waitFor = waiter; + } try { console.log(`Waiting for certificate ${arn} to become unused`); let inUseByResources; + let records = []; for (let attempt = 0; attempt < maxAttempts; attempt++) { const { Certificate } = await acm.describeCertificate({ CertificateArn: arn }).promise(); + if (cleanupRecords) { + records = getDomainValidationRecords(Certificate); + } inUseByResources = Certificate.InUseBy || []; - if (inUseByResources.length) { + if (inUseByResources.length || !records.length) { // Exponential backoff with jitter based on 200ms base // component of backoff fixed to ensure minimum total wait time on // slow targets. const base = Math.pow(2, attempt); await sleep(random() * base * 50 + base * 150); } else { - break + break; } } if (inUseByResources.length) { throw new Error(`Response from describeCertificate did not contain an empty InUseBy list after ${maxAttempts} attempts.`) } + if (cleanupRecords && !records.length) { + throw new Error(`Response from describeCertificate did not contain DomainValidationOptions after ${maxAttempts} attempts.`) + } console.log(`Deleting certificate ${arn}`); await acm.deleteCertificate({ CertificateArn: arn }).promise(); + + if (cleanupRecords) { + console.log(`Deleting ${records.length} DNS records from zone ${hostedZoneId}:`); + + await commitRoute53Records(route53, records, hostedZoneId, 'DELETE'); + } + } catch (err) { if (err.name !== 'ResourceNotFoundException') { throw err; @@ -234,6 +212,66 @@ const deleteCertificate = async function (arn, region) { } }; +/** + * Retrieve the unique domain validation options as records to be upserted (or deleted) from Route53. + * + * Returns an empty array ([]) if the domain validation options is empty or the records are not yet ready. + */ +function getDomainValidationRecords(certificate) { + const options = certificate.DomainValidationOptions || []; + // Ensure all records are ready; there is (at least a theory there's) a chance of a partial response here in rare cases. + if (options.length > 0 && options.every(opt => opt && !!opt.ResourceRecord)) { + // some alternative names will produce the same validation record + // as the main domain (eg. example.com + *.example.com) + // filtering duplicates to avoid errors with adding the same record + // to the route53 zone twice + const unique = options + .map((val) => val.ResourceRecord) + .reduce((acc, cur) => { + acc[cur.Name] = cur; + return acc; + }, {}); + return Object.keys(unique).sort().map(key => unique[key]); + } + return []; +} + +/** + * Execute Route53 ChangeResourceRecordSets for a set of records within a Hosted Zone, + * and wait for the records to commit. Defaults to an 'UPSERT' action. + */ +async function commitRoute53Records(route53, records, hostedZoneId, action = 'UPSERT') { + const changeBatch = await route53.changeResourceRecordSets({ + ChangeBatch: { + Changes: records.map((record) => { + console.log(`${record.Name} ${record.Type} ${record.Value}`); + return { + Action: action, + ResourceRecordSet: { + Name: record.Name, + Type: record.Type, + TTL: 60, + ResourceRecords: [{ + Value: record.Value + }] + } + }; + }), + }, + HostedZoneId: hostedZoneId + }).promise(); + + console.log('Waiting for DNS records to commit...'); + await route53.waitFor('resourceRecordSetsChanged', { + // Wait up to 5 minutes + $waiter: { + delay: 30, + maxAttempts: 10 + }, + Id: changeBatch.ChangeInfo.Id + }).promise(); +} + /** * Main handler, invoked by Lambda */ @@ -262,7 +300,13 @@ exports.certificateRequestHandler = async function (event, context) { // If the resource didn't create correctly, the physical resource ID won't be the // certificate ARN, so don't try to delete it in that case. if (physicalResourceId.startsWith('arn:')) { - await deleteCertificate(physicalResourceId, event.ResourceProperties.Region); + await deleteCertificate( + physicalResourceId, + event.ResourceProperties.Region, + event.ResourceProperties.HostedZoneId, + event.ResourceProperties.Route53Endpoint, + event.ResourceProperties.CleanupRecords === "true", + ); } break; default: 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 5766802aa1e3e..2195ee10d05b5 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,11 +29,11 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/sinon": "^9.0.11", "@aws-cdk/cdk-build-tools": "0.0.0", "aws-sdk": "^2.596.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "eslint": "^7.32.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.25.4", diff --git a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/test/handler.test.js b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/test/handler.test.js index 9f180f20211e9..a6a0632fd5aeb 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/test/handler.test.js +++ b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/test/handler.test.js @@ -996,4 +996,172 @@ describe('DNS Validated Certificate Handler', () => { expect(request.isDone()).toBe(true); }); }); + + describe('Delete option record cleanup', () => { + let describeCertificateFake; + let deleteCertificateFake; + let changeResourceRecordSetsFake; + + beforeEach(() => { + deleteCertificateFake = sinon.fake.resolves({}); + AWS.mock('ACM', 'deleteCertificate', deleteCertificateFake); + changeResourceRecordSetsFake = sinon.fake.resolves({ + ChangeInfo: { + Id: 'bogus' + } + }); + AWS.mock('Route53', 'changeResourceRecordSets', changeResourceRecordSetsFake); + + describeCertificateFake = sinon.fake.resolves({ + Certificate: { + CertificateArn: testCertificateArn, + DomainValidationOptions: [{ + ValidationStatus: 'SUCCESS', + ResourceRecord: { + Name: testRRName, + Type: 'CNAME', + Value: testRRValue + } + }] + } + }); + AWS.mock('ACM', 'describeCertificate', describeCertificateFake); + }); + + test('ignores records if CleanupRecords is not set', () => { + const request = nock(ResponseURL).put('/', body => { + return body.Status === 'SUCCESS'; + }).reply(200); + + return LambdaTester(handler.certificateRequestHandler) + .event({ + RequestType: 'Delete', + RequestId: testRequestId, + PhysicalResourceId: testCertificateArn, + ResourceProperties: { + Region: 'us-east-1', + HostedZoneId: testHostedZoneId, + } + }) + .expectResolve(() => { + sinon.assert.calledWith(describeCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.calledWith(deleteCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.notCalled(changeResourceRecordSetsFake); + expect(request.isDone()).toBe(true); + }); + }); + + test('ignores records if CleanupRecords is not set to "true"', () => { + const request = nock(ResponseURL).put('/', body => { + return body.Status === 'SUCCESS'; + }).reply(200); + + return LambdaTester(handler.certificateRequestHandler) + .event({ + RequestType: 'Delete', + RequestId: testRequestId, + PhysicalResourceId: testCertificateArn, + ResourceProperties: { + Region: 'us-east-1', + HostedZoneId: testHostedZoneId, + CleanupRecords: 'TRUE', // Not "true" + } + }) + .expectResolve(() => { + sinon.assert.calledWith(describeCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.calledWith(deleteCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.notCalled(changeResourceRecordSetsFake); + expect(request.isDone()).toBe(true); + }); + }); + + test('deletes records if CleanupRecords is set to true and records are present', () => { + const request = nock(ResponseURL).put('/', body => { + return body.Status === 'SUCCESS'; + }).reply(200); + + AWS.mock('Route53', 'changeResourceRecordSets', changeResourceRecordSetsFake); + + return LambdaTester(handler.certificateRequestHandler) + .event({ + RequestType: 'Delete', + RequestId: testRequestId, + PhysicalResourceId: testCertificateArn, + ResourceProperties: { + Region: 'us-east-1', + HostedZoneId: testHostedZoneId, + CleanupRecords: 'true', + }, + }) + .expectResolve(() => { + sinon.assert.calledWith(describeCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.calledWith(deleteCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.calledWith(changeResourceRecordSetsFake, sinon.match({ + ChangeBatch: { + Changes: [{ + Action: 'DELETE', + ResourceRecordSet: { + Name: testRRName, + Type: 'CNAME', + TTL: 60, + ResourceRecords: [{ + Value: testRRValue + }] + } + }] + }, + HostedZoneId: testHostedZoneId + })); + expect(request.isDone()).toBe(true); + }); + }); + + test('fails if CleanupRecords is set to true and records are not present', () => { + describeCertificateFake = sinon.fake.resolves({ + Certificate: { + CertificateArn: testCertificateArn, + } + }); + AWS.remock('ACM', 'describeCertificate', describeCertificateFake); + + const request = nock(ResponseURL).put('/', body => { + return body.Status === 'FAILED' && + body.Reason.startsWith('Response from describeCertificate did not contain DomainValidationOptions'); + }).reply(200); + + AWS.mock('Route53', 'changeResourceRecordSets', changeResourceRecordSetsFake); + + return LambdaTester(handler.certificateRequestHandler) + .event({ + RequestType: 'Delete', + RequestId: testRequestId, + PhysicalResourceId: testCertificateArn, + ResourceProperties: { + Region: 'us-east-1', + HostedZoneId: testHostedZoneId, + CleanupRecords: 'true', + }, + }) + .expectResolve(() => { + sinon.assert.calledWith(describeCertificateFake, sinon.match({ + CertificateArn: testCertificateArn + })); + sinon.assert.notCalled(deleteCertificateFake); + sinon.assert.notCalled(changeResourceRecordSetsFake); + expect(request.isDone()).toBe(true); + }); + }); + }); }); diff --git a/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts b/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts index 60c965e21a6ab..db7dc6f1f8663 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts +++ b/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts @@ -47,6 +47,17 @@ export interface DnsValidatedCertificateProps extends CertificateProps { */ readonly customResourceRole?: iam.IRole; + /** + * When set to true, when the DnsValidatedCertificate is deleted, + * the associated Route53 validation records are removed. + * + * CAUTION: If multiple certificates share the same domains (and same validation records), + * this can cause the other certificates to fail renewal and/or not validate. + * Not recommended for production use. + * + * @default false + */ + readonly cleanupRoute53Records?: boolean; } /** @@ -113,6 +124,8 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi HostedZoneId: this.hostedZoneId, Region: props.region, Route53Endpoint: props.route53Endpoint, + // Custom resources properties are always converted to strings; might as well be explict here. + CleanupRecords: props.cleanupRoute53Records ? 'true' : undefined, Tags: cdk.Lazy.list({ produce: () => this.tags.renderTags() }), }, }); diff --git a/packages/@aws-cdk/aws-certificatemanager/test/dns-validated-certificate.test.ts b/packages/@aws-cdk/aws-certificatemanager/test/dns-validated-certificate.test.ts index 935f8680415b4..d6881d61d79ac 100644 --- a/packages/@aws-cdk/aws-certificatemanager/test/dns-validated-certificate.test.ts +++ b/packages/@aws-cdk/aws-certificatemanager/test/dns-validated-certificate.test.ts @@ -14,10 +14,13 @@ test('creates CloudFormation Custom Resource', () => { new DnsValidatedCertificate(stack, 'Certificate', { domainName: 'test.example.com', hostedZone: exampleDotComZone, + subjectAlternativeNames: ['test2.example.com'], + cleanupRoute53Records: true, }); Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::CustomResource', { DomainName: 'test.example.com', + SubjectAlternativeNames: ['test2.example.com'], ServiceToken: { 'Fn::GetAtt': [ 'CertificateCertificateRequestorFunction5E845413', @@ -27,6 +30,7 @@ test('creates CloudFormation Custom Resource', () => { HostedZoneId: { Ref: 'ExampleDotCom4D1B83AA', }, + CleanupRecords: 'true', }); Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.certificateRequestHandler', diff --git a/packages/@aws-cdk/aws-chatbot/README.md b/packages/@aws-cdk/aws-chatbot/README.md index 5e871bb81db48..6f868ff02e17d 100644 --- a/packages/@aws-cdk/aws-chatbot/README.md +++ b/packages/@aws-cdk/aws-chatbot/README.md @@ -18,6 +18,7 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw ```ts import * as chatbot from '@aws-cdk/aws-chatbot'; import * as sns from '@aws-cdk/aws-sns'; +import * as iam from '@aws-cdk/aws-iam'; const slackChannel = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', { slackChannelConfigurationName: 'YOUR_CHANNEL_NAME', @@ -33,7 +34,7 @@ slackChannel.addToRolePolicy(new iam.PolicyStatement({ resources: ['arn:aws:s3:::abc/xyz/123.txt'], })); -slackChannel.addNotificationTopic(new sns.Topic(this, 'MyTopic')) +slackChannel.addNotificationTopic(new sns.Topic(this, 'MyTopic')); ``` ## Log Group diff --git a/packages/@aws-cdk/aws-chatbot/package.json b/packages/@aws-cdk/aws-chatbot/package.json index 2abb1900c7cfd..35a6887f3b5cb 100644 --- a/packages/@aws-cdk/aws-chatbot/package.json +++ b/packages/@aws-cdk/aws-chatbot/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-chatbot/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-chatbot/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..50d86e8a055ce --- /dev/null +++ b/packages/@aws-cdk/aws-chatbot/rosetta/default.ts-fixture @@ -0,0 +1,10 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9/README.md b/packages/@aws-cdk/aws-cloud9/README.md index 36bdc6d4a3d4c..ba418cc56dd46 100644 --- a/packages/@aws-cdk/aws-cloud9/README.md +++ b/packages/@aws-cdk/aws-cloud9/README.md @@ -23,17 +23,23 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. -AWS Cloud9 is a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a browser. It includes a code editor, debugger, and terminal. Cloud9 comes prepackaged with essential tools for popular programming languages, including JavaScript, Python, PHP, and more, so you don’t need to install files or configure your development machine to start new projects. Since your Cloud9 IDE is cloud-based, you can work on your projects from your office, home, or anywhere using an internet-connected machine. Cloud9 also provides a seamless experience for developing serverless applications enabling you to easily define resources, debug, and switch between local and remote execution of serverless applications. With Cloud9, you can quickly share your development environment with your team, enabling you to pair program and track each other's inputs in real time. +AWS Cloud9 is a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a +browser. It includes a code editor, debugger, and terminal. Cloud9 comes prepackaged with essential tools for popular +programming languages, including JavaScript, Python, PHP, and more, so you don’t need to install files or configure your +development machine to start new projects. Since your Cloud9 IDE is cloud-based, you can work on your projects from your +office, home, or anywhere using an internet-connected machine. Cloud9 also provides a seamless experience for developing +serverless applications enabling you to easily define resources, debug, and switch between local and remote execution of +serverless applications. With Cloud9, you can quickly share your development environment with your team, enabling you to pair +program and track each other's inputs in real time. ## Creating EC2 Environment -EC2 Environments are defined with `Ec2Environment`. To create an EC2 environment in the private subnet, specify `subnetSelection` with private `subnetType`. +EC2 Environments are defined with `Ec2Environment`. To create an EC2 environment in the private subnet, specify +`subnetSelection` with private `subnetType`. ```ts -import * as cloud9 from '@aws-cdk/aws-cloud9'; - // create a cloud9 ec2 environment in a new VPC const vpc = new ec2.Vpc(this, 'VPC', { maxAzs: 3}); new cloud9.Ec2Environment(this, 'Cloud9Env', { vpc }); @@ -42,19 +48,19 @@ new cloud9.Ec2Environment(this, 'Cloud9Env', { vpc }); const defaultVpc = ec2.Vpc.fromLookup(this, 'DefaultVPC', { isDefault: true }); new cloud9.Ec2Environment(this, 'Cloud9Env2', { vpc: defaultVpc, - instanceType: new ec2.InstanceType('t3.large') + instanceType: new ec2.InstanceType('t3.large'), }); // or specify in a different subnetSelection const c9env = new cloud9.Ec2Environment(this, 'Cloud9Env3', { - vpc, - subnetSelection: { - subnetType: ec2.SubnetType.PRIVATE - } + vpc, + subnetSelection: { + subnetType: ec2.SubnetType.PRIVATE, + }, }); // print the Cloud9 IDE URL in the output -new cdk.CfnOutput(this, 'URL', { value: c9env.ideUrl }); +new CfnOutput(this, 'URL', { value: c9env.ideUrl }); ``` ## Cloning Repositories @@ -62,16 +68,19 @@ new cdk.CfnOutput(this, 'URL', { value: c9env.ideUrl }); Use `clonedRepositories` to clone one or multiple AWS Codecommit repositories into the environment: ```ts +import * as codecommit from '@aws-cdk/aws-codecommit'; + // create a codecommit repository to clone into the cloud9 environment const repoNew = new codecommit.Repository(this, 'RepoNew', { repositoryName: 'new-repo', }); // import an existing codecommit repository to clone into the cloud9 environment -const repoExisting = codecommit.Repository.fromRepositoryName(stack, 'RepoExisting', 'existing-repo'); +const repoExisting = codecommit.Repository.fromRepositoryName(this, 'RepoExisting', 'existing-repo'); // create a new Cloud9 environment and clone the two repositories -new cloud9.Ec2Environment(stack, 'C9Env', { +declare const vpc: ec2.Vpc; +new cloud9.Ec2Environment(this, 'C9Env', { vpc, clonedRepositories: [ cloud9.CloneRepository.fromCodeCommit(repoNew, '/src/new-repo'), diff --git a/packages/@aws-cdk/aws-cloud9/package.json b/packages/@aws-cdk/aws-cloud9/package.json index a48706cb7d913..7078ef97f408e 100644 --- a/packages/@aws-cdk/aws-cloud9/package.json +++ b/packages/@aws-cdk/aws-cloud9/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-cloud9/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-cloud9/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..e2687d0d2c5bf --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9/rosetta/default.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with packages imported, but nothing else +import { CfnOutput, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as cloud9 from '@aws-cdk/aws-cloud9'; +import * as ec2 from '@aws-cdk/aws-ec2'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts b/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts index 9c30e6c08540c..b71a9f56aea61 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts @@ -99,21 +99,35 @@ export interface CustomResourceProps { * [resource provider framework]: https://docs.aws.amazon.com/cdk/api/latest/docs/custom-resources-readme.html * * ```ts - * // use the provider framework from aws-cdk/custom-resources: - * provider: new custom_resources.Provider({ + * import * as custom_resources from '@aws-cdk/custom-resources'; + * import * as lambda from '@aws-cdk/aws-lambda'; + * import { Stack } from '@aws-cdk/core'; + * declare const myOnEventLambda: lambda.Function; + * declare const myIsCompleteLambda: lambda.Function; + * const stack = new Stack(); + * + * const provider = new custom_resources.Provider(stack, 'myProvider', { * onEventHandler: myOnEventLambda, * isCompleteHandler: myIsCompleteLambda, // optional * }); * ``` * * ```ts + * import * as cloudformation from '@aws-cdk/aws-cloudformation'; + * import * as lambda from '@aws-cdk/aws-lambda'; + * declare const myFunction: lambda.Function; + * * // invoke an AWS Lambda function when a lifecycle event occurs: - * provider: CustomResourceProvider.fromLambda(myFunction) + * const provider = cloudformation.CustomResourceProvider.fromLambda(myFunction); * ``` * * ```ts + * import * as cloudformation from '@aws-cdk/aws-cloudformation'; + * import * as sns from '@aws-cdk/aws-sns'; + * declare const myTopic: sns.Topic; + * * // publish lifecycle events to an SNS topic: - * provider: CustomResourceProvider.fromTopic(myTopic) + * const provider = cloudformation.CustomResourceProvider.fromTopic(myTopic); * ``` */ readonly provider: ICustomResourceProvider; diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index f9d68e24b15a3..0b15fd01bfe35 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", @@ -79,7 +86,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/aws-cloudfront-origins/README.md b/packages/@aws-cdk/aws-cloudfront-origins/README.md index b0de0e2ffdaf4..f88a7e7111b1b 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/README.md +++ b/packages/@aws-cdk/aws-cloudfront-origins/README.md @@ -75,10 +75,15 @@ declare const loadBalancer: elbv2.ApplicationLoadBalancer; const origin = new origins.LoadBalancerV2Origin(loadBalancer, { connectionAttempts: 3, connectionTimeout: Duration.seconds(5), + readTimeout: Duration.seconds(45), protocolPolicy: cloudfront.OriginProtocolPolicy.MATCH_VIEWER, }); ``` +Note that the `readTimeout` property can extend its value over 60 seconds only if a limit increase request for CloudFront origin response timeout +quota has been approved in the target account; otherwise, values over 60 seconds will produce an error at deploy time. Consider that this value is +still limited to a maximum value of 180 seconds, which is a hard limit for that quota. + ## From an HTTP endpoint Origins can also be created from any other HTTP endpoint, given the domain name, and optionally, other origin properties. diff --git a/packages/@aws-cdk/aws-cloudfront-origins/lib/http-origin.ts b/packages/@aws-cdk/aws-cloudfront-origins/lib/http-origin.ts index d6aa44c0bb73d..50b94b0c52191 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/lib/http-origin.ts +++ b/packages/@aws-cdk/aws-cloudfront-origins/lib/http-origin.ts @@ -35,7 +35,10 @@ export interface HttpOriginProps extends cloudfront.OriginProps { /** * Specifies how long, in seconds, CloudFront waits for a response from the origin, also known as the origin response timeout. - * The valid range is from 1 to 60 seconds, inclusive. + * The valid range is from 1 to 180 seconds, inclusive. + * + * Note that values over 60 seconds are possible only after a limit increase request for the origin response timeout quota + * has been approved in the target account; otherwise, values over 60 seconds will produce an error at deploy time. * * @default Duration.seconds(30) */ @@ -58,7 +61,7 @@ export class HttpOrigin extends cloudfront.OriginBase { constructor(domainName: string, private readonly props: HttpOriginProps = {}) { super(domainName, props); - validateSecondsInRangeOrUndefined('readTimeout', 1, 60, props.readTimeout); + validateSecondsInRangeOrUndefined('readTimeout', 1, 180, props.readTimeout); validateSecondsInRangeOrUndefined('keepaliveTimeout', 1, 60, props.keepaliveTimeout); } diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/http-origin.test.ts b/packages/@aws-cdk/aws-cloudfront-origins/test/http-origin.test.ts index c64a04bf26ced..df149f56753b2 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/http-origin.test.ts +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/http-origin.test.ts @@ -70,14 +70,14 @@ test.each([ Duration.seconds(0), Duration.seconds(0.5), Duration.seconds(60.5), - Duration.seconds(61), + Duration.seconds(181), Duration.minutes(5), -])('validates readTimeout is an integer between 1 and 60 seconds', (readTimeout) => { +])('validates readTimeout is an integer between 1 and 180 seconds', (readTimeout) => { expect(() => { new HttpOrigin('www.example.com', { readTimeout, }); - }).toThrow(`readTimeout: Must be an int between 1 and 60 seconds (inclusive); received ${readTimeout.toSeconds()}.`); + }).toThrow(`readTimeout: Must be an int between 1 and 180 seconds (inclusive); received ${readTimeout.toSeconds()}.`); }); test.each([ diff --git a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts index c13a791bfe9b1..c593edd9efec7 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts @@ -430,10 +430,7 @@ export class Distribution extends Resource implements IDistribution { throw new Error('Explicitly disabled logging but provided a logging bucket.'); } - const bucket = props.logBucket ?? new s3.Bucket(this, 'LoggingBucket', { - encryption: s3.BucketEncryption.S3_MANAGED, - enforceSSL: true, - }); + const bucket = props.logBucket ?? new s3.Bucket(this, 'LoggingBucket'); return { bucket: bucket.bucketRegionalDomainName, includeCookies: props.logIncludesCookies, diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts index e590b5740c847..c0a332a2e1b89 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts @@ -954,10 +954,7 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu } if (props.loggingConfig) { - this.loggingBucket = props.loggingConfig.bucket || new s3.Bucket(this, 'LoggingBucket', { - encryption: s3.BucketEncryption.S3_MANAGED, - enforceSSL: true, - }); + this.loggingBucket = props.loggingConfig.bucket || new s3.Bucket(this, 'LoggingBucket'); distributionConfig = { ...distributionConfig, logging: { diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json index 2da6475aff16b..36a334898a57f 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-bucket-logging.expected.json @@ -75,67 +75,9 @@ }, "AnAmazingWebsiteProbably2LoggingBucket222F7CE9": { "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "SSEAlgorithm": "AES256" - } - } - ] - } - }, "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "AnAmazingWebsiteProbably2LoggingBucketPolicyE298B456": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "AnAmazingWebsiteProbably2LoggingBucket222F7CE9" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "AnAmazingWebsiteProbably2LoggingBucket222F7CE9", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "AnAmazingWebsiteProbably2LoggingBucket222F7CE9", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, "AnAmazingWebsiteProbably2CFDistribution7C1CCD12": { "Type": "AWS::CloudFront::Distribution", "Properties": { diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json index ca4afcdc81c17..4ddd5ddb8d373 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json @@ -2,67 +2,9 @@ "Resources": { "MyDistLoggingBucket9B8976BC": { "Type": "AWS::S3::Bucket", - "Properties": { - "BucketEncryption": { - "ServerSideEncryptionConfiguration": [ - { - "ServerSideEncryptionByDefault": { - "SSEAlgorithm": "AES256" - } - } - ] - } - }, "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "MyDistLoggingBucketPolicy847D8D11": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "MyDistLoggingBucket9B8976BC" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "MyDistLoggingBucket9B8976BC", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "MyDistLoggingBucket9B8976BC", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, "MyDistDB88FD9A": { "Type": "AWS::CloudFront::Distribution", "Properties": { diff --git a/packages/@aws-cdk/aws-cloudtrail/lib/cloudtrail.ts b/packages/@aws-cdk/aws-cloudtrail/lib/cloudtrail.ts index 12cf079c5279e..76f6603652dcf 100644 --- a/packages/@aws-cdk/aws-cloudtrail/lib/cloudtrail.ts +++ b/packages/@aws-cdk/aws-cloudtrail/lib/cloudtrail.ts @@ -209,7 +209,7 @@ export class Trail extends Resource { const cloudTrailPrincipal = new iam.ServicePrincipal('cloudtrail.amazonaws.com'); - this.s3bucket = props.bucket || new s3.Bucket(this, 'S3', { encryption: s3.BucketEncryption.UNENCRYPTED }); + this.s3bucket = props.bucket || new s3.Bucket(this, 'S3', { encryption: s3.BucketEncryption.UNENCRYPTED, enforceSSL: true }); this.s3bucket.addToResourcePolicy(new iam.PolicyStatement({ resources: [this.s3bucket.bucketArn], diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 6fb04cb8051e6..1d28c67dd7c28 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-cloudtrail/test/cloudtrail.test.ts b/packages/@aws-cdk/aws-cloudtrail/test/cloudtrail.test.ts index a53a45b2c97ce..1492a3c1377b1 100644 --- a/packages/@aws-cdk/aws-cloudtrail/test/cloudtrail.test.ts +++ b/packages/@aws-cdk/aws-cloudtrail/test/cloudtrail.test.ts @@ -1,5 +1,4 @@ -import { ABSENT, SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; @@ -13,6 +12,36 @@ import { ManagementEventSources, ReadWriteType, Trail } from '../lib'; const ExpectedBucketPolicyProperties = { PolicyDocument: { Statement: [ + { + Action: 's3:*', + Condition: { + Bool: { 'aws:SecureTransport': 'false' }, + }, + Effect: 'Deny', + Principal: { + AWS: '*', + }, + Resource: [ + { + 'Fn::GetAtt': [ + 'MyAmazingCloudTrailS3A580FE27', + 'Arn', + ], + }, + { + 'Fn::Join': [ + '', + [{ + 'Fn::GetAtt': [ + 'MyAmazingCloudTrailS3A580FE27', + 'Arn', + ], + }, + '/*'], + ], + }, + ], + }, { Action: 's3:GetBucketAcl', Effect: 'Allow', @@ -69,11 +98,11 @@ describe('cloudtrail', () => { test('with no properties', () => { const stack = getTestStack(); new Trail(stack, 'MyAmazingCloudTrail'); - expect(stack).toHaveResource('AWS::CloudTrail::Trail'); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::S3::BucketPolicy', ExpectedBucketPolicyProperties); - expect(stack).not.toHaveResource('AWS::Logs::LogGroup'); - const trail: any = SynthUtils.synthesize(stack).template.Resources.MyAmazingCloudTrail54516E8D; + Template.fromStack(stack).resourceCountIs('AWS::CloudTrail::Trail', 1); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', ExpectedBucketPolicyProperties); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 0); + const trail: any = Template.fromStack(stack).toJSON().Resources.MyAmazingCloudTrail54516E8D; expect(trail.DependsOn).toEqual(['MyAmazingCloudTrailS3Policy39C120B0']); }); @@ -98,10 +127,10 @@ describe('cloudtrail', () => { new Trail(stack, 'Trail', { bucket: Trailbucket }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail'); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::S3::BucketPolicy'); - expect(stack).not.toHaveResource('AWS::Logs::LogGroup'); + Template.fromStack(stack).resourceCountIs('AWS::CloudTrail::Trail', 1); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).resourceCountIs('AWS::S3::BucketPolicy', 1); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 0); }); test('with sns topic', () => { @@ -111,9 +140,9 @@ describe('cloudtrail', () => { new Trail(stack, 'Trail', { snsTopic: topic }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail'); - expect(stack).not.toHaveResource('AWS::Logs::LogGroup'); - expect(stack).toHaveResource('AWS::SNS::TopicPolicy', { + Template.fromStack(stack).resourceCountIs('AWS::CloudTrail::Trail', 1); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 0); + Template.fromStack(stack).hasResourceProperties('AWS::SNS::TopicPolicy', { PolicyDocument: { Statement: [ { @@ -137,7 +166,7 @@ describe('cloudtrail', () => { // WHEN new Trail(stack, 'Trail', { bucket }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { S3BucketName: 'somebucket', }); }); @@ -149,12 +178,46 @@ describe('cloudtrail', () => { // WHEN new Trail(stack, 'Trail', { s3KeyPrefix: 'someprefix' }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail'); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::S3::BucketPolicy', { + Template.fromStack(stack).resourceCountIs('AWS::CloudTrail::Trail', 1); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { Bucket: { Ref: 'TrailS30071F172' }, PolicyDocument: { Statement: [ + { + Action: 's3:*', + Condition: { + Bool: { + 'aws:SecureTransport': 'false', + }, + }, + Effect: 'Deny', + Principal: { + AWS: '*', + }, + Resource: [ + { + 'Fn::GetAtt': [ + 'TrailS30071F172', + 'Arn', + ], + }, + { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'TrailS30071F172', + 'Arn', + ], + }, + '/*', + ], + ], + }, + ], + }, { Action: 's3:GetBucketAcl', Effect: 'Allow', @@ -199,21 +262,21 @@ describe('cloudtrail', () => { trailName: 'UnencryptedTrail', }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { TrailName: 'EncryptionKeyTrail', KMSKeyId: { 'Fn::GetAtt': ['keyFEDD6EC0', 'Arn'], }, }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { TrailName: 'KmsKeyTrail', KMSKeyId: { 'Fn::GetAtt': ['keyFEDD6EC0', 'Arn'], }, }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { TrailName: 'UnencryptedTrail', - KMSKeyId: ABSENT, + KMSKeyId: Match.absent(), }); }); @@ -235,13 +298,13 @@ describe('cloudtrail', () => { sendToCloudWatchLogs: true, }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail'); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::S3::BucketPolicy', ExpectedBucketPolicyProperties); - expect(stack).toHaveResource('AWS::Logs::LogGroup'); - expect(stack).toHaveResource('AWS::IAM::Role'); - expect(stack).toHaveResource('AWS::Logs::LogGroup', { RetentionInDays: 365 }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).resourceCountIs('AWS::CloudTrail::Trail', 1); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', ExpectedBucketPolicyProperties); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 1); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 1); + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 365 }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [{ @@ -255,7 +318,7 @@ describe('cloudtrail', () => { PolicyName: logsRolePolicyName, Roles: [{ Ref: 'MyAmazingCloudTrailLogsRoleF2CCF977' }], }); - const trail: any = SynthUtils.synthesize(stack).template.Resources.MyAmazingCloudTrail54516E8D; + const trail: any = Template.fromStack(stack).toJSON().Resources.MyAmazingCloudTrail54516E8D; expect(trail.DependsOn).toEqual([logsRolePolicyName, logsRoleName, 'MyAmazingCloudTrailS3Policy39C120B0']); }); @@ -266,15 +329,15 @@ describe('cloudtrail', () => { cloudWatchLogsRetention: RetentionDays.ONE_WEEK, }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail'); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::S3::BucketPolicy', ExpectedBucketPolicyProperties); - expect(stack).toHaveResource('AWS::Logs::LogGroup'); - expect(stack).toHaveResource('AWS::IAM::Role'); - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).resourceCountIs('AWS::CloudTrail::Trail', 1); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', ExpectedBucketPolicyProperties); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 1); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 1); + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 7, }); - const trail: any = SynthUtils.synthesize(stack).template.Resources.MyAmazingCloudTrail54516E8D; + const trail: any = Template.fromStack(stack).toJSON().Resources.MyAmazingCloudTrail54516E8D; expect(trail.DependsOn).toEqual([logsRolePolicyName, logsRoleName, 'MyAmazingCloudTrailS3Policy39C120B0']); }); @@ -289,19 +352,19 @@ describe('cloudtrail', () => { cloudWatchLogGroup, }); - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 5, }); - expect(stack).toHaveResource('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { CloudWatchLogsLogGroupArn: stack.resolve(cloudWatchLogGroup.logGroupArn), }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: [{ + Statement: [Match.objectLike({ Resource: stack.resolve(cloudWatchLogGroup.logGroupArn), - }], + })], }, }); }); @@ -313,7 +376,7 @@ describe('cloudtrail', () => { cloudWatchLogsRetention: RetentionDays.ONE_WEEK, }); expect(t.logGroup).toBeUndefined(); - expect(stack).not.toHaveResource('AWS::Logs::LogGroup'); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 0); }); }); @@ -324,7 +387,7 @@ describe('cloudtrail', () => { const cloudTrail = new Trail(stack, 'MyAmazingCloudTrail'); cloudTrail.logAllS3DataEvents(); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { DataResources: [{ @@ -344,8 +407,8 @@ describe('cloudtrail', () => { }, ], }], - IncludeManagementEvents: ABSENT, - ReadWriteType: ABSENT, + IncludeManagementEvents: Match.absent(), + ReadWriteType: Match.absent(), }, ], }); @@ -362,7 +425,7 @@ describe('cloudtrail', () => { objectPrefix: 'prefix-1/prefix-2', }]); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { DataResources: [{ @@ -400,7 +463,7 @@ describe('cloudtrail', () => { const stack = getTestStack(); const cloudTrail = new Trail(stack, 'MyAmazingCloudTrail'); cloudTrail.addS3EventSelector([]); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [], }); }); @@ -411,7 +474,7 @@ describe('cloudtrail', () => { const cloudTrail = new Trail(stack, 'MyAmazingCloudTrail'); cloudTrail.logAllS3DataEvents({ includeManagementEvents: false, readWriteType: ReadWriteType.READ_ONLY }); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { DataResources: [{ @@ -443,7 +506,7 @@ describe('cloudtrail', () => { new Trail(stack, 'MyAmazingCloudTrail', { managementEvents: ReadWriteType.WRITE_ONLY }); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { IncludeManagementEvents: true, @@ -467,7 +530,7 @@ describe('cloudtrail', () => { excludeManagementEventSources: [], }); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { DataResources: [{ @@ -517,7 +580,7 @@ describe('cloudtrail', () => { const cloudTrail = new Trail(stack, 'MyAmazingCloudTrail'); cloudTrail.addLambdaEventSelector([lambdaFunction]); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { DataResources: [{ @@ -537,7 +600,7 @@ describe('cloudtrail', () => { const cloudTrail = new Trail(stack, 'MyAmazingCloudTrail'); cloudTrail.logAllLambdaDataEvents(); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { DataResources: [{ @@ -569,7 +632,7 @@ describe('cloudtrail', () => { managementEvents: ReadWriteType.NONE, }); - expect(stack).toHaveResourceLike('AWS::CloudTrail::Trail', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudTrail::Trail', { EventSelectors: [ { IncludeManagementEvents: false, @@ -596,7 +659,7 @@ describe('cloudtrail', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventPattern: { 'detail-type': [ 'AWS API Call via CloudTrail', @@ -612,4 +675,4 @@ describe('cloudtrail', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/aws-cloudtrail/test/integ.cloudtrail.lit.expected.json b/packages/@aws-cdk/aws-cloudtrail/test/integ.cloudtrail.lit.expected.json index 5b90761ade1ff..90c9dd9724771 100644 --- a/packages/@aws-cdk/aws-cloudtrail/test/integ.cloudtrail.lit.expected.json +++ b/packages/@aws-cdk/aws-cloudtrail/test/integ.cloudtrail.lit.expected.json @@ -97,6 +97,40 @@ }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "TrailS30071F172", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "TrailS30071F172", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:GetBucketAcl", "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/README.md b/packages/@aws-cdk/aws-cloudwatch-actions/README.md index f13861a8c1555..7788dc363a5ec 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/README.md +++ b/packages/@aws-cdk/aws-cloudwatch-actions/README.md @@ -11,18 +11,30 @@ This library contains a set of classes which can be used as CloudWatch Alarm actions. -The currently implemented actions are: EC2 Actions, SNS Actions, Autoscaling Actions and Aplication Autoscaling Actions +The currently implemented actions are: EC2 Actions, SNS Actions, SSM OpsCenter Actions, Autoscaling Actions and Application Autoscaling Actions ## EC2 Action Example ```ts -import * as cw from "@aws-cdk/aws-cloudwatch"; // Alarm must be configured with an EC2 per-instance metric -let alarm: cw.Alarm; +declare const alarm: cloudwatch.Alarm; // Attach a reboot when alarm triggers alarm.addAlarmAction( - new Ec2Action(Ec2InstanceActions.REBOOT) + new actions.Ec2Action(actions.Ec2InstanceAction.REBOOT), +); +``` + +## SSM OpsCenter Action Example + +```ts +declare const alarm: cloudwatch.Alarm; +// Create an OpsItem with specific severity and category when alarm triggers +alarm.addAlarmAction( + new actions.SsmAction( + actions.OpsItemSeverity.CRITICAL, + actions.OpsItemCategory.PERFORMANCE // category is optional + ) ); ``` diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts b/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts index 5a384eba01247..191da008b23a9 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts @@ -2,3 +2,4 @@ export * from './appscaling'; export * from './autoscaling'; export * from './sns'; export * from './ec2'; +export * from './ssm'; diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts b/packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts new file mode 100644 index 0000000000000..0d26c4258c0d5 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts @@ -0,0 +1,79 @@ +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import { Stack } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +/** + * Types of OpsItem severity available + */ +export enum OpsItemSeverity { + /** + * Set the severity to critical + */ + CRITICAL = '1', + /** + * Set the severity to high + */ + HIGH = '2', + /** + * Set the severity to medium + */ + MEDIUM = '3', + /** + * Set the severity to low + */ + LOW = '4' +} + +/** + * Types of OpsItem category available + */ +export enum OpsItemCategory { + /** + * Set the category to availability + */ + AVAILABILITY = 'Availability', + /** + * Set the category to cost + */ + COST = 'Cost', + /** + * Set the category to performance + */ + PERFORMANCE = 'Performance', + /** + * Set the category to recovery + */ + RECOVERY = 'Recovery', + /** + * Set the category to security + */ + SECURITY = 'Security' +} + +/** + * Use an SSM OpsItem action as an Alarm action + */ +export class SsmAction implements cloudwatch.IAlarmAction { + private severity: OpsItemSeverity; + private category?: OpsItemCategory; + + constructor(severity: OpsItemSeverity, category?: OpsItemCategory) { + this.severity = severity; + this.category = category; + } + + /** + * Returns an alarm action configuration to use an SSM OpsItem action as an alarm action + */ + bind(_scope: Construct, _alarm: cloudwatch.IAlarm): cloudwatch.AlarmActionConfig { + if (this.category === undefined) { + return { alarmActionArn: `arn:aws:ssm:${Stack.of(_scope).region}:${Stack.of(_scope).account}:opsitem:${this.severity}` }; + } else { + return { alarmActionArn: `arn:aws:ssm:${Stack.of(_scope).region}:${Stack.of(_scope).account}:opsitem:${this.severity}#CATEGORY=${this.category}` }; + } + } +} + diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/package.json b/packages/@aws-cdk/aws-cloudwatch-actions/package.json index 08a59bffab0d5..2a37790862f9b 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/package.json +++ b/packages/@aws-cdk/aws-cloudwatch-actions/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-cloudwatch-actions/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..c473f965be130 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch-actions/rosetta/default.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as actions from '@aws-cdk/aws-cloudwatch-actions'; +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts b/packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts new file mode 100644 index 0000000000000..a4bb582005b22 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts @@ -0,0 +1,82 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import { Stack } from '@aws-cdk/core'; +import * as actions from '../lib'; + +test('can use ssm with critical severity and performance category as alarm action', () => { + // GIVEN + const stack = new Stack(); + const alarm = new cloudwatch.Alarm(stack, 'Alarm', { + metric: new cloudwatch.Metric({ + namespace: 'AWS', + metricName: 'Test', + }), + evaluationPeriods: 3, + threshold: 100, + }); + + // WHEN + alarm.addAlarmAction(new actions.SsmAction(actions.OpsItemSeverity.CRITICAL, actions.OpsItemCategory.PERFORMANCE)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + AlarmActions: [ + { + 'Fn::Join': [ + '', + [ + 'arn:aws:ssm:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':opsitem:1#CATEGORY=Performance', + ], + ], + }, + ], + }); +}); + + +test('can use ssm with meduim severity and no category as alarm action', () => { + // GIVEN + const stack = new Stack(); + const alarm = new cloudwatch.Alarm(stack, 'Alarm', { + metric: new cloudwatch.Metric({ + namespace: 'AWS', + metricName: 'Test', + }), + evaluationPeriods: 3, + threshold: 100, + }); + + // WHEN + alarm.addAlarmAction(new actions.SsmAction(actions.OpsItemSeverity.MEDIUM)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + AlarmActions: [ + { + 'Fn::Join': [ + '', + [ + 'arn:aws:ssm:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':opsitem:3', + ], + ], + }, + ], + }); +}); + diff --git a/packages/@aws-cdk/aws-codeartifact/package.json b/packages/@aws-cdk/aws-codeartifact/package.json index dfde889c0446f..943a1e3a421a7 100644 --- a/packages/@aws-cdk/aws-codeartifact/package.json +++ b/packages/@aws-cdk/aws-codeartifact/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-codeartifact", "module": "aws_cdk.aws_codeartifact" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 6bde99b6e2c63..0f16ed23d8b1d 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -83,7 +83,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-codebuild/test/codebuild.test.ts b/packages/@aws-cdk/aws-codebuild/test/codebuild.test.ts index 1cc66df6b236f..5a058625649c3 100644 --- a/packages/@aws-cdk/aws-codebuild/test/codebuild.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/codebuild.test.ts @@ -1,5 +1,4 @@ -import { ABSENT, SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as codecommit from '@aws-cdk/aws-codecommit'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; @@ -17,7 +16,7 @@ describe('default properties', () => { new codebuild.PipelineProject(stack, 'MyProject'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyProjectRole9BBE5233': { 'Type': 'AWS::IAM::Role', @@ -177,7 +176,7 @@ describe('default properties', () => { source, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyRepoF4F48043': { 'Type': 'AWS::CodeCommit::Repository', @@ -361,7 +360,7 @@ describe('default properties', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyBucketF68F3FF0': { 'Type': 'AWS::S3::Bucket', @@ -572,7 +571,7 @@ describe('default properties', () => { }), }); - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { Type: 'GITHUB', Location: 'https://github.com/testowner/testrepo.git', @@ -584,7 +583,7 @@ describe('default properties', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Triggers: { Webhook: true, FilterGroups: [ @@ -620,7 +619,7 @@ describe('default properties', () => { }), }); - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { Type: 'GITHUB_ENTERPRISE', InsecureSsl: true, @@ -630,7 +629,7 @@ describe('default properties', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Triggers: { Webhook: true, FilterGroups: [ @@ -672,7 +671,7 @@ describe('default properties', () => { }), }); - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { Type: 'BITBUCKET', Location: 'https://bitbucket.org/testowner/testrepo.git', @@ -681,7 +680,7 @@ describe('default properties', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Triggers: { Webhook: true, FilterGroups: [ @@ -710,7 +709,7 @@ describe('default properties', () => { }), }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Triggers: { Webhook: true, BuildType: 'BUILD_BATCH', @@ -725,7 +724,7 @@ describe('default properties', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -740,7 +739,7 @@ describe('default properties', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -809,7 +808,7 @@ describe('default properties', () => { securityGroups: [securityGroup], }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'VpcConfig': { 'SecurityGroupIds': [ { @@ -900,7 +899,7 @@ describe('default properties', () => { new codebuild.PipelineProject(stack, 'MyProject'); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { EncryptionKey: 'alias/aws/s3', }); }); @@ -915,7 +914,7 @@ describe('default properties', () => { grantReportGroupPermissions: false, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ {}, // CloudWatch logs @@ -960,7 +959,7 @@ test('using timeout and path in S3 artifacts sets it correctly', () => { timeout: cdk.Duration.minutes(123), }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Artifacts': { 'Path': 'some/path', 'Name': 'some_name', @@ -999,7 +998,7 @@ describe('secondary sources', () => { identifier: 'id', })); - expect(() => SynthUtils.synthesize(stack)).toThrow(/secondary sources/); + expect(() => Template.fromStack(stack)).toThrow(/secondary sources/); }); test('added with an identifer after the Project has been created are rendered in the template', () => { @@ -1018,7 +1017,7 @@ describe('secondary sources', () => { identifier: 'source1', })); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'SecondarySources': [ { 'SourceIdentifier': 'source1', @@ -1047,7 +1046,7 @@ describe('secondary source versions', () => { version: 'someversion', })); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'SecondarySources': [ { 'SourceIdentifier': 'source1', @@ -1079,7 +1078,7 @@ describe('secondary source versions', () => { identifier: 'source1', })); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'SecondarySources': [ { 'SourceIdentifier': 'source1', @@ -1105,7 +1104,7 @@ describe('fileSystemLocations', () => { })], }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'FileSystemLocations': [ { 'Identifier': 'myidentifier2', @@ -1132,7 +1131,7 @@ describe('fileSystemLocations', () => { mountOptions: 'opts', })); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'FileSystemLocations': [ { 'Identifier': 'myidentifier3', @@ -1177,7 +1176,7 @@ describe('secondary artifacts', () => { identifier: 'id', })); - expect(() => SynthUtils.synthesize(stack)).toThrow(/secondary artifacts/); + expect(() => Template.fromStack(stack)).toThrow(/secondary artifacts/); }); test('added with an identifier after the Project has been created are rendered in the template', () => { @@ -1197,7 +1196,7 @@ describe('secondary artifacts', () => { identifier: 'artifact1', })); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'SecondaryArtifacts': [ { 'ArtifactIdentifier': 'artifact1', @@ -1225,7 +1224,7 @@ describe('secondary artifacts', () => { encryption: false, })); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'SecondaryArtifacts': [ { 'ArtifactIdentifier': 'artifact1', @@ -1243,7 +1242,7 @@ describe('artifacts', () => { new codebuild.PipelineProject(stack, 'MyProject'); - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Source': { 'Type': 'CODEPIPELINE', }, @@ -1283,9 +1282,9 @@ describe('artifacts', () => { }), }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Artifacts': { - 'Name': ABSENT, + 'Name': Match.absent(), 'ArtifactIdentifier': 'artifact1', 'OverrideArtifactName': true, }, @@ -1308,11 +1307,11 @@ describe('artifacts', () => { }), }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Artifacts': { 'ArtifactIdentifier': 'artifact1', 'Name': 'specificname', - 'OverrideArtifactName': ABSENT, + 'OverrideArtifactName': Match.absent(), }, }); }); @@ -1334,7 +1333,7 @@ test('events', () => { project.onStateChange('OnStateChange', { target: { bind: () => ({ arn: 'ARN', id: 'ID' }) } }); project.onBuildStarted('OnBuildStarted', { target: { bind: () => ({ arn: 'ARN', id: 'ID' }) } }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.codebuild', @@ -1356,7 +1355,7 @@ test('events', () => { 'State': 'ENABLED', }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.codebuild', @@ -1378,7 +1377,7 @@ test('events', () => { 'State': 'ENABLED', }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.codebuild', @@ -1397,7 +1396,7 @@ test('events', () => { 'State': 'ENABLED', }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.codebuild', @@ -1416,7 +1415,7 @@ test('events', () => { 'State': 'ENABLED', }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.codebuild', @@ -1455,7 +1454,7 @@ test('environment variables can be overridden at the project level', () => { }, }); - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Source': { 'Type': 'CODEPIPELINE', }, @@ -1554,7 +1553,7 @@ test('fromCodebuildImage', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'Image': 'aws/codebuild/standard:4.0', }, @@ -1571,7 +1570,7 @@ describe('Windows2019 image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'Type': 'WINDOWS_SERVER_2019_CONTAINER', 'ComputeType': 'BUILD_GENERAL1_MEDIUM', @@ -1591,7 +1590,7 @@ describe('ARM image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'Type': 'ARM_CONTAINER', 'ComputeType': 'BUILD_GENERAL1_LARGE', @@ -1608,7 +1607,7 @@ describe('ARM image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'Type': 'ARM_CONTAINER', 'ComputeType': 'BUILD_GENERAL1_SMALL', @@ -1638,7 +1637,7 @@ describe('ARM image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'Type': 'ARM_CONTAINER', 'ComputeType': 'BUILD_GENERAL1_LARGE', @@ -1867,7 +1866,7 @@ test('enableBatchBuilds()', () => { throw new Error('Expecting return value with role'); } - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { BuildBatchConfig: { ServiceRole: { 'Fn::GetAtt': [ @@ -1878,7 +1877,7 @@ test('enableBatchBuilds()', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -1893,7 +1892,7 @@ test('enableBatchBuilds()', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json index c1680ca40cea9..d98388270cec6 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -171,4 +175,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.expected.json index 17cec3ba454e2..011e192831fb9 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -166,4 +170,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-logging.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.project-logging.expected.json index 6dbe8a8127224..03ffa27190981 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-logging.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-logging.expected.json @@ -39,6 +39,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -223,4 +227,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.expected.json index a8c439a0eaad1..f8c74e1af60e6 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.expected.json @@ -64,6 +64,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -218,4 +222,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/linux-gpu-build-image.test.ts b/packages/@aws-cdk/aws-codebuild/test/linux-gpu-build-image.test.ts index d3b0d44dde984..106836acdac21 100644 --- a/packages/@aws-cdk/aws-codebuild/test/linux-gpu-build-image.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/linux-gpu-build-image.test.ts @@ -1,5 +1,4 @@ -import { arrayWith, objectLike } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ecr from '@aws-cdk/aws-ecr'; import * as cdk from '@aws-cdk/core'; import * as codebuild from '../lib'; @@ -22,7 +21,7 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Environment: { ComputeType: 'BUILD_GENERAL1_LARGE', Image: { @@ -37,9 +36,9 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith(objectLike({ + Statement: Match.arrayWith([Match.objectLike({ Action: [ 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', @@ -54,7 +53,7 @@ describe('Linux GPU build image', () => { ':123456789012:repository/my-repo', ]], }, - })), + })]), }, }); }); @@ -78,7 +77,7 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Environment: { ComputeType: 'BUILD_GENERAL1_LARGE', Image: { @@ -96,9 +95,9 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith(objectLike({ + Statement: Match.arrayWith([Match.objectLike({ Action: [ 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', @@ -116,7 +115,7 @@ describe('Linux GPU build image', () => { { Ref: 'myrepo5DFA62E5' }, ]], }, - })), + })]), }, }); }); @@ -138,7 +137,7 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Environment: { ComputeType: 'BUILD_GENERAL1_LARGE', Image: { @@ -154,9 +153,9 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith(objectLike({ + Statement: Match.arrayWith([Match.objectLike({ Action: [ 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', @@ -173,7 +172,7 @@ describe('Linux GPU build image', () => { ':repository/test-repo', ]], }, - })), + })]), }, }); }); @@ -195,7 +194,7 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Environment: { ComputeType: 'BUILD_GENERAL1_LARGE', Image: { @@ -210,9 +209,9 @@ describe('Linux GPU build image', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith(objectLike({ + Statement: Match.arrayWith([Match.objectLike({ Action: [ 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', @@ -227,7 +226,7 @@ describe('Linux GPU build image', () => { ':585695036304:repository/foo/bar/foo/fooo', ]], }, - })), + })]), }, }); }); diff --git a/packages/@aws-cdk/aws-codebuild/test/notification-rule.test.ts b/packages/@aws-cdk/aws-codebuild/test/notification-rule.test.ts index 45d6a65d76ce4..e0dc908185427 100644 --- a/packages/@aws-cdk/aws-codebuild/test/notification-rule.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/notification-rule.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; import * as codebuild from '../lib'; @@ -24,7 +24,7 @@ test('notifications rule', () => { project.notifyOnBuildFailed('NotifyOnBuildFailed', target); - expect(stack).toHaveResource('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyCodebuildProjectNotifyOnBuildSucceeded77719592', DetailType: 'FULL', EventTypeIds: [ @@ -46,7 +46,7 @@ test('notifications rule', () => { ], }); - expect(stack).toHaveResource('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyCodebuildProjectNotifyOnBuildFailedF680E310', DetailType: 'FULL', EventTypeIds: [ diff --git a/packages/@aws-cdk/aws-codebuild/test/project.test.ts b/packages/@aws-cdk/aws-codebuild/test/project.test.ts index bf93885378ac5..5c7a17d7eb40d 100644 --- a/packages/@aws-cdk/aws-codebuild/test/project.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/project.test.ts @@ -1,5 +1,4 @@ -import { ABSENT, objectLike, ResourcePart, arrayWith } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as logs from '@aws-cdk/aws-logs'; @@ -24,7 +23,7 @@ test('can use filename as buildspec', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { BuildSpec: 'hello.yml', }, @@ -41,7 +40,7 @@ test('can use buildspec literal', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { BuildSpec: '{\n "phases": [\n "say hi"\n ]\n}', }, @@ -67,7 +66,7 @@ test('can use yamlbuildspec literal', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { BuildSpec: 'text: text\ndecimal: 10\nlist:\n - say hi\nobj:\n text: text\n decimal: 10\n list:\n - say hi\n', }, @@ -110,7 +109,7 @@ describe('GitHub source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { Type: 'GITHUB', Location: 'https://github.com/testowner/testrepo.git', @@ -134,7 +133,7 @@ describe('GitHub source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { SourceVersion: 'testbranch', }); }); @@ -153,7 +152,7 @@ describe('GitHub source', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Source: { ReportBuildStatus: false, }, @@ -174,7 +173,7 @@ describe('GitHub source', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Triggers: { Webhook: true, }, @@ -207,7 +206,7 @@ describe('GitHub source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::SourceCredential', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::SourceCredential', { 'ServerType': 'GITHUB', 'AuthType': 'PERSONAL_ACCESS_TOKEN', 'Token': 'my-access-token', @@ -229,7 +228,7 @@ describe('GitHub Enterprise source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { SourceVersion: 'testbranch', }); }); @@ -244,7 +243,7 @@ describe('GitHub Enterprise source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::SourceCredential', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::SourceCredential', { 'ServerType': 'GITHUB_ENTERPRISE', 'AuthType': 'PERSONAL_ACCESS_TOKEN', 'Token': 'my-access-token', @@ -267,7 +266,7 @@ describe('BitBucket source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { SourceVersion: 'testbranch', }); }); @@ -283,7 +282,7 @@ describe('BitBucket source', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::SourceCredential', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::SourceCredential', { 'ServerType': 'BITBUCKET', 'AuthType': 'BASIC_AUTH', 'Username': 'my-username', @@ -303,10 +302,10 @@ describe('caching', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Cache: { Type: 'NO_CACHE', - Location: ABSENT, + Location: Match.absent(), }, }); }); @@ -327,7 +326,7 @@ describe('caching', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Cache: { Type: 'S3', Location: { @@ -361,7 +360,7 @@ describe('caching', () => { }); // THEN - expect(stack).toHaveResource('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { SourceVersion: 's3version', }); }); @@ -381,7 +380,7 @@ describe('caching', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Cache: { Type: 'LOCAL', Modes: [ @@ -406,10 +405,10 @@ describe('caching', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { Cache: { Type: 'NO_CACHE', - Location: ABSENT, + Location: Match.absent(), }, }); }); @@ -434,18 +433,18 @@ test('if a role is shared between projects in a VPC, the VPC Policy is only atta // - 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).toCountResources('AWS::IAM::Policy', 2); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 2); // THEN - both Projects have a DependsOn on the same policy - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResource('AWS::CodeBuild::Project', { Properties: { Name: 'P1' }, DependsOn: ['Project1PolicyDocumentF9761562'], - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResource('AWS::CodeBuild::Project', { Properties: { Name: 'P1' }, DependsOn: ['Project1PolicyDocumentF9761562'], - }, ResourcePart.CompleteDefinition); + }); }); test('can use an imported Role for a Project within a VPC', () => { @@ -462,7 +461,7 @@ test('can use an imported Role for a Project within a VPC', () => { vpc, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { // no need to do any assertions }); }); @@ -484,15 +483,15 @@ test('can use an imported Role with mutable = false for a Project within a VPC', vpc, }); - expect(stack).toCountResources('AWS::IAM::Policy', 0); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); // Check that the CodeBuild project does not have a DependsOn - expect(stack).toHaveResource('AWS::CodeBuild::Project', (res: any) => { + Template.fromStack(stack).hasResource('AWS::CodeBuild::Project', (res: any) => { if (res.DependsOn && res.DependsOn.length > 0) { throw new Error(`CodeBuild project should have no DependsOn, but got: ${JSON.stringify(res, undefined, 2)}`); } return true; - }, ResourcePart.CompleteDefinition); + }); }); test('can use an ImmutableRole for a Project within a VPC', () => { @@ -512,15 +511,15 @@ test('can use an ImmutableRole for a Project within a VPC', () => { vpc, }); - expect(stack).toCountResources('AWS::IAM::Policy', 0); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); // Check that the CodeBuild project does not have a DependsOn - expect(stack).toHaveResource('AWS::CodeBuild::Project', (res: any) => { + Template.fromStack(stack).hasResource('AWS::CodeBuild::Project', (res: any) => { if (res.DependsOn && res.DependsOn.length > 0) { throw new Error(`CodeBuild project should have no DependsOn, but got: ${JSON.stringify(res, undefined, 2)}`); } return true; - }, ResourcePart.CompleteDefinition); + }); }); test('metric method generates a valid CloudWatch metric', () => { @@ -557,7 +556,7 @@ describe('CodeBuild test reports group', () => { }); reportGroup.grantWrite(project); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ {}, @@ -598,8 +597,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - Environment: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + Environment: Match.objectLike({ RegistryCredential: { CredentialProvider: 'SECRETS_MANAGER', Credential: { 'Ref': 'SecretA720EF05' }, @@ -625,8 +624,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - Environment: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + Environment: Match.objectLike({ RegistryCredential: { CredentialProvider: 'SECRETS_MANAGER', Credential: 'MySecretName', @@ -655,8 +654,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - LogsConfig: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + LogsConfig: Match.objectLike({ CloudWatchLogs: { GroupName: 'MyLogGroupName', Status: 'ENABLED', @@ -684,8 +683,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - LogsConfig: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + LogsConfig: Match.objectLike({ CloudWatchLogs: { Status: 'DISABLED', }, @@ -713,8 +712,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - LogsConfig: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + LogsConfig: Match.objectLike({ S3Logs: { Location: 'mybucketname/my-logs', Status: 'ENABLED', @@ -746,8 +745,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - LogsConfig: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + LogsConfig: Match.objectLike({ CloudWatchLogs: { GroupName: 'MyLogGroupName', Status: 'ENABLED', @@ -780,8 +779,8 @@ describe('Environment', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - Environment: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + Environment: Match.objectLike({ Certificate: { 'Fn::Join': ['', [ 'arn:', @@ -818,8 +817,8 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { - Environment: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { + Environment: Match.objectLike({ EnvironmentVariables: [{ Name: 'ENV_VAR1', Type: 'PARAMETER_STORE', @@ -855,9 +854,9 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith(objectLike({ + 'Statement': Match.arrayWith([Match.objectLike({ 'Action': 'ssm:GetParameters', 'Effect': 'Allow', 'Resource': [{ @@ -900,7 +899,7 @@ describe('EnvironmentVariables', () => { ], ], }], - })), + })]), }, }); }); @@ -928,14 +927,14 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).not.toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', Match.not({ 'PolicyDocument': { - 'Statement': arrayWith(objectLike({ + 'Statement': Match.arrayWith([Match.objectLike({ 'Action': 'ssm:GetParameters', 'Effect': 'Allow', - })), + })]), }, - }); + })); }); }); @@ -955,7 +954,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -968,9 +967,9 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { @@ -984,7 +983,7 @@ describe('EnvironmentVariables', () => { ':secret:my-secret-??????', ]], }, - }), + }]), }, }); }); @@ -1004,7 +1003,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1017,9 +1016,9 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { @@ -1033,7 +1032,7 @@ describe('EnvironmentVariables', () => { ':secret:my-secret-??????', ]], }, - }), + }]), }, }); }); @@ -1053,7 +1052,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1066,26 +1065,26 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': 'arn:aws:secretsmanager:us-west-2:123456789012:secret:my-secret-123456*', - }), + }]), }, }); // THEN - expect(stack).not.toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', Match.not({ 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': 'arn:aws:kms:us-west-2:123456789012:key/*', - }), + }]), }, - }); + })); }); test('can be provided as a verbatim partial secret ARN', () => { @@ -1103,7 +1102,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1116,26 +1115,26 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': 'arn:aws:secretsmanager:us-west-2:123456789012:secret:mysecret*', - }), + }]), }, }); // THEN - expect(stack).not.toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', Match.not({ 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': 'arn:aws:kms:us-west-2:123456789012:key/*', - }), + }]), }, - }); + })); }); test("when provided as a verbatim partial secret ARN from another account, adds permission to decrypt keys in the Secret's account", () => { @@ -1156,13 +1155,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': 'arn:aws:kms:us-west-2:901234567890:key/*', - }), + }]), }, }); }); @@ -1189,13 +1188,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': 'arn:aws:kms:us-west-2:901234567890:key/*', - }), + }]), }, }); }); @@ -1216,7 +1215,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1229,13 +1228,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { 'Ref': 'SecretA720EF05' }, - }), + }]), }, }); }); @@ -1260,7 +1259,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1278,13 +1277,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { 'Ref': 'SecretA720EF05' }, - }), + }]), }, }); }); @@ -1305,7 +1304,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1323,13 +1322,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { 'Ref': 'SecretA720EF05' }, - }), + }]), }, }); }); @@ -1350,7 +1349,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1363,9 +1362,9 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { @@ -1379,7 +1378,7 @@ describe('EnvironmentVariables', () => { ':secret:mysecret-??????', ]], }, - }), + }]), }, }); }); @@ -1401,7 +1400,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1414,13 +1413,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': 'arn:aws:secretsmanager:us-west-2:123456789012:secret:mysecret*', - }), + }]), }, }); }); @@ -1442,7 +1441,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1455,13 +1454,13 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': 'arn:aws:secretsmanager:us-west-2:123456789012:secret:mysecret-123456*', - }), + }]), }, }); }); @@ -1488,7 +1487,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1508,9 +1507,9 @@ describe('EnvironmentVariables', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { @@ -1522,13 +1521,13 @@ describe('EnvironmentVariables', () => { ':012345678912:secret:secret-name-??????', ]], }, - }), + }]), }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': { @@ -1540,7 +1539,7 @@ describe('EnvironmentVariables', () => { ':012345678912:key/*', ]], }, - }), + }]), }, }); }); @@ -1567,7 +1566,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1587,9 +1586,9 @@ describe('EnvironmentVariables', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': { @@ -1601,13 +1600,13 @@ describe('EnvironmentVariables', () => { ':012345678912:secret:secret-name*', ]], }, - }), + }]), }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': { @@ -1619,7 +1618,7 @@ describe('EnvironmentVariables', () => { ':012345678912:key/*', ]], }, - }), + }]), }, }); }); @@ -1644,7 +1643,7 @@ describe('EnvironmentVariables', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { 'Environment': { 'EnvironmentVariables': [ { @@ -1656,23 +1655,23 @@ describe('EnvironmentVariables', () => { }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'secretsmanager:GetSecretValue', 'Effect': 'Allow', 'Resource': `${secretArn}*`, - }), + }]), }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { - 'Statement': arrayWith({ + 'Statement': Match.arrayWith([{ 'Action': 'kms:Decrypt', 'Effect': 'Allow', 'Resource': 'arn:aws:kms:us-west-2:901234567890:key/*', - }), + }]), }, }); }); @@ -1744,7 +1743,7 @@ describe('Timeouts', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { QueuedTimeoutInMinutes: 30, }); }); @@ -1763,7 +1762,7 @@ describe('Timeouts', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { TimeoutInMinutes: 30, }); }); @@ -1784,7 +1783,7 @@ describe('Maximum concurrency', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', { ConcurrentBuildLimit: 1, }); }); diff --git a/packages/@aws-cdk/aws-codebuild/test/report-group.test.ts b/packages/@aws-cdk/aws-codebuild/test/report-group.test.ts index 4459ef4672f0c..6e653d891f3fb 100644 --- a/packages/@aws-cdk/aws-codebuild/test/report-group.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/report-group.test.ts @@ -1,5 +1,4 @@ -import { ABSENT, ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; @@ -15,11 +14,11 @@ describe('Test Reports Groups', () => { new codebuild.ReportGroup(stack, 'ReportGroup'); - expect(stack).toHaveResourceLike('AWS::CodeBuild::ReportGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::ReportGroup', { "Type": "TEST", "ExportConfig": { "ExportConfigType": "NO_EXPORT", - "S3Destination": ABSENT, + "S3Destination": Match.absent(), }, }); }); @@ -31,7 +30,7 @@ describe('Test Reports Groups', () => { reportGroupName: 'my-report-group', }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::ReportGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::ReportGroup', { "Name": 'my-report-group', }); }); @@ -51,7 +50,7 @@ describe('Test Reports Groups', () => { })); expect(reportGroup.reportGroupName).toEqual('my-report-group'); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { "PolicyDocument": { "Statement": [ { @@ -80,15 +79,15 @@ describe('Test Reports Groups', () => { exportBucket: s3.Bucket.fromBucketName(stack, 'Bucket', 'my-bucket'), }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::ReportGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::ReportGroup', { "Type": "TEST", "ExportConfig": { "ExportConfigType": "S3", "S3Destination": { "Bucket": "my-bucket", - "EncryptionKey": ABSENT, - "EncryptionDisabled": ABSENT, - "Packaging": ABSENT, + "EncryptionKey": Match.absent(), + "EncryptionDisabled": Match.absent(), + "Packaging": Match.absent(), }, }, }); @@ -106,7 +105,7 @@ describe('Test Reports Groups', () => { zipExport: true, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::ReportGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::ReportGroup', { "Type": "TEST", "ExportConfig": { "ExportConfigType": "S3", @@ -125,10 +124,10 @@ describe('Test Reports Groups', () => { new codebuild.ReportGroup(stack, 'ReportGroup'); - expect(stack).toHaveResourceLike('AWS::CodeBuild::ReportGroup', { + Template.fromStack(stack).hasResource('AWS::CodeBuild::ReportGroup', { "DeletionPolicy": "Retain", "UpdateReplacePolicy": "Retain", - }, ResourcePart.CompleteDefinition); + }); }); test('can be created with RemovalPolicy.DESTROY', () => { @@ -138,9 +137,9 @@ describe('Test Reports Groups', () => { removalPolicy: cdk.RemovalPolicy.DESTROY, }); - expect(stack).toHaveResourceLike('AWS::CodeBuild::ReportGroup', { + Template.fromStack(stack).hasResource('AWS::CodeBuild::ReportGroup', { "DeletionPolicy": "Delete", "UpdateReplacePolicy": "Delete", - }, ResourcePart.CompleteDefinition); + }); }); }); diff --git a/packages/@aws-cdk/aws-codebuild/test/untrusted-code-boundary.test.ts b/packages/@aws-cdk/aws-codebuild/test/untrusted-code-boundary.test.ts index 89933d11a7ab8..af1f4fdfb328f 100644 --- a/packages/@aws-cdk/aws-codebuild/test/untrusted-code-boundary.test.ts +++ b/packages/@aws-cdk/aws-codebuild/test/untrusted-code-boundary.test.ts @@ -1,5 +1,4 @@ -import { arrayWith } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as codebuild from '../lib'; @@ -15,7 +14,7 @@ test('can attach permissions boundary to Project', () => { iam.PermissionsBoundary.of(project).apply(new codebuild.UntrustedCodeBoundaryPolicy(stack, 'Boundary')); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { PermissionsBoundary: { Ref: 'BoundaryEA298153' }, }); }); @@ -38,13 +37,13 @@ test('can add additional statements Boundary', () => { })); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::ManagedPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::ManagedPolicy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Effect: 'Allow', Action: 'a:a', Resource: 'b', - }), + }]), }, }); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 202cee9c796a3..c251e138255da 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -82,7 +82,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts index 6a6966167cd2f..ec130559aaff8 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; @@ -7,7 +7,7 @@ describe('CodeDeploy ECS Application', () => { const stack = new cdk.Stack(); new codedeploy.EcsApplication(stack, 'MyApp'); - expect(stack).toHaveResource('AWS::CodeDeploy::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::Application', { ComputePlatform: 'ECS', }); }); @@ -18,7 +18,7 @@ describe('CodeDeploy ECS Application', () => { applicationName: 'my-name', }); - expect(stack).toHaveResource('AWS::CodeDeploy::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::Application', { ApplicationName: 'my-name', ComputePlatform: 'ECS', }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts index eaa140d0c045c..6ccbd816935ba 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; @@ -6,7 +6,7 @@ describe('CodeDeploy Lambda Application', () => { test('can be created', () => { const stack = new cdk.Stack(); new codedeploy.LambdaApplication(stack, 'MyApp'); - expect(stack).toHaveResource('AWS::CodeDeploy::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::Application', { ComputePlatform: 'Lambda', }); }); @@ -16,7 +16,7 @@ describe('CodeDeploy Lambda Application', () => { new codedeploy.LambdaApplication(stack, 'MyApp', { applicationName: 'my-name', }); - expect(stack).toHaveResource('AWS::CodeDeploy::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::Application', { ApplicationName: 'my-name', ComputePlatform: 'Lambda', }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts index 9f69328613dac..7755402502857 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts @@ -1,5 +1,4 @@ -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; @@ -45,7 +44,7 @@ test('custom resource created', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::AWS', { + Template.fromStack(stack).hasResourceProperties('Custom::AWS', { ServiceToken: { 'Fn::GetAtt': [ 'AWS679f53fac002430cb0da5b7982bd22872D164C4C', @@ -57,7 +56,7 @@ test('custom resource created', () => { Delete: '{"service":"CodeDeploy","action":"deleteDeploymentConfig","parameters":{"deploymentConfigName":"CustomConfig.LambdaCanary5Percent1Minutes"}}', }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -91,7 +90,7 @@ test('custom resource created with specific name', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::AWS', { + Template.fromStack(stack).hasResourceProperties('Custom::AWS', { Create: '{"service":"CodeDeploy","action":"createDeploymentConfig","parameters":{"deploymentConfigName":"MyDeploymentConfig","computePlatform":"Lambda","trafficRoutingConfig":{"type":"TimeBasedCanary","timeBasedCanary":{"canaryInterval":"1","canaryPercentage":"5"}}},"physicalResourceId":{"id":"MyDeploymentConfig"}}', Update: '{"service":"CodeDeploy","action":"createDeploymentConfig","parameters":{"deploymentConfigName":"MyDeploymentConfig","computePlatform":"Lambda","trafficRoutingConfig":{"type":"TimeBasedCanary","timeBasedCanary":{"canaryInterval":"1","canaryPercentage":"5"}}},"physicalResourceId":{"id":"MyDeploymentConfig"}}', Delete: '{"service":"CodeDeploy","action":"deleteDeploymentConfig","parameters":{"deploymentConfigName":"MyDeploymentConfig"}}', @@ -112,7 +111,7 @@ test('can create linear custom config', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { DeploymentConfigName: 'CustomConfig.LambdaLinear5PercentEvery1Minutes', }); }); @@ -131,7 +130,7 @@ test('can create canary custom config', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { DeploymentConfigName: 'CustomConfig.LambdaCanary5Percent1Minutes', }); }); @@ -150,7 +149,7 @@ test('dependency on the config exists to ensure ordering', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResource('AWS::CodeDeploy::DeploymentGroup', { Properties: { DeploymentConfigName: 'CustomConfig.LambdaCanary5Percent1Minutes', }, @@ -158,5 +157,5 @@ test('dependency on the config exists to ensure ordering', () => { 'CustomConfigDeploymentConfigCustomResourcePolicy0426B684', 'CustomConfigDeploymentConfigE9E1F384', ], - }, ResourcePart.CompleteDefinition); + }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts index 1f4b18e852cff..365a03c4d5d30 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts @@ -1,5 +1,4 @@ -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; @@ -33,7 +32,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { deploymentConfig: codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE, }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { ApplicationName: { Ref: 'MyApp3CE31C26', }, @@ -56,7 +55,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }); - expect(stack).toHaveResource('AWS::Lambda::Alias', { + Template.fromStack(stack).hasResource('AWS::Lambda::Alias', { Type: 'AWS::Lambda::Alias', Properties: { FunctionName: { @@ -80,15 +79,23 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }, }, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [{ Action: 'sts:AssumeRole', Effect: 'Allow', Principal: { - Service: { 'Fn::Join': ['', ['codedeploy.', { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }]] }, + Service: { + 'Fn::FindInMap': [ + 'ServiceprincipalMap', + { + Ref: 'AWS::Region', + }, + 'codedeploy', + ], + }, }, }], Version: '2012-10-17', @@ -120,7 +127,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { deploymentGroupName: 'test', }); - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { DeploymentGroupName: 'test', }); }); @@ -140,7 +147,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { role: serviceRole, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [{ Action: 'sts:AssumeRole', @@ -176,7 +183,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { deploymentConfig: codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE, }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { ApplicationName: { Ref: 'MyApp3CE31C26', }, @@ -216,7 +223,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { })], }); - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { AlarmConfiguration: { Alarms: [{ Name: { @@ -268,7 +275,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { deploymentConfig: codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Alias', { + Template.fromStack(stack).hasResource('AWS::Lambda::Alias', { UpdatePolicy: { CodeDeployLambdaAliasUpdate: { ApplicationName: { @@ -282,9 +289,9 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }, }, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyName: 'MyDGServiceRoleDefaultPolicy65E8E1EA', Roles: [{ Ref: 'MyDGServiceRole5E94FD88', @@ -316,7 +323,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }); group.addPreHook(mockFunction(stack, 'PreHook')); - expect(stack).toHaveResourceLike('AWS::Lambda::Alias', { + Template.fromStack(stack).hasResource('AWS::Lambda::Alias', { UpdatePolicy: { CodeDeployLambdaAliasUpdate: { ApplicationName: { @@ -330,9 +337,9 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }, }, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyName: 'MyDGServiceRoleDefaultPolicy65E8E1EA', Roles: [{ Ref: 'MyDGServiceRole5E94FD88', @@ -364,7 +371,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { deploymentConfig: codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Alias', { + Template.fromStack(stack).hasResource('AWS::Lambda::Alias', { UpdatePolicy: { CodeDeployLambdaAliasUpdate: { ApplicationName: { @@ -378,9 +385,9 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }, }, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyName: 'MyDGServiceRoleDefaultPolicy65E8E1EA', Roles: [{ Ref: 'MyDGServiceRole5E94FD88', @@ -412,7 +419,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }); group.addPostHook(mockFunction(stack, 'PostHook')); - expect(stack).toHaveResourceLike('AWS::Lambda::Alias', { + Template.fromStack(stack).hasResource('AWS::Lambda::Alias', { UpdatePolicy: { CodeDeployLambdaAliasUpdate: { ApplicationName: { @@ -426,9 +433,9 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }, }, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyName: 'MyDGServiceRoleDefaultPolicy65E8E1EA', Roles: [{ Ref: 'MyDGServiceRole5E94FD88', @@ -467,7 +474,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { })], }); - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { AlarmConfiguration: { Alarms: [{ Name: { @@ -494,7 +501,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { ApplicationName: { Ref: 'MyApp3CE31C26', }, @@ -526,7 +533,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { AutoRollbackConfiguration: { Enabled: true, Events: [ @@ -557,7 +564,7 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { })], }); - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { AutoRollbackConfiguration: { Enabled: true, Events: [ diff --git a/packages/@aws-cdk/aws-codedeploy/test/server/deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/server/deployment-config.test.ts index 2838bacfdc7fc..52652e8024b28 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/server/deployment-config.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/deployment-config.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; @@ -12,7 +12,7 @@ describe('CodeDeploy DeploymentConfig', () => { minimumHealthyHosts: codedeploy.MinimumHealthyHosts.count(1), }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentConfig', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { 'MinimumHealthyHosts': { 'Type': 'HOST_COUNT', 'Value': 1, @@ -27,7 +27,7 @@ describe('CodeDeploy DeploymentConfig', () => { minimumHealthyHosts: codedeploy.MinimumHealthyHosts.percentage(75), }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentConfig', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { 'MinimumHealthyHosts': { 'Type': 'FLEET_PERCENT', 'Value': 75, diff --git a/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts index 79d20323d5a21..43acaadc3e7fc 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts @@ -1,5 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -18,7 +17,7 @@ describe('CodeDeploy Server Deployment Group', () => { application, }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'ApplicationName': { 'Ref': 'MyApp3CE31C26', }, @@ -36,9 +35,8 @@ describe('CodeDeploy Server Deployment Group', () => { value: serverDeploymentGroup.application.applicationName, }); - expect(stack2).toHaveOutput({ - outputName: 'Output', - outputValue: 'defaultmydgapplication78dba0bb0c7580b32033', + Template.fromStack(stack2).hasOutput('Output', { + Value: 'defaultmydgapplication78dba0bb0c7580b32033', }); }); @@ -68,7 +66,7 @@ describe('CodeDeploy Server Deployment Group', () => { installAgent: true, }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { 'UserData': { 'Fn::Base64': { 'Fn::Join': [ @@ -104,7 +102,7 @@ describe('CodeDeploy Server Deployment Group', () => { installAgent: true, }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { 'UserData': { 'Fn::Base64': { 'Fn::Join': [ @@ -135,7 +133,7 @@ describe('CodeDeploy Server Deployment Group', () => { autoScalingGroups: [asg], }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'AutoScalingGroups': [ { 'Ref': 'ASG46ED3070', @@ -156,7 +154,7 @@ describe('CodeDeploy Server Deployment Group', () => { const deploymentGroup = new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup'); deploymentGroup.addAutoScalingGroup(asg); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'AutoScalingGroups': [ { 'Ref': 'ASG46ED3070', @@ -178,7 +176,7 @@ describe('CodeDeploy Server Deployment Group', () => { loadBalancer: codedeploy.LoadBalancer.application(targetGroup), }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'LoadBalancerInfo': { 'TargetGroupInfoList': [ { @@ -210,7 +208,7 @@ describe('CodeDeploy Server Deployment Group', () => { loadBalancer: codedeploy.LoadBalancer.network(targetGroup), }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'LoadBalancerInfo': { 'TargetGroupInfoList': [ { @@ -241,7 +239,7 @@ describe('CodeDeploy Server Deployment Group', () => { ), }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'Ec2TagSet': { 'Ec2TagSetList': [ { @@ -276,7 +274,7 @@ describe('CodeDeploy Server Deployment Group', () => { ), }); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'OnPremisesTagSet': { 'OnPremisesTagSetList': [ { @@ -343,7 +341,7 @@ describe('CodeDeploy Server Deployment Group', () => { const deploymentGroup = new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup'); deploymentGroup.addAlarm(alarm); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'AlarmConfiguration': { 'Alarms': [ { @@ -362,7 +360,7 @@ describe('CodeDeploy Server Deployment Group', () => { new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup'); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'AutoRollbackConfiguration': { 'Enabled': true, 'Events': [ @@ -391,7 +389,7 @@ describe('CodeDeploy Server Deployment Group', () => { }); deploymentGroup.addAlarm(alarm); - expect(stack).toHaveResource('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'AutoRollbackConfiguration': { 'Enabled': true, 'Events': [ @@ -402,7 +400,8 @@ describe('CodeDeploy Server Deployment Group', () => { }); test('setting to roll back on alarms without providing any results in an exception', () => { - const stack = new cdk.Stack(); + const app = new cdk.App(); + const stack = new cdk.Stack(app); new codedeploy.ServerDeploymentGroup(stack, 'DeploymentGroup', { autoRollback: { @@ -410,7 +409,7 @@ describe('CodeDeploy Server Deployment Group', () => { }, }); - expect(() => SynthUtils.toCloudFormation(stack)).toThrow(/deploymentInAlarm/); + expect(() => app.synth()).toThrow(/deploymentInAlarm/); }); test('can be used with an imported ALB Target Group as the load balancer', () => { @@ -424,7 +423,7 @@ describe('CodeDeploy Server Deployment Group', () => { ), }); - expect(stack).toHaveResourceLike('AWS::CodeDeploy::DeploymentGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentGroup', { 'LoadBalancerInfo': { 'TargetGroupInfoList': [ { diff --git a/packages/@aws-cdk/aws-codeguruprofiler/README.md b/packages/@aws-cdk/aws-codeguruprofiler/README.md index 09a2ea2d6b731..6c8c54954a40e 100644 --- a/packages/@aws-cdk/aws-codeguruprofiler/README.md +++ b/packages/@aws-cdk/aws-codeguruprofiler/README.md @@ -17,7 +17,7 @@ Amazon CodeGuru Profiler collects runtime performance data from your live applic Import to your project: -```ts +```ts nofixture import * as codeguruprofiler from '@aws-cdk/aws-codeguruprofiler'; ``` @@ -27,11 +27,11 @@ Here's how to setup a profiling group and give your compute role permissions to ```ts // The execution role of your application that publishes to the ProfilingGroup via CodeGuru Profiler Profiling Agent. (the following is merely an example) -const publishAppRole = new Role(stack, 'PublishAppRole', { - assumedBy: new AccountRootPrincipal(), +const publishAppRole = new iam.Role(this, 'PublishAppRole', { + assumedBy: new iam.AccountRootPrincipal(), }); -const profilingGroup = new ProfilingGroup(stack, 'MyProfilingGroup'); +const profilingGroup = new codeguruprofiler.ProfilingGroup(this, 'MyProfilingGroup'); profilingGroup.grantPublish(publishAppRole); ``` @@ -41,7 +41,7 @@ Code Guru Profiler supports multiple compute environments. They can be configured when creating a Profiling Group by using the `computePlatform` property: ```ts -const profilingGroup = new ProfilingGroup(stack, 'MyProfilingGroup', { - computePlatform: ComputePlatform.AWS_LAMBDA, +const profilingGroup = new codeguruprofiler.ProfilingGroup(this, 'MyProfilingGroup', { + computePlatform: codeguruprofiler.ComputePlatform.AWS_LAMBDA, }); ``` diff --git a/packages/@aws-cdk/aws-codeguruprofiler/package.json b/packages/@aws-cdk/aws-codeguruprofiler/package.json index 92aaf5b19dbba..e0ca94e657487 100644 --- a/packages/@aws-cdk/aws-codeguruprofiler/package.json +++ b/packages/@aws-cdk/aws-codeguruprofiler/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-codeguruprofiler/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-codeguruprofiler/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..c809dffd674cb --- /dev/null +++ b/packages/@aws-cdk/aws-codeguruprofiler/rosetta/default.ts-fixture @@ -0,0 +1,12 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as codeguruprofiler from '@aws-cdk/aws-codeguruprofiler'; +import * as iam from '@aws-cdk/aws-iam'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-codegurureviewer/package.json b/packages/@aws-cdk/aws-codegurureviewer/package.json index d96fac0ca1c2a..6c72bd475e6ed 100644 --- a/packages/@aws-cdk/aws-codegurureviewer/package.json +++ b/packages/@aws-cdk/aws-codegurureviewer/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.CodeGuruReviewer", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/README.md b/packages/@aws-cdk/aws-codepipeline-actions/README.md index d3569b4ea5872..ff43850561651 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/README.md +++ b/packages/@aws-cdk/aws-codepipeline-actions/README.md @@ -764,6 +764,39 @@ const deployStage = pipeline.addStage({ [image definition file]: https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create.html#pipelines-create-image-definitions +#### Deploying ECS applications to existing services + +CodePipeline can deploy to an existing ECS service which uses the +[ECS service ARN format that contains the Cluster name](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids). +This also works if the service is in a different account and/or region than the pipeline: + +```ts +import * as ecs from '@aws-cdk/aws-ecs'; + +const service = ecs.BaseService.fromServiceArnWithCluster(this, 'EcsService', + 'arn:aws:ecs:us-east-1:123456789012:service/myClusterName/myServiceName' +); +const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); +const buildOutput = new codepipeline.Artifact(); +// add source and build stages to the pipeline as usual... +const deployStage = pipeline.addStage({ + stageName: 'Deploy', + actions: [ + new codepipeline_actions.EcsDeployAction({ + actionName: 'DeployAction', + service: service, + input: buildOutput, + }), + ], +}); +``` + +When deploying across accounts, especially in a CDK Pipelines self-mutating pipeline, +it is recommended to provide the `role` property to the `EcsDeployAction`. +The Role will need to have permissions assigned to it for ECS deployment. +See [the CodePipeline documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/how-to-custom-role.html#how-to-update-role-new-services) +for the permissions needed. + #### Deploying ECS applications stored in a separate source code repository The idiomatic CDK way of deploying an ECS application is to have your Dockerfiles and your CDK code in the same source code repository, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/bitbucket/bitbucket-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/bitbucket/bitbucket-source-action.test.ts index 959a7413a4d9e..7d22f2b690015 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/bitbucket/bitbucket-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/bitbucket/bitbucket-source-action.test.ts @@ -91,7 +91,10 @@ describeDeprecated('BitBucket source Action', () => { 'PolicyDocument': { 'Statement': Match.arrayWith([ Match.objectLike({ - 'Action': 's3:PutObjectAcl', + 'Action': [ + 's3:PutObjectAcl', + 's3:PutObjectVersionAcl', + ], 'Effect': 'Allow', 'Resource': { 'Fn::Join': [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts index 101b35c77d581..2ca60b9d5c49e 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts @@ -554,7 +554,10 @@ describe('CodeCommit Source Action', () => { Template.fromStack(repoStack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: Match.arrayWith([{ - 'Action': 's3:PutObjectAcl', + 'Action': [ + 's3:PutObjectAcl', + 's3:PutObjectVersionAcl', + ], 'Effect': 'Allow', 'Resource': { 'Fn::Join': ['', [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts index 6eba0f1e62cf8..eb85f0fc2d1e3 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts @@ -93,7 +93,10 @@ describe('CodeStar Connections source Action', () => { 'PolicyDocument': { 'Statement': Match.arrayWith([ Match.objectLike({ - 'Action': 's3:PutObjectAcl', + 'Action': [ + 's3:PutObjectAcl', + 's3:PutObjectVersionAcl', + ], 'Effect': 'Allow', 'Resource': { 'Fn::Join': ['', [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/ecs/ecs-deploy-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/ecs/ecs-deploy-action.test.ts index 91976ec921e74..0841d47946a23 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/ecs/ecs-deploy-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/ecs/ecs-deploy-action.test.ts @@ -196,6 +196,84 @@ describe('ecs deploy action', () => { }); + + test('can be created by existing service with cluster ARN format', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'PipelineStack', { + env: { + region: 'pipeline-region', account: 'pipeline-account', + }, + }); + const clusterName = 'cluster-name'; + const serviceName = 'service-name'; + const region = 'service-region'; + const account = 'service-account'; + const serviceArn = `arn:aws:ecs:${region}:${account}:service/${clusterName}/${serviceName}`; + const service = ecs.BaseService.fromServiceArnWithCluster(stack, 'FargateService', serviceArn); + + const artifact = new codepipeline.Artifact('Artifact'); + const bucket = new s3.Bucket(stack, 'PipelineBucket', { + versioned: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + const source = new cpactions.S3SourceAction({ + actionName: 'Source', + output: artifact, + bucket, + bucketKey: 'key', + }); + const action = new cpactions.EcsDeployAction({ + actionName: 'ECS', + service: service, + input: artifact, + }); + new codepipeline.Pipeline(stack, 'Pipeline', { + stages: [ + { + stageName: 'Source', + actions: [source], + }, + { + stageName: 'Deploy', + actions: [action], + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { + Stages: [ + {}, + { + Actions: [ + { + Name: 'ECS', + ActionTypeId: { + Category: 'Deploy', + Provider: 'ECS', + }, + Configuration: { + ClusterName: clusterName, + ServiceName: serviceName, + }, + Region: region, + RoleArn: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + `:iam::${account}:role/pipelinestack-support-serloyecsactionrole49867f847238c85af7c0`, + ], + ], + }, + }, + ], + }, + ], + }); + }); }); }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json index 3c59dc4a9305d..87d6594c254c4 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json @@ -159,6 +159,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -440,6 +444,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json index 61cef35a009c0..823b5af6c908b 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json @@ -153,6 +153,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -508,6 +512,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -621,6 +629,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1350,6 +1362,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1572,6 +1588,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json index 53614fd854b19..0990f457aae8f 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json @@ -153,6 +153,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -388,6 +392,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -669,6 +677,40 @@ }, "PolicyDocument": { "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "CloudTrailS310CD22F2", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CloudTrailS310CD22F2", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, { "Action": "s3:GetBucketAcl", "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json index 6662d025f667b..c255275baa82b 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json @@ -163,6 +163,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -387,6 +391,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json index 85ddb7d7dc4a9..167b278084683 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json @@ -39,6 +39,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -265,6 +269,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json index 81c9c5fc2a998..b3d024a33f2a8 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json @@ -164,6 +164,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -384,6 +388,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json index 707f673e11ea1..53189d1369dda 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json @@ -153,6 +153,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -440,6 +444,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -563,6 +571,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json index 3af1036c27a2b..854b01ae12ac6 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json @@ -110,6 +110,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json index cd9a20670da04..c1624ab7e2f7b 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json @@ -110,6 +110,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -196,6 +200,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -557,6 +565,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json index 53dffb9a5b78c..cb437e83eb651 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json @@ -92,6 +92,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -413,6 +417,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -671,6 +679,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json index ed452beed9f7a..23bdec497e551 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json @@ -224,6 +224,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -430,6 +434,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json index ad229d36e2207..1b20a71fd7def 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json @@ -39,6 +39,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -258,6 +262,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json index d2d0bea52821f..eb87655dd0e60 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json @@ -124,6 +124,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -341,6 +345,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json index 53c605d58da68..4720701233397 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -217,6 +221,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json index 9f9f3404c1413..725008ba28aa8 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json @@ -427,6 +427,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -576,6 +580,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -843,6 +851,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json index acee7feab59e6..a728e17a69655 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json @@ -397,6 +397,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -639,6 +643,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -962,6 +970,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1059,6 +1071,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json index d464eef509bdd..0a7d958491359 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json @@ -153,6 +153,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -374,6 +378,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -761,6 +769,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-jenkins.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-jenkins.expected.json index 5c89d0d1119b9..eda778cf7ec55 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-jenkins.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-jenkins.expected.json @@ -39,6 +39,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -276,6 +280,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json index f0c66384b1709..1193cbb2f30d0 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -243,6 +247,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json index ecd97ebdd239d..9cc08aec93f44 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json @@ -44,6 +44,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -261,6 +265,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -339,6 +347,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -366,7 +378,10 @@ ] }, { - "Action": "s3:PutObjectAcl", + "Action": [ + "s3:PutObjectAcl", + "s3:PutObjectVersionAcl" + ], "Effect": "Allow", "Resource": { "Fn::Join": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json index fe94e8c305ad7..d35d3f59e6bb2 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json @@ -196,6 +196,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -433,6 +437,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/lambda/lambda-invoke-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/lambda/lambda-invoke-action.test.ts index 7227ab34f8f1f..c8d3c5e1dba7d 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/lambda/lambda-invoke-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/lambda/lambda-invoke-action.test.ts @@ -183,6 +183,10 @@ describe('', () => { 'Action': [ 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', @@ -239,6 +243,10 @@ describe('', () => { 'Action': [ 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/s3/s3-deploy-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/s3/s3-deploy-action.test.ts index 656ea496aa8dd..99383e33ae541 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/s3/s3-deploy-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/s3/s3-deploy-action.test.ts @@ -64,6 +64,10 @@ describe('', () => { 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], }, diff --git a/packages/@aws-cdk/aws-codestar/README.md b/packages/@aws-cdk/aws-codestar/README.md index e9c6ec2e4bf75..6bdf425aab1d1 100644 --- a/packages/@aws-cdk/aws-codestar/README.md +++ b/packages/@aws-cdk/aws-codestar/README.md @@ -29,13 +29,13 @@ To create a new GitHub Repository and commit the assets from S3 bucket into the import * as codestar from '@aws-cdk/aws-codestar'; import * as s3 from '@aws-cdk/aws-s3' -new codestar.GitHubRepository(stack, 'GitHubRepo', { +new codestar.GitHubRepository(this, 'GitHubRepo', { owner: 'aws', repositoryName: 'aws-cdk', - accessToken: cdk.SecretValue.secretsManager('my-github-token', { + accessToken: SecretValue.secretsManager('my-github-token', { jsonField: 'token', }), - contentsBucket: s3.Bucket.fromBucketName(stack, 'Bucket', 'bucket-name'), + contentsBucket: s3.Bucket.fromBucketName(this, 'Bucket', 'bucket-name'), contentsKey: 'import.zip', }); ``` diff --git a/packages/@aws-cdk/aws-codestar/package.json b/packages/@aws-cdk/aws-codestar/package.json index e179f607ecbfc..a2ae9b1cbd339 100644 --- a/packages/@aws-cdk/aws-codestar/package.json +++ b/packages/@aws-cdk/aws-codestar/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-codestar/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-codestar/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..439308f8c0176 --- /dev/null +++ b/packages/@aws-cdk/aws-codestar/rosetta/default.ts-fixture @@ -0,0 +1,10 @@ +// Fixture with packages imported, but nothing else +import { SecretValue, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codestarconnections/package.json b/packages/@aws-cdk/aws-codestarconnections/package.json index 769985db53fbc..9d06e3ea4db51 100644 --- a/packages/@aws-cdk/aws-codestarconnections/package.json +++ b/packages/@aws-cdk/aws-codestarconnections/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-codestarnotifications/README.md b/packages/@aws-cdk/aws-codestarnotifications/README.md index b13a33089b01a..ba1da9ced0026 100644 --- a/packages/@aws-cdk/aws-codestarnotifications/README.md +++ b/packages/@aws-cdk/aws-codestarnotifications/README.md @@ -39,17 +39,17 @@ import * as codebuild from '@aws-cdk/aws-codebuild'; import * as sns from '@aws-cdk/aws-sns'; import * as chatbot from '@aws-cdk/aws-chatbot'; -const project = new codebuild.PipelineProject(stack, 'MyProject'); +const project = new codebuild.PipelineProject(this, 'MyProject'); -const topic = new sns.Topic(stack, 'MyTopic1'); +const topic = new sns.Topic(this, 'MyTopic1'); -const slack = new chatbot.SlackChannelConfiguration(stack, 'MySlackChannel', { +const slack = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', { slackChannelConfigurationName: 'YOUR_CHANNEL_NAME', slackWorkspaceId: 'YOUR_SLACK_WORKSPACE_ID', slackChannelId: 'YOUR_SLACK_CHANNEL_ID', }); -const rule = new notifications.NotificationRule(stack, 'NotificationRule', { +const rule = new notifications.NotificationRule(this, 'NotificationRule', { source: project, events: [ 'codebuild-project-build-state-succeeded', diff --git a/packages/@aws-cdk/aws-codestarnotifications/package.json b/packages/@aws-cdk/aws-codestarnotifications/package.json index 251b0b0fa69f7..e6d2bf301f3b4 100644 --- a/packages/@aws-cdk/aws-codestarnotifications/package.json +++ b/packages/@aws-cdk/aws-codestarnotifications/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-codestarnotifications/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-codestarnotifications/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..50d86e8a055ce --- /dev/null +++ b/packages/@aws-cdk/aws-codestarnotifications/rosetta/default.ts-fixture @@ -0,0 +1,10 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cognito-identitypool/README.md b/packages/@aws-cdk/aws-cognito-identitypool/README.md index fb1fc6ef0c0df..e1be0d70ec29c 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/README.md +++ b/packages/@aws-cdk/aws-cognito-identitypool/README.md @@ -31,6 +31,10 @@ sign-in options for your app users. This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. +```ts nofixture +import { IdentityPool, UserPoolAuthenticationProvider } from '@aws-cdk/aws-cognito-identitypool'; +``` + ## Table of Contents - [Identity Pools](#identity-pools) @@ -66,26 +70,28 @@ A basic Identity Pool with minimal configuration has no required props, with def unauthenticated (guest) roles applied to the identity pool: ```ts -new cognito.IdentityPool(this, 'myIdentityPool'); +new IdentityPool(this, 'myIdentityPool'); ``` By default, both the authenticated and unauthenticated roles will have no permissions attached. Grant permissions to roles using the public `authenticatedRole` and `unauthenticatedRole` properties: ```ts -const identityPool = new cognito.IdentityPool(this, 'myIdentityPool'); -const table = new dynamodb.Table(this, 'MyTable'); +import * as dynamodb from '@aws-cdk/aws-dynamodb'; + +const identityPool = new IdentityPool(this, 'myIdentityPool'); +declare const table: dynamodb.Table; // Grant permissions to authenticated users table.grantReadWriteData(identityPool.authenticatedRole); // Grant permissions to unauthenticated guest users -table.grantRead(identityPool.unauthenticatedRole); +table.grantReadData(identityPool.unauthenticatedRole); //Or add policy statements straight to the role -identityPool.authenticatedRole.addToPrincipalPolicy(new PolicyStatement({ - effect: Effect.ALLOW, +identityPool.authenticatedRole.addToPrincipalPolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, actions: ['dynamodb:*'], - resources: ['*'] + resources: ['*'], })); ``` @@ -93,14 +99,15 @@ The default roles can also be supplied in `IdentityPoolProps`: ```ts const stack = new Stack(); -const authenticatedRole = new Role(stack, 'authRole', { - assumedBy: new ServicePrincipal('service.amazonaws.com'), +const authenticatedRole = new iam.Role(this, 'authRole', { + assumedBy: new iam.ServicePrincipal('service.amazonaws.com'), }); -const unauthenticatedRole = new Role(stack, 'unauthRole', { - assumedBy: new ServicePrincipal('service.amazonaws.com'), +const unauthenticatedRole = new iam.Role(this, 'unauthRole', { + assumedBy: new iam.ServicePrincipal('service.amazonaws.com'), }); -const identityPool = new IdentityPool(stack, 'TestIdentityPoolActions', { - authenticatedRole, unauthenticatedRole +const identityPool = new IdentityPool(this, 'TestIdentityPoolActions', { + authenticatedRole, + unauthenticatedRole, }); ``` @@ -122,7 +129,7 @@ to gather the necessary properties from the user pool constructs. ```ts const userPool = new cognito.UserPool(this, 'Pool'); -new cognito.IdentityPool(this, 'myidentitypool', { +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', authenticationProviders: { userPools: [new UserPoolAuthenticationProvider({ userPool })], @@ -134,10 +141,11 @@ User pools can also be associated with an identity pool after instantiation. The returns the User Pool Client that has been created: ```ts +declare const identityPool: IdentityPool; const userPool = new cognito.UserPool(this, 'Pool'); -const userPoolClient = identityPool.addUserPoolAuthentication({ - userPools: [new UserPoolAuthenticationProvider({ userPool })]; -}); +const userPoolClient = identityPool.addUserPoolAuthentication(new UserPoolAuthenticationProvider({ + userPool, +})); ``` #### Server Side Token Check @@ -153,13 +161,12 @@ Setting `disableServerSideTokenCheck` to true will change the default behavior t more [here](https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_CognitoIdentityProvider.html#CognitoIdentity-Type-CognitoIdentityProvider-ServerSideTokenCheck): ```ts +declare const identityPool: IdentityPool; const userPool = new cognito.UserPool(this, 'Pool'); -identityPool.addUserPoolAuthentication({ - userPool: new UserPoolAuthenticationProvider({ - userPool, - disableServerSideTokenCheck: true, - }), -}); +identityPool.addUserPoolAuthentication(new UserPoolAuthenticationProvider({ + userPool, + disableServerSideTokenCheck: true, +})); ``` #### Associating an External Provider Directly @@ -168,7 +175,7 @@ One or more [external identity providers](https://docs.aws.amazon.com/cognito/la `authenticationProviders`: ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', authenticationProviders: { amazon: { @@ -179,7 +186,7 @@ new cognito.IdentityPool(this, 'myidentitypool', { }, google: { clientId: '12345678012.apps.googleusercontent.com', - } + }, apple: { servicesId: 'com.myappleapp.auth', }, @@ -206,15 +213,15 @@ so that different users can be granted different sets of permissions. Associatin with an identity pool: ```ts -const openIdConnectProvider = new iam.OpenIdConnectProvider(this, 'my-openid-connect-provider', ...); -const samlProvider = new iam.SamlProvider(this, 'my-saml-provider', ...); +declare const openIdConnectProvider: iam.OpenIdConnectProvider; +declare const samlProvider: iam.SamlProvider; -new cognito.IdentityPool(this, 'myidentitypool', { +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', authenticationProviders: { - openIdConnectProvider: openIdConnectProvider, - samlProvider: samlProvider, - } + openIdConnectProviders: [openIdConnectProvider], + samlProviders: [samlProvider], + }, }); ``` @@ -228,13 +235,16 @@ Like the supported external providers, though, only one custom provider can be d pool. ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +declare const openIdConnectProvider: iam.OpenIdConnectProvider; +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', authenticationProviders: { - google: '12345678012.apps.googleusercontent.com', - openIdConnectProvider: openIdConnectProvider, + google: { + clientId: '12345678012.apps.googleusercontent.com', + }, + openIdConnectProviders: [openIdConnectProvider], customProvider: 'my-custom-provider.example.com', - } + }, }); ``` @@ -249,7 +259,9 @@ Using a [token-based approach](https://docs.aws.amazon.com/cognito/latest/develo `cognito:preferred_role` claims from the identity provider: ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +import { IdentityPoolProviderUrl } from '@aws-cdk/aws-cognito-identitypool'; + +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', roleMappings: [{ providerUrl: IdentityPoolProviderUrl.AMAZON, @@ -261,7 +273,11 @@ new cognito.IdentityPool(this, 'myidentitypool', { Using a rule-based approach to role mapping allows roles to be assigned based on custom claims passed from the identity provider: ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +import { IdentityPoolProviderUrl, RoleMappingMatchType } from '@aws-cdk/aws-cognito-identitypool'; + +declare const adminRole: iam.Role; +declare const nonAdminRole: iam.Role; +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', // Assign specific roles to users based on whether or not the custom admin claim is passed from the identity provider roleMappings: [{ @@ -286,6 +302,13 @@ new cognito.IdentityPool(this, 'myidentitypool', { Role mappings can also be added after instantiation with the Identity Pool's `addRoleMappings` method: ```ts +import { IdentityPoolRoleMapping } from '@aws-cdk/aws-cognito-identitypool'; + +declare const identityPool: IdentityPool; +declare const myAddedRoleMapping1: IdentityPoolRoleMapping; +declare const myAddedRoleMapping2: IdentityPoolRoleMapping; +declare const myAddedRoleMapping3: IdentityPoolRoleMapping; + identityPool.addRoleMappings(myAddedRoleMapping1, myAddedRoleMapping2, myAddedRoleMapping3); ``` @@ -295,7 +318,9 @@ Role mappings must be associated with the url of an Identity Provider which can `IdentityPoolProviderUrl`. Supported Providers have static Urls that can be used: ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +import { IdentityPoolProviderUrl } from '@aws-cdk/aws-cognito-identitypool'; + +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', roleMappings: [{ providerUrl: IdentityPoolProviderUrl.FACEBOOK, @@ -307,7 +332,9 @@ new cognito.IdentityPool(this, 'myidentitypool', { For identity providers that don't have static Urls, a custom Url or User Pool Client Url can be supplied: ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +import { IdentityPoolProviderUrl } from '@aws-cdk/aws-cognito-identitypool'; + +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', roleMappings: [ { @@ -330,7 +357,7 @@ Identity Pool [Authentication Flow](https://docs.aws.amazon.com/cognito/latest/d can also be implemented using `allowClassicFlow`: ```ts -new cognito.IdentityPool(this, 'myidentitypool', { +new IdentityPool(this, 'myidentitypool', { identityPoolName: 'myidentitypool', allowClassicFlow: true, }); diff --git a/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts b/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts index cefab0caa54d9..1fd89f238e0b0 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts +++ b/packages/@aws-cdk/aws-cognito-identitypool/lib/identitypool.ts @@ -437,7 +437,12 @@ export class IdentityPool extends Resource implements IIdentityPool { unauthenticatedRole: this.unauthenticatedRole, roleMappings: props.roleMappings, }); - attachment.node.addDependency(this); + + // This added by the original author, but it's causing cyclic dependencies. + // Don't know why this was added in the first place, but I'm disabling it for now and if + // no complaints come from this, we're probably safe to remove it altogether. + // attachment.node.addDependency(this); + Array.isArray(attachment); } /** @@ -461,7 +466,12 @@ export class IdentityPool extends Resource implements IIdentityPool { unauthenticatedRole: this.unauthenticatedRole, roleMappings, }); - attachment.node.addDependency(this); + + // This added by the original author, but it's causing cyclic dependencies. + // Don't know why this was added in the first place, but I'm disabling it for now and if + // no complaints come from this, we're probably safe to remove it altogether. + // attachment.node.addDependency(this); + Array.isArray(attachment); } /** diff --git a/packages/@aws-cdk/aws-cognito-identitypool/package.json b/packages/@aws-cdk/aws-cognito-identitypool/package.json index db55fece60596..c4e0c079d7daa 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/package.json +++ b/packages/@aws-cdk/aws-cognito-identitypool/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-cognito-identitypool/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-cognito-identitypool/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..a362c0eca88f3 --- /dev/null +++ b/packages/@aws-cdk/aws-cognito-identitypool/rosetta/default.ts-fixture @@ -0,0 +1,13 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as cognito from '@aws-cdk/aws-cognito'; +import * as iam from '@aws-cdk/aws-iam'; +import { IdentityPool, UserPoolAuthenticationProvider } from '@aws-cdk/aws-cognito-identitypool'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json b/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json index b555de31baa1e..b2ec1baf42d81 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json +++ b/packages/@aws-cdk/aws-cognito-identitypool/test/integ.identitypool.expected.json @@ -402,11 +402,6 @@ } }, "DependsOn": [ - "identitypoolAuthenticatedRoleDefaultPolicyCB4D2992", - "identitypoolAuthenticatedRoleB074B49D", - "identitypoolE2A6D099", - "identitypoolUnauthenticatedRoleDefaultPolicyBFACCE98", - "identitypoolUnauthenticatedRoleE61CAC70", "OtherPool7DA7F2F7", "OtherPoolUserPoolAuthenticationProviderClient08F670F8", "PoolD3F588B8", diff --git a/packages/@aws-cdk/aws-cognito/README.md b/packages/@aws-cdk/aws-cognito/README.md index ef968f5151bd2..96629cf13decd 100644 --- a/packages/@aws-cdk/aws-cognito/README.md +++ b/packages/@aws-cdk/aws-cognito/README.md @@ -91,7 +91,7 @@ new cognito.UserPool(this, 'myuserpool', { emailBody: 'Thanks for signing up to our awesome app! Your verification code is {####}', emailStyle: cognito.VerificationEmailStyle.CODE, smsMessage: 'Thanks for signing up to our awesome app! Your verification code is {####}', - } + }, }); ``` @@ -108,8 +108,8 @@ new cognito.UserPool(this, 'myuserpool', { userInvitation: { emailSubject: 'Invite to join our awesome app!', emailBody: 'Hello {username}, you have been invited to join our awesome app! Your temporary password is {####}', - smsMessage: 'Hello {username}, your temporary password for our awesome app is {####}' - } + smsMessage: 'Hello {username}, your temporary password for our awesome app is {####}', + }, }); ``` @@ -136,7 +136,7 @@ new cognito.UserPool(this, 'myuserpool', { // ... signInAliases: { username: true, - email: true + email: true, }, }); ``` @@ -165,7 +165,7 @@ new cognito.UserPool(this, 'myuserpool', { // ... // ... signInAliases: { username: true, email: true }, - autoVerify: { email: true, phone: true } + autoVerify: { email: true, phone: true }, }); ``` @@ -241,7 +241,7 @@ const poolSmsRole = new iam.Role(this, 'userpoolsmsrole', { new cognito.UserPool(this, 'myuserpool', { // ... smsRole: poolSmsRole, - smsRoleExternalId: 'c87467be-4f34-11ea-b77f-2e728ce88125' + smsRoleExternalId: 'c87467be-4f34-11ea-b77f-2e728ce88125', }); ``` @@ -327,7 +327,7 @@ Cognito to send emails through Amazon SES, which is detailed below. ```ts new cognito.UserPool(this, 'myuserpool', { - email: UserPoolEmail.withCognito('support@myawesomeapp.com'), + email: cognito.UserPoolEmail.withCognito('support@myawesomeapp.com'), }); ``` @@ -341,7 +341,7 @@ Once the SES setup is complete, the UserPool can be configured to use the SES em ```ts new cognito.UserPool(this, 'myuserpool', { - email: UserPoolEmail.withSES({ + email: cognito.UserPoolEmail.withSES({ fromEmail: 'noreply@myawesomeapp.com', fromName: 'Awesome App', replyTo: 'support@myawesomeapp.com', @@ -354,7 +354,7 @@ If the UserPool is being created in a different region, `sesRegion` must be used ```ts new cognito.UserPool(this, 'myuserpool', { - email: UserPoolEmail.withSES({ + email: cognito.UserPoolEmail.withSES({ sesRegion: 'us-east-1', fromEmail: 'noreply@myawesomeapp.com', fromName: 'Awesome App', @@ -395,7 +395,7 @@ on the construct, as so - const authChallengeFn = new lambda.Function(this, 'authChallengeFn', { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', - code: lambda.Code.fromAsset(/* path to lambda asset */), + code: lambda.Code.fromAsset(path.join(__dirname, 'path/to/asset')), }); const userpool = new cognito.UserPool(this, 'myuserpool', { @@ -403,13 +403,13 @@ const userpool = new cognito.UserPool(this, 'myuserpool', { lambdaTriggers: { createAuthChallenge: authChallengeFn, // ... - } + }, }); userpool.addTrigger(cognito.UserPoolOperation.USER_MIGRATION, new lambda.Function(this, 'userMigrationFn', { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', - code: lambda.Code.fromAsset(/* path to lambda asset */), + code: lambda.Code.fromAsset(path.join(__dirname, 'path/to/asset')), })); ``` @@ -428,7 +428,15 @@ Error message when running `cdk synth` or `cdk deploy`: To work around the circular dependency issue, use the `attachInlinePolicy()` API instead, as shown below. -```ts fixture=with-lambda-trigger +```ts +declare const postAuthFn: lambda.Function; + +const userpool = new cognito.UserPool(this, 'myuserpool', { + lambdaTriggers: { + postAuthentication: postAuthFn, + }, +}); + // provide permissions to describe the user pool scoped to the ARN the user pool postAuthFn.role?.attachInlinePolicy(new iam.Policy(this, 'userpool-policy', { statements: [new iam.PolicyStatement({ @@ -504,8 +512,8 @@ new cognito.UserPoolIdentityProviderAmazon(this, 'Amazon', { custom: { // custom user pool attributes go here uniqueId: cognito.ProviderAttribute.AMAZON_USER_ID, - } - } + }, + }, }); ``` @@ -530,7 +538,7 @@ and imported user pools, clients can also be created via the `UserPoolClient` co ```ts const importedPool = cognito.UserPool.fromUserPoolId(this, 'imported-pool', 'us-east-1_oiuR12Abd'); new cognito.UserPoolClient(this, 'customer-app-client', { - userPool: importedPool + userPool: importedPool, }); ``` @@ -547,7 +555,7 @@ pool.addClient('app-client', { authFlows: { userPassword: true, userSrp: true, - } + }, }); ``` @@ -575,7 +583,7 @@ pool.addClient('app-client', { scopes: [ cognito.OAuthScope.OPENID ], callbackUrls: [ 'https://my-app-domain.com/welcome' ], logoutUrls: [ 'https://my-app-domain.com/signin' ], - } + }, }); ``` @@ -605,22 +613,29 @@ pool.addClient('app-client', { supportedIdentityProviders: [ cognito.UserPoolClientIdentityProvider.AMAZON, cognito.UserPoolClientIdentityProvider.COGNITO, - ] + ], }); ``` -If the identity provider and the app client are created in the same stack, specify the dependency between both constructs to make sure that the identity provider already exists when the app client will be created. The app client cannot handle the dependency to the identity provider automatically because the client does not have access to the provider's construct. +If the identity provider and the app client are created in the same stack, specify the dependency between both constructs to +make sure that the identity provider already exists when the app client will be created. The app client cannot handle the +dependency to the identity provider automatically because the client does not have access to the provider's construct. ```ts +const pool = new cognito.UserPool(this, 'Pool'); const provider = new cognito.UserPoolIdentityProviderAmazon(this, 'Amazon', { - // ... + userPool: pool, + clientId: 'amzn-client-id', + clientSecret: 'amzn-client-secret', }); + const client = pool.addClient('app-client', { // ... supportedIdentityProviders: [ cognito.UserPoolClientIdentityProvider.AMAZON, ], -} +}); + client.node.addDependency(provider); ``` @@ -638,16 +653,17 @@ pool.addClient('app-client', { }); ``` -Clients can (and should) be allowed to read and write relevant user attributes only. Usually every client can be allowed to read the `given_name` -attribute but not every client should be allowed to set the `email_verified` attribute. +Clients can (and should) be allowed to read and write relevant user attributes only. Usually every client can be allowed to +read the `given_name` attribute but not every client should be allowed to set the `email_verified` attribute. The same criteria applies for both standard and custom attributes, more info is available at [Attribute Permissions and Scopes](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-attribute-permissions-and-scopes). -The default behaviour is to allow read and write permissions on all attributes. The following code shows how this can be configured for a client. +The default behaviour is to allow read and write permissions on all attributes. The following code shows how this can be +configured for a client. ```ts const pool = new cognito.UserPool(this, 'Pool'); -const clientWriteAttributes = (new ClientAttributes()) +const clientWriteAttributes = (new cognito.ClientAttributes()) .withStandardAttributes({fullname: true, email: true}) .withCustomAttributes('favouritePizza', 'favouriteBeverage'); @@ -662,8 +678,9 @@ pool.addClient('app-client', { }); ``` -[Token revocation](https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html -) can be configured to be able to revoke refresh tokens in app clients. By default, token revocation is enabled for new user pools. The property can be used to enable the token revocation in existing app clients or to change the default behavior. +[Token revocation](https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html) +can be configured to be able to revoke refresh tokens in app clients. By default, token revocation is enabled for new user +pools. The property can be used to enable the token revocation in existing app clients or to change the default behavior. ```ts const pool = new cognito.UserPool(this, 'Pool'); @@ -687,8 +704,8 @@ app clients and configures the clients to use these scopes. ```ts const pool = new cognito.UserPool(this, 'Pool'); -const readOnlyScope = new ResourceServerScope({ scopeName: 'read', scopeDescription: 'Read-only access' }); -const fullAccessScope = new ResourceServerScope({ scopeName: '*', scopeDescription: 'Full access' }); +const readOnlyScope = new cognito.ResourceServerScope({ scopeName: 'read', scopeDescription: 'Read-only access' }); +const fullAccessScope = new cognito.ResourceServerScope({ scopeName: '*', scopeDescription: 'Full access' }); const userServer = pool.addResourceServer('ResourceServer', { identifier: 'users', @@ -699,7 +716,7 @@ const readOnlyClient = pool.addClient('read-only-client', { // ... oAuth: { // ... - scopes: [ OAuthScope.resourceServer(userServer, readOnlyScope) ], + scopes: [ cognito.OAuthScope.resourceServer(userServer, readOnlyScope) ], }, }); @@ -707,7 +724,7 @@ const fullAccessClient = pool.addClient('full-access-client', { // ... oAuth: { // ... - scopes: [ OAuthScope.resourceServer(userServer, fullAccessScope) ], + scopes: [ cognito.OAuthScope.resourceServer(userServer, fullAccessScope) ], }, }); ``` @@ -720,7 +737,8 @@ configured using domains. There are two ways to set up a domain - either the Ama with an available domain prefix, or a custom domain name can be chosen. The custom domain must be one that is already owned, and whose certificate is registered in AWS Certificate Manager. -The following code sets up a user pool domain in Amazon Cognito hosted domain with the prefix 'my-awesome-app', and another domain with the custom domain 'user.myapp.com' - +The following code sets up a user pool domain in Amazon Cognito hosted domain with the prefix 'my-awesome-app', and +another domain with the custom domain 'user.myapp.com' - ```ts const pool = new cognito.UserPool(this, 'Pool'); @@ -763,15 +781,15 @@ const client = userpool.addClient('Client', { callbackUrls: [ 'https://myapp.com/home', 'https://myapp.com/users', - ] - } -}) + ], + }, +}); const domain = userpool.addDomain('Domain', { // ... }); const signInUrl = domain.signInUrl(client, { redirectUri: 'https://myapp.com/home', // must be a URL configured under 'callbackUrls' with the client -}) +}); ``` Existing domains can be imported into CDK apps using `UserPoolDomain.fromDomainName()` API diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index 78953fd30c053..e0f10c43ca32a 100644 --- a/packages/@aws-cdk/aws-cognito/package.json +++ b/packages/@aws-cdk/aws-cognito/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-cognito/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-cognito/rosetta/default.ts-fixture index e0b5bfc8747a3..996a448f8bfc6 100644 --- a/packages/@aws-cdk/aws-cognito/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-cognito/rosetta/default.ts-fixture @@ -5,6 +5,7 @@ import * as certificatemanager from '@aws-cdk/aws-certificatemanager'; import * as cognito from '@aws-cdk/aws-cognito'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; +import * as path from 'path'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/aws-cognito/rosetta/with-lambda-trigger.ts-fixture b/packages/@aws-cdk/aws-cognito/rosetta/with-lambda-trigger.ts-fixture deleted file mode 100644 index de9aa90eedfc2..0000000000000 --- a/packages/@aws-cdk/aws-cognito/rosetta/with-lambda-trigger.ts-fixture +++ /dev/null @@ -1,26 +0,0 @@ -// Fixture with packages imported, but nothing else -import { Stack } from '@aws-cdk/core'; -import { Construct } from 'constructs'; -import * as cognito from '@aws-cdk/aws-cognito'; -import * as iam from '@aws-cdk/aws-iam'; -import * as lambda from '@aws-cdk/aws-lambda'; - -class Fixture extends Stack { - constructor(scope: Construct, id: string) { - super(scope, id); - - const postAuthFn = new lambda.Function(this, 'postAuthFn', { - code: lambda.Code.fromInline('post authentication'), - runtime: lambda.Runtime.NODEJS_12_X, - handler: 'index.handler', - }); - - const userpool = new cognito.UserPool(this, 'myuserpool', { - lambdaTriggers: { - postAuthentication: postAuthFn, - }, - }); - - /// here - } -} diff --git a/packages/@aws-cdk/aws-connect/package.json b/packages/@aws-cdk/aws-connect/package.json index 6bd8d4ba57d84..0d413aaf0db47 100644 --- a/packages/@aws-cdk/aws-connect/package.json +++ b/packages/@aws-cdk/aws-connect/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-connect", "module": "aws_cdk.aws_connect" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-cur/package.json b/packages/@aws-cdk/aws-cur/package.json index ea61b4d3f963c..c871e8b723bc9 100644 --- a/packages/@aws-cdk/aws-cur/package.json +++ b/packages/@aws-cdk/aws-cur/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-cur", "module": "aws_cdk.aws_cur" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-customerprofiles/package.json b/packages/@aws-cdk/aws-customerprofiles/package.json index 281b809346819..e73bd86cba198 100644 --- a/packages/@aws-cdk/aws-customerprofiles/package.json +++ b/packages/@aws-cdk/aws-customerprofiles/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.CustomerProfiles", diff --git a/packages/@aws-cdk/aws-databrew/package.json b/packages/@aws-cdk/aws-databrew/package.json index 4ab4173606602..66e42d3cd4e94 100644 --- a/packages/@aws-cdk/aws-databrew/package.json +++ b/packages/@aws-cdk/aws-databrew/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-databrew", "module": "aws_cdk.aws_databrew" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-datapipeline/package.json b/packages/@aws-cdk/aws-datapipeline/package.json index d3d4c24c94d09..0aff6f0f1389c 100644 --- a/packages/@aws-cdk/aws-datapipeline/package.json +++ b/packages/@aws-cdk/aws-datapipeline/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-datasync/package.json b/packages/@aws-cdk/aws-datasync/package.json index 2e9a5d220c1ad..1dc0061ea02ec 100644 --- a/packages/@aws-cdk/aws-datasync/package.json +++ b/packages/@aws-cdk/aws-datasync/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-datasync", "module": "aws_cdk.aws_datasync" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-dax/package.json b/packages/@aws-cdk/aws-dax/package.json index 4d9b73a99022e..c00396c2220ae 100644 --- a/packages/@aws-cdk/aws-dax/package.json +++ b/packages/@aws-cdk/aws-dax/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-detective/package.json b/packages/@aws-cdk/aws-detective/package.json index 8e10f2018e368..2522e915d02b3 100644 --- a/packages/@aws-cdk/aws-detective/package.json +++ b/packages/@aws-cdk/aws-detective/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-devopsguru/package.json b/packages/@aws-cdk/aws-devopsguru/package.json index 2b3b4e4077e2a..2082aba14cbd3 100644 --- a/packages/@aws-cdk/aws-devopsguru/package.json +++ b/packages/@aws-cdk/aws-devopsguru/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-devopsguru", "module": "aws_cdk.aws_devopsguru" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-directoryservice/package.json b/packages/@aws-cdk/aws-directoryservice/package.json index d68d97956dd36..7e32fa22bdfc5 100644 --- a/packages/@aws-cdk/aws-directoryservice/package.json +++ b/packages/@aws-cdk/aws-directoryservice/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-dlm/package.json b/packages/@aws-cdk/aws-dlm/package.json index a4dac9e9c23c0..d59d69ba32dc5 100644 --- a/packages/@aws-cdk/aws-dlm/package.json +++ b/packages/@aws-cdk/aws-dlm/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-dms/package.json b/packages/@aws-cdk/aws-dms/package.json index a6168fcf47cac..2c7da4b13bac7 100644 --- a/packages/@aws-cdk/aws-dms/package.json +++ b/packages/@aws-cdk/aws-dms/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-dynamodb-global/README.md b/packages/@aws-cdk/aws-dynamodb-global/README.md index a31c37eb3e114..77f2725e76797 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/README.md +++ b/packages/@aws-cdk/aws-dynamodb-global/README.md @@ -26,9 +26,9 @@ import { App } from '@aws-cdk/core'; const app = new App(); new GlobalTable(app, 'globdynamodb', { - partitionKey: { name: 'hashKey', type: AttributeType.String }, + partitionKey: { name: 'hashKey', type: AttributeType.STRING }, tableName: 'GlobalTable', - regions: [ "us-east-1", "us-east-2", "us-west-2" ] + regions: [ "us-east-1", "us-east-2", "us-west-2" ], }); app.synth(); ``` 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 d9b2afa5c1fbd..8dd6ba54c66da 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 @@ -30,7 +30,7 @@ "license": "Apache-2.0", "devDependencies": { "aws-sdk": "^2.596.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "eslint": "^7.32.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.25.4", diff --git a/packages/@aws-cdk/aws-dynamodb-global/package.json b/packages/@aws-cdk/aws-dynamodb-global/package.json index 61e2d44daab47..882e1b789e912 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/package.json @@ -39,7 +39,14 @@ "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-dynamodb/README.md b/packages/@aws-cdk/aws-dynamodb/README.md index b79b1e0efe465..f006eb277d984 100644 --- a/packages/@aws-cdk/aws-dynamodb/README.md +++ b/packages/@aws-cdk/aws-dynamodb/README.md @@ -58,6 +58,23 @@ const table = new dynamodb.Table(this, 'Table', { Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode. +## Table Class + +DynamoDB supports two table classes: + +* STANDARD - the default mode, and is recommended for the vast majority of workloads. +* STANDARD_INFREQUENT_ACCESS - optimized for tables where storage is the dominant cost. + +```ts +const table = new dynamodb.Table(this, 'Table', { + partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }, + tableClass: dynamodb.TableClass.STANDARD_INFREQUENT_ACCESS, +}); +``` + +Further reading: +https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.TableClasses.html + ## Configure AutoScaling for your table You can have DynamoDB automatically raise and lower the read and write capacities diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 020ca091aa777..bc4b6ce39c9f0 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -117,6 +117,12 @@ export enum TableEncryption { /** * Server-side KMS encryption with a customer master key managed by customer. * If `encryptionKey` is specified, this key will be used, otherwise, one will be defined. + * + * > **NOTE**: if `encryptionKey` is not specified and the `Table` construct creates + * > a KMS key for you, the key will be created with default permissions. If you are using + * > CDKv2, these permissions will be sufficient to enable the key for use with DynamoDB tables. + * > If you are using CDKv1, make sure the feature flag `@aws-cdk/aws-kms:defaultKeyPolicies` + * > is set to `true` in your `cdk.json`. */ CUSTOMER_MANAGED = 'CUSTOMER_MANAGED', @@ -193,11 +199,24 @@ export interface TableOptions extends SchemaOptions { */ readonly serverSideEncryption?: boolean; + /** + * Specify the table class. + * @default STANDARD + */ + readonly tableClass?: TableClass; + /** * Whether server-side encryption with an AWS managed customer master key is enabled. * * This property cannot be set if `serverSideEncryption` is set. * + * > **NOTE**: if you set this to `CUSTOMER_MANAGED` and `encryptionKey` is not + * > specified, the key that the Tablet generates for you will be created with + * > default permissions. If you are using CDKv2, these permissions will be + * > sufficient to enable the key for use with DynamoDB tables. If you are + * > using CDKv1, make sure the feature flag + * > `@aws-cdk/aws-kms:defaultKeyPolicies` is set to `true` in your `cdk.json`. + * * @default - server-side encryption is enabled with an AWS owned customer master key */ readonly encryption?: TableEncryption; @@ -1169,6 +1188,7 @@ export class Table extends TableBase { }, sseSpecification, streamSpecification, + tableClass: props.tableClass, timeToLiveSpecification: props.timeToLiveAttribute ? { attributeName: props.timeToLiveAttribute, enabled: true } : undefined, contributorInsightsSpecification: props.contributorInsightsEnabled !== undefined ? { enabled: props.contributorInsightsEnabled } : undefined, kinesisStreamSpecification: props.kinesisStream ? { streamArn: props.kinesisStream.streamArn } : undefined, @@ -1760,6 +1780,19 @@ export enum StreamViewType { KEYS_ONLY = 'KEYS_ONLY' } +/** + * DynamoDB's table class. + * + * @see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.TableClasses.html + */ +export enum TableClass { + /** Default table class for DynamoDB. */ + STANDARD = 'STANDARD', + + /** Table class for DynamoDB that reduces storage costs compared to existing DynamoDB Standard tables. */ + STANDARD_INFREQUENT_ACCESS = 'STANDARD_INFREQUENT_ACCESS', +} + /** * Just a convenient way to keep track of both attributes */ diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 146ec9cc2d741..6b22e0088a0b4 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -84,11 +84,11 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "jest": "^27.4.7", "sinon": "^9.2.4", "ts-jest": "^27.1.3" diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index 448aa7119eee6..6ca8b3bd0cf65 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -17,6 +17,7 @@ import { ProjectionType, StreamViewType, Table, + TableClass, TableEncryption, Operation, CfnTable, @@ -719,6 +720,47 @@ test('if an encryption key is included, encrypt/decrypt permissions are added to }); }); +test('when specifying STANDARD_INFREQUENT_ACCESS table class', () => { + const stack = new Stack(); + new Table(stack, CONSTRUCT_NAME, { + partitionKey: TABLE_PARTITION_KEY, + tableClass: TableClass.STANDARD_INFREQUENT_ACCESS, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', + { + TableClass: 'STANDARD_INFREQUENT_ACCESS', + }, + ); +}); + +test('when specifying STANDARD table class', () => { + const stack = new Stack(); + new Table(stack, CONSTRUCT_NAME, { + partitionKey: TABLE_PARTITION_KEY, + tableClass: TableClass.STANDARD, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', + { + TableClass: 'STANDARD', + }, + ); +}); + +test('when specifying no table class', () => { + const stack = new Stack(); + new Table(stack, CONSTRUCT_NAME, { + partitionKey: TABLE_PARTITION_KEY, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', + { + TableClass: Match.absent(), + }, + ); +}); + test('when specifying PAY_PER_REQUEST billing mode', () => { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index c40b8e6879709..fb2db769617e7 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -1051,6 +1051,37 @@ new ec2.Instance(this, 'Instance', { ``` +It is also possible to encrypt the block devices. In this example we will create an customer managed key encrypted EBS-backed root device: + +```ts +import { Key } from '@aws-cdk/aws-kms'; + +declare const vpc: ec2.Vpc; +declare const instanceType: ec2.InstanceType; +declare const machineImage: ec2.IMachineImage; + +const kmsKey = new Key(this, 'KmsKey') + +new ec2.Instance(this, 'Instance', { + vpc, + instanceType, + machineImage, + + // ... + + blockDevices: [ + { + deviceName: '/dev/sda1', + volume: ec2.BlockDeviceVolume.ebs(50, { + encrypted: true, + kmsKey: kmsKey, + }), + }, + ], +}); + +``` + ### Volumes Whereas a `BlockDeviceVolume` is an EBS volume that is created and destroyed as part of the creation and destruction of a specific instance. A `Volume` is for when you want an EBS volume separate from any particular instance. A `Volume` is an EBS block device that can be attached to, or detached from, any instance at any time. Some types of `Volume`s can also be attached to multiple instances at the same time to allow you to have shared storage between those instances. diff --git a/packages/@aws-cdk/aws-ec2/lib/client-vpn-endpoint.ts b/packages/@aws-cdk/aws-ec2/lib/client-vpn-endpoint.ts index 3ee8e8956d3d1..f28dd43462838 100644 --- a/packages/@aws-cdk/aws-ec2/lib/client-vpn-endpoint.ts +++ b/packages/@aws-cdk/aws-ec2/lib/client-vpn-endpoint.ts @@ -150,6 +150,37 @@ export interface ClientVpnEndpointOptions { * @default true */ readonly authorizeAllUsersToVpcCidr?: boolean; + + /** + * The maximum VPN session duration time. + * + * @default ClientVpnSessionTimeout.TWENTY_FOUR_HOURS + */ + readonly sessionTimeout?: ClientVpnSessionTimeout; + + /** + * Customizable text that will be displayed in a banner on AWS provided clients + * when a VPN session is established. + * + * UTF-8 encoded characters only. Maximum of 1400 characters. + * + * @default - no banner is presented to the client + */ + readonly clientLoginBanner?: string; +} + +/** + * Maximum VPN session duration time + */ +export enum ClientVpnSessionTimeout { + /** 8 hours */ + EIGHT_HOURS = 8, + /** 10 hours */ + TEN_HOURS = 10, + /** 12 hours */ + TWELVE_HOURS = 12, + /** 24 hours */ + TWENTY_FOUR_HOURS = 24, } /** @@ -284,6 +315,12 @@ export class ClientVpnEndpoint extends Resource implements IClientVpnEndpoint { throw new Error('The name of the Lambda function must begin with the `AWSClientVPN-` prefix'); } + if (props.clientLoginBanner + && !Token.isUnresolved(props.clientLoginBanner) + && props.clientLoginBanner.length > 1400) { + throw new Error(`The maximum length for the client login banner is 1400, got ${props.clientLoginBanner.length}`); + } + const logging = props.logging ?? true; const logGroup = logging ? props.logGroup ?? new logs.LogGroup(this, 'LogGroup') @@ -317,6 +354,13 @@ export class ClientVpnEndpoint extends Resource implements IClientVpnEndpoint { transportProtocol: props.transportProtocol, vpcId: props.vpc.vpcId, vpnPort: props.port, + sessionTimeoutHours: props.sessionTimeout, + clientLoginBannerOptions: props.clientLoginBanner + ? { + enabled: true, + bannerText: props.clientLoginBanner, + } + : undefined, }); this.endpointId = endpoint.ref; diff --git a/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts b/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts index dc91f6d795011..52c7738afdbef 100644 --- a/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts +++ b/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts @@ -24,8 +24,11 @@ function synthesizeBlockDeviceMappings(construct: Construct, blockDevic return blockDevices.map(({ deviceName, volume, mappingEnabled }): RT => { const { virtualName, ebsDevice: ebs } = volume; + let finalEbs: CfnLaunchTemplate.EbsProperty | CfnInstance.EbsProperty | undefined; + if (ebs) { - const { iops, volumeType } = ebs; + + const { iops, volumeType, kmsKey, ...rest } = ebs; if (!iops) { if (volumeType === EbsDeviceVolumeType.IO1) { @@ -34,9 +37,25 @@ function synthesizeBlockDeviceMappings(construct: Construct, blockDevic } else if (volumeType !== EbsDeviceVolumeType.IO1) { Annotations.of(construct).addWarning('iops will be ignored without volumeType: EbsDeviceVolumeType.IO1'); } + + /** + * Because the Ebs properties of the L2 Constructs do not match the Ebs properties of the Cfn Constructs, + * we have to do some transformation and handle all destructed properties + */ + + finalEbs = { + ...rest, + iops, + volumeType, + kmsKeyId: kmsKey?.keyArn, + }; + + } else { + finalEbs = undefined; } + const noDevice = mappingEnabled === false ? noDeviceValue : undefined; - return { deviceName, ebs, virtualName, noDevice } as any; + return { deviceName, ebs: finalEbs, virtualName, noDevice } as any; }); } diff --git a/packages/@aws-cdk/aws-ec2/lib/user-data.ts b/packages/@aws-cdk/aws-ec2/lib/user-data.ts index 1e92eb888e6f8..82e256b9a895a 100644 --- a/packages/@aws-cdk/aws-ec2/lib/user-data.ts +++ b/packages/@aws-cdk/aws-ec2/lib/user-data.ts @@ -1,5 +1,5 @@ import { IBucket } from '@aws-cdk/aws-s3'; -import { CfnElement, Fn, Resource, Stack } from '@aws-cdk/core'; +import { Fn, Resource, Stack, CfnResource } from '@aws-cdk/core'; import { OperatingSystemType } from './machine-image'; /** @@ -178,7 +178,7 @@ class LinuxUserData extends UserData { public addSignalOnExitCommand( resource: Resource ): void { const stack = Stack.of(resource); - const resourceID = stack.getLogicalId(resource.node.defaultChild as CfnElement); + const resourceID = (resource.node.defaultChild as CfnResource).logicalId; this.addOnExitCommands(`/opt/aws/bin/cfn-signal --stack ${stack.stackName} --resource ${resourceID} --region ${stack.region} -e $exitCode || echo 'Failed to send Cloudformation Signal'`); } @@ -235,7 +235,7 @@ class WindowsUserData extends UserData { public addSignalOnExitCommand( resource: Resource ): void { const stack = Stack.of(resource); - const resourceID = stack.getLogicalId(resource.node.defaultChild as CfnElement); + const resourceID = (resource.node.defaultChild as CfnResource).logicalId; this.addOnExitCommands(`cfn-signal --stack ${stack.stackName} --resource ${resourceID} --region ${stack.region} --success ($success.ToString().ToLower())`); } diff --git a/packages/@aws-cdk/aws-ec2/lib/volume.ts b/packages/@aws-cdk/aws-ec2/lib/volume.ts index 87e92b3f5006a..723c0f91e602e 100644 --- a/packages/@aws-cdk/aws-ec2/lib/volume.ts +++ b/packages/@aws-cdk/aws-ec2/lib/volume.ts @@ -89,6 +89,17 @@ export interface EbsDeviceOptions extends EbsDeviceOptionsBase { * @default false */ readonly encrypted?: boolean; + + /** + * The ARN of the AWS Key Management Service (AWS KMS) CMK used for encryption. + * + * You have to ensure that the KMS CMK has the correct permissions to be used by the service launching the ec2 instances. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html#ebs-encryption-requirements + * + * @default - If encrypted is true, the default aws/ebs KMS key will be used. + */ + readonly kmsKey?: IKey; } /** @@ -108,7 +119,7 @@ export interface EbsDeviceSnapshotOptions extends EbsDeviceOptionsBase { /** * Properties of an EBS block device */ -export interface EbsDeviceProps extends EbsDeviceSnapshotOptions { +export interface EbsDeviceProps extends EbsDeviceSnapshotOptions, EbsDeviceOptions { /** * The snapshot ID of the volume to use * diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc-flow-logs.ts b/packages/@aws-cdk/aws-ec2/lib/vpc-flow-logs.ts index e7a2b881d91be..fdc4a06ad2227 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc-flow-logs.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc-flow-logs.ts @@ -198,7 +198,6 @@ class S3Destination extends FlowLogDestination { if (this.props.s3Bucket === undefined) { s3Bucket = new s3.Bucket(scope, 'Bucket', { encryption: s3.BucketEncryption.UNENCRYPTED, - enforceSSL: true, removalPolicy: RemovalPolicy.RETAIN, }); } else { diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 5d51459d3b4d2..27ad97c60186d 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -86,7 +86,7 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, @@ -693,7 +693,9 @@ "props-physical-name:@aws-cdk/aws-ec2.VpnGatewayProps", "props-physical-name:@aws-cdk/aws-ec2.ClientVpnEndpointProps", "props-physical-name:@aws-cdk/aws-ec2.ClientVpnAuthorizationRuleProps", - "props-physical-name:@aws-cdk/aws-ec2.ClientVpnRouteProps" + "props-physical-name:@aws-cdk/aws-ec2.ClientVpnRouteProps", + "duration-prop-type:@aws-cdk/aws-ec2.ClientVpnEndpointOptions.sessionTimeout", + "duration-prop-type:@aws-cdk/aws-ec2.ClientVpnEndpointProps.sessionTimeout" ] }, "stability": "stable", diff --git a/packages/@aws-cdk/aws-ec2/test/client-vpn-endpoint.test.ts b/packages/@aws-cdk/aws-ec2/test/client-vpn-endpoint.test.ts index 2bf2357f9c32f..2b087d6da1f10 100644 --- a/packages/@aws-cdk/aws-ec2/test/client-vpn-endpoint.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/client-vpn-endpoint.test.ts @@ -242,6 +242,35 @@ test('client vpn endpoint with custom route', () => { }); }); +test('client vpn endpoint with custom session timeout', () => { + vpc.addClientVpnEndpoint('Endpoint', { + cidr: '10.100.0.0/16', + serverCertificateArn: 'server-certificate-arn', + clientCertificateArn: 'client-certificate-arn', + sessionTimeout: ec2.ClientVpnSessionTimeout.TEN_HOURS, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::ClientVpnEndpoint', { + SessionTimeoutHours: 10, + }); +}); + +test('client vpn endpoint with client login banner', () => { + vpc.addClientVpnEndpoint('Endpoint', { + cidr: '10.100.0.0/16', + serverCertificateArn: 'server-certificate-arn', + clientCertificateArn: 'client-certificate-arn', + clientLoginBanner: 'Welcome!', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::ClientVpnEndpoint', { + ClientLoginBannerOptions: { + Enabled: true, + BannerText: 'Welcome!', + }, + }); +}); + test('throws with more than 2 dns servers', () => { expect(() => vpc.addClientVpnEndpoint('Endpoint', { cidr: '10.100.0.0/16', diff --git a/packages/@aws-cdk/aws-ec2/test/instance.test.ts b/packages/@aws-cdk/aws-ec2/test/instance.test.ts index e2d4e47e78fde..7f40cd8f202a0 100644 --- a/packages/@aws-cdk/aws-ec2/test/instance.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/instance.test.ts @@ -1,5 +1,6 @@ import * as path from 'path'; import { Match, Template } from '@aws-cdk/assertions'; +import { Key } from '@aws-cdk/aws-kms'; import { Asset } from '@aws-cdk/aws-s3-assets'; import { StringParameter } from '@aws-cdk/aws-ssm'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; @@ -184,6 +185,7 @@ describe('instance', () => { describe('blockDeviceMappings', () => { test('can set blockDeviceMappings', () => { // WHEN + const kmsKey = new Key(stack, 'EbsKey'); new Instance(stack, 'Instance', { vpc, machineImage: new AmazonLinuxImage(), @@ -197,6 +199,16 @@ describe('instance', () => { volumeType: EbsDeviceVolumeType.IO1, iops: 5000, }), + }, { + deviceName: 'ebs-cmk', + mappingEnabled: true, + volume: BlockDeviceVolume.ebs(15, { + deleteOnTermination: true, + encrypted: true, + kmsKey: kmsKey, + volumeType: EbsDeviceVolumeType.IO1, + iops: 5000, + }), }, { deviceName: 'ebs-snapshot', mappingEnabled: false, @@ -224,6 +236,22 @@ describe('instance', () => { VolumeType: 'io1', }, }, + { + DeviceName: 'ebs-cmk', + Ebs: { + DeleteOnTermination: true, + Encrypted: true, + KmsKeyId: { + 'Fn::GetAtt': [ + 'EbsKeyD3FEE551', + 'Arn', + ], + }, + Iops: 5000, + VolumeSize: 15, + VolumeType: 'io1', + }, + }, { DeviceName: 'ebs-snapshot', Ebs: { diff --git a/packages/@aws-cdk/aws-ec2/test/integ.vpc-flow-logs.expected.json b/packages/@aws-cdk/aws-ec2/test/integ.vpc-flow-logs.expected.json index 5164db5b00faf..ab9eb13b2c415 100644 --- a/packages/@aws-cdk/aws-ec2/test/integ.vpc-flow-logs.expected.json +++ b/packages/@aws-cdk/aws-ec2/test/integ.vpc-flow-logs.expected.json @@ -527,53 +527,6 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "VPCFlowLogsS3BucketPolicyB2C2A045": { - "Type": "AWS::S3::BucketPolicy", - "Properties": { - "Bucket": { - "Ref": "VPCFlowLogsS3BucketFB7DC2BE" - }, - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:*", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", - "Principal": { - "AWS": "*" - }, - "Resource": [ - { - "Fn::GetAtt": [ - "VPCFlowLogsS3BucketFB7DC2BE", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "VPCFlowLogsS3BucketFB7DC2BE", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - } - } - }, "VPCFlowLogsS3FlowLogB5256CFF": { "Type": "AWS::EC2::FlowLog", "Properties": { diff --git a/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts b/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts index 499368608e7a6..49b979ca7084d 100644 --- a/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts @@ -4,6 +4,7 @@ import { Role, ServicePrincipal, } from '@aws-cdk/aws-iam'; +import { Key } from '@aws-cdk/aws-kms'; import { App, Duration, @@ -254,6 +255,7 @@ describe('LaunchTemplate', () => { test('Given blockDeviceMapping', () => { // GIVEN + const kmsKey = new Key(stack, 'EbsKey'); const blockDevices: BlockDevice[] = [ { deviceName: 'ebs', @@ -264,6 +266,16 @@ describe('LaunchTemplate', () => { volumeType: EbsDeviceVolumeType.IO1, iops: 5000, }), + }, { + deviceName: 'ebs-cmk', + mappingEnabled: true, + volume: BlockDeviceVolume.ebs(15, { + deleteOnTermination: true, + encrypted: true, + kmsKey: kmsKey, + volumeType: EbsDeviceVolumeType.IO1, + iops: 5000, + }), }, { deviceName: 'ebs-snapshot', mappingEnabled: false, @@ -297,6 +309,22 @@ describe('LaunchTemplate', () => { VolumeType: 'io1', }, }, + { + DeviceName: 'ebs-cmk', + Ebs: { + DeleteOnTermination: true, + Encrypted: true, + KmsKeyId: { + 'Fn::GetAtt': [ + 'EbsKeyD3FEE551', + 'Arn', + ], + }, + Iops: 5000, + VolumeSize: 15, + VolumeType: 'io1', + }, + }, { DeviceName: 'ebs-snapshot', Ebs: { diff --git a/packages/@aws-cdk/aws-ec2/test/userdata.test.ts b/packages/@aws-cdk/aws-ec2/test/userdata.test.ts index e2596d8699abd..c385ce83a7254 100644 --- a/packages/@aws-cdk/aws-ec2/test/userdata.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/userdata.test.ts @@ -1,5 +1,6 @@ import { Bucket } from '@aws-cdk/aws-s3'; -import { Aws, Stack } from '@aws-cdk/core'; +import { Template, Match } from '@aws-cdk/assertions'; +import { Aws, Stack, CfnResource } from '@aws-cdk/core'; import * as ec2 from '../lib'; describe('user data', () => { @@ -41,6 +42,7 @@ describe('user data', () => { const stack = new Stack(); const resource = new ec2.Vpc(stack, 'RESOURCE'); const userData = ec2.UserData.forWindows(); + const logicalId = (resource.node.defaultChild as CfnResource).logicalId; // WHEN userData.addSignalOnExitCommand( resource ); @@ -49,9 +51,10 @@ describe('user data', () => { // THEN const rendered = userData.render(); + expect(stack.resolve(logicalId)).toEqual('RESOURCE1989552F'); expect(rendered).toEqual('trap {\n' + '$success=($PSItem.Exception.Message -eq "Success")\n' + - `cfn-signal --stack Default --resource RESOURCE1989552F --region ${Aws.REGION} --success ($success.ToString().ToLower())\n` + + `cfn-signal --stack Default --resource ${logicalId} --region ${Aws.REGION} --success ($success.ToString().ToLower())\n` + 'break\n' + '}\n' + 'command1\n' + @@ -59,6 +62,44 @@ describe('user data', () => { ); }); + test('can create Windows with Signal Command and userDataCausesReplacement', () => { + // GIVEN + const stack = new Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const userData = ec2.UserData.forWindows(); + const resource = new ec2.Instance(stack, 'RESOURCE', { + vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.LARGE), + machineImage: ec2.MachineImage.genericWindows({ ['us-east-1']: 'ami-12345678' }), + userDataCausesReplacement: true, + userData, + }); + + const logicalId = (resource.node.defaultChild as CfnResource).logicalId; + + // WHEN + userData.addSignalOnExitCommand( resource ); + userData.addCommands('command1'); + + // THEN + Template.fromStack(stack).templateMatches({ + Resources: Match.objectLike({ + RESOURCE1989552Fdfd505305f427919: { + Type: 'AWS::EC2::Instance', + }, + }), + }); + expect(stack.resolve(logicalId)).toEqual('RESOURCE1989552Fdfd505305f427919'); + const rendered = userData.render(); + expect(rendered).toEqual('trap {\n' + + '$success=($PSItem.Exception.Message -eq "Success")\n' + + `cfn-signal --stack Default --resource ${logicalId} --region ${Aws.REGION} --success ($success.ToString().ToLower())\n` + + 'break\n' + + '}\n' + + 'command1\n' + + 'throw "Success"', + ); + }); test('can windows userdata download S3 files', () => { // GIVEN const stack = new Stack(); @@ -174,6 +215,7 @@ describe('user data', () => { // GIVEN const stack = new Stack(); const resource = new ec2.Vpc(stack, 'RESOURCE'); + const logicalId = (resource.node.defaultChild as CfnResource).logicalId; // WHEN const userData = ec2.UserData.forLinux(); @@ -182,15 +224,53 @@ describe('user data', () => { // THEN const rendered = userData.render(); + expect(stack.resolve(logicalId)).toEqual('RESOURCE1989552F'); expect(rendered).toEqual('#!/bin/bash\n' + 'function exitTrap(){\n' + 'exitCode=$?\n' + - `/opt/aws/bin/cfn-signal --stack Default --resource RESOURCE1989552F --region ${Aws.REGION} -e $exitCode || echo \'Failed to send Cloudformation Signal\'\n` + + `/opt/aws/bin/cfn-signal --stack Default --resource ${logicalId} --region ${Aws.REGION} -e $exitCode || echo \'Failed to send Cloudformation Signal\'\n` + '}\n' + 'trap exitTrap EXIT\n' + 'command1'); }); + test('can create Linux with Signal Command and userDataCausesReplacement', () => { + // GIVEN + const stack = new Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const userData = ec2.UserData.forLinux(); + const resource = new ec2.Instance(stack, 'RESOURCE', { + vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.LARGE), + machineImage: ec2.MachineImage.genericLinux({ ['us-east-1']: 'ami-12345678' }), + userDataCausesReplacement: true, + userData, + }); + + const logicalId = (resource.node.defaultChild as CfnResource).logicalId; + + // WHEN + userData.addSignalOnExitCommand( resource ); + userData.addCommands('command1'); + + // THEN + Template.fromStack(stack).templateMatches({ + Resources: Match.objectLike({ + RESOURCE1989552F74a24ef4fbc89422: { + Type: 'AWS::EC2::Instance', + }, + }), + }); + expect(stack.resolve(logicalId)).toEqual('RESOURCE1989552F74a24ef4fbc89422'); + const rendered = userData.render(); + expect(rendered).toEqual('#!/bin/bash\n' + + 'function exitTrap(){\n' + + 'exitCode=$?\n' + + `/opt/aws/bin/cfn-signal --stack Default --resource ${logicalId} --region ${Aws.REGION} -e $exitCode || echo \'Failed to send Cloudformation Signal\'\n` + + '}\n' + + 'trap exitTrap EXIT\n' + + 'command1'); + }); test('can linux userdata download S3 files', () => { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-ecr/README.md b/packages/@aws-cdk/aws-ecr/README.md index 193d5de2f0645..6e67349024891 100644 --- a/packages/@aws-cdk/aws-ecr/README.md +++ b/packages/@aws-cdk/aws-ecr/README.md @@ -79,6 +79,32 @@ You can set tag immutability on images in our repository using the `imageTagMuta new ecr.Repository(this, 'Repo', { imageTagMutability: ecr.TagMutability.IMMUTABLE }); ``` +### Encryption + +By default, Amazon ECR uses server-side encryption with Amazon S3-managed encryption keys which encrypts your data at rest using an AES-256 encryption algorithm. For more control over the encryption for your Amazon ECR repositories, you can use server-side encryption with KMS keys stored in AWS Key Management Service (AWS KMS). Read more about this feature in the [ECR Developer Guide](https://docs.aws.amazon.com/AmazonECR/latest/userguide/encryption-at-rest.html). + +When you use AWS KMS to encrypt your data, you can either use the default AWS managed key, which is managed by Amazon ECR, by specifying `RepositoryEncryption.KMS` in the `encryption` property. Or specify your own customer managed KMS key, by specifying the `encryptionKey` property. + +When `encryptionKey` is set, the `encryption` property must be `KMS` or empty. + +In the case `encryption` is set to `KMS` but no `encryptionKey` is set, an AWS managed KMS key is used. + +```ts +new ecr.Repository(this, 'Repo', { + encryption: ecr.RepositoryEncryption.KMS +}); +``` + +Otherwise, a customer-managed KMS key is used if `encryptionKey` was set and `encryption` was optionally set to `KMS`. + +```ts +import * as kms from '@aws-cdk/aws-kms'; + +new ecr.Repository(this, 'Repo', { + encryptionKey: new kms.Key(this, 'Key'), +}); +``` + ## Automatically clean up repositories You can set life cycle rules to automatically clean up old images from your diff --git a/packages/@aws-cdk/aws-ecr/lib/repository.ts b/packages/@aws-cdk/aws-ecr/lib/repository.ts index 74df965ff5d58..3d4e44c0776a9 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -1,6 +1,7 @@ import { EOL } from 'os'; import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; +import * as kms from '@aws-cdk/aws-kms'; import { ArnFormat, IResource, Lazy, RemovalPolicy, Resource, Stack, Token } from '@aws-cdk/core'; import { IConstruct, Construct } from 'constructs'; import { CfnRepository } from './ecr.generated'; @@ -327,6 +328,27 @@ export interface RepositoryProps { */ readonly repositoryName?: string; + /** + * The kind of server-side encryption to apply to this repository. + * + * If you choose KMS, you can specify a KMS key via `encryptionKey`. If + * encryptionKey is not specified, an AWS managed KMS key is used. + * + * @default - `KMS` if `encryptionKey` is specified, or `AES256` otherwise. + */ + readonly encryption?: RepositoryEncryption; + + /** + * External KMS key to use for repository encryption. + * + * The 'encryption' property must be either not specified or set to "KMS". + * An error will be emitted if encryption is set to "AES256". + * + * @default - If encryption is set to `KMS` and this property is undefined, + * an AWS managed KMS key is used. + */ + readonly encryptionKey?: kms.IKey; + /** * Life cycle rules to apply to this registry * @@ -490,6 +512,7 @@ export class Repository extends RepositoryBase { scanOnPush: true, }, imageTagMutability: props.imageTagMutability || undefined, + encryptionConfiguration: this.parseEncryption(props), }); resource.applyRemovalPolicy(props.removalPolicy); @@ -602,6 +625,34 @@ export class Repository extends RepositoryBase { validateAnyRuleLast(ret); return ret; } + + /** + * Set up key properties and return the Repository encryption property from the + * user's configuration. + */ + private parseEncryption(props: RepositoryProps): CfnRepository.EncryptionConfigurationProperty | undefined { + + // default based on whether encryptionKey is specified + const encryptionType = props.encryption ?? (props.encryptionKey ? RepositoryEncryption.KMS : RepositoryEncryption.AES_256); + + // if encryption key is set, encryption must be set to KMS. + if (encryptionType !== RepositoryEncryption.KMS && props.encryptionKey) { + throw new Error(`encryptionKey is specified, so 'encryption' must be set to KMS (value: ${encryptionType.value})`); + } + + if (encryptionType === RepositoryEncryption.AES_256) { + return undefined; + } + + if (encryptionType === RepositoryEncryption.KMS) { + return { + encryptionType: 'KMS', + kmsKey: props.encryptionKey?.keyArn, + }; + } + + throw new Error(`Unexpected 'encryptionType': ${encryptionType}`); + } } function validateAnyRuleLast(rules: LifecycleRule[]) { @@ -664,3 +715,24 @@ export enum TagMutability { IMMUTABLE = 'IMMUTABLE', } + +/** + * Indicates whether server-side encryption is enabled for the object, and whether that encryption is + * from the AWS Key Management Service (AWS KMS) or from Amazon S3 managed encryption (SSE-S3). + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html#SysMetadata + */ +export class RepositoryEncryption { + /** + * 'AES256' + */ + public static readonly AES_256 = new RepositoryEncryption('AES256'); + /** + * 'KMS' + */ + public static readonly KMS = new RepositoryEncryption('KMS'); + + /** + * @param value the string value of the encryption + */ + protected constructor(public readonly value: string) { } +} diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index e2b8bccc7c29f..3141f0c8dc6d1 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -93,6 +93,7 @@ "dependencies": { "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, @@ -100,6 +101,7 @@ "peerDependencies": { "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, diff --git a/packages/@aws-cdk/aws-ecr/test/repository.test.ts b/packages/@aws-cdk/aws-ecr/test/repository.test.ts index 470638d89355e..232ad400cdff6 100644 --- a/packages/@aws-cdk/aws-ecr/test/repository.test.ts +++ b/packages/@aws-cdk/aws-ecr/test/repository.test.ts @@ -1,6 +1,7 @@ import { EOL } from 'os'; import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; +import * as kms from '@aws-cdk/aws-kms'; import * as cdk from '@aws-cdk/core'; import * as ecr from '../lib'; @@ -363,6 +364,79 @@ describe('repository', () => { expect(() => app.synth()).toThrow(/A PolicyStatement used in a resource-based policy must specify at least one IAM principal/); }); + test('default encryption configuration', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'my-stack'); + + // WHEN + new ecr.Repository(stack, 'Repo', { encryption: ecr.RepositoryEncryption.AES_256 }); + + // THEN + Template.fromStack(stack).templateMatches({ + Resources: { + Repo02AC86CF: { + Type: 'AWS::ECR::Repository', + DeletionPolicy: 'Retain', + UpdateReplacePolicy: 'Retain', + }, + }, + }); + }); + + test('kms encryption configuration', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'my-stack'); + + // WHEN + new ecr.Repository(stack, 'Repo', { encryption: ecr.RepositoryEncryption.KMS }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', + { + EncryptionConfiguration: { + EncryptionType: 'KMS', + }, + }); + }); + + test('kms encryption with custom kms configuration', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'my-stack'); + + // WHEN + const custom_key = new kms.Key(stack, 'Key'); + new ecr.Repository(stack, 'Repo', { encryptionKey: custom_key }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', + { + EncryptionConfiguration: { + EncryptionType: 'KMS', + KmsKey: { + 'Fn::GetAtt': [ + 'Key961B73FD', + 'Arn', + ], + }, + }, + }); + }); + + test('fails if with custom kms key and AES256 as encryption', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'my-stack'); + const custom_key = new kms.Key(stack, 'Key'); + + // THEN + expect(() => { + new ecr.Repository(stack, 'Repo', { encryption: ecr.RepositoryEncryption.AES_256, encryptionKey: custom_key }); + }).toThrow('encryptionKey is specified, so \'encryption\' must be set to KMS (value: AES256)'); + }); + describe('events', () => { test('onImagePushed without imageTag creates the correct event', () => { const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/README.md b/packages/@aws-cdk/aws-ecs-patterns/README.md index 386682e0b75d9..593421abeaa1a 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/README.md +++ b/packages/@aws-cdk/aws-ecs-patterns/README.md @@ -544,6 +544,26 @@ const queueProcessingFargateService = new ecsPatterns.QueueProcessingFargateServ }); ``` +### Set a custom container-level Healthcheck for QueueProcessingFargateService + +```ts +declare const vpc: ec2.Vpc; +declare const securityGroup: ec2.SecurityGroup; +const queueProcessingFargateService = new ecsPatterns.QueueProcessingFargateService(this, 'Service', { + vpc, + memoryLimitMiB: 512, + image: ecs.ContainerImage.fromRegistry('test'), + healthCheck: { + command: [ "CMD-SHELL", "curl -f http://localhost/ || exit 1" ], + // the properties below are optional + interval: Duration.minutes(30), + retries: 123, + startPeriod: Duration.minutes(30), + timeout: Duration.minutes(30), + }, +}); +``` + ### Set capacityProviderStrategies for QueueProcessingEc2Service ```ts diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts index 033d19dc39612..ff63c4a3502d8 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts @@ -1,5 +1,5 @@ import * as ec2 from '@aws-cdk/aws-ec2'; -import { FargatePlatformVersion, FargateService, FargateTaskDefinition } from '@aws-cdk/aws-ecs'; +import { FargatePlatformVersion, FargateService, FargateTaskDefinition, HealthCheck } from '@aws-cdk/aws-ecs'; import * as cxapi from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import { QueueProcessingServiceBase, QueueProcessingServiceBaseProps } from '../base/queue-processing-service-base'; @@ -69,6 +69,13 @@ export interface QueueProcessingFargateServiceProps extends QueueProcessingServi */ readonly containerName?: string; + /** + * The health check command and associated configuration parameters for the container. + * + * @default - Health check configuration from container. + */ + readonly healthCheck?: HealthCheck; + /** * The subnets to associate with the service. * @@ -127,6 +134,7 @@ export class QueueProcessingFargateService extends QueueProcessingServiceBase { environment: this.environment, secrets: this.secrets, logging: this.logDriver, + healthCheck: props.healthCheck, }); // The desiredCount should be removed from the fargate service when the feature flag is removed. diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json index ae18adac13bbf..8af962023d1d2 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json @@ -604,6 +604,15 @@ } ], "Essential": true, + "HealthCheck": { + "Command": [ + "CMD-SHELL", + "curl -f http://localhost/ || exit 1" + ], + "Interval": 720, + "Retries": 34, + "Timeout": 5 + }, "Image": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.ts index 5b0d74f9e3ceb..4877a7d211747 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; -import { App, Stack } from '@aws-cdk/core'; +import { App, Stack, Duration } from '@aws-cdk/core'; import { QueueProcessingFargateService } from '../../lib'; @@ -14,6 +14,11 @@ new QueueProcessingFargateService(stack, 'PublicQueueService', { memoryLimitMiB: 512, image: new ecs.AssetImage(path.join(__dirname, '..', 'sqs-reader')), assignPublicIp: true, + healthCheck: { + command: ['CMD-SHELL', 'curl -f http://localhost/ || exit 1'], + interval: Duration.minutes(12), + retries: 34, + }, }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index f1e50e28ea909..d99b830aad6dd 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -96,6 +96,15 @@ const cluster = new ecs.Cluster(this, 'Cluster', { }); ``` +The following code imports an existing cluster using the ARN which can be used to +import an Amazon ECS service either EC2 or Fargate. + +```ts +const clusterArn = 'arn:aws:ecs:us-east-1:012345678910:cluster/clusterName'; + +const cluster = ecs.Cluster.fromClusterArn(this, 'Cluster', clusterArn); +``` + To use tasks with Amazon EC2 launch-type, you have to add capacity to the cluster in order for tasks to be scheduled on your instances. Typically, you add an AutoScalingGroup with instances running the latest diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index 34eb1dc409975..4e5de4c90eacf 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -5,10 +5,10 @@ import * as elb from '@aws-cdk/aws-elasticloadbalancing'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as iam from '@aws-cdk/aws-iam'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; -import { Annotations, Duration, IResolvable, IResource, Lazy, Resource, Stack } from '@aws-cdk/core'; +import { Annotations, Duration, IResolvable, IResource, Lazy, Resource, Stack, ArnFormat } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { LoadBalancerTargetOptions, NetworkMode, TaskDefinition } from '../base/task-definition'; -import { ICluster, CapacityProviderStrategy, ExecuteCommandLogging } from '../cluster'; +import { ICluster, CapacityProviderStrategy, ExecuteCommandLogging, Cluster } from '../cluster'; import { ContainerDefinition, Protocol } from '../container-definition'; import { CfnService } from '../ecs.generated'; import { ScalableTaskCount } from './scalable-task-count'; @@ -315,6 +315,46 @@ export interface IBaseService extends IService { */ export abstract class BaseService extends Resource implements IBaseService, elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget, elb.ILoadBalancerTarget { + /** + * Import an existing ECS/Fargate Service using the service cluster format. + * The format is the "new" format "arn:aws:ecs:region:aws_account_id:service/cluster-name/service-name". + * @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids + */ + public static fromServiceArnWithCluster(scope: Construct, id: string, serviceArn: string): IBaseService { + const stack = Stack.of(scope); + const arn = stack.splitArn(serviceArn, ArnFormat.SLASH_RESOURCE_NAME); + const resourceName = arn.resourceName; + if (!resourceName) { + throw new Error('Missing resource Name from service ARN: ${serviceArn}'); + } + const resourceNameParts = resourceName.split('/'); + if (resourceNameParts.length !== 2) { + throw new Error(`resource name ${resourceName} from service ARN: ${serviceArn} is not using the ARN cluster format`); + } + const clusterName = resourceNameParts[0]; + const serviceName = resourceNameParts[1]; + + const clusterArn = Stack.of(scope).formatArn({ + partition: arn.partition, + region: arn.region, + account: arn.account, + service: 'ecs', + resource: 'cluster', + resourceName: clusterName, + }); + + const cluster = Cluster.fromClusterArn(scope, `${id}Cluster`, clusterArn); + + class Import extends Resource implements IBaseService { + public readonly serviceArn = serviceArn; + public readonly serviceName = serviceName; + public readonly cluster = cluster; + } + + return new Import(scope, id, { + environmentFromArn: serviceArn, + }); + } /** * The security groups which manage the allowed network traffic for the service. diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index 1dc50ac97ae49..8e48e2be59cec 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -6,7 +6,7 @@ import * as kms from '@aws-cdk/aws-kms'; import * as logs from '@aws-cdk/aws-logs'; import * as s3 from '@aws-cdk/aws-s3'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; -import { Duration, Lazy, IResource, Resource, Stack, Aspects, IAspect, IConstruct } from '@aws-cdk/core'; +import { Duration, Lazy, IResource, Resource, Stack, Aspects, IAspect, IConstruct, ArnFormat } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { BottleRocketImage, EcsOptimizedAmi } from './amis'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; @@ -105,6 +105,41 @@ export class Cluster extends Resource implements ICluster { return new ImportedCluster(scope, id, attrs); } + /** + * Import an existing cluster to the stack from the cluster ARN. + * This does not provide access to the vpc, hasEc2Capacity, or connections - + * use the `fromClusterAttributes` method to access those properties. + */ + public static fromClusterArn(scope: Construct, id: string, clusterArn: string): ICluster { + const stack = Stack.of(scope); + const arn = stack.splitArn(clusterArn, ArnFormat.SLASH_RESOURCE_NAME); + const clusterName = arn.resourceName; + + if (!clusterName) { + throw new Error(`Missing required Cluster Name from Cluster ARN: ${clusterArn}`); + } + + const errorSuffix = 'is not available for a Cluster imported using fromClusterArn(), please use fromClusterAttributes() instead.'; + + class Import extends Resource implements ICluster { + public readonly clusterArn = clusterArn; + public readonly clusterName = clusterName!; + get hasEc2Capacity(): boolean { + throw new Error(`hasEc2Capacity ${errorSuffix}`); + } + get connections(): ec2.Connections { + throw new Error(`connections ${errorSuffix}`); + } + get vpc(): ec2.IVpc { + throw new Error(`vpc ${errorSuffix}`); + } + } + + return new Import(scope, id, { + environmentFromArn: clusterArn, + }); + } + /** * Manage the allowed network connections for the cluster with Security Groups. */ diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index e7a6d72b8ceb7..b46e66df585fa 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -408,6 +408,11 @@ export class ContainerDefinition extends CoreConstruct { */ public readonly referencesSecretJsonField?: boolean; + /** + * The name of the image referenced by this container. + */ + public readonly imageName: string; + /** * The inference accelerators referenced by this container. */ @@ -441,6 +446,8 @@ export class ContainerDefinition extends CoreConstruct { this.containerName = props.containerName ?? this.node.id; this.imageConfig = props.image.bind(this, this); + this.imageName = this.imageConfig.imageName; + if (props.logging) { this.logDriverConfig = props.logging.bind(this, this); } diff --git a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts index 3b65516ba7dfa..6056682b73666 100644 --- a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-task-definition.ts @@ -20,9 +20,9 @@ export interface Ec2TaskDefinitionProps extends CommonTaskDefinitionProps { /** * The Docker networking mode to use for the containers in the task. * - * The valid values are none, bridge, awsvpc, and host. + * The valid values are NONE, BRIDGE, AWS_VPC, and HOST. * - * @default - NetworkMode.Bridge for EC2 tasks, AwsVpc for Fargate tasks. + * @default - NetworkMode.BRIDGE for EC2 tasks, AWS_VPC for Fargate tasks. */ readonly networkMode?: NetworkMode; diff --git a/packages/@aws-cdk/aws-ecs/test/base-service.test.ts b/packages/@aws-cdk/aws-ecs/test/base-service.test.ts new file mode 100644 index 0000000000000..6e3b563cf4e1e --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/base-service.test.ts @@ -0,0 +1,44 @@ +import * as cdk from '@aws-cdk/core'; +import * as ecs from '../lib'; + +let stack: cdk.Stack; + +beforeEach(() => { + stack = new cdk.Stack(); +}); + +describe('When import an ECS Service', () => { + test('with serviceArnWithCluster', () => { + // GIVEN + const clusterName = 'cluster-name'; + const serviceName = 'my-http-service'; + const region = 'service-region'; + const account = 'service-account'; + const serviceArn = `arn:aws:ecs:${region}:${account}:service/${clusterName}/${serviceName}`; + + // WHEN + const service = ecs.BaseService.fromServiceArnWithCluster(stack, 'Service', serviceArn); + + // THEN + expect(service.serviceArn).toEqual(serviceArn); + expect(service.serviceName).toEqual(serviceName); + expect(service.env.account).toEqual(account); + expect(service.env.region).toEqual(region); + + expect(service.cluster.clusterName).toEqual(clusterName); + expect(service.cluster.env.account).toEqual(account); + expect(service.cluster.env.region).toEqual(region); + }); + + test('throws an expection if no resourceName provided on fromServiceArnWithCluster', () => { + expect(() => { + ecs.BaseService.fromServiceArnWithCluster(stack, 'Service', 'arn:aws:ecs:service-region:service-account:service'); + }).toThrowError(/Missing resource Name from service ARN/); + }); + + test('throws an expection if not using cluster arn format on fromServiceArnWithCluster', () => { + expect(() => { + ecs.BaseService.fromServiceArnWithCluster(stack, 'Service', 'arn:aws:ecs:service-region:service-account:service/my-http-service'); + }).toThrowError(/is not using the ARN cluster format/); + }); +}); diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index f6b34f76439c0..d167c30989ded 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -2140,6 +2140,30 @@ describe('cluster', () => { }); + + test('When importing ECS Cluster via Arn', () => { + // GIVEN + const stack = new cdk.Stack(); + const clusterName = 'my-cluster'; + const region = 'service-region'; + const account = 'service-account'; + const cluster = ecs.Cluster.fromClusterArn(stack, 'Cluster', `arn:aws:ecs:${region}:${account}:cluster/${clusterName}`); + + // THEN + expect(cluster.clusterName).toEqual(clusterName); + expect(cluster.env.region).toEqual(region); + expect(cluster.env.account).toEqual(account); + }); + + test('throws error when import ECS Cluster without resource name in arn', () => { + // GIVEN + const stack = new cdk.Stack(); + + // THEN + expect(() => { + ecs.Cluster.fromClusterArn(stack, 'Cluster', 'arn:aws:ecs:service-region:service-account:cluster'); + }).toThrowError(/Missing required Cluster Name from Cluster ARN: /); + }); }); test('can add ASG capacity via Capacity Provider by not specifying machineImageType', () => { diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index a433e0049d83e..6eece8eabfa41 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -2005,4 +2005,30 @@ describe('container definition', () => { }); }); + + test('exposes image name', () => { + // GIVEN + const stack = new cdk.Stack(); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef'); + + // WHEN + const container = taskDefinition.addContainer('cont', { + image: ecs.ContainerImage.fromAsset(path.join(__dirname, 'demo-image')), + }); + + // THEN + expect(stack.resolve(container.imageName)).toEqual({ + 'Fn::Join': [ + '', + [ + { Ref: 'AWS::AccountId' }, + '.dkr.ecr.', + { Ref: 'AWS::Region' }, + '.', + { Ref: 'AWS::URLSuffix' }, + '/aws-cdk/assets:baa2d6eb2a17c75424df631c8c70ff39f2d5f3bee8b9e1a109ee24ca17300540', + ], + ], + }); + }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json index 824783dc8c597..9219258ba7016 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json @@ -1354,6 +1354,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-efs/README.md b/packages/@aws-cdk/aws-efs/README.md index 1a26f9e0e9c5a..058198d0c897e 100644 --- a/packages/@aws-cdk/aws-efs/README.md +++ b/packages/@aws-cdk/aws-efs/README.md @@ -36,7 +36,7 @@ const fileSystem = new efs.FileSystem(this, 'MyEfsFileSystem', { vpc: new ec2.Vpc(this, 'VPC'), lifecyclePolicy: efs.LifecyclePolicy.AFTER_14_DAYS, // files are not transitioned to infrequent access (IA) storage by default performanceMode: efs.PerformanceMode.GENERAL_PURPOSE, // default - outInfrequentAccessPolicy: efs.OutOfInfrequentAccessPolicy.AFTER_1_ACCESS, // files are not transitioned back from (infrequent access) IA to primary storage by default + outOfInfrequentAccessPolicy: efs.OutOfInfrequentAccessPolicy.AFTER_1_ACCESS, // files are not transitioned back from (infrequent access) IA to primary storage by default }); ``` @@ -159,6 +159,6 @@ You can configure the file system to be destroyed on stack deletion by setting a ```ts const fileSystem = new efs.FileSystem(this, 'EfsFileSystem', { vpc: new ec2.Vpc(this, 'VPC'), - removalPolicy: RemovalPolicy.DESTROY + removalPolicy: RemovalPolicy.DESTROY, }); ``` diff --git a/packages/@aws-cdk/aws-efs/package.json b/packages/@aws-cdk/aws-efs/package.json index 4ce72aacb86e5..09d1c1f6f2ca6 100644 --- a/packages/@aws-cdk/aws-efs/package.json +++ b/packages/@aws-cdk/aws-efs/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-efs/rosetta/with-filesystem-instance.ts-fixture b/packages/@aws-cdk/aws-efs/rosetta/with-filesystem-instance.ts-fixture index 092b572afa726..427e2ed030b4b 100644 --- a/packages/@aws-cdk/aws-efs/rosetta/with-filesystem-instance.ts-fixture +++ b/packages/@aws-cdk/aws-efs/rosetta/with-filesystem-instance.ts-fixture @@ -3,6 +3,7 @@ import { Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as efs from '@aws-cdk/aws-efs'; import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/aws-eks-legacy/package.json b/packages/@aws-cdk/aws-eks-legacy/package.json index 685633b13c976..c05d56e6b3ddb 100644 --- a/packages/@aws-cdk/aws-eks-legacy/package.json +++ b/packages/@aws-cdk/aws-eks-legacy/package.json @@ -77,7 +77,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-eks-legacy/test/awsauth.test.ts b/packages/@aws-cdk/aws-eks-legacy/test/awsauth.test.ts index 3a5f28f648441..f642836ded6e2 100644 --- a/packages/@aws-cdk/aws-eks-legacy/test/awsauth.test.ts +++ b/packages/@aws-cdk/aws-eks-legacy/test/awsauth.test.ts @@ -1,6 +1,6 @@ -import '@aws-cdk/assert-internal/jest'; -import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; +import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; import { Cluster, KubernetesResource } from '../lib'; import { AwsAuth } from '../lib/aws-auth'; import { testFixtureNoVpc } from './util'; @@ -17,7 +17,7 @@ describeDeprecated('awsauth', () => { new AwsAuth(stack, 'AwsAuth', { cluster }); // THEN - expect(stack).toHaveResource(KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesResource.RESOURCE_TYPE, { Manifest: JSON.stringify([{ apiVersion: 'v1', kind: 'ConfigMap', @@ -44,8 +44,8 @@ describeDeprecated('awsauth', () => { cluster.awsAuth.addAccount('5566776655'); // THEN - expect(stack).toCountResources(KubernetesResource.RESOURCE_TYPE, 1); - expect(stack).toHaveResource(KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).resourceCountIs(KubernetesResource.RESOURCE_TYPE, 1); + Template.fromStack(stack).hasResourceProperties(KubernetesResource.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -106,7 +106,7 @@ describeDeprecated('awsauth', () => { cluster.awsAuth.addUserMapping(user, { groups: ['group2'] }); // THEN - expect(stack).toHaveResource(KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesResource.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', diff --git a/packages/@aws-cdk/aws-eks-legacy/test/cluster.test.ts b/packages/@aws-cdk/aws-eks-legacy/test/cluster.test.ts index b7f1666fb79b0..61188e858110a 100644 --- a/packages/@aws-cdk/aws-eks-legacy/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-eks-legacy/test/cluster.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; @@ -18,7 +18,7 @@ describeDeprecated('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, kubectlEnabled: false, defaultCapacity: 0 }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Cluster', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Cluster', { ResourcesVpcConfig: { SubnetIds: [ { Ref: 'VPCPublicSubnet1SubnetB4246D30' }, @@ -40,7 +40,7 @@ describeDeprecated('cluster', () => { new eks.Cluster(stack, 'cluster'); // THEN - expect(stack).toHaveResource('AWS::EC2::VPC'); + Template.fromStack(stack).resourceCountIs('AWS::EC2::VPC', 1); }); @@ -55,8 +55,8 @@ describeDeprecated('cluster', () => { // THEN expect(cluster.defaultCapacity).toBeDefined(); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { DesiredCapacity: '2' }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { InstanceType: 'm5.large' }); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { DesiredCapacity: '2' }); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { InstanceType: 'm5.large' }); }); @@ -72,8 +72,8 @@ describeDeprecated('cluster', () => { // THEN expect(cluster.defaultCapacity).toBeDefined(); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { DesiredCapacity: '10' }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { InstanceType: 'm2.xlarge' }); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { DesiredCapacity: '10' }); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { InstanceType: 'm2.xlarge' }); }); @@ -86,8 +86,8 @@ describeDeprecated('cluster', () => { // THEN expect(cluster.defaultCapacity).toBeUndefined(); - expect(stack).not.toHaveResource('AWS::AutoScaling::AutoScalingGroup'); - expect(stack).not.toHaveResource('AWS::AutoScaling::LaunchConfiguration'); + Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::AutoScalingGroup', 0); + Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::LaunchConfiguration', 0); }); }); @@ -100,7 +100,7 @@ describeDeprecated('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, kubectlEnabled: false, defaultCapacity: 0 }); // THEN - expect(stack).toHaveResource('AWS::EC2::Subnet', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::Subnet', { Tags: [ { Key: 'aws-cdk:subnet-name', Value: 'Private' }, { Key: 'aws-cdk:subnet-type', Value: 'Private' }, @@ -120,7 +120,7 @@ describeDeprecated('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, kubectlEnabled: false, defaultCapacity: 0 }); // THEN - expect(stack).toHaveResource('AWS::EC2::Subnet', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::Subnet', { MapPublicIpOnLaunch: true, Tags: [ { Key: 'aws-cdk:subnet-name', Value: 'Public' }, @@ -144,7 +144,7 @@ describeDeprecated('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { Tags: [ { Key: { 'Fn::Join': ['', ['kubernetes.io/cluster/', { Ref: 'ClusterEB0386A7' }]] }, @@ -182,7 +182,7 @@ describeDeprecated('cluster', () => { new cdk.CfnOutput(stack2, 'ClusterARN', { value: imported.clusterArn }); // THEN - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Outputs: { ClusterARN: { Value: { @@ -216,7 +216,7 @@ describeDeprecated('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, mastersRole: role, defaultCapacity: 0 }); // THEN - expect(stack).toHaveResource(eks.KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesResource.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -247,11 +247,11 @@ describeDeprecated('cluster', () => { cluster.addResource('manifest2', { bar: 123 }, { boor: [1, 2, 3] }); // THEN - expect(stack).toHaveResource(eks.KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesResource.RESOURCE_TYPE, { Manifest: '[{"foo":123}]', }); - expect(stack).toHaveResource(eks.KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesResource.RESOURCE_TYPE, { Manifest: '[{"bar":123},{"boor":[1,2,3]}]', }); @@ -269,7 +269,7 @@ describeDeprecated('cluster', () => { }); // THEN - expect(stack).toHaveResource(eks.KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesResource.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -302,7 +302,7 @@ describeDeprecated('cluster', () => { }); // THEN - expect(stack).not.toHaveResource(eks.KubernetesResource.RESOURCE_TYPE); + Template.fromStack(stack).resourceCountIs(eks.KubernetesResource.RESOURCE_TYPE, 0); }); @@ -317,7 +317,7 @@ describeDeprecated('cluster', () => { }); // THEN - expect(stack).not.toHaveResource(eks.KubernetesResource.RESOURCE_TYPE); + Template.fromStack(stack).resourceCountIs(eks.KubernetesResource.RESOURCE_TYPE, 0); }); @@ -524,7 +524,7 @@ describeDeprecated('cluster', () => { }); // THEN - expect(stack).toHaveResource(eks.KubernetesResource.RESOURCE_TYPE, { Manifest: JSON.stringify(spotInterruptHandler()) }); + Template.fromStack(stack).hasResourceProperties(eks.KubernetesResource.RESOURCE_TYPE, { Manifest: JSON.stringify(spotInterruptHandler()) }); }); @@ -540,7 +540,7 @@ describeDeprecated('cluster', () => { }); // THEN - expect(stack).not.toHaveResource(eks.KubernetesResource.RESOURCE_TYPE); + Template.fromStack(stack).resourceCountIs(eks.KubernetesResource.RESOURCE_TYPE, 0); }); diff --git a/packages/@aws-cdk/aws-eks-legacy/test/helm-chart.test.ts b/packages/@aws-cdk/aws-eks-legacy/test/helm-chart.test.ts index 9606f892d5f1c..90b814a8bc0b3 100644 --- a/packages/@aws-cdk/aws-eks-legacy/test/helm-chart.test.ts +++ b/packages/@aws-cdk/aws-eks-legacy/test/helm-chart.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; import * as eks from '../lib'; import { testFixtureCluster } from './util'; @@ -15,7 +15,7 @@ describeDeprecated('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Namespace: 'default' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Namespace: 'default' }); }); test('should have a lowercase default release name', () => { @@ -26,7 +26,7 @@ describeDeprecated('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Release: 'stackmychartff398361' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Release: 'stackmychartff398361' }); }); test('should trim the last 63 of the default release name', () => { @@ -37,7 +37,7 @@ describeDeprecated('helm chart', () => { new eks.HelmChart(stack, 'MyChartNameWhichISMostProbablyLongerThenSixtyThreeCharacters', { cluster, chart: 'chart' }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Release: 'rtnamewhichismostprobablylongerthensixtythreecharactersb800614d' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Release: 'rtnamewhichismostprobablylongerthensixtythreecharactersb800614d' }); }); test('with values', () => { @@ -48,7 +48,7 @@ describeDeprecated('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart', values: { foo: 123 } }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Values: '{\"foo\":123}' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Values: '{\"foo\":123}' }); }); }); diff --git a/packages/@aws-cdk/aws-eks-legacy/test/manifest.test.ts b/packages/@aws-cdk/aws-eks-legacy/test/manifest.test.ts index 9a3de8f587f4c..2558945d61e94 100644 --- a/packages/@aws-cdk/aws-eks-legacy/test/manifest.test.ts +++ b/packages/@aws-cdk/aws-eks-legacy/test/manifest.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; import { Cluster, KubernetesResource } from '../lib'; import { testFixtureNoVpc } from './util'; @@ -69,7 +69,7 @@ describeDeprecated('manifest', () => { manifest, }); - expect(stack).toHaveResource(KubernetesResource.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesResource.RESOURCE_TYPE, { Manifest: JSON.stringify(manifest), }); diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index 1403e56d817fe..d389487e2f419 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -1397,6 +1397,31 @@ Kubernetes [endpoint access](#endpoint-access), you must also specify: * `kubectlPrivateSubnetIds` - a list of private VPC subnets IDs that will be used to access the Kubernetes endpoint. +## Logging + +EKS supports cluster logging for 5 different types of events: + +* API requests to the cluster. +* Cluster access via the Kubernetes API. +* Authentication requests into the cluster. +* State of cluster controllers. +* Scheduling decisions. + +You can enable logging for each one separately using the `clusterLogging` +property. For example: + +```ts +const cluster = new eks.Cluster(this, 'Cluster', { + // ... + version: eks.KubernetesVersion.V1_21, + clusterLogging: [ + eks.ClusterLoggingTypes.API, + eks.ClusterLoggingTypes.AUTHENTICATOR, + eks.ClusterLoggingTypes.SCHEDULER, + ], +}); +``` + ## Known Issues and Limitations * [One cluster per stack](https://github.com/aws/aws-cdk/issues/10073) diff --git a/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts index 61a33ddb3ab05..0ad46af16eaef 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts @@ -285,6 +285,10 @@ function parseProps(props: any): aws.EKS.CreateClusterRequest { parsed.resourcesVpcConfig.endpointPublicAccess = parsed.resourcesVpcConfig.endpointPublicAccess === 'true'; } + if (typeof (parsed.logging?.clusterLogging[0].enabled) === 'string') { + parsed.logging.clusterLogging[0].enabled = parsed.logging.clusterLogging[0].enabled === 'true'; + } + return parsed; } diff --git a/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts b/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts index 6a947380e3dd1..db5dc023ae32a 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts @@ -29,6 +29,7 @@ export interface ClusterResourceProps { readonly onEventLayer?: lambda.ILayerVersion; readonly clusterHandlerSecurityGroup?: ec2.ISecurityGroup; readonly tags?: { [key: string]: string }; + readonly logging?: { [key: string]: [ { [key: string]: any } ] }; } /** @@ -91,6 +92,7 @@ export class ClusterResource extends CoreConstruct { publicAccessCidrs: props.publicAccessCidrs, }, tags: props.tags, + logging: props.logging, }, AssumeRoleArn: this.adminRole.roleArn, diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 10a3f2123aac1..c8a05ef59beaa 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -757,6 +757,13 @@ export interface ClusterProps extends ClusterOptions { * @default - none */ readonly tags?: { [key: string]: string }; + + /** + * The cluster log types which you want to enable. + * + * @default - none + */ + readonly clusterLogging?: ClusterLoggingTypes[]; } /** @@ -815,6 +822,32 @@ export class KubernetesVersion { private constructor(public readonly version: string) { } } +/** + * EKS cluster logging types + */ +export enum ClusterLoggingTypes { + /** + * Logs pertaining to API requests to the cluster. + */ + API = 'api', + /** + * Logs pertaining to cluster access via the Kubernetes API. + */ + AUDIT = 'audit', + /** + * Logs pertaining to authentication requests into the cluster. + */ + AUTHENTICATOR = 'authenticator', + /** + * Logs pertaining to state of cluster controllers. + */ + CONTROLLER_MANAGER = 'controllerManager', + /** + * Logs pertaining to scheduling decisions. + */ + SCHEDULER = 'scheduler', +} + abstract class ClusterBase extends Resource implements ICluster { public abstract readonly connections: ec2.Connections; public abstract readonly vpc: ec2.IVpc; @@ -1253,6 +1286,8 @@ export class Cluster extends ClusterBase { private readonly version: KubernetesVersion; + private readonly logging?: { [key: string]: [ { [key: string]: any } ] }; + /** * A dummy CloudFormation resource that is used as a wait barrier which * represents that the cluster is ready to receive "kubectl" commands. @@ -1313,6 +1348,14 @@ export class Cluster extends ClusterBase { // Get subnetIds for all selected subnets const subnetIds = Array.from(new Set(flatten(selectedSubnetIdsPerGroup))); + this.logging = props.clusterLogging ? { + clusterLogging: [ + { + enabled: true, + types: Object.values(props.clusterLogging), + }, + ], + } : undefined; this.endpointAccess = props.endpointAccess ?? EndpointAccess.PUBLIC_AND_PRIVATE; this.kubectlEnvironment = props.kubectlEnvironment; @@ -1379,6 +1422,7 @@ export class Cluster extends ClusterBase { clusterHandlerSecurityGroup: this.clusterHandlerSecurityGroup, onEventLayer: this.onEventLayer, tags: props.tags, + logging: this.logging, }); if (this.endpointAccess._config.privateAccess && privateSubnets.length !== 0) { diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index 79ea6601f9fff..012d51c2315ba 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -79,18 +79,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "@types/yaml": "1.9.6", "aws-sdk": "^2.848.0", - "cdk8s": "^1.4.6", - "cdk8s-plus-21": "^1.0.0-beta.77", + "cdk8s": "^1.4.13", + "cdk8s-plus-21": "^1.0.0-beta.79", "jest": "^27.4.7", "sinon": "^9.2.4" }, diff --git a/packages/@aws-cdk/aws-eks/test/alb-controller.test.ts b/packages/@aws-cdk/aws-eks/test/alb-controller.test.ts index 7a1720a93f844..66d2d3f0ff1fd 100644 --- a/packages/@aws-cdk/aws-eks/test/alb-controller.test.ts +++ b/packages/@aws-cdk/aws-eks/test/alb-controller.test.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; -import '@aws-cdk/assert-internal/jest'; import { Cluster, KubernetesVersion, AlbController, AlbControllerVersion, HelmChart } from '../lib'; import { testFixture } from './util'; @@ -40,7 +40,7 @@ test('can configure a custom repository', () => { repository: 'custom', }); - expect(stack).toHaveResource(HelmChart.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(HelmChart.RESOURCE_TYPE, { Values: { 'Fn::Join': [ '', diff --git a/packages/@aws-cdk/aws-eks/test/awsauth.test.ts b/packages/@aws-cdk/aws-eks/test/awsauth.test.ts index 0f6d3cc4b5891..c141965738084 100644 --- a/packages/@aws-cdk/aws-eks/test/awsauth.test.ts +++ b/packages/@aws-cdk/aws-eks/test/awsauth.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Cluster, KubernetesManifest, KubernetesVersion } from '../lib'; @@ -58,7 +58,7 @@ describe('aws auth', () => { new AwsAuth(stack, 'AwsAuth', { cluster }); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([{ apiVersion: 'v1', kind: 'ConfigMap', @@ -85,8 +85,8 @@ describe('aws auth', () => { cluster.awsAuth.addAccount('5566776655'); // THEN - expect(stack).toCountResources(KubernetesManifest.RESOURCE_TYPE, 1); - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).resourceCountIs(KubernetesManifest.RESOURCE_TYPE, 1); + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -175,7 +175,7 @@ describe('aws auth', () => { cluster.awsAuth.addUserMapping(user, { groups: ['group2'] }); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -236,7 +236,7 @@ describe('aws auth', () => { cluster.awsAuth.addMastersRole(role); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', diff --git a/packages/@aws-cdk/aws-eks/test/cluster.test.ts b/packages/@aws-cdk/aws-eks/test/cluster.test.ts index e3c32b1e345c2..77556af1a0da4 100644 --- a/packages/@aws-cdk/aws-eks/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-eks/test/cluster.test.ts @@ -1,7 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import '@aws-cdk/assert-internal/jest'; -import { SynthUtils } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as asg from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; @@ -32,7 +31,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-HelmChart', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-HelmChart', { Chart: 'aws-load-balancer-controller', }); expect(cluster.albController).toBeDefined(); @@ -51,8 +50,9 @@ describe('cluster', () => { const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.ClusterResourceProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - expect(template.Resources.OnEventHandler42BEBAE0.Properties.Environment).toEqual({ Variables: { foo: 'bar' } }); + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + Environment: { Variables: { foo: 'bar' } }, + }); }); test('can specify security group to cluster resource handler', () => { @@ -70,8 +70,11 @@ describe('cluster', () => { const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.ClusterResourceProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - expect(template.Resources.OnEventHandler42BEBAE0.Properties.VpcConfig.SecurityGroupIds).toEqual([{ Ref: 'referencetoStackProxyInstanceSG80B79D87GroupId' }]); + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: { + SecurityGroupIds: [{ Ref: 'referencetoStackProxyInstanceSG80B79D87GroupId' }], + }, + }); }); test('throws when trying to place cluster handlers in a vpc with no private subnets', () => { @@ -150,7 +153,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-Cluster', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-Cluster', { Config: { resourcesVpcConfig: { subnetIds: { @@ -162,8 +165,6 @@ describe('cluster', () => { }, }, }); - - }); }); @@ -176,8 +177,6 @@ describe('cluster', () => { }); expect(() => cluster.clusterSecurityGroup).toThrow(/"clusterSecurityGroup" is not defined for this imported cluster/); - - }); test('can place cluster handlers in the cluster vpc', () => { @@ -190,10 +189,11 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.ClusterResourceProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); + const template = Template.fromStack(nested); + const resources = template.findResources('AWS::Lambda::Function'); function assertFunctionPlacedInVpc(id: string) { - expect(template.Resources[id].Properties.VpcConfig.SubnetIds).toEqual([ + expect(resources[id].Properties.VpcConfig.SubnetIds).toEqual([ { Ref: 'referencetoStackClusterDefaultVpcPrivateSubnet1SubnetA64D1BF0Ref' }, { Ref: 'referencetoStackClusterDefaultVpcPrivateSubnet2Subnet32D85AB8Ref' }, ]); @@ -204,8 +204,6 @@ describe('cluster', () => { assertFunctionPlacedInVpc('ProviderframeworkonEvent83C1D0A7'); assertFunctionPlacedInVpc('ProviderframeworkisComplete26D7B0CB'); assertFunctionPlacedInVpc('ProviderframeworkonTimeout0B47CA38'); - - }); test('can access cluster security group for imported cluster with cluster security group id', () => { @@ -241,13 +239,12 @@ describe('cluster', () => { instanceType: new ec2.InstanceType('t2.medium'), }); - const template = SynthUtils.toCloudFormation(stack); - expect(template.Resources.ClusterselfmanagedLaunchConfigA5B57EF6.Properties.SecurityGroups).toEqual([ - { 'Fn::GetAtt': ['ClusterselfmanagedInstanceSecurityGroup64468C3A', 'GroupId'] }, - { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, - ]); - - + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { + SecurityGroups: [ + { 'Fn::GetAtt': ['ClusterselfmanagedInstanceSecurityGroup64468C3A', 'GroupId'] }, + { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, + ], + }); }); test('security group of self-managed asg is not tagged with owned', () => { @@ -264,11 +261,10 @@ describe('cluster', () => { instanceType: new ec2.InstanceType('t2.medium'), }); - const template = SynthUtils.toCloudFormation(stack); - // make sure the "kubernetes.io/cluster/: owned" tag isn't here. - expect(template.Resources.ClusterselfmanagedInstanceSecurityGroup64468C3A.Properties.Tags).toEqual([ - { Key: 'Name', Value: 'Stack/Cluster/self-managed' }, - ]); + let template = Template.fromStack(stack); + template.hasResourceProperties('AWS::EC2::SecurityGroup', { + Tags: [{ Key: 'Name', Value: 'Stack/Cluster/self-managed' }], + }); }); test('connect autoscaling group with imported cluster', () => { @@ -296,11 +292,12 @@ describe('cluster', () => { // WHEN importedCluster.connectAutoScalingGroupCapacity(selfManaged, {}); - const template = SynthUtils.toCloudFormation(stack); - expect(template.Resources.selfmanagedLaunchConfigD41289EB.Properties.SecurityGroups).toEqual([ - { 'Fn::GetAtt': ['selfmanagedInstanceSecurityGroupEA6D80C9', 'GroupId'] }, - { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, - ]); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { + SecurityGroups: [ + { 'Fn::GetAtt': ['selfmanagedInstanceSecurityGroupEA6D80C9', 'GroupId'] }, + { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, + ], + }); }); test('cluster security group is attached when connecting self-managed nodes', () => { @@ -323,13 +320,12 @@ describe('cluster', () => { // WHEN cluster.connectAutoScalingGroupCapacity(selfManaged, {}); - const template = SynthUtils.toCloudFormation(stack); - expect(template.Resources.selfmanagedLaunchConfigD41289EB.Properties.SecurityGroups).toEqual([ - { 'Fn::GetAtt': ['selfmanagedInstanceSecurityGroupEA6D80C9', 'GroupId'] }, - { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, - ]); - - + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { + SecurityGroups: [ + { 'Fn::GetAtt': ['selfmanagedInstanceSecurityGroupEA6D80C9', 'GroupId'] }, + { 'Fn::GetAtt': ['Cluster9EE0221C', 'ClusterSecurityGroupId'] }, + ], + }); }); test('spot interrupt handler is not added if spotInterruptHandler is false when connecting self-managed nodes', () => { @@ -370,8 +366,6 @@ describe('cluster', () => { const someConstruct = new constructs.Construct(stack, 'SomeConstruct'); expect(() => cluster.addCdk8sChart('chart', someConstruct)).toThrow(/Invalid cdk8s chart. Must contain a \'toJson\' method, but found undefined/); - - }); test('throws when a core construct is added as cdk8s chart', () => { @@ -387,8 +381,6 @@ describe('cluster', () => { const someConstruct = new cdk.Construct(stack, 'SomeConstruct'); expect(() => cluster.addCdk8sChart('chart', someConstruct)).toThrow(/Invalid cdk8s chart. Must contain a \'toJson\' method, but found undefined/); - - }); test('cdk8s chart can be added to cluster', () => { @@ -417,7 +409,7 @@ describe('cluster', () => { cluster.addCdk8sChart('cdk8s-chart', chart); - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-KubernetesResource', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesResource', { Manifest: { 'Fn::Join': [ '', @@ -431,8 +423,6 @@ describe('cluster', () => { ], }, }); - - }); test('cluster connections include both control plane and cluster security group', () => { @@ -487,8 +477,6 @@ describe('cluster', () => { // make sure we can synth (no circular dependencies between the stacks) app.synth(); - - }); test('can declare a manifest with a token from a different stack than the cluster that depends on the cluster stack', () => { @@ -706,7 +694,7 @@ describe('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, defaultCapacity: 0, version: CLUSTER_VERSION, prune: false }); // THEN - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-Cluster', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-Cluster', { Config: { roleArn: { 'Fn::GetAtt': ['ClusterRoleFA261979', 'Arn'] }, version: '1.21', @@ -733,7 +721,7 @@ describe('cluster', () => { new eks.Cluster(stack, 'cluster', { version: CLUSTER_VERSION, prune: false }); // THEN - expect(stack).toHaveResource('AWS::EC2::VPC'); + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', Match.anyValue()); }); @@ -748,7 +736,7 @@ describe('cluster', () => { // THEN expect(cluster.defaultNodegroup).toBeDefined(); - expect(stack).toHaveResource('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { InstanceTypes: [ 'm5.large', ], @@ -775,7 +763,7 @@ describe('cluster', () => { // THEN expect(cluster.defaultNodegroup).toBeDefined(); - expect(stack).toHaveResource('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ScalingConfig: { DesiredSize: 10, MaxSize: 10, @@ -795,8 +783,8 @@ describe('cluster', () => { // THEN expect(cluster.defaultCapacity).toBeUndefined(); - expect(stack).not.toHaveResource('AWS::AutoScaling::AutoScalingGroup'); - expect(stack).not.toHaveResource('AWS::AutoScaling::LaunchConfiguration'); + Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::AutoScalingGroup', 0); + Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::LaunchConfiguration', 0); }); }); @@ -809,7 +797,7 @@ describe('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, defaultCapacity: 0, version: CLUSTER_VERSION, prune: false }); // THEN - expect(stack).toHaveResource('AWS::EC2::Subnet', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::Subnet', { Tags: [ { Key: 'aws-cdk:subnet-name', Value: 'Private' }, { Key: 'aws-cdk:subnet-type', Value: 'Private' }, @@ -829,7 +817,7 @@ describe('cluster', () => { new eks.Cluster(stack, 'Cluster', { vpc, defaultCapacity: 0, version: CLUSTER_VERSION, prune: false }); // THEN - expect(stack).toHaveResource('AWS::EC2::Subnet', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::Subnet', { MapPublicIpOnLaunch: true, Tags: [ { Key: 'aws-cdk:subnet-name', Value: 'Public' }, @@ -857,9 +845,9 @@ describe('cluster', () => { instanceType: new ec2.InstanceType('t2.medium'), }); - const template = SynthUtils.toCloudFormation(stack); - expect(template.Resources.ClusterASG0E4BA723.UpdatePolicy).toEqual({ AutoScalingScheduledAction: { IgnoreUnmodifiedGroupSizeProperties: true } }); - + Template.fromStack(stack).hasResource('AWS::AutoScaling::AutoScalingGroup', { + UpdatePolicy: { AutoScalingScheduledAction: { IgnoreUnmodifiedGroupSizeProperties: true } }, + }); }); test('adding capacity creates an ASG with tags', () => { @@ -878,7 +866,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { Tags: [ { Key: { 'Fn::Join': ['', ['kubernetes.io/cluster/', { Ref: 'Cluster9EE0221C' }]] }, @@ -919,7 +907,7 @@ describe('cluster', () => { // THEN expect(cluster.defaultNodegroup).toBeDefined(); - expect(stack).toHaveResource('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ScalingConfig: { DesiredSize: 10, MaxSize: 10, @@ -946,7 +934,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { Tags: [ { Key: { 'Fn::Join': ['', ['kubernetes.io/cluster/', { Ref: 'Cluster9EE0221C' }]] }, @@ -1000,67 +988,112 @@ describe('cluster', () => { expect(cluster.kubectlProvider).toEqual(kubectlProvider); }); - test('import cluster with existing kubectl provider function should work as expected with resources relying on kubectl getOrCreate', () => { + describe('import cluster with existing kubectl provider function should work as expected with resources relying on kubectl getOrCreate', () => { + test('creates helm chart', () => { + const { stack } = testFixture(); - const { stack } = testFixture(); + const handlerRole = iam.Role.fromRoleArn(stack, 'HandlerRole', 'arn:aws:iam::123456789012:role/lambda-role'); + const kubectlProvider = KubectlProvider.fromKubectlProviderAttributes(stack, 'KubectlProvider', { + functionArn: 'arn:aws:lambda:us-east-2:123456789012:function:my-function:1', + kubectlRoleArn: 'arn:aws:iam::123456789012:role/kubectl-role', + handlerRole: handlerRole, + }); - const handlerRole = iam.Role.fromRoleArn(stack, 'HandlerRole', 'arn:aws:iam::123456789012:role/lambda-role'); - const kubectlProvider = KubectlProvider.fromKubectlProviderAttributes(stack, 'KubectlProvider', { - functionArn: 'arn:aws:lambda:us-east-2:123456789012:function:my-function:1', - kubectlRoleArn: 'arn:aws:iam::123456789012:role/kubectl-role', - handlerRole: handlerRole, - }); + const cluster = eks.Cluster.fromClusterAttributes(stack, 'Cluster', { + clusterName: 'cluster', + kubectlProvider: kubectlProvider, + }); - const cluster = eks.Cluster.fromClusterAttributes(stack, 'Cluster', { - clusterName: 'cluster', - kubectlProvider: kubectlProvider, - }); + new eks.HelmChart(stack, 'Chart', { + cluster: cluster, + chart: 'chart', + }); - new eks.HelmChart(stack, 'Chart', { - cluster: cluster, - chart: 'chart', + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-HelmChart', { + ServiceToken: kubectlProvider.serviceToken, + RoleArn: kubectlProvider.roleArn, + }); }); - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-HelmChart', { - ServiceToken: kubectlProvider.serviceToken, - RoleArn: kubectlProvider.roleArn, - }); + test('creates Kubernetes patch', () => { + const { stack } = testFixture(); - new eks.KubernetesPatch(stack, 'Patch', { - cluster: cluster, - applyPatch: {}, - restorePatch: {}, - resourceName: 'PatchResource', - }); + const handlerRole = iam.Role.fromRoleArn(stack, 'HandlerRole', 'arn:aws:iam::123456789012:role/lambda-role'); + const kubectlProvider = KubectlProvider.fromKubectlProviderAttributes(stack, 'KubectlProvider', { + functionArn: 'arn:aws:lambda:us-east-2:123456789012:function:my-function:1', + kubectlRoleArn: 'arn:aws:iam::123456789012:role/kubectl-role', + handlerRole: handlerRole, + }); - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-KubernetesPatch', { - ServiceToken: kubectlProvider.serviceToken, - RoleArn: kubectlProvider.roleArn, - }); + const cluster = eks.Cluster.fromClusterAttributes(stack, 'Cluster', { + clusterName: 'cluster', + kubectlProvider: kubectlProvider, + }); - new eks.KubernetesManifest(stack, 'Manifest', { - cluster: cluster, - manifest: [], - }); + new eks.HelmChart(stack, 'Chart', { + cluster: cluster, + chart: 'chart', + }); - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-KubernetesResource', { - ServiceToken: kubectlProvider.serviceToken, - RoleArn: kubectlProvider.roleArn, - }); + new eks.KubernetesPatch(stack, 'Patch', { + cluster: cluster, + applyPatch: {}, + restorePatch: {}, + resourceName: 'PatchResource', + }); - new eks.KubernetesObjectValue(stack, 'ObjectValue', { - cluster: cluster, - jsonPath: '', - objectName: 'name', - objectType: 'type', + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { + ServiceToken: kubectlProvider.serviceToken, + RoleArn: kubectlProvider.roleArn, + }); }); - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-KubernetesObjectValue', { - ServiceToken: kubectlProvider.serviceToken, - RoleArn: kubectlProvider.roleArn, - }); + test('creates Kubernetes object value', () => { + const { stack } = testFixture(); + + const handlerRole = iam.Role.fromRoleArn(stack, 'HandlerRole', 'arn:aws:iam::123456789012:role/lambda-role'); + const kubectlProvider = KubectlProvider.fromKubectlProviderAttributes(stack, 'KubectlProvider', { + functionArn: 'arn:aws:lambda:us-east-2:123456789012:function:my-function:1', + kubectlRoleArn: 'arn:aws:iam::123456789012:role/kubectl-role', + handlerRole: handlerRole, + }); + + const cluster = eks.Cluster.fromClusterAttributes(stack, 'Cluster', { + clusterName: 'cluster', + kubectlProvider: kubectlProvider, + }); + + new eks.HelmChart(stack, 'Chart', { + cluster: cluster, + chart: 'chart', + }); + + new eks.KubernetesPatch(stack, 'Patch', { + cluster: cluster, + applyPatch: {}, + restorePatch: {}, + resourceName: 'PatchResource', + }); + + new eks.KubernetesManifest(stack, 'Manifest', { + cluster: cluster, + manifest: [], + }); + + new eks.KubernetesObjectValue(stack, 'ObjectValue', { + cluster: cluster, + jsonPath: '', + objectName: 'name', + objectType: 'type', + }); - expect(cluster.kubectlProvider).not.toBeInstanceOf(eks.KubectlProvider); + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesObjectValue', { + ServiceToken: kubectlProvider.serviceToken, + RoleArn: kubectlProvider.roleArn, + }); + + expect(cluster.kubectlProvider).not.toBeInstanceOf(eks.KubectlProvider); + }); }); test('import cluster with new kubectl private subnets', () => { @@ -1111,7 +1144,7 @@ describe('cluster', () => { new cdk.CfnOutput(stack2, 'ClusterARN', { value: imported.clusterArn }); // THEN - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Outputs: { ClusterARN: { Value: { @@ -1154,7 +1187,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -1197,11 +1230,11 @@ describe('cluster', () => { cluster.addManifest('manifest2', { bar: 123 }, { boor: [1, 2, 3] }); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: '[{"foo":123}]', }); - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: '[{"bar":123},{"boor":[1,2,3]}]', }); @@ -1224,7 +1257,7 @@ describe('cluster', () => { app.synth(); // no cyclic dependency (see https://github.com/aws/aws-cdk/issues/7231) // expect a single resource in the 2nd stack - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Resources: { myresource49C6D325: { Type: 'Custom::AWSCDK-EKS-KubernetesResource', @@ -1261,7 +1294,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -1313,7 +1346,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -1531,7 +1564,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Release: 'stackclusterchartspotinterrupthandlerdec62e07', Chart: 'aws-node-termination-handler', Values: '{\"nodeSelector\":{\"lifecycle\":\"Ec2Spot\"}}', @@ -1575,7 +1608,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toCountResources(eks.HelmChart.RESOURCE_TYPE, 1); + Template.fromStack(stack).resourceCountIs(eks.HelmChart.RESOURCE_TYPE, 1); }); @@ -1653,7 +1686,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); @@ -1674,7 +1707,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); @@ -1695,7 +1728,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); @@ -1801,7 +1834,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-Cluster', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-Cluster', { Config: { name: 'my-cluster-name', roleArn: { 'Fn::GetAtt': ['MyClusterRoleBA20FE72', 'Arn'] }, @@ -1823,7 +1856,7 @@ describe('cluster', () => { }); // role can be assumed by 3 lambda handlers (2 for the cluster resource and 1 for the kubernetes resource) - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -1844,7 +1877,7 @@ describe('cluster', () => { }); // policy allows creation role to pass the cluster role and to interact with the cluster (given we know the explicit cluster name) - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -1963,7 +1996,7 @@ describe('cluster', () => { new eks.Cluster(stack, 'MyCluster', { version: CLUSTER_VERSION, prune: false }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2046,7 +2079,7 @@ describe('cluster', () => { // THEN const providerStack = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(providerStack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(providerStack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2089,7 +2122,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { ResourceName: 'deployment/coredns', ResourceNamespace: 'kube-system', ApplyPatchJson: '{"spec":{"template":{"metadata":{"annotations":{"eks.amazonaws.com/compute-type":"fargate"}}}}}', @@ -2116,7 +2149,7 @@ describe('cluster', () => { // THEN expect(provider).toEqual(cluster.openIdConnectProvider); - expect(stack).toHaveResource('Custom::AWSCDKOpenIdConnectProvider', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDKOpenIdConnectProvider', { ServiceToken: { 'Fn::GetAtt': [ 'CustomAWSCDKOpenIdConnectProviderCustomResourceProviderHandlerF2C543E0', @@ -2152,7 +2185,7 @@ describe('cluster', () => { const sanitized = YAML.parse(fileContents); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([sanitized]), }); @@ -2219,7 +2252,7 @@ describe('cluster', () => { // THEN const providerStack = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(providerStack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(providerStack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2260,11 +2293,11 @@ describe('cluster', () => { // the kubectl provider is inside a nested stack. const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - expect(template.Resources.ProviderframeworkonEvent83C1D0A7.Properties.VpcConfig.SecurityGroupIds).toEqual( - [{ Ref: 'referencetoStackCluster18DFEAC17ClusterSecurityGroupId' }]); - - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: { + SecurityGroupIds: [{ Ref: 'referencetoStackCluster18DFEAC17ClusterSecurityGroupId' }], + }, + }); }); test('kubectl provider passes environment to lambda', () => { @@ -2293,7 +2326,7 @@ describe('cluster', () => { // the kubectl provider is inside a nested stack. const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(nested).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { Environment: { Variables: { Foo: 'Bar', @@ -2332,7 +2365,7 @@ describe('cluster', () => { // the kubectl provider is inside a nested stack. const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(nested).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { Role: { Ref: 'referencetoStackKubectlIamRole02F8947EArn', }, @@ -2362,12 +2395,12 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); // we don't attach vpc config in case endpoint is public only, regardless of whether // the vpc has private subnets or not. - expect(template.Resources.Handler886CB40B.Properties.VpcConfig).toEqual(undefined); - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: Match.absent(), + }); }); @@ -2382,13 +2415,12 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); // we don't attach vpc config in case endpoint is public only, regardless of whether // the vpc has private subnets or not. - expect(template.Resources.Handler886CB40B.Properties.VpcConfig).toEqual(undefined); - - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: Match.absent(), + }); }); test('private without private subnets', () => { @@ -2417,13 +2449,10 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - - // handler should have vpc config - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); - + const functions = Template.fromStack(nested).findResources('AWS::Lambda::Function'); + expect(functions.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); + expect(functions.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); }); test('private and non restricted public without private subnets', () => { @@ -2437,12 +2466,12 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); // we don't have private subnets, but we don't need them since public access // is not restricted. - expect(template.Resources.Handler886CB40B.Properties.VpcConfig).toEqual(undefined); - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: Match.absent(), + }); }); @@ -2456,13 +2485,11 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); // we have private subnets so we should use them. - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); - - + const functions = Template.fromStack(nested).findResources('AWS::Lambda::Function'); + expect(functions.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); + expect(functions.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); }); test('private and restricted public without private subnets', () => { @@ -2490,12 +2517,11 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); // we have private subnets so we should use them. - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); - + const functions = Template.fromStack(nested).findResources('AWS::Lambda::Function'); + expect(functions.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).not.toEqual(0); + expect(functions.Handler886CB40B.Properties.VpcConfig.SecurityGroupIds.length).not.toEqual(0); }); @@ -2552,13 +2578,9 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds).toEqual([ - 'subnet-private-in-us-east-1a', - ]); - - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: { SubnetIds: ['subnet-private-in-us-east-1a'] }, + }); }); test('private endpoint access selects only private subnets from looked up vpc with concrete subnet selection', () => { @@ -2620,13 +2642,9 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds).toEqual([ - 'subnet-private-in-us-east-1a', - ]); - - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: { SubnetIds: ['subnet-private-in-us-east-1a'] }, + }); }); test('private endpoint access selects only private subnets from managed vpc with concrete subnet selection', () => { @@ -2650,14 +2668,14 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds).toEqual([ - { Ref: 'referencetoStackVpcPrivateSubnet1Subnet8E6A14CBRef' }, - 'subnet-unknown', - ]); - - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: { + SubnetIds: [ + { Ref: 'referencetoStackVpcPrivateSubnet1Subnet8E6A14CBRef' }, + 'subnet-unknown', + ], + }, + }); }); test('private endpoint access considers specific subnet selection', () => { @@ -2676,13 +2694,9 @@ describe('cluster', () => { }); const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - - expect(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds).toEqual([ - 'subnet1', - ]); - - + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { + VpcConfig: { SubnetIds: ['subnet1'] }, + }); }); test('can configure private endpoint access', () => { @@ -2737,7 +2751,7 @@ describe('cluster', () => { // the kubectl provider is inside a nested stack. const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(nested).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: { SecurityGroupIds: [ { @@ -2803,10 +2817,8 @@ describe('cluster', () => { // the kubectl provider is inside a nested stack. const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - const template = SynthUtils.toCloudFormation(nested); - expect(16).toEqual(template.Resources.Handler886CB40B.Properties.VpcConfig.SubnetIds.length); - - + const functions = Template.fromStack(nested).findResources('AWS::Lambda::Function'); + expect(functions.Handler886CB40B.Properties.VpcConfig.SubnetIds.length).toEqual(16); }); test('kubectl provider considers vpc subnet selection', () => { @@ -2855,7 +2867,7 @@ describe('cluster', () => { // the kubectl provider is inside a nested stack. const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(nested).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(nested).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: { SecurityGroupIds: [ { @@ -2937,10 +2949,11 @@ describe('cluster', () => { const expectedKubernetesGetId = 'Cluster1myserviceLoadBalancerAddress198CCB03'; - const template = SynthUtils.toCloudFormation(stack); + let template = Template.fromStack(stack); + const resources = template.findResources('Custom::AWSCDK-EKS-KubernetesObjectValue'); // make sure the custom resource is created correctly - expect(template.Resources[expectedKubernetesGetId].Properties).toEqual({ + expect(resources[expectedKubernetesGetId].Properties).toEqual({ ServiceToken: { 'Fn::GetAtt': [ 'awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B', @@ -2964,8 +2977,9 @@ describe('cluster', () => { }); // make sure the attribute points to the expected custom resource and extracts the correct attribute - expect(template.Outputs.LoadBalancerAddress.Value).toEqual({ 'Fn::GetAtt': [expectedKubernetesGetId, 'Value'] }); - + template.hasOutput('LoadBalancerAddress', { + Value: { 'Fn::GetAtt': [expectedKubernetesGetId, 'Value'] }, + }); }); test('custom kubectl layer can be provided', () => { @@ -2982,7 +2996,7 @@ describe('cluster', () => { // THEN const providerStack = stack.node.tryFindChild('@aws-cdk/aws-eks.KubectlProvider') as cdk.NestedStack; - expect(providerStack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(providerStack).hasResourceProperties('AWS::Lambda::Function', { Layers: ['arn:of:layer'], }); @@ -3002,7 +3016,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-Cluster', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-Cluster', { Config: { encryptionConfig: [{ provider: { @@ -3070,7 +3084,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-Cluster', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-Cluster', { Config: { kubernetesNetworkConfig: { serviceIpv4Cidr: customCidr, diff --git a/packages/@aws-cdk/aws-eks/test/fargate.test.ts b/packages/@aws-cdk/aws-eks/test/fargate.test.ts index 776da61d1561a..ddd195c7574d4 100644 --- a/packages/@aws-cdk/aws-eks/test/fargate.test.ts +++ b/packages/@aws-cdk/aws-eks/test/fargate.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; @@ -21,7 +20,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'MyCluster8AD82BF8' }, podExecutionRoleArn: { 'Fn::GetAtt': ['MyClusterfargateprofileMyProfilePodExecutionRole4795C054', 'Arn'] }, @@ -43,7 +42,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'MyCluster8AD82BF8' }, podExecutionRoleArn: { 'Fn::GetAtt': ['MyClusterfargateprofileMyProfilePodExecutionRole4795C054', 'Arn'] }, @@ -67,7 +66,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'MyCluster8AD82BF8' }, podExecutionRoleArn: { 'Fn::GetAtt': ['MyRoleF48FFE04', 'Arn'] }, @@ -91,7 +90,7 @@ describe('fargate', () => { Tags.of(cluster).add('propTag', '123'); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { selectors: [{ namespace: 'default' }], clusterName: { Ref: 'MyCluster8AD82BF8' }, @@ -122,7 +121,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'MyCluster8AD82BF8' }, podExecutionRoleArn: { 'Fn::GetAtt': ['MyClusterfargateprofileMyProfilePodExecutionRole4795C054', 'Arn'] }, @@ -161,7 +160,7 @@ describe('fargate', () => { new eks.FargateCluster(stack, 'FargateCluster', { version: CLUSTER_VERSION }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { ResourceName: 'deployment/coredns', ResourceNamespace: 'kube-system', ApplyPatchJson: '{"spec":{"template":{"metadata":{"annotations":{"eks.amazonaws.com/compute-type":"fargate"}}}}}', @@ -171,7 +170,7 @@ describe('fargate', () => { }, }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'FargateCluster019F03E8', @@ -204,7 +203,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'FargateCluster019F03E8', @@ -238,7 +237,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'FargateCluster019F03E8', @@ -272,14 +271,14 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-FargateProfile', { Config: { clusterName: { Ref: 'MyCluster8AD82BF8' }, podExecutionRoleArn: { 'Fn::GetAtt': ['MyClusterfargateprofileMyProfile1PodExecutionRole794E9E37', 'Arn'] }, selectors: [{ namespace: 'namespace1' }], }, }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-FargateProfile', { + Template.fromStack(stack).hasResource('Custom::AWSCDK-EKS-FargateProfile', { Properties: { ServiceToken: { 'Fn::GetAtt': [ @@ -298,7 +297,7 @@ describe('fargate', () => { 'MyClusterfargateprofileMyProfile1PodExecutionRole794E9E37', 'MyClusterfargateprofileMyProfile1879D501A', ], - }, ResourcePart.CompleteDefinition); + }); }); @@ -311,7 +310,7 @@ describe('fargate', () => { new eks.FargateCluster(stack, 'FargateCluster', { version: CLUSTER_VERSION }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesResource', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesResource', { Manifest: { 'Fn::Join': [ '', @@ -353,7 +352,7 @@ describe('fargate', () => { new eks.FargateCluster(stack, 'FargateCluster', { version: CLUSTER_VERSION }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -442,7 +441,7 @@ describe('fargate', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-Cluster', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-Cluster', { Config: { encryptionConfig: [{ provider: { diff --git a/packages/@aws-cdk/aws-eks/test/helm-chart.test.ts b/packages/@aws-cdk/aws-eks/test/helm-chart.test.ts index cfc7961f5e5e5..5f0c7536a0872 100644 --- a/packages/@aws-cdk/aws-eks/test/helm-chart.test.ts +++ b/packages/@aws-cdk/aws-eks/test/helm-chart.test.ts @@ -1,5 +1,5 @@ -import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; +import { Template } from '@aws-cdk/assertions'; import { Asset } from '@aws-cdk/aws-s3-assets'; import { Duration } from '@aws-cdk/core'; import * as eks from '../lib'; @@ -17,7 +17,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Namespace: 'default' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Namespace: 'default' }); }); test('should have a lowercase default release name', () => { // GIVEN @@ -27,7 +27,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Release: 'stackmychartff398361', }); }); @@ -92,7 +92,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chartAsset }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { ChartAssetURL: { 'Fn::Join': [ '', @@ -144,7 +144,7 @@ describe('helm chart', () => { }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Release: 'hismostprobablylongerthanfiftythreecharacterscaf15d09', }); }); @@ -156,7 +156,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart', values: { foo: 123 } }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Values: '{"foo":123}' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Values: '{"foo":123}' }); }); test('should support create namespaces by default', () => { // GIVEN @@ -166,7 +166,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart' }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { CreateNamespace: true }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { CreateNamespace: true }); }); test('should support create namespaces when explicitly specified', () => { // GIVEN @@ -176,7 +176,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart', createNamespace: true }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { CreateNamespace: true }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { CreateNamespace: true }); }); test('should not create namespaces when disabled', () => { // GIVEN @@ -186,7 +186,8 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyChart', { cluster, chart: 'chart', createNamespace: false }); // THEN - expect(stack).not.toHaveResource(eks.HelmChart.RESOURCE_TYPE, { CreateNamespace: true }); + const charts = Template.fromStack(stack).findResources(eks.HelmChart.RESOURCE_TYPE, { CreateNamespace: true }); + expect(Object.keys(charts).length).toEqual(0); }); test('should support waiting until everything is completed before marking release as successful', () => { // GIVEN @@ -196,7 +197,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyWaitingChart', { cluster, chart: 'chart', wait: true }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); }); test('should default to not waiting before marking release as successful', () => { // GIVEN @@ -206,7 +207,9 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyWaitingChart', { cluster, chart: 'chart' }); // THEN - expect(stack).not.toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); + const charts = Template.fromStack(stack).findResources(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); + expect(Object.keys(charts).length).toEqual(0); + }); test('should enable waiting when specified', () => { // GIVEN @@ -216,7 +219,7 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyWaitingChart', { cluster, chart: 'chart', wait: true }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); }); test('should disable waiting when specified as false', () => { // GIVEN @@ -226,7 +229,8 @@ describe('helm chart', () => { new eks.HelmChart(stack, 'MyWaitingChart', { cluster, chart: 'chart', wait: false }); // THEN - expect(stack).not.toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); + const charts = Template.fromStack(stack).findResources(eks.HelmChart.RESOURCE_TYPE, { Wait: true }); + expect(Object.keys(charts).length).toEqual(0); }); test('should timeout only after 10 minutes', () => { @@ -241,7 +245,7 @@ describe('helm chart', () => { }); // THEN - expect(stack).toHaveResource(eks.HelmChart.RESOURCE_TYPE, { Timeout: '600s' }); + Template.fromStack(stack).hasResourceProperties(eks.HelmChart.RESOURCE_TYPE, { Timeout: '600s' }); }); }); }); diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json index 7b5af8f848f2e..1476182e23c37 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json @@ -954,6 +954,14 @@ }, "tags": { "foo": "bar" + }, + "logging": { + "clusterLogging": [ + { + "enabled": true, + "types": [ "api", "authenticator", "scheduler" ] + } + ] } }, "AssumeRoleArn": { diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts index 8dd012b3e2d66..1ae9a97e5bb37 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts @@ -41,6 +41,11 @@ class EksClusterStack extends TestStack { tags: { foo: 'bar', }, + clusterLogging: [ + eks.ClusterLoggingTypes.API, + eks.ClusterLoggingTypes.AUTHENTICATOR, + eks.ClusterLoggingTypes.SCHEDULER, + ], }); this.assertFargateProfile(); diff --git a/packages/@aws-cdk/aws-eks/test/k8s-manifest.test.ts b/packages/@aws-cdk/aws-eks/test/k8s-manifest.test.ts index 9b0295c23efff..34b2a47cc6d14 100644 --- a/packages/@aws-cdk/aws-eks/test/k8s-manifest.test.ts +++ b/packages/@aws-cdk/aws-eks/test/k8s-manifest.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { SynthUtils } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import { CfnResource, Stack } from '@aws-cdk/core'; import { Cluster, KubernetesManifest, KubernetesVersion, HelmChart } from '../lib'; import { testFixtureNoVpc, testFixtureCluster } from './util'; @@ -72,7 +71,7 @@ describe('k8s manifest', () => { manifest, }); - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify(manifest), }); @@ -91,13 +90,13 @@ describe('k8s manifest', () => { cluster.addHelmChart('helm', { chart: 'hello-world' }); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: '[{"bar":2334}]', ClusterName: 'my-cluster-name', RoleArn: 'arn:aws:iam::1111111:role/iam-role-that-has-masters-access', }); - expect(stack).toHaveResource(HelmChart.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(HelmChart.RESOURCE_TYPE, { ClusterName: 'my-cluster-name', RoleArn: 'arn:aws:iam::1111111:role/iam-role-that-has-masters-access', Release: 'myclustercharthelm78d2c26a', @@ -140,7 +139,7 @@ describe('k8s manifest', () => { }); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([{ apiVersion: 'v1beta1', kind: 'Foo', @@ -182,7 +181,7 @@ describe('k8s manifest', () => { ); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([ { apiVersion: 'v1beta', @@ -244,7 +243,7 @@ describe('k8s manifest', () => { }); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([ { apiVersion: 'v1beta', @@ -259,7 +258,7 @@ describe('k8s manifest', () => { PruneLabel: 'aws.cdk.eks/prune-c89a5983505f58231ac2a9a86fd82735ccf2308eac', }); - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([ { apiVersion: 'v1', @@ -297,7 +296,7 @@ describe('k8s manifest', () => { }); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([{ malformed: { resource: 'yes' } }]), PruneLabel: 'aws.cdk.eks/prune-c89a5983505f58231ac2a9a86fd82735ccf2308eac', }); @@ -314,7 +313,7 @@ describe('k8s manifest', () => { cluster.addManifest('m1', ['foo']); // THEN - expect(stack).toHaveResource(KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(KubernetesManifest.RESOURCE_TYPE, { Manifest: JSON.stringify([['foo']]), PruneLabel: 'aws.cdk.eks/prune-c89a5983505f58231ac2a9a86fd82735ccf2308eac', }); @@ -347,7 +346,7 @@ describe('k8s manifest', () => { }); // THEN - const template = SynthUtils.synthesize(stack).template; + const template = Template.fromStack(stack).toJSON(); const m1 = template.Resources.Clustermanifestm1E5FBE3C1.Properties; const m2 = template.Resources.m201F909C5.Properties; diff --git a/packages/@aws-cdk/aws-eks/test/k8s-object-value.test.ts b/packages/@aws-cdk/aws-eks/test/k8s-object-value.test.ts index 8bc87ba294763..8d29fa19d6471 100644 --- a/packages/@aws-cdk/aws-eks/test/k8s-object-value.test.ts +++ b/packages/@aws-cdk/aws-eks/test/k8s-object-value.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import { App, Stack, Duration } from '@aws-cdk/core'; import * as eks from '../lib'; import { KubernetesObjectValue } from '../lib/k8s-object-value'; diff --git a/packages/@aws-cdk/aws-eks/test/k8s-patch.test.ts b/packages/@aws-cdk/aws-eks/test/k8s-patch.test.ts index c59851500eff0..4040a070c15a0 100644 --- a/packages/@aws-cdk/aws-eks/test/k8s-patch.test.ts +++ b/packages/@aws-cdk/aws-eks/test/k8s-patch.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Names, Stack } from '@aws-cdk/core'; import * as eks from '../lib'; import { KubernetesPatch, PatchType } from '../lib/k8s-patch'; @@ -20,7 +20,7 @@ describe('k8s patch', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { ServiceToken: { 'Fn::GetAtt': [ 'awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B', @@ -59,7 +59,7 @@ describe('k8s patch', () => { restorePatch: { restore: { patch: 123 } }, resourceName: 'myResourceName', }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { PatchType: 'strategic', }); @@ -92,15 +92,15 @@ describe('k8s patch', () => { patchType: PatchType.STRATEGIC, }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { ResourceName: 'jsonPatchResource', PatchType: 'json', }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { ResourceName: 'mergePatchResource', PatchType: 'merge', }); - expect(stack).toHaveResource('Custom::AWSCDK-EKS-KubernetesPatch', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDK-EKS-KubernetesPatch', { ResourceName: 'strategicPatchResource', PatchType: 'strategic', }); diff --git a/packages/@aws-cdk/aws-eks/test/nodegroup.test.ts b/packages/@aws-cdk/aws-eks/test/nodegroup.test.ts index 717b091acf2aa..dd8d0aa1274cd 100644 --- a/packages/@aws-cdk/aws-eks/test/nodegroup.test.ts +++ b/packages/@aws-cdk/aws-eks/test/nodegroup.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cdk from '@aws-cdk/core'; @@ -95,7 +95,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_x86_64_GPU', }); @@ -114,7 +114,7 @@ describe('node group', () => { new eks.Nodegroup(stack, 'Nodegroup', { cluster }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ClusterName: { Ref: 'Cluster9EE0221C', }, @@ -159,7 +159,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ClusterName: { Ref: 'Cluster9EE0221C', }, @@ -204,7 +204,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ClusterName: { Ref: 'Cluster9EE0221C', }, @@ -254,7 +254,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_x86_64', }); }); @@ -281,7 +281,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_ARM_64', }); }); @@ -309,7 +309,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'AL2_x86_64_GPU', }); }); @@ -463,9 +463,9 @@ describe('node group', () => { }); /** - * BOTTOEROCKET_X86_64 with defined instance types w/o launchTemplateSpec should deploy correctly. + * BOTTLEROCKET_X86_64 with defined instance types w/o launchTemplateSpec should deploy correctly. */ - test('BOTTOEROCKET_X86_64 with defined instance types w/o launchTemplateSpec should deploy correctly', () => { + test('BOTTLEROCKET_X86_64 with defined instance types w/o launchTemplateSpec should deploy correctly', () => { // GIVEN const { stack, vpc } = testFixture(); const cluster = new eks.Cluster(stack, 'Cluster', { @@ -480,15 +480,15 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'BOTTLEROCKET_x86_64', }); }); /** - * BOTTOEROCKET_ARM_64 with defined instance types w/o launchTemplateSpec should deploy correctly. + * BOTTLEROCKET_ARM_64 with defined instance types w/o launchTemplateSpec should deploy correctly. */ - test('BOTTOEROCKET_ARM_64 with defined instance types w/o launchTemplateSpec should deploy correctly', () => { + test('BOTTLEROCKET_ARM_64 with defined instance types w/o launchTemplateSpec should deploy correctly', () => { // GIVEN const { stack, vpc } = testFixture(); const cluster = new eks.Cluster(stack, 'Cluster', { @@ -503,7 +503,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { AmiType: 'BOTTLEROCKET_ARM_64', }); }); @@ -521,7 +521,7 @@ describe('node group', () => { new eks.Nodegroup(stack, 'Nodegroup', { cluster }); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { Manifest: { 'Fn::Join': [ '', @@ -582,7 +582,7 @@ describe('node group', () => { }, }); // THEN - expect(stack).toHaveResource('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { RemoteAccess: { Ec2SshKey: 'foo', SourceSecurityGroups: [ @@ -611,7 +611,7 @@ describe('node group', () => { new eks.Nodegroup(stack, 'Nodegroup', { cluster, forceUpdate: false }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ForceUpdateEnabled: false, }); @@ -633,7 +633,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { InstanceTypes: [ 'm5.large', ], @@ -658,7 +658,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { InstanceTypes: [ 'm5.large', ], @@ -687,7 +687,7 @@ describe('node group', () => { capacityType: eks.CapacityType.SPOT, }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { InstanceTypes: [ 'm5.large', 't3.large', @@ -718,7 +718,7 @@ describe('node group', () => { capacityType: eks.CapacityType.ON_DEMAND, }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { InstanceTypes: [ 'm5.large', 't3.large', @@ -765,7 +765,7 @@ describe('node group', () => { capacityType: eks.CapacityType.SPOT, }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { CapacityType: 'SPOT', }); @@ -830,7 +830,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { RemoteAccess: { Ec2SshKey: 'foo', }, @@ -856,7 +856,7 @@ describe('node group', () => { new cdk.CfnOutput(stack2, 'NodegroupName', { value: imported.nodegroupName }); // THEN - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Outputs: { NodegroupName: { Value: { @@ -880,7 +880,7 @@ describe('node group', () => { cluster.addNodegroupCapacity('ng'); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ClusterName: { Ref: 'Cluster9EE0221C', }, @@ -929,7 +929,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ClusterName: { Ref: 'Cluster9EE0221C', }, @@ -984,7 +984,7 @@ describe('node group', () => { desiredSize: 4, }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ScalingConfig: { MinSize: 2, MaxSize: 6, @@ -1010,7 +1010,7 @@ describe('node group', () => { desiredSize: cdk.Lazy.number({ produce: () => 20 }), }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { ScalingConfig: { MinSize: 5, MaxSize: 1, @@ -1050,7 +1050,7 @@ describe('node group', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::EKS::Nodegroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EKS::Nodegroup', { LaunchTemplate: { Id: { Ref: 'LaunchTemplate', diff --git a/packages/@aws-cdk/aws-eks/test/service-account.test.ts b/packages/@aws-cdk/aws-eks/test/service-account.test.ts index e457598ccaa4f..e4db2f6680a97 100644 --- a/packages/@aws-cdk/aws-eks/test/service-account.test.ts +++ b/packages/@aws-cdk/aws-eks/test/service-account.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as eks from '../lib'; import { testFixture, testFixtureCluster } from './util'; @@ -15,7 +15,7 @@ describe('service account', () => { new eks.ServiceAccount(stack, 'MyServiceAccount', { cluster }); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { ServiceToken: { 'Fn::GetAtt': [ 'awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B', @@ -38,7 +38,7 @@ describe('service account', () => { ], }, }); - expect(stack).toHaveResource(iam.CfnRole.CFN_RESOURCE_TYPE_NAME, { + Template.fromStack(stack).hasResourceProperties(iam.CfnRole.CFN_RESOURCE_TYPE_NAME, { AssumeRolePolicyDocument: { Statement: [ { @@ -73,7 +73,7 @@ describe('service account', () => { cluster.addServiceAccount('MyOtherServiceAccount'); // THEN - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { ServiceToken: { 'Fn::GetAtt': [ 'awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B', @@ -122,7 +122,7 @@ describe('service account', () => { cluster.addServiceAccount('MyServiceAccount'); - expect(stack).toHaveResource(eks.KubernetesManifest.RESOURCE_TYPE, { + Template.fromStack(stack).hasResourceProperties(eks.KubernetesManifest.RESOURCE_TYPE, { ServiceToken: { 'Fn::GetAtt': [ 'StackClusterF0EB02FAKubectlProviderNestedStackStackClusterF0EB02FAKubectlProviderNestedStackResource739D12C4', @@ -147,7 +147,7 @@ describe('service account', () => { }, }); - expect(stack).toHaveResource(iam.CfnRole.CFN_RESOURCE_TYPE_NAME, { + Template.fromStack(stack).hasResourceProperties(iam.CfnRole.CFN_RESOURCE_TYPE_NAME, { AssumeRolePolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-elasticache/package.json b/packages/@aws-cdk/aws-elasticache/package.json index 5af18606b0558..61fe8976e0ced 100644 --- a/packages/@aws-cdk/aws-elasticache/package.json +++ b/packages/@aws-cdk/aws-elasticache/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-elasticbeanstalk/package.json b/packages/@aws-cdk/aws-elasticbeanstalk/package.json index 3607699ce8b8b..46506c219aedb 100644 --- a/packages/@aws-cdk/aws-elasticbeanstalk/package.json +++ b/packages/@aws-cdk/aws-elasticbeanstalk/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/README.md b/packages/@aws-cdk/aws-elasticloadbalancing/README.md index 6d66ca5965c69..45ec1828bb466 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/README.md +++ b/packages/@aws-cdk/aws-elasticloadbalancing/README.md @@ -21,17 +21,19 @@ balancer, set up listeners and a health check, and supply the fleet(s) you want to load balance to in the `targets` property. ```ts +declare const vpc: ec2.IVpc; const lb = new elb.LoadBalancer(this, 'LB', { - vpc, - internetFacing: true, - healthCheck: { - port: 80 - }, + vpc, + internetFacing: true, + healthCheck: { + port: 80, + }, }); +declare const myAutoScalingGroup: autoscaling.AutoScalingGroup; lb.addTarget(myAutoScalingGroup); lb.addListener({ - externalPort: 80, + externalPort: 80, }); ``` @@ -39,8 +41,10 @@ The load balancer allows all connections by default. If you want to change that, pass the `allowConnectionsFrom` property while setting up the listener: ```ts +declare const mySecurityGroup: ec2.SecurityGroup; +declare const lb: elb.LoadBalancer; lb.addListener({ - externalPort: 80, - allowConnectionsFrom: [mySecurityGroup] + externalPort: 80, + allowConnectionsFrom: [mySecurityGroup], }); ``` diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 190f42846901b..c43acdd739437 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-elasticloadbalancing/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..036e5ddf38c1b --- /dev/null +++ b/packages/@aws-cdk/aws-elasticloadbalancing/rosetta/default.ts-fixture @@ -0,0 +1,13 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as elb from '@aws-cdk/aws-elasticloadbalancing'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as autoscaling from '@aws-cdk/aws-autoscaling'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json index 90449a5d94204..deaf25cd5eda8 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json index 9990e7ea75a73..76d0b7ba72ee8 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-load-balancer.ts index b54ae026b86fb..93e4e7cdd9b7a 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-load-balancer.ts @@ -1,7 +1,5 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; -import { PolicyStatement, ServicePrincipal } from '@aws-cdk/aws-iam'; -import { IBucket } from '@aws-cdk/aws-s3'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { Resource } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; @@ -125,41 +123,6 @@ export class NetworkLoadBalancer extends BaseLoadBalancer implements INetworkLoa }); } - /** - * Enable access logging for this load balancer. - * - * A region must be specified on the stack containing the load balancer; you cannot enable logging on - * environment-agnostic stacks. See https://docs.aws.amazon.com/cdk/latest/guide/environments.html - * - * This is extending the BaseLoadBalancer.logAccessLogs method to match the bucket permissions described - * at https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-access-logs.html#access-logging-bucket-requirements - */ - public logAccessLogs(bucket: IBucket, prefix?: string) { - super.logAccessLogs(bucket, prefix); - - const logsDeliveryServicePrincipal = new ServicePrincipal('delivery.logs.amazonaws.com'); - - bucket.addToResourcePolicy( - new PolicyStatement({ - actions: ['s3:PutObject'], - principals: [logsDeliveryServicePrincipal], - resources: [ - bucket.arnForObjects(`${prefix ? prefix + '/' : ''}AWSLogs/${this.stack.account}/*`), - ], - conditions: { - StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' }, - }, - }), - ); - bucket.addToResourcePolicy( - new PolicyStatement({ - actions: ['s3:GetBucketAcl'], - principals: [logsDeliveryServicePrincipal], - resources: [bucket.bucketArn], - }), - ); - } - /** * Return the given named metric for this Network Load Balancer * @@ -326,4 +289,4 @@ class LookedUpNetworkLoadBalancer extends Resource implements INetworkLoadBalanc ...props, }); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts index 345583bf9e5f8..0fcb0f4916c87 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts @@ -1,5 +1,6 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; +import { PolicyStatement, ServicePrincipal } from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { ContextProvider, IResource, Lazy, Resource, Stack, Token } from '@aws-cdk/core'; @@ -258,7 +259,27 @@ export abstract class BaseLoadBalancer extends Resource { throw new Error(`Cannot enable access logging; don't know ELBv2 account for region ${region}`); } + const logsDeliveryServicePrincipal = new ServicePrincipal('delivery.logs.amazonaws.com'); bucket.grantPut(new iam.AccountPrincipal(account), `${(prefix ? prefix + '/' : '')}AWSLogs/${Stack.of(this).account}/*`); + bucket.addToResourcePolicy( + new PolicyStatement({ + actions: ['s3:PutObject'], + principals: [logsDeliveryServicePrincipal], + resources: [ + bucket.arnForObjects(`${prefix ? prefix + '/' : ''}AWSLogs/${this.stack.account}/*`), + ], + conditions: { + StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' }, + }, + }), + ); + bucket.addToResourcePolicy( + new PolicyStatement({ + actions: ['s3:GetBucketAcl'], + principals: [logsDeliveryServicePrincipal], + resources: [bucket.bucketArn], + }), + ); // make sure the bucket's policy is created before the ALB (see https://github.com/aws/aws-cdk/issues/1633) this.node.addDependency(bucket); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/load-balancer.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/load-balancer.test.ts index 4b89f79b75d31..5bb54df75ac24 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/load-balancer.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/load-balancer.test.ts @@ -2,7 +2,7 @@ import { Match, Template } from '@aws-cdk/assertions'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as s3 from '@aws-cdk/aws-s3'; -import { testFutureBehavior } from '@aws-cdk/cdk-build-tools/lib/feature-flag'; +import { testFutureBehavior, testLegacyBehavior } from '@aws-cdk/cdk-build-tools/lib/feature-flag'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import * as elbv2 from '../../lib'; @@ -146,105 +146,222 @@ describe('tests', () => { expect(loadBalancer.listeners).toContain(listener); }); - testFutureBehavior('Access logging', s3GrantWriteCtx, cdk.App, (app) => { - // GIVEN - const stack = new cdk.Stack(app, undefined, { env: { region: 'us-east-1' } }); - const vpc = new ec2.Vpc(stack, 'Stack'); - const bucket = new s3.Bucket(stack, 'AccessLoggingBucket'); - const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc }); + describe('logAccessLogs', () => { - // WHEN - lb.logAccessLogs(bucket); + function loggingSetup(app: cdk.App): { stack: cdk.Stack, bucket: s3.Bucket, lb: elbv2.ApplicationLoadBalancer } { + const stack = new cdk.Stack(app, undefined, { env: { region: 'us-east-1' } }); + const vpc = new ec2.Vpc(stack, 'Stack'); + const bucket = new s3.Bucket(stack, 'AccessLoggingBucket'); + const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc }); + return { stack, bucket, lb }; + } - // THEN + test('sets load balancer attributes', () => { + // GIVEN + const app = new cdk.App(); + const { stack, bucket, lb } = loggingSetup(app); - // verify that the LB attributes reference the bucket - Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { - LoadBalancerAttributes: Match.arrayWith([ - { - Key: 'access_logs.s3.enabled', - Value: 'true', - }, - { - Key: 'access_logs.s3.bucket', - Value: { Ref: 'AccessLoggingBucketA6D88F29' }, - }, - { - Key: 'access_logs.s3.prefix', - Value: '', - }, - ]), - }); + // WHEN + lb.logAccessLogs(bucket); - // verify the bucket policy allows the ALB to put objects in the bucket - Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { - PolicyDocument: { - Version: '2012-10-17', - Statement: [ + //THEN + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { + LoadBalancerAttributes: Match.arrayWith([ { - Action: ['s3:PutObject', 's3:Abort*'], - Effect: 'Allow', - Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, - Resource: { - 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/AWSLogs/', - { Ref: 'AWS::AccountId' }, '/*']], - }, + Key: 'access_logs.s3.enabled', + Value: 'true', }, - ], - }, + { + Key: 'access_logs.s3.bucket', + Value: { Ref: 'AccessLoggingBucketA6D88F29' }, + }, + { + Key: 'access_logs.s3.prefix', + Value: '', + }, + ]), + }); }); - // verify the ALB depends on the bucket *and* the bucket policy - Template.fromStack(stack).hasResource('AWS::ElasticLoadBalancingV2::LoadBalancer', { - DependsOn: ['AccessLoggingBucketPolicy700D7CC6', 'AccessLoggingBucketA6D88F29'], + test('adds a dependency on the bucket', () => { + // GIVEN + const app = new cdk.App(); + const { stack, bucket, lb } = loggingSetup(app); + + // WHEN + lb.logAccessLogs(bucket); + + // THEN + // verify the ALB depends on the bucket *and* the bucket policy + Template.fromStack(stack).hasResource('AWS::ElasticLoadBalancingV2::LoadBalancer', { + DependsOn: ['AccessLoggingBucketPolicy700D7CC6', 'AccessLoggingBucketA6D88F29'], + }); }); - }); - testFutureBehavior('access logging with prefix', s3GrantWriteCtx, cdk.App, (app) => { - // GIVEN - const stack = new cdk.Stack(app, undefined, { env: { region: 'us-east-1' } }); - const vpc = new ec2.Vpc(stack, 'Stack'); - const bucket = new s3.Bucket(stack, 'AccessLoggingBucket'); - const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc }); + testLegacyBehavior('legacy bucket permissions', cdk.App, (app) => { + const { stack, bucket, lb } = loggingSetup(app); - // WHEN - lb.logAccessLogs(bucket, 'prefix-of-access-logs'); + // WHEN + lb.logAccessLogs(bucket); - // THEN - // verify that the LB attributes reference the bucket - Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { - LoadBalancerAttributes: Match.arrayWith([ - { - Key: 'access_logs.s3.enabled', - Value: 'true', - }, - { - Key: 'access_logs.s3.bucket', - Value: { Ref: 'AccessLoggingBucketA6D88F29' }, + // THEN + // verify the bucket policy allows the ALB to put objects in the bucket + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: ['s3:PutObject*', 's3:Abort*'], + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/AWSLogs/', + { Ref: 'AWS::AccountId' }, '/*']], + }, + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Principal: { Service: 'delivery.logs.amazonaws.com' }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/AWSLogs/', + { Ref: 'AWS::AccountId' }, '/*']], + }, + Condition: { StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' } }, + }, + { + Action: 's3:GetBucketAcl', + Effect: 'Allow', + Principal: { Service: 'delivery.logs.amazonaws.com' }, + Resource: { + 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'], + }, + }, + ], }, - { - Key: 'access_logs.s3.prefix', - Value: 'prefix-of-access-logs', + }); + }); + + testFutureBehavior('logging bucket permissions', s3GrantWriteCtx, cdk.App, (app) => { + // GIVEN + const { stack, bucket, lb } = loggingSetup(app); + + // WHEN + lb.logAccessLogs(bucket); + + // THEN + // verify the bucket policy allows the ALB to put objects in the bucket + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: [ + 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', + 's3:Abort*', + ], + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/AWSLogs/', + { Ref: 'AWS::AccountId' }, '/*']], + }, + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Principal: { Service: 'delivery.logs.amazonaws.com' }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/AWSLogs/', + { Ref: 'AWS::AccountId' }, '/*']], + }, + Condition: { StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' } }, + }, + { + Action: 's3:GetBucketAcl', + Effect: 'Allow', + Principal: { Service: 'delivery.logs.amazonaws.com' }, + Resource: { + 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'], + }, + }, + ], }, - ]), + }); }); - // verify the bucket policy allows the ALB to put objects in the bucket - Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { - PolicyDocument: { - Version: '2012-10-17', - Statement: [ + testFutureBehavior('access logging with prefix', s3GrantWriteCtx, cdk.App, (app) => { + // GIVEN + const { stack, bucket, lb } = loggingSetup(app); + + // WHEN + lb.logAccessLogs(bucket, 'prefix-of-access-logs'); + + // THEN + // verify that the LB attributes reference the bucket + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { + LoadBalancerAttributes: Match.arrayWith([ { - Action: ['s3:PutObject', 's3:Abort*'], - Effect: 'Allow', - Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, - Resource: { - 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/prefix-of-access-logs/AWSLogs/', - { Ref: 'AWS::AccountId' }, '/*']], - }, + Key: 'access_logs.s3.enabled', + Value: 'true', + }, + { + Key: 'access_logs.s3.bucket', + Value: { Ref: 'AccessLoggingBucketA6D88F29' }, + }, + { + Key: 'access_logs.s3.prefix', + Value: 'prefix-of-access-logs', }, - ], - }, + ]), + }); + + // verify the bucket policy allows the ALB to put objects in the bucket + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: [ + 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', + 's3:Abort*', + ], + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/prefix-of-access-logs/AWSLogs/', + { Ref: 'AWS::AccountId' }, '/*']], + }, + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Principal: { Service: 'delivery.logs.amazonaws.com' }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'] }, '/prefix-of-access-logs/AWSLogs/', + { Ref: 'AWS::AccountId' }, '/*']], + }, + Condition: { StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' } }, + }, + { + Action: 's3:GetBucketAcl', + Effect: 'Allow', + Principal: { Service: 'delivery.logs.amazonaws.com' }, + Resource: { + 'Fn::GetAtt': ['AccessLoggingBucketA6D88F29', 'Arn'], + }, + }, + ], + }, + }); }); }); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts index d0faa87d5cdaa..2454cd1ccfb01 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/load-balancer.test.ts @@ -108,7 +108,14 @@ describe('tests', () => { Version: '2012-10-17', Statement: [ { - Action: ['s3:PutObject', 's3:Abort*'], + Action: [ + 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', + 's3:Abort*', + ], Effect: 'Allow', Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, Resource: { @@ -179,7 +186,14 @@ describe('tests', () => { Version: '2012-10-17', Statement: [ { - Action: ['s3:PutObject', 's3:Abort*'], + Action: [ + 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', + 's3:Abort*', + ], Effect: 'Allow', Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::127311923021:root']] } }, Resource: { diff --git a/packages/@aws-cdk/aws-emr/package.json b/packages/@aws-cdk/aws-emr/package.json index 60da5b871705d..ae9f2a89744ea 100644 --- a/packages/@aws-cdk/aws-emr/package.json +++ b/packages/@aws-cdk/aws-emr/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-emrcontainers/package.json b/packages/@aws-cdk/aws-emrcontainers/package.json index 9098ed9069a61..5762e656d6761 100644 --- a/packages/@aws-cdk/aws-emrcontainers/package.json +++ b/packages/@aws-cdk/aws-emrcontainers/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-emrcontainers", "module": "aws_cdk.aws_emrcontainers" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index 602a915b8be8a..e4b630d0d9637 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -88,7 +88,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", "aws-sdk": "^2.848.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "jest": "^27.4.7" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json index bc6bec13d1d5f..912eb616ad2c8 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json @@ -159,6 +159,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-events-targets/test/kinesis-firehose/integ.kinesis-firehose-stream.expected.json b/packages/@aws-cdk/aws-events-targets/test/kinesis-firehose/integ.kinesis-firehose-stream.expected.json index 6b76a3d96a32e..0c7a34c7f4bd5 100644 --- a/packages/@aws-cdk/aws-events-targets/test/kinesis-firehose/integ.kinesis-firehose-stream.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/kinesis-firehose/integ.kinesis-firehose-stream.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index f871d4ec7b6a4..57af72c704d52 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -81,7 +81,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-events/test/archive.test.ts b/packages/@aws-cdk/aws-events/test/archive.test.ts index 0c37a0ec4ae39..8119961738cf1 100644 --- a/packages/@aws-cdk/aws-events/test/archive.test.ts +++ b/packages/@aws-cdk/aws-events/test/archive.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Duration, Stack } from '@aws-cdk/core'; import { EventBus } from '../lib'; import { Archive } from '../lib/archive'; @@ -9,7 +9,7 @@ describe('archive', () => { const stack = new Stack(); // WHEN - let eventBus = new EventBus(stack, 'Bus'); + const eventBus = new EventBus(stack, 'Bus'); new Archive(stack, 'Archive', { sourceEventBus: eventBus, @@ -20,11 +20,11 @@ describe('archive', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'Bus', }); - expect(stack).toHaveResource('AWS::Events::Archive', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Archive', { EventPattern: { account: [{ Ref: 'AWS::AccountId', @@ -38,15 +38,14 @@ describe('archive', () => { ], }, }); - - }); + test('creates an archive for an EventBus with a pattern including a detailType property', () => { // GIVEN const stack = new Stack(); // WHEN - let eventBus = new EventBus(stack, 'Bus'); + const eventBus = new EventBus(stack, 'Bus'); new Archive(stack, 'Archive', { sourceEventBus: eventBus, @@ -58,11 +57,11 @@ describe('archive', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'Bus', }); - expect(stack).toHaveResource('AWS::Events::Archive', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Archive', { EventPattern: { 'account': [{ Ref: 'AWS::AccountId', @@ -77,7 +76,5 @@ describe('archive', () => { ], }, }); - - }); }); diff --git a/packages/@aws-cdk/aws-events/test/event-bus.test.ts b/packages/@aws-cdk/aws-events/test/event-bus.test.ts index 71f089a58b23c..34406986c608e 100644 --- a/packages/@aws-cdk/aws-events/test/event-bus.test.ts +++ b/packages/@aws-cdk/aws-events/test/event-bus.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Aws, CfnResource, Stack, Arn, App, PhysicalName, CfnOutput } from '@aws-cdk/core'; @@ -13,7 +13,7 @@ describe('event bus', () => { new EventBus(stack, 'Bus'); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'Bus', }); }); @@ -26,7 +26,7 @@ describe('event bus', () => { new EventBus(stack, 'Bus', {}); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'Bus', }); }); @@ -41,11 +41,9 @@ describe('event bus', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'myEventBus', }); - - }); test('partner event bus', () => { @@ -58,12 +56,10 @@ describe('event bus', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'aws.partner/PartnerName/acct1/repo1', EventSourceName: 'aws.partner/PartnerName/acct1/repo1', }); - - }); test('imported event bus', () => { @@ -82,12 +78,10 @@ describe('event bus', () => { }, }); - expect(stack).toHaveResource('Test::Resource', { + Template.fromStack(stack).hasResourceProperties('Test::Resource', { EventBusArn1: { 'Fn::GetAtt': ['BusEA82B648', 'Arn'] }, EventBusArn2: { 'Fn::GetAtt': ['BusEA82B648', 'Arn'] }, }); - - }); test('imported event bus from name', () => { @@ -99,8 +93,6 @@ describe('event bus', () => { // WHEN expect(stack.resolve(eventBus.eventBusName)).toEqual(stack.resolve(importEB.eventBusName)); - - }); test('same account imported event bus has right resource env', () => { @@ -113,8 +105,6 @@ describe('event bus', () => { // WHEN expect(stack.resolve(importEB.env.account)).toEqual({ 'Fn::Select': [4, { 'Fn::Split': [':', { 'Fn::GetAtt': ['BusEA82B648', 'Arn'] }] }] }); expect(stack.resolve(importEB.env.region)).toEqual({ 'Fn::Select': [3, { 'Fn::Split': [':', { 'Fn::GetAtt': ['BusEA82B648', 'Arn'] }] }] }); - - }); test('cross account imported event bus has right resource env', () => { @@ -134,8 +124,6 @@ describe('event bus', () => { // WHEN expect(importEB.env.account).toEqual(arnParts.account); expect(importEB.env.region).toEqual(arnParts.region); - - }); test('can get bus name', () => { @@ -154,11 +142,9 @@ describe('event bus', () => { }); // THEN - expect(stack).toHaveResource('Test::Resource', { + Template.fromStack(stack).hasResourceProperties('Test::Resource', { EventBusName: { Ref: 'BusEA82B648' }, }); - - }); test('can get bus arn', () => { @@ -177,11 +163,9 @@ describe('event bus', () => { }); // THEN - expect(stack).toHaveResource('Test::Resource', { + Template.fromStack(stack).hasResourceProperties('Test::Resource', { EventBusArn: { 'Fn::GetAtt': ['BusEA82B648', 'Arn'] }, }); - - }); test('event bus name cannot be default', () => { @@ -197,8 +181,6 @@ describe('event bus', () => { expect(() => { createInvalidBus(); }).toThrow(/'eventBusName' must not be 'default'/); - - }); test('event bus name cannot contain slash', () => { @@ -214,8 +196,6 @@ describe('event bus', () => { expect(() => { createInvalidBus(); }).toThrow(/'eventBusName' must not contain '\/'/); - - }); test('event bus cannot have name and source name', () => { @@ -232,8 +212,6 @@ describe('event bus', () => { expect(() => { createInvalidBus(); }).toThrow(/'eventBusName' and 'eventSourceName' cannot both be provided/); - - }); test('event bus name cannot be empty string', () => { @@ -249,8 +227,6 @@ describe('event bus', () => { expect(() => { createInvalidBus(); }).toThrow(/'eventBusName' must satisfy: /); - - }); test('does not throw if eventBusName is a token', () => { @@ -261,8 +237,6 @@ describe('event bus', () => { expect(() => new EventBus(stack, 'EventBus', { eventBusName: Aws.STACK_NAME, })).not.toThrow(); - - }); test('event bus source name must follow pattern', () => { @@ -278,8 +252,6 @@ describe('event bus', () => { expect(() => { createInvalidBus(); }).toThrow(/'eventSourceName' must satisfy: \/\^aws/); - - }); test('event bus source name cannot be empty string', () => { @@ -295,8 +267,6 @@ describe('event bus', () => { expect(() => { createInvalidBus(); }).toThrow(/'eventSourceName' must satisfy: /); - - }); testDeprecated('can grant PutEvents', () => { @@ -310,7 +280,7 @@ describe('event bus', () => { EventBus.grantPutEvents(role); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -327,8 +297,6 @@ describe('event bus', () => { }, ], }); - - }); test('can grant PutEvents using grantAllPutEvents', () => { @@ -342,7 +310,7 @@ describe('event bus', () => { EventBus.grantAllPutEvents(role); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -359,9 +327,8 @@ describe('event bus', () => { }, ], }); - - }); + test('can grant PutEvents to a specific event bus', () => { // GIVEN const stack = new Stack(); @@ -375,7 +342,7 @@ describe('event bus', () => { eventBus.grantPutEventsTo(role); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -397,9 +364,8 @@ describe('event bus', () => { }, ], }); - - }); + test('can archive events', () => { // GIVEN const stack = new Stack(); @@ -415,11 +381,11 @@ describe('event bus', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'Bus', }); - expect(stack).toHaveResource('AWS::Events::Archive', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Archive', { SourceArn: { 'Fn::GetAtt': [ 'BusEA82B648', @@ -448,9 +414,8 @@ describe('event bus', () => { RetentionDays: 0, ArchiveName: 'MyArchive', }); - - }); + test('can archive events from an imported EventBus', () => { // GIVEN const stack = new Stack(); @@ -468,11 +433,11 @@ describe('event bus', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBus', { Name: 'Bus', }); - expect(stack).toHaveResource('AWS::Events::Archive', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Archive', { SourceArn: { 'Fn::GetAtt': [ 'BusEA82B648', @@ -524,9 +489,8 @@ describe('event bus', () => { RetentionDays: 0, ArchiveName: 'MyArchive', }); - - }); + test('cross account event bus uses generated physical name', () => { // GIVEN const app = new App(); @@ -551,7 +515,7 @@ describe('event bus', () => { new CfnOutput(stack2, 'BusName', { value: bus1.eventBusName }); // THEN - expect(stack1).toHaveResource('AWS::Events::EventBus', { + Template.fromStack(stack1).hasResourceProperties('AWS::Events::EventBus', { Name: 'stack1stack1busca19bdf8ab2e51b62a5a', }); }); diff --git a/packages/@aws-cdk/aws-events/test/input.test.ts b/packages/@aws-cdk/aws-events/test/input.test.ts index 76ec2cd2d2bd9..2c5536620a9fb 100644 --- a/packages/@aws-cdk/aws-events/test/input.test.ts +++ b/packages/@aws-cdk/aws-events/test/input.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { User } from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { EventField, IRuleTarget, RuleTargetInput, Schedule } from '../lib'; @@ -17,14 +17,13 @@ describe('input', () => { rule.addTarget(new SomeTarget(RuleTargetInput.fromObject({ SomeObject: 'withAValue' }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Input: '{"SomeObject":"withAValue"}', }, ], }); - }); test('can use joined JSON containing refs in JSON object', () => { @@ -41,7 +40,7 @@ describe('input', () => { }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { InputTransformer: { @@ -62,8 +61,6 @@ describe('input', () => { }, ], }); - - }); test('can use joined JSON containing refs in JSON object with tricky inputs', () => { @@ -80,7 +77,7 @@ describe('input', () => { }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { InputTransformer: { @@ -101,8 +98,6 @@ describe('input', () => { }, ], }); - - }); test('can use joined JSON containing refs in JSON object and concat', () => { @@ -119,7 +114,7 @@ describe('input', () => { }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { InputTransformer: { @@ -140,8 +135,6 @@ describe('input', () => { }, ], }); - - }); test('can use joined JSON containing refs in JSON object and quotes', () => { @@ -158,7 +151,7 @@ describe('input', () => { }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { InputTransformer: { @@ -179,8 +172,6 @@ describe('input', () => { }, ], }); - - }); test('can use joined JSON containing refs in JSON object and multiple keys', () => { @@ -197,7 +188,7 @@ describe('input', () => { }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { InputTransformer: { @@ -218,8 +209,6 @@ describe('input', () => { }, ], }); - - }); test('can use token', () => { @@ -234,7 +223,7 @@ describe('input', () => { rule.addTarget(new SomeTarget(RuleTargetInput.fromObject({ userArn: user.userArn }))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Input: { @@ -255,7 +244,6 @@ describe('input', () => { }, ], }); - }); }); @@ -271,15 +259,13 @@ describe('input', () => { rule.addTarget(new SomeTarget(RuleTargetInput.fromMultilineText('I have\nmultiple lines'))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Input: '"I have"\n"multiple lines"', }, ], }); - - }); test('escaped newlines are not interpreted as newlines', () => { @@ -290,18 +276,16 @@ describe('input', () => { }); // WHEN - rule.addTarget(new SomeTarget(RuleTargetInput.fromMultilineText('this is not\\na real newline'))), + rule.addTarget(new SomeTarget(RuleTargetInput.fromMultilineText('this is not\\na real newline'))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Input: '"this is not\\\\na real newline"', }, ], }); - - }); test('can use Tokens in text templates', () => { @@ -317,15 +301,13 @@ describe('input', () => { rule.addTarget(new SomeTarget(RuleTargetInput.fromText(`hello ${world}`))); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Input: '"hello world"', }, ], }); - - }); }); }); diff --git a/packages/@aws-cdk/aws-events/test/rule.test.ts b/packages/@aws-cdk/aws-events/test/rule.test.ts index 21782c089b40c..1cbd776441fce 100644 --- a/packages/@aws-cdk/aws-events/test/rule.test.ts +++ b/packages/@aws-cdk/aws-events/test/rule.test.ts @@ -1,5 +1,5 @@ /* eslint-disable object-curly-newline */ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { EventBus, EventField, IRule, IRuleTarget, RuleTargetConfig, RuleTargetInput, Schedule } from '../lib'; @@ -15,7 +15,7 @@ describe('rule', () => { schedule: Schedule.rate(cdk.Duration.minutes(10)), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyRuleA44AB831': { 'Type': 'AWS::Events::Rule', @@ -26,7 +26,6 @@ describe('rule', () => { }, }, }); - }); test('can get rule name', () => { @@ -42,11 +41,9 @@ describe('rule', () => { }, }); - expect(stack).toHaveResource('Test::Resource', { + Template.fromStack(stack).hasResourceProperties('Test::Resource', { RuleName: { Ref: 'MyRuleA44AB831' }, }); - - }); test('get rate as token', () => { @@ -60,18 +57,15 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'Name': 'rateInMinutes', 'ScheduleExpression': 'rate(5 minutes)', }); - - }); test('Seconds is not an allowed value for Schedule rate', () => { const lazyDuration = cdk.Duration.seconds(cdk.Lazy.number({ produce: () => 5 })); expect(() => Schedule.rate(lazyDuration)).toThrow(/Allowed units for scheduling/i); - }); test('Millis is not an allowed value for Schedule rate', () => { @@ -79,7 +73,6 @@ describe('rule', () => { // THEN expect(() => Schedule.rate(lazyDuration)).toThrow(/Allowed units for scheduling/i); - }); test('rule with physical name', () => { @@ -93,11 +86,9 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Name: 'PhysicalName', }); - - }); test('eventPattern is rendered properly', () => { @@ -119,7 +110,7 @@ describe('rule', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyRuleA44AB831': { 'Type': 'AWS::Events::Rule', @@ -140,8 +131,6 @@ describe('rule', () => { }, }, }); - - }); test('fails synthesis if neither eventPattern nor scheudleExpression are specified', () => { @@ -149,7 +138,6 @@ describe('rule', () => { const stack = new cdk.Stack(app, 'MyStack'); new Rule(stack, 'Rule'); expect(() => app.synth()).toThrow(/Either 'eventPattern' or 'schedule' must be defined/); - }); test('addEventPattern can be used to add filters', () => { @@ -173,7 +161,7 @@ describe('rule', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyRuleA44AB831': { 'Type': 'AWS::Events::Rule', @@ -202,7 +190,6 @@ describe('rule', () => { }, }, }); - }); test('addEventPattern can de-duplicate filters and keep the order', () => { @@ -217,7 +204,7 @@ describe('rule', () => { detailType: ['EC2 Instance State-change Notification', 'AWS API Call via CloudTrail'], }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyRuleA44AB831': { 'Type': 'AWS::Events::Rule', @@ -233,7 +220,6 @@ describe('rule', () => { }, }, }); - }); test('targets can be added via props or addTarget with input transformer', () => { @@ -261,7 +247,7 @@ describe('rule', () => { rule.addTarget(t2); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'EventRule5A491D2C': { 'Type': 'AWS::Events::Rule', @@ -291,7 +277,6 @@ describe('rule', () => { }, }, }); - }); test('input template can contain tokens', () => { @@ -337,7 +322,7 @@ describe('rule', () => { }), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'EventRule5A491D2C': { 'Type': 'AWS::Events::Rule', @@ -378,8 +363,6 @@ describe('rule', () => { }, }, }); - - }); test('target can declare role which will be used', () => { @@ -404,7 +387,7 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'Targets': [ { 'Arn': 'ARN2', @@ -413,8 +396,6 @@ describe('rule', () => { }, ], }); - - }); test('in cross-account scenario, target role is only used in target account', () => { @@ -442,7 +423,7 @@ describe('rule', () => { }); // THEN - expect(ruleStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(ruleStack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Arn: { 'Fn::Join': ['', [ @@ -453,7 +434,7 @@ describe('rule', () => { }, ], }); - expect(targetStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(targetStack).hasResourceProperties('AWS::Events::Rule', { 'Targets': [ { 'Arn': 'ARN2', @@ -462,8 +443,6 @@ describe('rule', () => { }, ], }); - - }); test('asEventRuleTarget can use the ruleArn and a uniqueId of the rule', () => { @@ -490,7 +469,6 @@ describe('rule', () => { expect(stack.resolve(receivedRuleArn)).toEqual(stack.resolve(rule.ruleArn)); expect(receivedRuleId).toEqual(cdk.Names.uniqueId(rule)); - }); test('fromEventRuleArn', () => { @@ -503,7 +481,6 @@ describe('rule', () => { // THEN expect(importedRule.ruleArn).toEqual('arn:aws:events:us-east-2:123456789012:rule/example'); expect(importedRule.ruleName).toEqual('example'); - }); test('rule can be disabled', () => { @@ -517,11 +494,9 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'State': 'DISABLED', }); - - }); test('can add multiple targets with the same id', () => { @@ -535,7 +510,7 @@ describe('rule', () => { rule.addTarget(new SomeTarget()); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { 'Arn': 'ARN1', @@ -553,8 +528,6 @@ describe('rule', () => { }, ], }); - - }); test('sqsParameters are generated when they are specified in target props', () => { @@ -572,7 +545,7 @@ describe('rule', () => { targets: [t1], }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { 'Arn': 'ARN1', @@ -583,7 +556,6 @@ describe('rule', () => { }, ], }); - }); test('associate rule with event bus', () => { @@ -600,13 +572,11 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventBusName: { Ref: 'EventBus7B8748AA', }, }); - - }); test('throws with eventBus and schedule', () => { @@ -620,7 +590,6 @@ describe('rule', () => { schedule: Schedule.rate(cdk.Duration.minutes(10)), eventBus, })).toThrow(/Cannot associate rule with 'eventBus' when using 'schedule'/); - }); test('allow an imported target if is in the same account and region', () => { @@ -639,7 +608,7 @@ describe('rule', () => { rule.addTarget(new SomeTarget('T', resource)); - expect(sourceStack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(sourceStack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { 'Arn': 'ARN1', @@ -650,8 +619,6 @@ describe('rule', () => { }, ], }); - - }); describe('for cross-account and/or cross-region targets', () => { @@ -668,8 +635,6 @@ describe('rule', () => { expect(() => { rule.addTarget(new SomeTarget('T', resource)); }).toThrow(/You need to provide a concrete region/); - - }); test('requires that the target stack specify a concrete account', () => { @@ -685,8 +650,6 @@ describe('rule', () => { expect(() => { rule.addTarget(new SomeTarget('T', resource)); }).toThrow(/You need to provide a concrete account for the target stack when using cross-account or cross-region events/); - - }); test('requires that the target stack specify a concrete region', () => { @@ -703,8 +666,6 @@ describe('rule', () => { expect(() => { rule.addTarget(new SomeTarget('T', resource)); }).toThrow(/You need to provide a concrete region for the target stack when using cross-account or cross-region events/); - - }); test('creates cross-account targets if in the same region', () => { @@ -726,7 +687,7 @@ describe('rule', () => { rule.addTarget(new SomeTarget('T', resource)); - expect(sourceStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(sourceStack).hasResourceProperties('AWS::Events::Rule', { 'State': 'ENABLED', 'Targets': [ { @@ -745,7 +706,7 @@ describe('rule', () => { ], }); - expect(targetStack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(targetStack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { 'Arn': 'ARN1', @@ -756,8 +717,6 @@ describe('rule', () => { }, ], }); - - }); test('creates cross-region targets', () => { @@ -779,7 +738,7 @@ describe('rule', () => { rule.addTarget(new SomeTarget('T', resource)); - expect(sourceStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(sourceStack).hasResourceProperties('AWS::Events::Rule', { 'State': 'ENABLED', 'Targets': [ { @@ -798,7 +757,7 @@ describe('rule', () => { ], }); - expect(targetStack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(targetStack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { 'Arn': 'ARN1', @@ -809,8 +768,6 @@ describe('rule', () => { }, ], }); - - }); test('do not create duplicated targets', () => { @@ -834,7 +791,7 @@ describe('rule', () => { // same target should be skipped rule.addTarget(new SomeTarget('T1', resource)); - expect(sourceStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(sourceStack).hasResourceProperties('AWS::Events::Rule', { 'State': 'ENABLED', 'Targets': [ { @@ -853,7 +810,7 @@ describe('rule', () => { ], }); - expect(sourceStack).not.toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(sourceStack).hasResourceProperties('AWS::Events::Rule', Match.not({ 'State': 'ENABLED', 'Targets': [ { @@ -870,9 +827,7 @@ describe('rule', () => { }, }, ], - }); - - + })); }); test('requires that the target is not imported', () => { @@ -893,8 +848,6 @@ describe('rule', () => { expect(() => { rule.addTarget(new SomeTarget('T', resource)); }).toThrow(/Cannot create a cross-account or cross-region rule for an imported resource/); - - }); test('requires that the source and target stacks be part of the same App', () => { @@ -911,8 +864,6 @@ describe('rule', () => { expect(() => { rule.addTarget(new SomeTarget('T', resource)); }).toThrow(/Event stack and target stack must belong to the same CDK app/); - - }); test('generates the correct rules in the source and target stacks when eventPattern is passed in the constructor', () => { @@ -944,7 +895,7 @@ describe('rule', () => { rule.addTarget(new SomeTarget('T1', resource1)); rule.addTarget(new SomeTarget('T2', resource2)); - expect(sourceStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(sourceStack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'some-event', @@ -968,7 +919,7 @@ describe('rule', () => { ], }); - expect(targetStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(targetStack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'some-event', @@ -982,7 +933,7 @@ describe('rule', () => { }, ], }); - expect(targetStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(targetStack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'some-event', @@ -998,13 +949,11 @@ describe('rule', () => { }); const eventBusPolicyStack = app.node.findChild(`EventBusPolicy-${sourceAccount}-us-west-2-${targetAccount}`) as cdk.Stack; - expect(eventBusPolicyStack).toHaveResourceLike('AWS::Events::EventBusPolicy', { + Template.fromStack(eventBusPolicyStack).hasResourceProperties('AWS::Events::EventBusPolicy', { 'Action': 'events:PutEvents', 'StatementId': `Allow-account-${sourceAccount}`, 'Principal': sourceAccount, }); - - }); test('generates the correct rule in the target stack when addEventPattern in the source rule is used', () => { @@ -1034,7 +983,7 @@ describe('rule', () => { source: ['some-event'], }); - expect(targetStack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(targetStack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'some-event', @@ -1048,8 +997,6 @@ describe('rule', () => { }, ], }); - - }); }); }); diff --git a/packages/@aws-cdk/aws-events/test/schedule.test.ts b/packages/@aws-cdk/aws-events/test/schedule.test.ts index d853da9ba6c30..9dea322c62d0a 100644 --- a/packages/@aws-cdk/aws-events/test/schedule.test.ts +++ b/packages/@aws-cdk/aws-events/test/schedule.test.ts @@ -8,7 +8,6 @@ describe('schedule', () => { minute: '0/10', weekDay: 'MON-FRI', }).expressionString); - }); test('cron expressions day and dow are mutex: given month day', () => { @@ -18,7 +17,6 @@ describe('schedule', () => { hour: '8', day: '1', }).expressionString); - }); test('cron expressions day and dow are mutex: given neither', () => { @@ -27,28 +25,24 @@ describe('schedule', () => { minute: '0', hour: '10', }).expressionString); - }); test('rate must be whole number of minutes', () => { expect(() => { events.Schedule.rate(Duration.minutes(0.13456)); }).toThrow(/'0.13456 minutes' cannot be converted into a whole number of seconds/); - }); test('rate must be whole number', () => { expect(() => { events.Schedule.rate(Duration.minutes(1/8)); }).toThrow(/'0.125 minutes' cannot be converted into a whole number of seconds/); - }); test('rate cannot be 0', () => { expect(() => { events.Schedule.rate(Duration.days(0)); }).toThrow(/Duration cannot be 0/); - }); test('rate can be from a token', () => { @@ -56,41 +50,35 @@ describe('schedule', () => { const lazyDuration = Duration.minutes(Lazy.number({ produce: () => 5 })); const rate = events.Schedule.rate(lazyDuration); expect('rate(5 minutes)').toEqual(stack.resolve(rate).expressionString); - }); test('rate can be in minutes', () => { expect('rate(10 minutes)').toEqual( events.Schedule.rate(Duration.minutes(10)) .expressionString); - }); test('rate can be in days', () => { expect('rate(10 days)').toEqual( events.Schedule.rate(Duration.days(10)) .expressionString); - }); test('rate can be in hours', () => { expect('rate(10 hours)').toEqual( events.Schedule.rate(Duration.hours(10)) .expressionString); - }); test('rate can be in seconds', () => { expect('rate(2 minutes)').toEqual( events.Schedule.rate(Duration.seconds(120)) .expressionString); - }); test('rate must not be in seconds when specified as a token', () => { expect(() => { events.Schedule.rate(Duration.seconds(Lazy.number({ produce: () => 5 }))); }).toThrow(/Allowed units for scheduling/); - }); }); diff --git a/packages/@aws-cdk/aws-events/test/util.test.ts b/packages/@aws-cdk/aws-events/test/util.test.ts index eb354ca9c48a0..b885041163316 100644 --- a/packages/@aws-cdk/aws-events/test/util.test.ts +++ b/packages/@aws-cdk/aws-events/test/util.test.ts @@ -23,21 +23,18 @@ describe('util', () => { case: [1], }, }); - }); test('merge into an empty destination', () => { expect(mergeEventPattern(undefined, { foo: ['123'] })).toEqual({ foo: ['123'] }); expect(mergeEventPattern(undefined, { foo: { bar: ['123'] } })).toEqual({ foo: { bar: ['123'] } }); expect(mergeEventPattern({ }, { foo: { bar: ['123'] } })).toEqual({ foo: { bar: ['123'] } }); - }); test('fails if a field is not an array', () => { expect(() => mergeEventPattern(undefined, 123)).toThrow(/Invalid event pattern '123', expecting an object or an array/); expect(() => mergeEventPattern(undefined, 'Hello')).toThrow(/Invalid event pattern '"Hello"', expecting an object or an array/); expect(() => mergeEventPattern(undefined, { foo: '123' })).toThrow(/Invalid event pattern field { foo: "123" }. All fields must be arrays/); - }); test('fails if mismatch between dest and src', () => { @@ -52,7 +49,6 @@ describe('util', () => { }, }, })).toThrow(/Invalid event pattern field array. Type mismatch between existing pattern \[1\] and added pattern \{"value":\["hello"\]\}/); - }); test('deduplicate match values in pattern array', () => { @@ -90,7 +86,6 @@ describe('util', () => { 'detail-type': ['AWS API Call via CloudTrail'], 'time': [{ prefix: '2017-10-02' }, { prefix: '2017-10-03' }], }); - }); }); }); diff --git a/packages/@aws-cdk/aws-eventschemas/package.json b/packages/@aws-cdk/aws-eventschemas/package.json index d1597e155ee9d..d8d89608e6e95 100644 --- a/packages/@aws-cdk/aws-eventschemas/package.json +++ b/packages/@aws-cdk/aws-eventschemas/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-finspace/package.json b/packages/@aws-cdk/aws-finspace/package.json index 3d0e223d1d146..7c3ae60c58506 100644 --- a/packages/@aws-cdk/aws-finspace/package.json +++ b/packages/@aws-cdk/aws-finspace/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-finspace", "module": "aws_cdk.aws_finspace" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-fis/package.json b/packages/@aws-cdk/aws-fis/package.json index a2e1bbe7845ec..614d64919c252 100644 --- a/packages/@aws-cdk/aws-fis/package.json +++ b/packages/@aws-cdk/aws-fis/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-fis", "module": "aws_cdk.aws_fis" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-fms/package.json b/packages/@aws-cdk/aws-fms/package.json index 6a018450a9b2f..1366f66c49454 100644 --- a/packages/@aws-cdk/aws-fms/package.json +++ b/packages/@aws-cdk/aws-fms/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-frauddetector/package.json b/packages/@aws-cdk/aws-frauddetector/package.json index 220eed4178f21..b30bf59153cd7 100644 --- a/packages/@aws-cdk/aws-frauddetector/package.json +++ b/packages/@aws-cdk/aws-frauddetector/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-frauddetector", "module": "aws_cdk.aws_frauddetector" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-gamelift/package.json b/packages/@aws-cdk/aws-gamelift/package.json index e1a7578e757d1..8e0aa9635f092 100644 --- a/packages/@aws-cdk/aws-gamelift/package.json +++ b/packages/@aws-cdk/aws-gamelift/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json b/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json index 4ead0ec949749..aef8ed88576af 100644 --- a/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json +++ b/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", @@ -75,7 +82,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", "aws-sdk": "^2.848.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "jest": "^27.4.7" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-glue/test/integ.job.expected.json b/packages/@aws-cdk/aws-glue/test/integ.job.expected.json index 61f4f60434db1..d50a9de59a00e 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.job.expected.json +++ b/packages/@aws-cdk/aws-glue/test/integ.job.expected.json @@ -43,6 +43,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.expected.json b/packages/@aws-cdk/aws-glue/test/integ.table.expected.json index 32fda38f59b0f..c76cbb5544660 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.expected.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.expected.json @@ -482,6 +482,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -559,6 +563,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -671,6 +679,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-glue/test/table.test.ts b/packages/@aws-cdk/aws-glue/test/table.test.ts index a7aa71474724f..e3f5df9bb6a3f 100644 --- a/packages/@aws-cdk/aws-glue/test/table.test.ts +++ b/packages/@aws-cdk/aws-glue/test/table.test.ts @@ -1281,6 +1281,10 @@ describe('grants', () => { Action: [ 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], Effect: 'Allow', @@ -1393,6 +1397,10 @@ describe('grants', () => { 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], Effect: 'Allow', diff --git a/packages/@aws-cdk/aws-greengrass/package.json b/packages/@aws-cdk/aws-greengrass/package.json index 3d9e5f4a5aeb4..d4b6a23bc436e 100644 --- a/packages/@aws-cdk/aws-greengrass/package.json +++ b/packages/@aws-cdk/aws-greengrass/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-greengrassv2/package.json b/packages/@aws-cdk/aws-greengrassv2/package.json index 9c6217595cba7..af50ddd6ab9c6 100644 --- a/packages/@aws-cdk/aws-greengrassv2/package.json +++ b/packages/@aws-cdk/aws-greengrassv2/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-groundstation/package.json b/packages/@aws-cdk/aws-groundstation/package.json index dfa3a183aed81..faf4496480b5f 100644 --- a/packages/@aws-cdk/aws-groundstation/package.json +++ b/packages/@aws-cdk/aws-groundstation/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-groundstation", "module": "aws_cdk.aws_groundstation" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-guardduty/package.json b/packages/@aws-cdk/aws-guardduty/package.json index 532e2f1554e23..e802c1d15247d 100644 --- a/packages/@aws-cdk/aws-guardduty/package.json +++ b/packages/@aws-cdk/aws-guardduty/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-healthlake/package.json b/packages/@aws-cdk/aws-healthlake/package.json index 556be6a207dd9..20fabb1ffe47f 100644 --- a/packages/@aws-cdk/aws-healthlake/package.json +++ b/packages/@aws-cdk/aws-healthlake/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-healthlake", "module": "aws_cdk.aws_healthlake" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index abe60b8a954a0..9fcef6fa79537 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -79,13 +79,12 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "jest": "^27.4.7", diff --git a/packages/@aws-cdk/aws-iam/test/access-key.test.ts b/packages/@aws-cdk/aws-iam/test/access-key.test.ts index fe54ffef2b159..c63bfa7d739c2 100644 --- a/packages/@aws-cdk/aws-iam/test/access-key.test.ts +++ b/packages/@aws-cdk/aws-iam/test/access-key.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, Stack } from '@aws-cdk/core'; import { AccessKey, AccessKeyStatus, User } from '../lib'; @@ -13,7 +13,7 @@ describe('IAM Access keys', () => { new AccessKey(stack, 'MyAccessKey', { user }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyUserDC45028B: { Type: 'AWS::IAM::User', @@ -38,7 +38,7 @@ describe('IAM Access keys', () => { new AccessKey(stack, 'MyAccessKey', { user, status: AccessKeyStatus.ACTIVE }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::AccessKey', { Status: 'Active' }); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::AccessKey', { Status: 'Active' }); }); test('inactive status is specified with correct capitalization', () => { @@ -54,7 +54,7 @@ describe('IAM Access keys', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::AccessKey', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::AccessKey', { Status: 'Inactive', }); }); diff --git a/packages/@aws-cdk/aws-iam/test/auto-cross-stack-refs.test.ts b/packages/@aws-cdk/aws-iam/test/auto-cross-stack-refs.test.ts index 1f8384c600fb9..4ede7daf57355 100644 --- a/packages/@aws-cdk/aws-iam/test/auto-cross-stack-refs.test.ts +++ b/packages/@aws-cdk/aws-iam/test/auto-cross-stack-refs.test.ts @@ -1,5 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as iam from '../lib'; @@ -25,7 +24,7 @@ describe('automatic cross-stack references', () => { // // THEN - expect(stackWithUser).toMatchTemplate({ + Template.fromStack(stackWithUser).templateMatches({ Resources: { User00B015A1: { Type: 'AWS::IAM::User', @@ -35,7 +34,7 @@ describe('automatic cross-stack references', () => { }, }, }); - expect(stackWithGroup).toMatchTemplate({ + Template.fromStack(stackWithGroup).templateMatches({ Outputs: { ExportsOutputRefGroupC77FDACD8CF7DD5B: { Value: { Ref: 'GroupC77FDACD' }, @@ -59,6 +58,6 @@ describe('automatic cross-stack references', () => { group.addUser(user); // THEN - expect(() => SynthUtils.synthesize(stack1)).toThrow(/Cannot reference across apps/); + expect(() => cdk.App.of(stack1)!.synth()).toThrow(/Cannot reference across apps/); }); }); diff --git a/packages/@aws-cdk/aws-iam/test/cross-account.test.ts b/packages/@aws-cdk/aws-iam/test/cross-account.test.ts index 21ca3ce48c945..12eef2860ce1e 100644 --- a/packages/@aws-cdk/aws-iam/test/cross-account.test.ts +++ b/packages/@aws-cdk/aws-iam/test/cross-account.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as constructs from 'constructs'; import * as iam from '../lib'; @@ -191,7 +191,7 @@ function doGrant(resource: FakeResource, principal: iam.IPrincipal) { } function assertTrustCreated(stack: cdk.Stack, principal: any) { - expect(stack).toHaveResource('Test::Fake::Resource', { + Template.fromStack(stack).hasResourceProperties('Test::Fake::Resource', { ResourcePolicy: { Statement: [ { @@ -207,17 +207,17 @@ function assertTrustCreated(stack: cdk.Stack, principal: any) { } function noTrustCreated(stack: cdk.Stack) { - expect(stack).not.toHaveResourceLike('Test::Fake::Resource', { + expect(Template.fromStack(stack).findResources('Test::Fake::Resource', { ResourcePolicy: { Statement: [ {}, ], }, - }); + })).toEqual({}); } function assertPolicyCreated(stack: cdk.Stack) { - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -232,5 +232,5 @@ function assertPolicyCreated(stack: cdk.Stack) { } function noPolicyCreated(stack: cdk.Stack) { - expect(stack).not.toHaveResource('AWS::IAM::Policy'); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); } diff --git a/packages/@aws-cdk/aws-iam/test/escape-hatch.test.ts b/packages/@aws-cdk/aws-iam/test/escape-hatch.test.ts index b9467638c7307..f22639c83c447 100644 --- a/packages/@aws-cdk/aws-iam/test/escape-hatch.test.ts +++ b/packages/@aws-cdk/aws-iam/test/escape-hatch.test.ts @@ -1,7 +1,7 @@ // tests for the L1 escape hatches (overrides). those are in the IAM module // because we want to verify them end-to-end, as a complement to the unit // tests in the @aws-cdk/core module -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import * as iam from '../lib'; @@ -17,7 +17,7 @@ describe('IAM escape hatches', () => { const cfn = user.node.findChild('Resource') as iam.CfnUser; cfn.addPropertyOverride('UserName', 'OverriddenUserName'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'user2C2B57AE': { 'Type': 'AWS::IAM::User', @@ -39,7 +39,7 @@ describe('IAM escape hatches', () => { cfn.addPropertyOverride('Hello.World', 'Boom'); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'user2C2B57AE': { 'Type': 'AWS::IAM::User', @@ -69,7 +69,7 @@ describe('IAM escape hatches', () => { cfn.addOverride('UpdatePolicy.UseOnlineResharding.Type', 'None'); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'user2C2B57AE': { 'Type': 'AWS::IAM::User', diff --git a/packages/@aws-cdk/aws-iam/test/grant.test.ts b/packages/@aws-cdk/aws-iam/test/grant.test.ts index 04b2466ea21da..1a4ca30016d6c 100644 --- a/packages/@aws-cdk/aws-iam/test/grant.test.ts +++ b/packages/@aws-cdk/aws-iam/test/grant.test.ts @@ -1,5 +1,4 @@ -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { CfnResource, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as iam from '../lib'; @@ -109,9 +108,9 @@ function applyGrantWithDependencyTo(principal: iam.IPrincipal) { } function expectDependencyOn(id: string) { - expect(stack).toHaveResource('CDK::Test::SomeResource', (props: any) => { + Template.fromStack(stack).hasResource('CDK::Test::SomeResource', (props: any) => { return (props?.DependsOn ?? []).includes(id); - }, ResourcePart.CompleteDefinition); + }); } class FakeResourceWithPolicy extends Resource implements iam.IResourceWithPolicy { diff --git a/packages/@aws-cdk/aws-iam/test/group.test.ts b/packages/@aws-cdk/aws-iam/test/group.test.ts index 6fc7129a3a1d9..781d2b2ece0b5 100644 --- a/packages/@aws-cdk/aws-iam/test/group.test.ts +++ b/packages/@aws-cdk/aws-iam/test/group.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, Stack } from '@aws-cdk/core'; import { Group, ManagedPolicy, User } from '../lib'; @@ -8,7 +8,7 @@ describe('IAM groups', () => { const stack = new Stack(app, 'MyStack'); new Group(stack, 'MyGroup'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyGroupCBA54B1B: { Type: 'AWS::IAM::Group' } }, }); }); @@ -22,7 +22,7 @@ describe('IAM groups', () => { user1.addToGroup(group); group.addUser(user2); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyGroupCBA54B1B: { Type: 'AWS::IAM::Group' }, @@ -50,7 +50,7 @@ describe('IAM groups', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Group', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Group', { ManagedPolicyArns: [ { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/asdf']] }, ], diff --git a/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts b/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts index 3ba5c870f8659..12cb38dc9542b 100644 --- a/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts +++ b/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import * as iam from '../lib'; @@ -33,7 +33,7 @@ describe('ImmutableRole', () => { immutableRole.attachInlinePolicy(policy); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ { @@ -58,7 +58,7 @@ describe('ImmutableRole', () => { immutableRole.addManagedPolicy({ managedPolicyArn: 'Arn2' }); - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { 'ManagedPolicyArns': [ 'Arn1', ], @@ -76,7 +76,7 @@ describe('ImmutableRole', () => { actions: ['s3:*'], })); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Version': '2012-10-17', 'Statement': [ @@ -98,7 +98,7 @@ describe('ImmutableRole', () => { resourceArns: ['*'], }); - expect(stack).not.toHaveResourceLike('AWS::IAM::Policy', { + expect(Template.fromStack(stack).findResources('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ { @@ -108,7 +108,7 @@ describe('ImmutableRole', () => { }, ], }, - }); + })).toEqual({}); }); // this pattern is used here: diff --git a/packages/@aws-cdk/aws-iam/test/lazy-role.test.ts b/packages/@aws-cdk/aws-iam/test/lazy-role.test.ts index 34d8919ccd14c..a21baf9fb5963 100644 --- a/packages/@aws-cdk/aws-iam/test/lazy-role.test.ts +++ b/packages/@aws-cdk/aws-iam/test/lazy-role.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as iam from '../lib'; @@ -13,7 +13,7 @@ describe('IAM lazy role', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::IAM::Role'); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 0); }); test('creates the resource when a property is read', () => { @@ -27,7 +27,7 @@ describe('IAM lazy role', () => { // THEN expect(roleArn).not.toBeNull(); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [{ diff --git a/packages/@aws-cdk/aws-iam/test/managed-policy.test.ts b/packages/@aws-cdk/aws-iam/test/managed-policy.test.ts index 9ed969b4a0afe..718915956d29b 100644 --- a/packages/@aws-cdk/aws-iam/test/managed-policy.test.ts +++ b/packages/@aws-cdk/aws-iam/test/managed-policy.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import { Group, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal, User } from '../lib'; @@ -49,7 +49,7 @@ describe('managed policy', () => { const group = new Group(stack, 'MyGroup'); group.addManagedPolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -89,7 +89,7 @@ describe('managed policy', () => { }), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -120,7 +120,7 @@ describe('managed policy', () => { statements: [new PolicyStatement({ resources: ['arn'], actions: ['sns:Subscribe'] })], }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -148,7 +148,7 @@ describe('managed policy', () => { const group = new Group(stack, 'MyGroup'); group.addManagedPolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -192,7 +192,7 @@ describe('managed policy', () => { statements: [new PolicyStatement({ resources: ['*'], actions: ['dynamodb:PutItem'] })], }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { User1E278A736: { Type: 'AWS::IAM::User' }, Group1BEBD4686: { Type: 'AWS::IAM::Group' }, @@ -248,7 +248,7 @@ describe('managed policy', () => { p.attachToRole(role); p.attachToRole(role); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -295,7 +295,7 @@ describe('managed policy', () => { p.attachToRole(new Role(stack, 'Role1', { assumedBy: new ServicePrincipal('test.service') })); p.addStatements(new PolicyStatement({ resources: ['*'], actions: ['dynamodb:GetItem'] })); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -346,7 +346,7 @@ describe('managed policy', () => { policy.addStatements(new PolicyStatement({ resources: ['*'], actions: ['*'] })); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyManagedPolicy9F3720AE: { Type: 'AWS::IAM::ManagedPolicy', @@ -390,7 +390,7 @@ describe('managed policy', () => { group.addManagedPolicy(policy); role.addManagedPolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyUserDC45028B: { Type: 'AWS::IAM::User', @@ -466,7 +466,7 @@ describe('managed policy', () => { group.addManagedPolicy(policy); role.addManagedPolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyUserDC45028B: { Type: 'AWS::IAM::User', @@ -594,7 +594,7 @@ describe('managed policy', () => { value: mp.managedPolicyArn, }); - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Outputs: { Output: { Value: { diff --git a/packages/@aws-cdk/aws-iam/test/oidc-provider.test.ts b/packages/@aws-cdk/aws-iam/test/oidc-provider.test.ts index 805e7bbb66ac0..eda196a97aa5f 100644 --- a/packages/@aws-cdk/aws-iam/test/oidc-provider.test.ts +++ b/packages/@aws-cdk/aws-iam/test/oidc-provider.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, Stack, Token } from '@aws-cdk/core'; import * as sinon from 'sinon'; import * as iam from '../lib'; @@ -20,7 +20,7 @@ describe('OpenIdConnectProvider resource', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDKOpenIdConnectProvider', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDKOpenIdConnectProvider', { Url: 'https://openid-endpoint', }); }); @@ -61,7 +61,7 @@ describe('OpenIdConnectProvider resource', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWSCDKOpenIdConnectProvider', { + Template.fromStack(stack).hasResourceProperties('Custom::AWSCDKOpenIdConnectProvider', { Url: 'https://my-url', ClientIDList: ['client1', 'client2'], ThumbprintList: ['thumb1'], @@ -103,7 +103,7 @@ describe('custom resource provider infrastructure', () => { new iam.OpenIdConnectProvider(stack, 'Provider1', { url: 'provider1' }); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { Policies: [ { PolicyName: 'Inline', diff --git a/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts b/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts index ab304f3481a15..b30137798074f 100644 --- a/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts +++ b/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts @@ -1,6 +1,5 @@ import * as path from 'path'; -import { ABSENT } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { App, CfnResource, CustomResourceProvider, CustomResourceProviderRuntime, Stack } from '@aws-cdk/core'; import * as iam from '../lib'; @@ -21,7 +20,7 @@ test('apply imported boundary to a role', () => { iam.PermissionsBoundary.of(role).apply(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess')); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { PermissionsBoundary: { 'Fn::Join': ['', [ 'arn:', @@ -40,7 +39,7 @@ test('apply imported boundary to a user', () => { iam.PermissionsBoundary.of(user).apply(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess')); // THEN - expect(stack).toHaveResource('AWS::IAM::User', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::User', { PermissionsBoundary: { 'Fn::Join': ['', [ 'arn:', @@ -68,7 +67,7 @@ test('apply newly created boundary to a role', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { PermissionsBoundary: { Ref: 'Policy23B91518' }, }); }); @@ -91,7 +90,7 @@ test('apply boundary to role created by a custom resource', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { PermissionsBoundary: { Ref: 'Policy23B91518' }, }); }); @@ -113,7 +112,7 @@ test('apply boundary to users created via CfnResource', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::User', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::User', { PermissionsBoundary: { Ref: 'Policy23B91518' }, }); }); @@ -135,7 +134,7 @@ test('apply boundary to roles created via CfnResource', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { PermissionsBoundary: { Ref: 'Policy23B91518' }, }); }); @@ -149,8 +148,8 @@ test('unapply inherited boundary from a user: order 1', () => { iam.PermissionsBoundary.of(user).clear(); // THEN - expect(stack).toHaveResource('AWS::IAM::User', { - PermissionsBoundary: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::IAM::User', { + PermissionsBoundary: Match.absent(), }); }); @@ -163,7 +162,7 @@ test('unapply inherited boundary from a user: order 2', () => { iam.PermissionsBoundary.of(stack).apply(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess')); // THEN - expect(stack).toHaveResource('AWS::IAM::User', { - PermissionsBoundary: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::IAM::User', { + PermissionsBoundary: Match.absent(), }); }); diff --git a/packages/@aws-cdk/aws-iam/test/policy-document.test.ts b/packages/@aws-cdk/aws-iam/test/policy-document.test.ts index 16a9904087647..ca759d438ab89 100644 --- a/packages/@aws-cdk/aws-iam/test/policy-document.test.ts +++ b/packages/@aws-cdk/aws-iam/test/policy-document.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Lazy, Stack, Token } from '@aws-cdk/core'; import { @@ -492,7 +492,7 @@ describe('IAM policy document', () => { ), }); - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -568,7 +568,7 @@ describe('IAM policy document', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts b/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts index 09b6e30b1e4c0..7498ee2814d80 100644 --- a/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts +++ b/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import { Stack } from '@aws-cdk/core'; import { AnyPrincipal, Group, PolicyDocument, PolicyStatement } from '../lib'; diff --git a/packages/@aws-cdk/aws-iam/test/policy.test.ts b/packages/@aws-cdk/aws-iam/test/policy.test.ts index cb6c2fc88cf52..ea40450756935 100644 --- a/packages/@aws-cdk/aws-iam/test/policy.test.ts +++ b/packages/@aws-cdk/aws-iam/test/policy.test.ts @@ -1,5 +1,4 @@ -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, CfnResource, Stack } from '@aws-cdk/core'; import { AnyPrincipal, CfnPolicy, Group, Policy, PolicyDocument, PolicyStatement, Role, ServicePrincipal, User } from '../lib'; @@ -28,7 +27,7 @@ describe('IAM policy', () => { const group = new Group(stack, 'MyGroup'); group.attachInlinePolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyPolicy39D66CF6: @@ -69,7 +68,7 @@ describe('IAM policy', () => { const group = new Group(stack, 'MyGroup'); group.attachInlinePolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyPolicy39D66CF6: { Type: 'AWS::IAM::Policy', @@ -96,7 +95,7 @@ describe('IAM policy', () => { const user = new User(stack, 'MyUser'); user.attachInlinePolicy(policy); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyPolicy39D66CF6: @@ -135,7 +134,7 @@ describe('IAM policy', () => { statements: [new PolicyStatement({ resources: ['*'], actions: ['dynamodb:PutItem'] })], }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { User1E278A736: { Type: 'AWS::IAM::User' }, @@ -186,7 +185,7 @@ describe('IAM policy', () => { p.attachToUser(user); p.attachToUser(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyPolicy39D66CF6: @@ -219,7 +218,7 @@ describe('IAM policy', () => { p.attachToRole(new Role(stack, 'Role1', { assumedBy: new ServicePrincipal('test.service') })); p.addStatements(new PolicyStatement({ resources: ['*'], actions: ['dynamodb:GetItem'] })); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyTestPolicy316BDB50: @@ -275,7 +274,7 @@ describe('IAM policy', () => { policy.addStatements(new PolicyStatement({ resources: ['*'], actions: ['*'] })); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyPolicy39D66CF6: @@ -349,7 +348,7 @@ describe('IAM policy', () => { test("generated policy name is the same as the logical id if it's shorter than 128 characters", () => { createPolicyWithLogicalId(stack, 'Foo'); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyName': 'Foo', }); }); @@ -360,7 +359,7 @@ describe('IAM policy', () => { createPolicyWithLogicalId(stack, logicalIdOver128); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyName': logicalId128, }); @@ -385,7 +384,7 @@ describe('IAM policy', () => { res.node.addDependency(pol); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { Resource: { Type: 'Some::Resource', @@ -411,10 +410,10 @@ describe('IAM policy', () => { res.node.addDependency(pol); // THEN - expect(stack).toHaveResource('Some::Resource', { + Template.fromStack(stack).hasResource('Some::Resource', { Type: 'Some::Resource', DependsOn: ['Pol0FE9AD5D'], - }, ResourcePart.CompleteDefinition); + }); }); test('empty policy is OK if force=false', () => { diff --git a/packages/@aws-cdk/aws-iam/test/principals.test.ts b/packages/@aws-cdk/aws-iam/test/principals.test.ts index fdbeaa0dc87d4..7a07a50e80fb9 100644 --- a/packages/@aws-cdk/aws-iam/test/principals.test.ts +++ b/packages/@aws-cdk/aws-iam/test/principals.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import { Template } from '@aws-cdk/assertions'; import { App, CfnOutput, Stack } from '@aws-cdk/core'; import * as iam from '../lib'; @@ -21,7 +20,7 @@ test('use of cross-stack role reference does not lead to URLSuffix being exporte // THEN app.synth(); - expect(first).toMatchTemplate({ + Template.fromStack(first).templateMatches({ Resources: { Role1ABCC5F0: { Type: 'AWS::IAM::Role', @@ -145,7 +144,7 @@ test('SAML principal', () => { // THEN expect(stack.resolve(principal.federated)).toStrictEqual({ Ref: 'MyProvider730BA1C8' }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -214,7 +213,7 @@ test('PrincipalWithConditions.addCondition should work', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -277,7 +276,7 @@ test('Can enable session tags', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-iam/test/role.from-role-arn.test.ts b/packages/@aws-cdk/aws-iam/test/role.from-role-arn.test.ts index 68e36b388fa0f..d03dd908370da 100644 --- a/packages/@aws-cdk/aws-iam/test/role.from-role-arn.test.ts +++ b/packages/@aws-cdk/aws-iam/test/role.from-role-arn.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, Aws, CfnElement, Lazy, Stack } from '@aws-cdk/core'; import { AnyPrincipal, ArnPrincipal, IRole, Policy, PolicyStatement, Role } from '../lib'; @@ -196,7 +196,7 @@ describe('IAM Role.fromRoleArn', () => { }); test("does NOT generate a default Policy resource pointing at the imported role's physical name", () => { - expect(roleStack).not.toHaveResourceLike('AWS::IAM::Policy'); + Template.fromStack(roleStack).resourceCountIs('AWS::IAM::Policy', 0); }); }); @@ -283,7 +283,7 @@ describe('IAM Role.fromRoleArn', () => { }); test("does NOT generate a default Policy resource pointing at the imported role's physical name", () => { - expect(roleStack).not.toHaveResourceLike('AWS::IAM::Policy'); + Template.fromStack(roleStack).resourceCountIs('AWS::IAM::Policy', 0); }); }); @@ -322,7 +322,7 @@ describe('IAM Role.fromRoleArn', () => { }); test("does NOT generate a default Policy resource pointing at the imported role's physical name", () => { - expect(roleStack).not.toHaveResourceLike('AWS::IAM::Policy'); + Template.fromStack(roleStack).resourceCountIs('AWS::IAM::Policy', 0); }); }); }); @@ -357,7 +357,7 @@ describe('IAM Role.fromRoleArn', () => { assumedBy: new ArnPrincipal(importedRole.roleName), }); - expect(roleStack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(roleStack).hasResourceProperties('AWS::IAM::Role', { 'AssumeRolePolicyDocument': { 'Statement': [ { @@ -515,7 +515,7 @@ describe('IAM Role.fromRoleArn', () => { roles: [importedRole], }); - expect(roleStack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(roleStack).hasResourceProperties('AWS::IAM::Policy', { 'Roles': [ 'codebuild-role', ], @@ -535,7 +535,7 @@ describe('IAM Role.fromRoleArn', () => { roles: [importedRole], }); - expect(roleStack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(roleStack).hasResourceProperties('AWS::IAM::Policy', { 'Roles': [ 'codebuild-role', ], @@ -611,5 +611,5 @@ function _assertStackContainsPolicyResource(stack: Stack, roleNames: any[], name expected.PolicyName = nameOfPolicy; } - expect(stack).toHaveResourceLike('AWS::IAM::Policy', expected); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', expected); } diff --git a/packages/@aws-cdk/aws-iam/test/role.test.ts b/packages/@aws-cdk/aws-iam/test/role.test.ts index ffa1ccd305c58..e0927e14b80ad 100644 --- a/packages/@aws-cdk/aws-iam/test/role.test.ts +++ b/packages/@aws-cdk/aws-iam/test/role.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Duration, Stack, App } from '@aws-cdk/core'; import { AnyPrincipal, ArnPrincipal, CompositePrincipal, FederatedPrincipal, ManagedPolicy, PolicyStatement, Role, ServicePrincipal, User, Policy, PolicyDocument } from '../lib'; @@ -11,7 +11,7 @@ describe('IAM role', () => { assumedBy: new ServicePrincipal('sns.amazonaws.com'), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyRoleF48FFE04: @@ -45,7 +45,7 @@ describe('IAM role', () => { role.grantPassRole(user); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -70,7 +70,7 @@ describe('IAM role', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -98,7 +98,7 @@ describe('IAM role', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -126,7 +126,7 @@ describe('IAM role', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -147,13 +147,13 @@ describe('IAM role', () => { // by default we don't expect a role policy const before = new Stack(); new Role(before, 'MyRole', { assumedBy: new ServicePrincipal('sns.amazonaws.com') }); - expect(before).not.toHaveResource('AWS::IAM::Policy'); + Template.fromStack(before).resourceCountIs('AWS::IAM::Policy', 0); // add a policy to the role const after = new Stack(); const afterRole = new Role(after, 'MyRole', { assumedBy: new ServicePrincipal('sns.amazonaws.com') }); afterRole.addToPolicy(new PolicyStatement({ resources: ['myresource'], actions: ['service:myaction'] })); - expect(after).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(after).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -183,7 +183,7 @@ describe('IAM role', () => { }); role.addManagedPolicy({ managedPolicyArn: 'managed3' }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyRoleF48FFE04: @@ -218,7 +218,7 @@ describe('IAM role', () => { new Role(stack, 'MyRole', { assumedBy: cognitoPrincipal }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [ @@ -240,7 +240,7 @@ describe('IAM role', () => { test('is not specified by default', () => { const stack = new Stack(); new Role(stack, 'MyRole', { assumedBy: new ServicePrincipal('sns.amazonaws.com') }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyRoleF48FFE04: { Type: 'AWS::IAM::Role', @@ -268,7 +268,7 @@ describe('IAM role', () => { new Role(stack, 'MyRole', { maxSessionDuration: Duration.seconds(3700), assumedBy: new ServicePrincipal('sns.amazonaws.com') }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { MaxSessionDuration: 3700, }); }); @@ -301,7 +301,7 @@ describe('IAM role', () => { ), }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -335,7 +335,7 @@ describe('IAM role', () => { permissionsBoundary, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { PermissionsBoundary: { 'Fn::Join': [ '', @@ -363,7 +363,7 @@ describe('IAM role', () => { assumedBy: new AnyPrincipal(), }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -385,7 +385,7 @@ describe('IAM role', () => { description: 'This is a role description.', }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyRoleF48FFE04: @@ -418,7 +418,7 @@ describe('IAM role', () => { description: '', }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyRoleF48FFE04: @@ -555,7 +555,7 @@ test('managed policy ARNs are deduplicated', () => { }); role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('SuperDeveloper')); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { ManagedPolicyArns: [ { 'Fn::Join': [ diff --git a/packages/@aws-cdk/aws-iam/test/saml-provider.test.ts b/packages/@aws-cdk/aws-iam/test/saml-provider.test.ts index 83ade3741fb3a..edcbde4685296 100644 --- a/packages/@aws-cdk/aws-iam/test/saml-provider.test.ts +++ b/packages/@aws-cdk/aws-iam/test/saml-provider.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { SamlMetadataDocument, SamlProvider } from '../lib'; @@ -12,7 +12,7 @@ test('SAML provider', () => { metadataDocument: SamlMetadataDocument.fromXml('document'), }); - expect(stack).toHaveResource('AWS::IAM::SAMLProvider', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::SAMLProvider', { SamlMetadataDocument: 'document', }); }); @@ -23,7 +23,7 @@ test('SAML provider name', () => { name: 'provider-name', }); - expect(stack).toHaveResource('AWS::IAM::SAMLProvider', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::SAMLProvider', { SamlMetadataDocument: 'document', Name: 'provider-name', }); diff --git a/packages/@aws-cdk/aws-iam/test/user.test.ts b/packages/@aws-cdk/aws-iam/test/user.test.ts index d7435b08cbf24..83e610e43a51b 100644 --- a/packages/@aws-cdk/aws-iam/test/user.test.ts +++ b/packages/@aws-cdk/aws-iam/test/user.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, SecretValue, Stack, Token } from '@aws-cdk/core'; import { Group, ManagedPolicy, Policy, PolicyStatement, User } from '../lib'; @@ -7,7 +7,7 @@ describe('IAM user', () => { const app = new App(); const stack = new Stack(app, 'MyStack'); new User(stack, 'MyUser'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyUserDC45028B: { Type: 'AWS::IAM::User' } }, }); }); @@ -19,7 +19,7 @@ describe('IAM user', () => { password: SecretValue.plainText('1234'), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyUserDC45028B: @@ -48,7 +48,7 @@ describe('IAM user', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::User', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::User', { ManagedPolicyArns: [ { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/asdf']] }, ], @@ -65,7 +65,7 @@ describe('IAM user', () => { permissionsBoundary, }); - expect(stack).toHaveResource('AWS::IAM::User', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::User', { PermissionsBoundary: { 'Fn::Join': [ '', @@ -212,7 +212,7 @@ describe('IAM user', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { Users: ['john'], PolicyDocument: { Statement: [ @@ -243,7 +243,7 @@ describe('IAM user', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { Users: ['john'], PolicyDocument: { Statement: [ @@ -270,7 +270,7 @@ describe('IAM user', () => { otherGroup.addUser(user); // THEN - expect(stack).toHaveResource('AWS::IAM::UserToGroupAddition', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::UserToGroupAddition', { GroupName: { Ref: 'GroupC77FDACD', }, @@ -279,7 +279,7 @@ describe('IAM user', () => { ], }); - expect(stack).toHaveResource('AWS::IAM::UserToGroupAddition', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::UserToGroupAddition', { GroupName: { Ref: 'OtherGroup85E5C653', }, diff --git a/packages/@aws-cdk/aws-imagebuilder/package.json b/packages/@aws-cdk/aws-imagebuilder/package.json index 4a03d80bcc982..bacd907148a73 100644 --- a/packages/@aws-cdk/aws-imagebuilder/package.json +++ b/packages/@aws-cdk/aws-imagebuilder/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-inspector/package.json b/packages/@aws-cdk/aws-inspector/package.json index b237844ea1221..5bdb7a30c188c 100644 --- a/packages/@aws-cdk/aws-inspector/package.json +++ b/packages/@aws-cdk/aws-inspector/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index be475fe028210..860643683efd1 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -21,6 +21,7 @@ supported AWS Services. Instances of these classes should be passed to Currently supported are: +- Republish a message to another MQTT topic - Invoke a Lambda function - Put objects to a S3 bucket - Put logs to CloudWatch Logs @@ -30,6 +31,22 @@ Currently supported are: - Put records to Kinesis Data Firehose stream - Send messages to SQS queues +## Republish a message to another MQTT topic + +The code snippet below creates an AWS IoT Rule that republish a message to +another MQTT topic when it is triggered. + +```ts +new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id, timestamp() as timestamp, temperature FROM 'device/+/data'"), + actions: [ + new actions.IotRepublishMqttAction('${topic()}/republish', { + qualityOfService: actions.MqttQualityOfService.AT_LEAST_ONCE, // optional property, default is MqttQualityOfService.ZERO_OR_MORE_TIMES + }), + ], +}); +``` + ## Invoke a Lambda function The code snippet below creates an AWS IoT Rule that invoke a Lambda function diff --git a/packages/@aws-cdk/aws-iot-actions/lib/index.ts b/packages/@aws-cdk/aws-iot-actions/lib/index.ts index a0ee864a5bce2..c3a7bb547b1c8 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/index.ts @@ -3,6 +3,7 @@ export * from './cloudwatch-put-metric-action'; export * from './cloudwatch-set-alarm-state-action'; export * from './common-action-props'; export * from './firehose-put-record-action'; +export * from './iot-republish-action'; export * from './kinesis-put-record-action'; export * from './lambda-function-action'; export * from './s3-put-object-action'; diff --git a/packages/@aws-cdk/aws-iot-actions/lib/iot-republish-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/iot-republish-action.ts new file mode 100644 index 0000000000000..77aadb876c4d9 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/iot-republish-action.ts @@ -0,0 +1,72 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import { CommonActionProps } from './common-action-props'; +import { singletonActionRole } from './private/role'; + +/** + * MQTT Quality of Service (QoS) indicates the level of assurance for delivery of an MQTT Message. + * + * @see https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html#mqtt-qos + */ +export enum MqttQualityOfService { + /** + * QoS level 0. Sent zero or more times. + * This level should be used for messages that are sent over reliable communication links or that can be missed without a problem. + */ + ZERO_OR_MORE_TIMES, + + /** + * QoS level 1. Sent at least one time, and then repeatedly until a PUBACK response is received. + * The message is not considered complete until the sender receives a PUBACK response to indicate successful delivery. + */ + AT_LEAST_ONCE, +} + +/** + * Configuration properties of an action to republish MQTT messages. + */ +export interface IotRepublishMqttActionProps extends CommonActionProps { + /** + * The Quality of Service (QoS) level to use when republishing messages. + * + * @see https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html#mqtt-qos + * + * @default MqttQualityOfService.ZERO_OR_MORE_TIMES + */ + readonly qualityOfService?: MqttQualityOfService; +} + +/** + * The action to put the record from an MQTT message to republish another MQTT topic. + */ +export class IotRepublishMqttAction implements iot.IAction { + private readonly qualityOfService?: MqttQualityOfService; + private readonly role?: iam.IRole; + + /** + * @param topic The MQTT topic to which to republish the message. + * @param props Optional properties to not use default. + */ + constructor(private readonly topic: string, props: IotRepublishMqttActionProps = {}) { + this.qualityOfService = props.qualityOfService; + this.role = props.role; + } + + bind(rule: iot.ITopicRule): iot.ActionConfig { + const role = this.role ?? singletonActionRole(rule); + role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['iot:Publish'], + resources: ['*'], + })); + + return { + configuration: { + republish: { + topic: this.topic, + qos: this.qualityOfService, + roleArn: role.roleArn, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-iot-actions/test/iot/integ.iot-republish-action.expected.json b/packages/@aws-cdk/aws-iot-actions/test/iot/integ.iot-republish-action.expected.json new file mode 100644 index 0000000000000..c396017676ac4 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/iot/integ.iot-republish-action.expected.json @@ -0,0 +1,65 @@ +{ + "Resources": { + "TopicRule40A4EA44": { + "Type": "AWS::IoT::TopicRule", + "Properties": { + "TopicRulePayload": { + "Actions": [ + { + "Republish": { + "Qos": 1, + "RoleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + }, + "Topic": "${topic()}/republish" + } + } + ], + "AwsIotSqlVersion": "2016-03-23", + "Sql": "SELECT * FROM 'device/+/data'" + } + } + }, + "TopicRuleTopicRuleActionRole246C4F77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "iot:Publish", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "Roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/iot/integ.iot-republish-action.ts b/packages/@aws-cdk/aws-iot-actions/test/iot/integ.iot-republish-action.ts new file mode 100644 index 0000000000000..d5a71b32f6a0d --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/iot/integ.iot-republish-action.ts @@ -0,0 +1,25 @@ +import * as iot from '@aws-cdk/aws-iot'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const topicRule = new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT * FROM 'device/+/data'", + ), + }); + + topicRule.addAction( + new actions.IotRepublishMqttAction('${topic()}/republish', { + qualityOfService: actions.MqttQualityOfService.AT_LEAST_ONCE, + }), + ); + } +} + +const app = new cdk.App(); +new TestStack(app, 'iot-republish-action-test-stack'); +app.synth(); diff --git a/packages/@aws-cdk/aws-iot-actions/test/iot/iot-republish-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/iot/iot-republish-action.test.ts new file mode 100644 index 0000000000000..8a78e272285ef --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/iot/iot-republish-action.test.ts @@ -0,0 +1,107 @@ +import { Template, Match } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +let stack: cdk.Stack; +let topicRule:iot.TopicRule; +beforeEach(() => { + stack = new cdk.Stack(); + topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); +}); + +test('Default IoT republish action', () => { + // WHEN + topicRule.addAction( + new actions.IotRepublishMqttAction('test-topic'), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + Republish: { + Topic: 'test-topic', + RoleArn: { + 'Fn::GetAtt': ['MyTopicRuleTopicRuleActionRoleCE2D05DA', 'Arn'], + }, + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'iot.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'iot:Publish', + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyTopicRuleTopicRuleActionRoleDefaultPolicy54A701F7', + Roles: [ + { Ref: 'MyTopicRuleTopicRuleActionRoleCE2D05DA' }, + ], + }); +}); + +test('can set qualityOfService', () => { + // WHEN + topicRule.addAction( + new actions.IotRepublishMqttAction('test-topic', { qualityOfService: actions.MqttQualityOfService.AT_LEAST_ONCE }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ Republish: { Qos: 1 } }), + ], + }, + }); +}); + +test('can set role', () => { + // WHEN + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); + topicRule.addAction( + new actions.IotRepublishMqttAction('test-topic', { role }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ Republish: { RoleArn: 'arn:aws:iam::123456789012:role/ForTest' } }), + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyName: 'MyRolePolicy64AB00A5', + Roles: ['ForTest'], + }); +}); diff --git a/packages/@aws-cdk/aws-iot-actions/test/kinesis-firehose/integ.firehose-put-record-action.expected.json b/packages/@aws-cdk/aws-iot-actions/test/kinesis-firehose/integ.firehose-put-record-action.expected.json index d1565669b5c04..5c484ba7f5049 100644 --- a/packages/@aws-cdk/aws-iot-actions/test/kinesis-firehose/integ.firehose-put-record-action.expected.json +++ b/packages/@aws-cdk/aws-iot-actions/test/kinesis-firehose/integ.firehose-put-record-action.expected.json @@ -123,6 +123,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-iot1click/package.json b/packages/@aws-cdk/aws-iot1click/package.json index 8c3bf4dbdfa02..656dee749a698 100644 --- a/packages/@aws-cdk/aws-iot1click/package.json +++ b/packages/@aws-cdk/aws-iot1click/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-iotanalytics/package.json b/packages/@aws-cdk/aws-iotanalytics/package.json index 69865e7d5cf54..534ffed3675a9 100644 --- a/packages/@aws-cdk/aws-iotanalytics/package.json +++ b/packages/@aws-cdk/aws-iotanalytics/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json b/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json index 4fb4501c9beef..ecb59fded3ba8 100644 --- a/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.IoTCoreDeviceAdvisor", diff --git a/packages/@aws-cdk/aws-iotevents/README.md b/packages/@aws-cdk/aws-iotevents/README.md index 6dc6a681636cc..760b6098d9d41 100644 --- a/packages/@aws-cdk/aws-iotevents/README.md +++ b/packages/@aws-cdk/aws-iotevents/README.md @@ -40,15 +40,33 @@ Import it into your code: import * as iotevents from '@aws-cdk/aws-iotevents'; ``` -## `Input` +## `DetectorModel` -Add an AWS IoT Events input to your stack: +The following example creates an AWS IoT Events detector model to your stack. +The detector model need a reference to at least one AWS IoT Events input. +AWS IoT Events inputs enable the detector to get MQTT payload values from IoT Core rules. ```ts import * as iotevents from '@aws-cdk/aws-iotevents'; -new iotevents.Input(this, 'MyInput', { - inputName: 'my_input', - attributeJsonPaths: ['payload.temperature'], +const input = new iotevents.Input(this, 'MyInput', { + inputName: 'my_input', // optional + attributeJsonPaths: ['payload.deviceId', 'payload.temperature'], +}); + +const onlineState = new iotevents.State({ + stateName: 'online', + onEnter: [{ + eventName: 'test-event', + condition: iotevents.Expression.currentInput(input), + }], +}); + +new iotevents.DetectorModel(this, 'MyDetectorModel', { + detectorModelName: 'test-detector-model', // optional + description: 'test-detector-model-description', // optional property, default is none + evaluationMethod: iotevents.EventEvaluation.SERIAL, // optional property, default is iotevents.EventEvaluation.BATCH + detectorKey: 'payload.deviceId', // optional property, default is none and single detector instance will be created and all inputs will be routed to it + initialState: onlineState, }); ``` diff --git a/packages/@aws-cdk/aws-iotevents/lib/detector-model.ts b/packages/@aws-cdk/aws-iotevents/lib/detector-model.ts new file mode 100644 index 0000000000000..5ef50fd871d75 --- /dev/null +++ b/packages/@aws-cdk/aws-iotevents/lib/detector-model.ts @@ -0,0 +1,133 @@ +import * as iam from '@aws-cdk/aws-iam'; +import { Resource, IResource } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnDetectorModel } from './iotevents.generated'; +import { State } from './state'; + +/** + * Represents an AWS IoT Events detector model + */ +export interface IDetectorModel extends IResource { + /** + * The name of the detector model. + * + * @attribute + */ + readonly detectorModelName: string; +} + +/** + * Information about the order in which events are evaluated and how actions are executed. + */ +export enum EventEvaluation { + /** + * When setting to SERIAL, variables are updated and event conditions are evaluated in the order + * that the events are defined. + */ + BATCH = 'BATCH', + /** + * When setting to BATCH, variables within a state are updated and events within a state are + * performed only after all event conditions are evaluated. + */ + SERIAL = 'SERIAL', +} + +/** + * Properties for defining an AWS IoT Events detector model + */ +export interface DetectorModelProps { + /** + * The name of the detector model. + * + * @default - CloudFormation will generate a unique name of the detector model + */ + readonly detectorModelName?: string; + + /** + * A brief description of the detector model. + * + * @default none + */ + readonly description?: string; + + /** + * Information about the order in which events are evaluated and how actions are executed. + * + * When setting to SERIAL, variables are updated and event conditions are evaluated in the order + * that the events are defined. + * When setting to BATCH, variables within a state are updated and events within a state are + * performed only after all event conditions are evaluated. + * + * @default EventEvaluation.BATCH + */ + readonly evaluationMethod?: EventEvaluation; + + /** + * The value used to identify a detector instance. When a device or system sends input, a new + * detector instance with a unique key value is created. AWS IoT Events can continue to route + * input to its corresponding detector instance based on this identifying information. + * + * This parameter uses a JSON-path expression to select the attribute-value pair in the message + * payload that is used for identification. To route the message to the correct detector instance, + * the device must send a message payload that contains the same attribute-value. + * + * @default - none (single detector instance will be created and all inputs will be routed to it) + */ + readonly detectorKey?: string; + + /** + * The state that is entered at the creation of each detector. + */ + readonly initialState: State; + + /** + * The role that grants permission to AWS IoT Events to perform its operations. + * + * @default - a role will be created with default permissions + */ + readonly role?: iam.IRole; +} + +/** + * Defines an AWS IoT Events detector model in this stack. + */ +export class DetectorModel extends Resource implements IDetectorModel { + /** + * Import an existing detector model. + */ + public static fromDetectorModelName(scope: Construct, id: string, detectorModelName: string): IDetectorModel { + return new class extends Resource implements IDetectorModel { + public readonly detectorModelName = detectorModelName; + }(scope, id); + } + + public readonly detectorModelName: string; + + constructor(scope: Construct, id: string, props: DetectorModelProps) { + super(scope, id, { + physicalName: props.detectorModelName, + }); + + if (!props.initialState._onEnterEventsHaveAtLeastOneCondition()) { + throw new Error('Detector Model must have at least one Input with a condition'); + } + + const role = props.role ?? new iam.Role(this, 'DetectorModelRole', { + assumedBy: new iam.ServicePrincipal('iotevents.amazonaws.com'), + }); + + const resource = new CfnDetectorModel(this, 'Resource', { + detectorModelName: this.physicalName, + detectorModelDescription: props.description, + evaluationMethod: props.evaluationMethod, + key: props.detectorKey, + detectorModelDefinition: { + initialStateName: props.initialState.stateName, + states: [props.initialState._toStateJson()], + }, + roleArn: role.roleArn, + }); + + this.detectorModelName = this.getResourceNameAttribute(resource.ref); + } +} diff --git a/packages/@aws-cdk/aws-iotevents/lib/event.ts b/packages/@aws-cdk/aws-iotevents/lib/event.ts new file mode 100644 index 0000000000000..610469db9c32c --- /dev/null +++ b/packages/@aws-cdk/aws-iotevents/lib/event.ts @@ -0,0 +1,18 @@ +import { Expression } from './expression'; + +/** + * Specifies the actions to be performed when the condition evaluates to TRUE. + */ +export interface Event { + /** + * The name of the event. + */ + readonly eventName: string; + + /** + * The Boolean expression that, when TRUE, causes the actions to be performed. + * + * @default - none (the actions are always executed) + */ + readonly condition?: Expression; +} diff --git a/packages/@aws-cdk/aws-iotevents/lib/expression.ts b/packages/@aws-cdk/aws-iotevents/lib/expression.ts new file mode 100644 index 0000000000000..27fdf069c1b9f --- /dev/null +++ b/packages/@aws-cdk/aws-iotevents/lib/expression.ts @@ -0,0 +1,75 @@ +import { IInput } from './input'; + +/** + * Expression for events in Detector Model state + * @see https://docs.aws.amazon.com/iotevents/latest/developerguide/iotevents-expressions.html + */ +export abstract class Expression { + /** + * Create a expression from the given string + */ + public static fromString(value: string): Expression { + return new StringExpression(value); + } + + /** + * Create a expression for function `currentInput()`. + * It is evaluated to true if the specified input message was received. + */ + public static currentInput(input: IInput): Expression { + return this.fromString(`currentInput("${input.inputName}")`); + } + + /** + * Create a expression for get an input attribute as `$input.TemperatureInput.temperatures[2]`. + */ + public static inputAttribute(input: IInput, path: string): Expression { + return this.fromString(`$input.${input.inputName}.${path}`); + } + + /** + * Create a expression for the Equal operator + */ + public static eq(left: Expression, right: Expression): Expression { + return new BinaryOperationExpression(left, '==', right); + } + + /** + * Create a expression for the AND operator + */ + public static and(left: Expression, right: Expression): Expression { + return new BinaryOperationExpression(left, '&&', right); + } + + constructor() { + } + + /** + * this is called to evaluate the expression + */ + public abstract evaluate(): string; +} + +class StringExpression extends Expression { + constructor(private readonly value: string) { + super(); + } + + public evaluate() { + return this.value; + } +} + +class BinaryOperationExpression extends Expression { + constructor( + private readonly left: Expression, + private readonly operator: string, + private readonly right: Expression, + ) { + super(); + } + + public evaluate() { + return `${this.left.evaluate()} ${this.operator} ${this.right.evaluate()}`; + } +} diff --git a/packages/@aws-cdk/aws-iotevents/lib/index.ts b/packages/@aws-cdk/aws-iotevents/lib/index.ts index 3851e30984391..24913635ebe50 100644 --- a/packages/@aws-cdk/aws-iotevents/lib/index.ts +++ b/packages/@aws-cdk/aws-iotevents/lib/index.ts @@ -1,4 +1,8 @@ +export * from './detector-model'; +export * from './event'; +export * from './expression'; export * from './input'; +export * from './state'; // AWS::IoTEvents CloudFormation Resources: export * from './iotevents.generated'; diff --git a/packages/@aws-cdk/aws-iotevents/lib/state.ts b/packages/@aws-cdk/aws-iotevents/lib/state.ts new file mode 100644 index 0000000000000..e16d911d60004 --- /dev/null +++ b/packages/@aws-cdk/aws-iotevents/lib/state.ts @@ -0,0 +1,65 @@ +import { Event } from './event'; +import { CfnDetectorModel } from './iotevents.generated'; + +/** + * Properties for defining a state of a detector + */ +export interface StateProps { + /** + * The name of the state. + */ + readonly stateName: string; + + /** + * Specifies the events on enter. the conditions of the events are evaluated when the state is entered. + * If the condition is `TRUE`, the actions of the event are performed. + * + * @default - events on enter will not be set + */ + readonly onEnter?: Event[]; +} + +/** + * Defines a state of a detector + */ +export class State { + /** + * The name of the state + */ + public readonly stateName: string; + + constructor(private readonly props: StateProps) { + this.stateName = props.stateName; + } + + /** + * Return the state property JSON + * + * @internal + */ + public _toStateJson(): CfnDetectorModel.StateProperty { + const { stateName, onEnter } = this.props; + return { + stateName, + onEnter: onEnter && { events: getEventJson(onEnter) }, + }; + } + + /** + * returns true if this state has at least one condition via events + * + * @internal + */ + public _onEnterEventsHaveAtLeastOneCondition(): boolean { + return this.props.onEnter?.some(event => event.condition) ?? false; + } +} + +function getEventJson(events: Event[]): CfnDetectorModel.EventProperty[] { + return events.map(e => { + return { + eventName: e.eventName, + condition: e.condition?.evaluate(), + }; + }); +} diff --git a/packages/@aws-cdk/aws-iotevents/package.json b/packages/@aws-cdk/aws-iotevents/package.json index 339b7d938a853..37c2035b4378f 100644 --- a/packages/@aws-cdk/aws-iotevents/package.json +++ b/packages/@aws-cdk/aws-iotevents/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", @@ -83,10 +90,12 @@ "jest": "^27.4.7" }, "dependencies": { + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, "peerDependencies": { + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, diff --git a/packages/@aws-cdk/aws-iotevents/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-iotevents/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..50d86e8a055ce --- /dev/null +++ b/packages/@aws-cdk/aws-iotevents/rosetta/default.ts-fixture @@ -0,0 +1,10 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts b/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts new file mode 100644 index 0000000000000..a15ba6a986049 --- /dev/null +++ b/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts @@ -0,0 +1,314 @@ +import { Match, Template } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import * as iotevents from '../lib'; + +let stack: cdk.Stack; +beforeEach(() => { + stack = new cdk.Stack(); +}); + +test('Default property', () => { + // WHEN + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.fromString('test-eventCondition'), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + InitialStateName: 'test-state', + States: [{ + StateName: 'test-state', + OnEnter: { + Events: [{ + EventName: 'test-eventName', + Condition: 'test-eventCondition', + }], + }, + }], + }, + RoleArn: { + 'Fn::GetAtt': ['MyDetectorModelDetectorModelRoleF2FB4D88', 'Arn'], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: 'iotevents.amazonaws.com' }, + }], + }, + }); +}); + +test('can get detector model name', () => { + // GIVEN + const detectorModel = new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.fromString('test-eventCondition'), + }], + }), + }); + + // WHEN + new cdk.CfnResource(stack, 'Res', { + type: 'Test::Resource', + properties: { + DetectorModelName: detectorModel.detectorModelName, + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('Test::Resource', { + DetectorModelName: { Ref: 'MyDetectorModel559C0B0E' }, + }); +}); + +test.each([ + ['physical name', { detectorModelName: 'test-detector-model' }, { DetectorModelName: 'test-detector-model' }], + ['description', { description: 'test-detector-model-description' }, { DetectorModelDescription: 'test-detector-model-description' }], + ['evaluationMethod', { evaluationMethod: iotevents.EventEvaluation.SERIAL }, { EvaluationMethod: 'SERIAL' }], + ['detectorKey', { detectorKey: 'payload.deviceId' }, { Key: 'payload.deviceId' }], +])('can set %s', (_, partialProps, expected) => { + // WHEN + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + ...partialProps, + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.fromString('test-eventCondition'), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', expected); +}); + +test('can set multiple events to State', () => { + // WHEN + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [ + { + eventName: 'test-eventName1', + condition: iotevents.Expression.fromString('test-eventCondition'), + }, + { + eventName: 'test-eventName2', + }, + ], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + States: [ + Match.objectLike({ + OnEnter: { + Events: [ + { + EventName: 'test-eventName1', + Condition: 'test-eventCondition', + }, + { + EventName: 'test-eventName2', + }, + ], + }, + }), + ], + }, + }); +}); + +test('can set role', () => { + // WHEN + const role = iam.Role.fromRoleArn(stack, 'test-role', 'arn:aws:iam::123456789012:role/ForTest'); + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + role, + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.fromString('test-eventCondition'), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + RoleArn: 'arn:aws:iam::123456789012:role/ForTest', + }); +}); + +test('can import a DetectorModel by detectorModelName', () => { + // WHEN + const detectorModelName = 'detector-model-name'; + const detectorModel = iotevents.DetectorModel.fromDetectorModelName(stack, 'ExistingDetectorModel', detectorModelName); + + // THEN + expect(detectorModel).toMatchObject({ + detectorModelName: detectorModelName, + }); +}); + +test('cannot create without condition', () => { + expect(() => { + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + }], + }), + }); + }).toThrow('Detector Model must have at least one Input with a condition'); +}); + +test('cannot create without event', () => { + expect(() => { + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + }), + }); + }).toThrow('Detector Model must have at least one Input with a condition'); +}); + +describe('Expression', () => { + test('currentInput', () => { + // WHEN + const input = iotevents.Input.fromInputName(stack, 'MyInput', 'test-input'); + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.currentInput(input), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + States: [ + Match.objectLike({ + OnEnter: { + Events: [Match.objectLike({ + Condition: 'currentInput("test-input")', + })], + }, + }), + ], + }, + }); + }); + + test('inputAttribute', () => { + // WHEN + const input = iotevents.Input.fromInputName(stack, 'MyInput', 'test-input'); + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.inputAttribute(input, 'json.path'), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + States: [ + Match.objectLike({ + OnEnter: { + Events: [Match.objectLike({ + Condition: '$input.test-input.json.path', + })], + }, + }), + ], + }, + }); + }); + + test('eq', () => { + // WHEN + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.eq( + iotevents.Expression.fromString('"aaa"'), + iotevents.Expression.fromString('"bbb"'), + ), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + States: [ + Match.objectLike({ + OnEnter: { + Events: [Match.objectLike({ + Condition: '"aaa" == "bbb"', + })], + }, + }), + ], + }, + }); + }); + + test('eq', () => { + // WHEN + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ + eventName: 'test-eventName', + condition: iotevents.Expression.and( + iotevents.Expression.fromString('true'), + iotevents.Expression.fromString('false'), + ), + }], + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + States: [ + Match.objectLike({ + OnEnter: { + Events: [Match.objectLike({ + Condition: 'true && false', + })], + }, + }), + ], + }, + }); + }); +}); diff --git a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json index 1f5d452b5475d..3b1b598427701 100644 --- a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json +++ b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json @@ -5,6 +5,9 @@ "Properties": { "InputDefinition": { "Attributes": [ + { + "JsonPath": "payload.deviceId" + }, { "JsonPath": "payload.temperature" } @@ -12,6 +15,69 @@ }, "InputName": "test_input" } + }, + "MyDetectorModelDetectorModelRoleF2FB4D88": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iotevents.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyDetectorModel559C0B0E": { + "Type": "AWS::IoTEvents::DetectorModel", + "Properties": { + "DetectorModelDefinition": { + "InitialStateName": "online", + "States": [ + { + "OnEnter": { + "Events": [ + { + "Condition": { + "Fn::Join": [ + "", + [ + "currentInput(\"", + { + "Ref": "MyInput08947B23" + }, + "\") && $input.", + { + "Ref": "MyInput08947B23" + }, + ".payload.temperature == 31.5" + ] + ] + }, + "EventName": "test-event" + } + ] + }, + "StateName": "online" + } + ] + }, + "RoleArn": { + "Fn::GetAtt": [ + "MyDetectorModelDetectorModelRoleF2FB4D88", + "Arn" + ] + }, + "DetectorModelDescription": "test-detector-model-description", + "DetectorModelName": "test-detector-model", + "EvaluationMethod": "SERIAL", + "Key": "payload.deviceId" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts index cb900c83a3f44..dc90a7d505dbf 100644 --- a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts +++ b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts @@ -1,18 +1,40 @@ import * as cdk from '@aws-cdk/core'; import * as iotevents from '../lib'; -const app = new cdk.App(); - class TestStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); - new iotevents.Input(this, 'MyInput', { + const input = new iotevents.Input(this, 'MyInput', { inputName: 'test_input', - attributeJsonPaths: ['payload.temperature'], + attributeJsonPaths: ['payload.deviceId', 'payload.temperature'], + }); + + const onlineState = new iotevents.State({ + stateName: 'online', + onEnter: [{ + eventName: 'test-event', + // meaning `condition: 'currentInput("test_input") && $input.test_input.payload.temperature == 31.5'` + condition: iotevents.Expression.and( + iotevents.Expression.currentInput(input), + iotevents.Expression.eq( + iotevents.Expression.inputAttribute(input, 'payload.temperature'), + iotevents.Expression.fromString('31.5'), + ), + ), + }], + }); + + new iotevents.DetectorModel(this, 'MyDetectorModel', { + detectorModelName: 'test-detector-model', + description: 'test-detector-model-description', + evaluationMethod: iotevents.EventEvaluation.SERIAL, + detectorKey: 'payload.deviceId', + initialState: onlineState, }); } } -new TestStack(app, 'test-stack'); +const app = new cdk.App(); +new TestStack(app, 'detector-model-test-stack'); app.synth(); diff --git a/packages/@aws-cdk/aws-iotfleethub/package.json b/packages/@aws-cdk/aws-iotfleethub/package.json index 0265539be552c..dde15541e17b5 100644 --- a/packages/@aws-cdk/aws-iotfleethub/package.json +++ b/packages/@aws-cdk/aws-iotfleethub/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-iotfleethub", "module": "aws_cdk.aws_iotfleethub" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-iotsitewise/package.json b/packages/@aws-cdk/aws-iotsitewise/package.json index 1d4fcbae037cd..d84b4f75d06dc 100644 --- a/packages/@aws-cdk/aws-iotsitewise/package.json +++ b/packages/@aws-cdk/aws-iotsitewise/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-iotsitewise", "module": "aws_cdk.aws_iotsitewise" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-iotthingsgraph/package.json b/packages/@aws-cdk/aws-iotthingsgraph/package.json index 38352027ee8ae..fd5ffd5531c81 100644 --- a/packages/@aws-cdk/aws-iotthingsgraph/package.json +++ b/packages/@aws-cdk/aws-iotthingsgraph/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-iotwireless/package.json b/packages/@aws-cdk/aws-iotwireless/package.json index b9cdebdb5a3d8..b5a5f1aee6dc7 100644 --- a/packages/@aws-cdk/aws-iotwireless/package.json +++ b/packages/@aws-cdk/aws-iotwireless/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-iotwireless", "module": "aws_cdk.aws_iotwireless" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-ivs/package.json b/packages/@aws-cdk/aws-ivs/package.json index 2ef5a15ca7b34..d078cf3eceeaa 100644 --- a/packages/@aws-cdk/aws-ivs/package.json +++ b/packages/@aws-cdk/aws-ivs/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-ivs", "module": "aws_cdk.aws_ivs" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "awslint": { diff --git a/packages/@aws-cdk/aws-kafkaconnect/.eslintrc.js b/packages/@aws-cdk/aws-kafkaconnect/.eslintrc.js new file mode 100644 index 0000000000000..2658ee8727166 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-kafkaconnect/.gitignore b/packages/@aws-cdk/aws-kafkaconnect/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/.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-kafkaconnect/.npmignore b/packages/@aws-cdk/aws-kafkaconnect/.npmignore new file mode 100644 index 0000000000000..f931fede67c44 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/.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-kafkaconnect/LICENSE b/packages/@aws-cdk/aws-kafkaconnect/LICENSE new file mode 100644 index 0000000000000..82ad00bb02d0b --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/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-2022 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-kafkaconnect/NOTICE b/packages/@aws-cdk/aws-kafkaconnect/NOTICE new file mode 100644 index 0000000000000..1b7adbb891265 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-kafkaconnect/README.md b/packages/@aws-cdk/aws-kafkaconnect/README.md new file mode 100644 index 0000000000000..79f7c2fc4b807 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/README.md @@ -0,0 +1,31 @@ +# AWS::KafkaConnect 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 nofixture +import * as kafkaconnect from '@aws-cdk/aws-kafkaconnect'; +``` + + + +There are no hand-written ([L2](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib)) constructs for this service yet. +However, you can still use the automatically generated [L1](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_l1_using) constructs, and use this service exactly as you would using CloudFormation directly. + +For more information on the resources and properties available for this service, see the [CloudFormation documentation for AWS::KafkaConnect](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_KafkaConnect.html). + +(Read the [CDK Contributing Guide](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) if you are interested in contributing to this construct library.) + + diff --git a/packages/@aws-cdk/aws-kafkaconnect/jest.config.js b/packages/@aws-cdk/aws-kafkaconnect/jest.config.js new file mode 100644 index 0000000000000..3a2fd93a1228a --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-kafkaconnect/lib/index.ts b/packages/@aws-cdk/aws-kafkaconnect/lib/index.ts new file mode 100644 index 0000000000000..5745f9372ea21 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::KafkaConnect CloudFormation Resources: +export * from './kafkaconnect.generated'; diff --git a/packages/@aws-cdk/aws-kafkaconnect/package.json b/packages/@aws-cdk/aws-kafkaconnect/package.json new file mode 100644 index 0000000000000..76f3beafd989e --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/package.json @@ -0,0 +1,110 @@ +{ + "name": "@aws-cdk/aws-kafkaconnect", + "version": "0.0.0", + "description": "AWS::KafkaConnect Construct Library", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.KafkaConnect", + "packageId": "Amazon.CDK.AWS.KafkaConnect", + "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.kafkaconnect", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "kafkaconnect" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-kafkaconnect", + "module": "aws_cdk.aws_kafkaconnect" + } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-kafkaconnect" + }, + "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::KafkaConnect", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::KafkaConnect", + "aws-kafkaconnect" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assertions": "0.0.0", + "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/cfn2ts": "0.0.0", + "@aws-cdk/pkglint": "0.0.0", + "@types/jest": "^27.4.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-kafkaconnect/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-kafkaconnect/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..e208762bca03c --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/rosetta/default.ts-fixture @@ -0,0 +1,8 @@ +import { Construct } from 'constructs'; +import { Stack } from '@aws-cdk/core'; + +class MyStack extends Stack { + constructor(scope: Construct, id: string) { + /// here + } +} diff --git a/packages/@aws-cdk/aws-kafkaconnect/test/kafkaconnect.test.ts b/packages/@aws-cdk/aws-kafkaconnect/test/kafkaconnect.test.ts new file mode 100644 index 0000000000000..465c7bdea0693 --- /dev/null +++ b/packages/@aws-cdk/aws-kafkaconnect/test/kafkaconnect.test.ts @@ -0,0 +1,6 @@ +import '@aws-cdk/assertions'; +import {} from '../lib'; + +test('No tests are specified for this package', () => { + expect(true).toBe(true); +}); diff --git a/packages/@aws-cdk/aws-kendra/package.json b/packages/@aws-cdk/aws-kendra/package.json index 4c1c9b79a4d9b..2da553adf56a5 100644 --- a/packages/@aws-cdk/aws-kendra/package.json +++ b/packages/@aws-cdk/aws-kendra/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.Kendra", diff --git a/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.lit.expected.json b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.lit.expected.json index 5ae3347a50989..913dba1638ec3 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.lit.expected.json +++ b/packages/@aws-cdk/aws-kinesisfirehose-destinations/test/integ.s3-bucket.lit.expected.json @@ -481,6 +481,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -527,6 +531,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", 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 index 1c23bbcee1092..65ac018add362 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.expected.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.source-stream.expected.json b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.source-stream.expected.json index a37685e2e47e8..ccbca77c32829 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.source-stream.expected.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/test/integ.delivery-stream.source-stream.expected.json @@ -34,6 +34,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -74,11 +78,8 @@ "SourceStream95FF52BE": { "Type": "AWS::Kinesis::Stream", "Properties": { - "ShardCount": 1, - "StreamModeDetails": { - "StreamMode": "PROVISIONED" - }, "RetentionPeriodHours": 24, + "ShardCount": 1, "StreamEncryption": { "Fn::If": [ "AwsCdkKinesisEncryptedStreamsUnsupportedRegions", @@ -90,6 +91,9 @@ "KeyId": "alias/aws/kinesis" } ] + }, + "StreamModeDetails": { + "StreamMode": "PROVISIONED" } } }, @@ -284,4 +288,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lakeformation/package.json b/packages/@aws-cdk/aws-lakeformation/package.json index e1cdfff184bc3..2c37cb38a608f 100644 --- a/packages/@aws-cdk/aws-lakeformation/package.json +++ b/packages/@aws-cdk/aws-lakeformation/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md index e7080c9ab72a0..95d7559cf25f3 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/README.md +++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md @@ -116,6 +116,11 @@ OR $ yarn add --dev esbuild@0 ``` +If you're using a monorepo layout, the `esbuild` dependency needs to be installed in the "root" `package.json` file, +not in the workspace. From the reference architecture described [above](#reference-project-architecture), the `esbuild` +dev dependency needs to be in `./package.json`, not `packages/cool-package/package.json` or +`packages/super-package/package.json`. + To force bundling in a Docker container even if `esbuild` is available in your environment, set `bundling.forceDockerBundling` to `true`. This is useful if your function relies on node modules that should be installed (`nodeModules` prop, see [below](#install-modules)) in a Lambda @@ -253,7 +258,7 @@ new lambda.NodejsFunction(this, 'my-handler', { }); ``` -Note: A [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) is required +Note: A [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) is required ## Customizing Docker bundling diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts index d0cae32489818..8a251bf8dda24 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts @@ -5,7 +5,7 @@ import * as cdk from '@aws-cdk/core'; import { PackageInstallation } from './package-installation'; import { PackageManager } from './package-manager'; import { BundlingOptions, OutputFormat, SourceMapMode } from './types'; -import { exec, extractDependencies, findUp } from './util'; +import { exec, extractDependencies, findUp, getTsconfigCompilerOptions } from './util'; const ESBUILD_MAJOR_VERSION = '0'; @@ -68,13 +68,8 @@ export class Bundling implements cdk.BundlingOptions { this.tscInstallation = undefined; } - public static clearTscCompilationCache(): void { - this.tscCompiled = false; - } - private static esbuildInstallation?: PackageInstallation; private static tscInstallation?: PackageInstallation; - private static tscCompiled = false // Core bundling options public readonly image: cdk.DockerImage; @@ -156,26 +151,17 @@ export class Bundling implements cdk.BundlingOptions { private createBundlingCommand(options: BundlingCommandOptions): string { const pathJoin = osPathJoin(options.osPlatform); - let tscCommand: string = ''; - let relativeEntryPath = this.relativeEntryPath; + let relativeEntryPath = pathJoin(options.inputDir, this.relativeEntryPath); + let tscCommand = ''; if (this.props.preCompilation) { - - let tsconfig = this.relativeTsconfigPath; + const tsconfig = this.props.tsconfig ?? findUp('tsconfig.json', path.dirname(this.props.entry)); if (!tsconfig) { - const findConfig = findUp('tsconfig.json', path.dirname(this.props.entry)); - if (!findConfig) { - throw new Error('Cannot find a tsconfig.json, please specify the prop: tsconfig'); - } - tsconfig = path.relative(this.projectRoot, findConfig); + throw new Error('Cannot find a `tsconfig.json` but `preCompilation` is set to `true`, please specify it via `tsconfig`'); } - + const compilerOptions = getTsconfigCompilerOptions(tsconfig); + tscCommand = `${options.tscRunner} "${relativeEntryPath}" ${compilerOptions}`; relativeEntryPath = relativeEntryPath.replace(/\.ts(x?)$/, '.js$1'); - if (!Bundling.tscCompiled) { - // Intentionally Setting rootDir and outDir, so that the compiled js file always end up next ts file. - tscCommand = `${options.tscRunner} --project ${pathJoin(options.inputDir, tsconfig)} --rootDir ./ --outDir ./`; - Bundling.tscCompiled = true; - } } const loaders = Object.entries(this.props.loader ?? {}); @@ -193,7 +179,7 @@ export class Bundling implements cdk.BundlingOptions { const outFile = this.props.format === OutputFormat.ESM ? 'index.mjs' : 'index.js'; const esbuildCommand: string[] = [ options.esbuildRunner, - '--bundle', `"${pathJoin(options.inputDir, relativeEntryPath)}"`, + '--bundle', `"${relativeEntryPath}"`, `--target=${this.props.target ?? toTarget(this.props.runtime)}`, '--platform=node', ...this.props.format ? [`--format=${this.props.format}`] : [], diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts index 0ececb74ab95f..cc3d314c32416 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts @@ -144,3 +144,65 @@ export function extractDependencies(pkgPath: string, modules: string[]): { [key: return dependencies; } + +export function getTsconfigCompilerOptions(tsconfigPath: string): string { + const compilerOptions = extractTsConfig(tsconfigPath); + const excludedCompilerOptions = [ + 'composite', + 'tsBuildInfoFile', + ]; + + const options: Record = { + ...compilerOptions, + // Overrides + incremental: false, + // Intentionally Setting rootDir and outDir, so that the compiled js file always end up next to .ts file. + rootDir: './', + outDir: './', + }; + + let compilerOptionsString = ''; + Object.keys(options).forEach((key: string) => { + + if (excludedCompilerOptions.includes(key)) { + return; + } + + const value = options[key]; + const option = '--' + key; + const type = typeof value; + + if (type === 'boolean') { + if (value) { + compilerOptionsString += option + ' '; + } + } else if (type === 'string') { + compilerOptionsString += option + ' ' + value + ' '; + } else if (type === 'object') { + if (Array.isArray(value)) { + compilerOptionsString += option + ' ' + value.join(',') + ' '; + } + } else { + throw new Error(`Missing support for compilerOption: [${key}]: { ${type}, ${value}} \n`); + } + }); + + return compilerOptionsString.trim(); +} + + +function extractTsConfig(tsconfigPath: string, previousCompilerOptions?: Record): Record | undefined { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const { extends: extendedConfig, compilerOptions } = require(tsconfigPath); + const updatedCompilerOptions = { + ...(previousCompilerOptions ?? {}), + ...compilerOptions, + }; + if (extendedConfig) { + return extractTsConfig( + path.resolve(tsconfigPath.replace(/[^\/]+$/, ''), extendedConfig), + updatedCompilerOptions, + ); + } + return updatedCompilerOptions; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index a9a64704b6be9..ca2f96190fa3c 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -78,7 +78,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", "delay": "5.0.0", - "esbuild": "^0.14.11" + "esbuild": "^0.14.14" }, "dependencies": { "@aws-cdk/aws-lambda": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts index f9b8301eacacc..3224d1afbd014 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts @@ -16,7 +16,6 @@ beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); Bundling.clearEsbuildInstallationCache(); - Bundling.clearTscInstallationCache(); jest.spyOn(Code, 'fromAsset'); @@ -574,61 +573,6 @@ test('esbuild bundling with projectRoot and externals and dependencies', () => { }); test('esbuild bundling with pre compilations', () => { - Bundling.bundle({ - entry, - projectRoot, - depsLockFilePath, - runtime: Runtime.NODEJS_14_X, - forceDockerBundling: true, - tsconfig, - preCompilation: true, - architecture: Architecture.X86_64, - }); - - // Correctly bundles with esbuild - expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(depsLockFilePath), { - assetHashType: AssetHashType.OUTPUT, - bundling: expect.objectContaining({ - command: [ - 'bash', '-c', - [ - 'tsc --project /asset-input/lib/custom-tsconfig.ts --rootDir ./ --outDir ./ &&', - 'esbuild --bundle \"/asset-input/lib/handler.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\"', - '--external:aws-sdk --tsconfig=/asset-input/lib/custom-tsconfig.ts', - ].join(' '), - ], - }), - }); - - Bundling.bundle({ - entry, - projectRoot, - depsLockFilePath, - runtime: Runtime.NODEJS_14_X, - forceDockerBundling: true, - tsconfig, - preCompilation: true, - architecture: Architecture.X86_64, - }); - - // Correctly bundles with esbuild - expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(depsLockFilePath), { - assetHashType: AssetHashType.OUTPUT, - bundling: expect.objectContaining({ - command: [ - 'bash', '-c', - [ - 'esbuild --bundle \"/asset-input/lib/handler.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\"', - '--external:aws-sdk --tsconfig=/asset-input/lib/custom-tsconfig.ts', - ].join(' '), - ], - }), - }); - -}); - -test('esbuild bundling with pre compilations with undefined tsconfig ( Should find in root directory )', () => { - Bundling.clearTscCompilationCache(); const packageLock = path.join(__dirname, '..', 'package-lock.json'); Bundling.bundle({ @@ -636,11 +580,13 @@ test('esbuild bundling with pre compilations with undefined tsconfig ( Should fi projectRoot: path.dirname(packageLock), depsLockFilePath: packageLock, runtime: Runtime.NODEJS_14_X, - forceDockerBundling: true, preCompilation: true, + forceDockerBundling: true, architecture: Architecture.X86_64, }); + const compilerOptions = util.getTsconfigCompilerOptions(path.join(__dirname, '..', 'tsconfig.json')); + // Correctly bundles with esbuild expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(packageLock), { assetHashType: AssetHashType.OUTPUT, @@ -648,16 +594,15 @@ test('esbuild bundling with pre compilations with undefined tsconfig ( Should fi command: [ 'bash', '-c', [ - 'tsc --project /asset-input/tsconfig.json --rootDir ./ --outDir ./ &&', - 'esbuild --bundle \"/asset-input/test/bundling.test.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\"', - '--external:aws-sdk', + `tsc \"/asset-input/test/bundling.test.ts\" ${compilerOptions} &&`, + 'esbuild --bundle \"/asset-input/test/bundling.test.js\" --target=node14 --platform=node --outfile=\"/asset-output/index.js\" --external:aws-sdk', ].join(' '), ], }), }); }); -test('esbuild bundling with pre compilations and undefined tsconfig ( Should throw) ', () => { +test('throws with pre compilation and not found tsconfig', () => { expect(() => { Bundling.bundle({ entry, @@ -668,7 +613,7 @@ test('esbuild bundling with pre compilations and undefined tsconfig ( Should thr preCompilation: true, architecture: Architecture.X86_64, }); - }).toThrow('Cannot find a tsconfig.json, please specify the prop: tsconfig'); + }).toThrow('Cannot find a `tsconfig.json` but `preCompilation` is set to `true`, please specify it via `tsconfig`'); }); diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts index d2249f4f59118..085c333579933 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts @@ -1,7 +1,7 @@ import * as child_process from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; -import { callsites, exec, extractDependencies, findUp, findUpMultiple } from '../lib/util'; +import { callsites, exec, extractDependencies, findUp, findUpMultiple, getTsconfigCompilerOptions } from '../lib/util'; beforeEach(() => { jest.clearAllMocks(); @@ -179,3 +179,18 @@ describe('extractDependencies', () => { fs.unlinkSync(pkgPath); }); }); + +describe('getTsconfigCompilerOptions', () => { + test('should extract compiler options and returns as string', () => { + const tsconfig = path.join(__dirname, '..', 'tsconfig.json'); + const compilerOptions = getTsconfigCompilerOptions(tsconfig); + expect(compilerOptions).toEqual([ + '--alwaysStrict --charset utf8 --declaration --experimentalDecorators', + '--inlineSourceMap --inlineSources --lib es2019 --module CommonJS', + '--newLine lf --noEmitOnError --noFallthroughCasesInSwitch --noImplicitAny', + '--noImplicitReturns --noImplicitThis --noUnusedLocals --noUnusedParameters', + '--resolveJsonModule --strict --strictNullChecks --strictPropertyInitialization', + '--target ES2019 --rootDir ./ --outDir ./', + ].join(' ')); + }); +}); diff --git a/packages/@aws-cdk/aws-lambda-python/README.md b/packages/@aws-cdk/aws-lambda-python/README.md index 2a8de2d34c4de..ef52db457ef2b 100644 --- a/packages/@aws-cdk/aws-lambda-python/README.md +++ b/packages/@aws-cdk/aws-lambda-python/README.md @@ -167,9 +167,34 @@ new lambda.PythonFunction(this, 'function', { entry, runtime: Runtime.PYTHON_3_8, bundling: { - buildArgs: { PIP_INDEX_URL: indexUrl }, + environment: { PIP_INDEX_URL: indexUrl }, }, }); ``` -This type of an example should work for `pip` and `poetry` based dependencies, but will not work for `pipenv`. +The index URL or the token are only used during bundling and thus not included in the final asset. Setting only environment variable for `PIP_INDEX_URL` or `PIP_EXTRA_INDEX_URL` should work for accesing private Python repositories with `pip`, `pipenv` and `poetry` based dependencies. + +If you also want to use the Code Artifact repo for building the base Docker image for bundling, use `buildArgs`. However, note that setting custom build args for bundling will force the base bundling image to be rebuilt every time (i.e. skip the Docker cache). Build args can be customized as: + +```ts +import { execSync } from 'child_process'; + +const entry = '/path/to/function'; +const image = DockerImage.fromBuild(entry); + +const domain = 'my-domain'; +const domainOwner = '111122223333'; +const repoName = 'my_repo'; +const region = 'us-east-1'; +const codeArtifactAuthToken = execSync(`aws codeartifact get-authorization-token --domain ${domain} --domain-owner ${domainOwner} --query authorizationToken --output text`).toString().trim(); + +const indexUrl = `https://aws:${codeArtifactAuthToken}@${domain}-${domainOwner}.d.codeartifact.${region}.amazonaws.com/pypi/${repoName}/simple/`; + +new lambda.PythonFunction(this, 'function', { + entry, + runtime: Runtime.PYTHON_3_8, + bundling: { + buildArgs: { PIP_INDEX_URL: indexUrl }, + }, +}); +``` diff --git a/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts index ddd3e167204c9..bd2020a5a228b 100644 --- a/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts @@ -51,6 +51,7 @@ export class Bundling implements CdkBundlingOptions { public readonly image: DockerImage; public readonly command: string[]; + public readonly environment?: { [key: string]: string }; constructor(props: BundlingProps) { const { @@ -78,6 +79,7 @@ export class Bundling implements CdkBundlingOptions { }); this.image = image ?? defaultImage; this.command = ['bash', '-c', chain(bundlingCommands)]; + this.environment = props.environment; } private createBundlingCommand(options: BundlingCommandOptions): string[] { diff --git a/packages/@aws-cdk/aws-lambda-python/lib/types.ts b/packages/@aws-cdk/aws-lambda-python/lib/types.ts index 1f2b1e8c7aabf..e818eadc4401b 100644 --- a/packages/@aws-cdk/aws-lambda-python/lib/types.ts +++ b/packages/@aws-cdk/aws-lambda-python/lib/types.ts @@ -30,6 +30,13 @@ export interface BundlingOptions { */ readonly buildArgs?: { [key: string]: string }; + /** + * Environment variables defined when bundling runs. + * + * @default - no environment variables are defined. + */ + readonly environment?: { [key: string]: string; }; + /** * Determines how asset hash is calculated. Assets will get rebuild and * uploaded only if their hash has changed. diff --git a/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts index 4af556b3b9a62..a75ecc8625960 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-python/test/bundling.test.ts @@ -229,3 +229,22 @@ test('Bundling with custom build args', () => { }), })); }); + +test('Bundling with custom environment vars`', () => { + const entry = path.join(__dirname, 'lambda-handler'); + Bundling.bundle({ + entry: entry, + runtime: Runtime.PYTHON_3_7, + environment: { + KEY: 'value', + }, + }); + + expect(Code.fromAsset).toHaveBeenCalledWith(entry, expect.objectContaining({ + bundling: expect.objectContaining({ + environment: { + KEY: 'value', + }, + }), + })); +}); diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/index.py b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/index.py index c033f37560534..04f99eb108b30 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/index.py +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/index.py @@ -1,11 +1,8 @@ import requests -from PIL import Image def handler(event, context): response = requests.get('https://a0.awsstatic.com/main/images/logos/aws_smile-header-desktop-en-white_59x35.png', stream=True) - img = Image.open(response.raw) print(response.status_code) - print(img.size) return response.status_code diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/requirements.txt b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/requirements.txt index c636db83b8c9e..4fcd85719fe3a 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/requirements.txt +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-custom-build/requirements.txt @@ -5,5 +5,3 @@ idna==2.10 urllib3==1.26.7 # Requests used by this lambda requests==2.26.0 -# Pillow 6.x so that python 2.7 and 3.x can both use this fixture -Pillow==8.4.0 diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile index a309b821c5801..78d783bc4b9b0 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile @@ -5,4 +5,3 @@ verify_ssl = true [packages] requests = "==2.26.0" -Pillow = "==8.4.0" diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile.lock b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile.lock index f92befb9e3dd6..441acc679505f 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile.lock +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "fe29bbb3f12db421fd27678820291d33cf6b3dce6bb189274449dee89cf434e8" + "sha256": "6cfaa5a495be5cf47942a14b04d50e639f14743101e621684e86449dbac8da61" }, "pipfile-spec": 6, "requires": {}, @@ -23,11 +23,11 @@ }, "charset-normalizer": { "hashes": [ - "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721", - "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c" + "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd", + "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455" ], "markers": "python_version >= '3'", - "version": "==2.0.9" + "version": "==2.0.10" }, "idna": { "hashes": [ @@ -37,53 +37,6 @@ "markers": "python_version >= '3'", "version": "==3.3" }, - "pillow": { - "hashes": [ - "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76", - "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585", - "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b", - "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8", - "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55", - "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc", - "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645", - "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff", - "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc", - "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b", - "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6", - "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20", - "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e", - "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a", - "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779", - "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02", - "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39", - "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f", - "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a", - "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409", - "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c", - "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488", - "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b", - "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d", - "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09", - "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b", - "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153", - "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9", - "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad", - "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df", - "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df", - "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed", - "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed", - "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698", - "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29", - "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649", - "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49", - "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b", - "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2", - "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a", - "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78" - ], - "index": "pypi", - "version": "==8.4.0" - }, "requests": { "hashes": [ "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", @@ -94,11 +47,11 @@ }, "urllib3": { "hashes": [ - "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", - "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" + "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed", + "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.7" + "version": "==1.26.8" } }, "develop": {} diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/index.py b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/index.py index c033f37560534..04f99eb108b30 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/index.py +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-pipenv/index.py @@ -1,11 +1,8 @@ import requests -from PIL import Image def handler(event, context): response = requests.get('https://a0.awsstatic.com/main/images/logos/aws_smile-header-desktop-en-white_59x35.png', stream=True) - img = Image.open(response.raw) print(response.status_code) - print(img.size) return response.status_code diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/index.py b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/index.py index c033f37560534..04f99eb108b30 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/index.py +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/index.py @@ -1,11 +1,8 @@ import requests -from PIL import Image def handler(event, context): response = requests.get('https://a0.awsstatic.com/main/images/logos/aws_smile-header-desktop-en-white_59x35.png', stream=True) - img = Image.open(response.raw) print(response.status_code) - print(img.size) return response.status_code diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/poetry.lock b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/poetry.lock index d07a92e9ef100..6b59241f10c2d 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/poetry.lock +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/poetry.lock @@ -25,14 +25,6 @@ category = "main" optional = false python-versions = ">=3.5" -[[package]] -name = "pillow" -version = "8.4.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "requests" version = "2.26.0" @@ -82,49 +74,6 @@ idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] -pillow = [ - {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, - {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, - {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"}, - {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"}, - {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"}, - {file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"}, - {file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"}, - {file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"}, - {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"}, - {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"}, - {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"}, - {file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"}, - {file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"}, - {file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"}, - {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"}, - {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"}, - {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"}, - {file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"}, - {file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"}, - {file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"}, - {file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"}, - {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"}, - {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"}, - {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"}, - {file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"}, - {file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"}, - {file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"}, - {file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"}, - {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"}, - {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"}, - {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"}, - {file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"}, - {file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"}, - {file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"}, - {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"}, - {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"}, - {file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"}, - {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"}, -] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/pyproject.toml b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/pyproject.toml index c4dd461c007a7..6d90c4b4fec9b 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/pyproject.toml +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-poetry/pyproject.toml @@ -7,7 +7,6 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = "^3.6" requests = "2.26.0" -Pillow = "8.4.0" [tool.poetry.dev-dependencies] diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/lambda/index.py b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/lambda/index.py index 6ac592242c8fb..fb1e8bb1ce0ab 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/lambda/index.py +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/lambda/index.py @@ -1,12 +1,9 @@ import requests -from PIL import Image import shared def handler(event, context): response = requests.get(shared.get_url(), stream=True) - img = Image.open(response.raw) print(response.status_code) - print(img.size) return response.status_code diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/shared/requirements.txt b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/shared/requirements.txt index d87aff1f66a75..eff24435fa632 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/shared/requirements.txt +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler-project/shared/requirements.txt @@ -4,5 +4,3 @@ chardet==3.0.4 idna==2.10 urllib3==1.26.7 # Requests used by this lambda -requests==2.26.0 -Pillow==8.4.0 diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/index.py b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/index.py index c033f37560534..04f99eb108b30 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/index.py +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/index.py @@ -1,11 +1,8 @@ import requests -from PIL import Image def handler(event, context): response = requests.get('https://a0.awsstatic.com/main/images/logos/aws_smile-header-desktop-en-white_59x35.png', stream=True) - img = Image.open(response.raw) print(response.status_code) - print(img.size) return response.status_code diff --git a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/requirements.txt b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/requirements.txt index c636db83b8c9e..4fcd85719fe3a 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/requirements.txt +++ b/packages/@aws-cdk/aws-lambda-python/test/lambda-handler/requirements.txt @@ -5,5 +5,3 @@ idna==2.10 urllib3==1.26.7 # Requests used by this lambda requests==2.26.0 -# Pillow 6.x so that python 2.7 and 3.x can both use this fixture -Pillow==8.4.0 diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index c67a32c6e3085..0c20400dcd01a 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -89,7 +89,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/cfnspec": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "@types/lodash": "^4.14.178", "jest": "^27.4.7", diff --git a/packages/@aws-cdk/aws-licensemanager/package.json b/packages/@aws-cdk/aws-licensemanager/package.json index 802c3eaebd97f..2a080395c788e 100644 --- a/packages/@aws-cdk/aws-licensemanager/package.json +++ b/packages/@aws-cdk/aws-licensemanager/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-licensemanager", "module": "aws_cdk.aws_licensemanager" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-lightsail/package.json b/packages/@aws-cdk/aws-lightsail/package.json index 75ce8a8401de5..c99305e2ad42c 100644 --- a/packages/@aws-cdk/aws-lightsail/package.json +++ b/packages/@aws-cdk/aws-lightsail/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.Lightsail", diff --git a/packages/@aws-cdk/aws-location/package.json b/packages/@aws-cdk/aws-location/package.json index 714e4a652448c..7acaf2d81fd60 100644 --- a/packages/@aws-cdk/aws-location/package.json +++ b/packages/@aws-cdk/aws-location/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-location", "module": "aws_cdk.aws_location" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-logs-destinations/package.json b/packages/@aws-cdk/aws-logs-destinations/package.json index ceb9c5c120499..e1e43b017c992 100644 --- a/packages/@aws-cdk/aws-logs-destinations/package.json +++ b/packages/@aws-cdk/aws-logs-destinations/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 9837e00b2eb05..a27346c078a74 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -84,11 +84,11 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "jest": "^27.4.7", "nock": "^13.2.2", "sinon": "^9.2.4" diff --git a/packages/@aws-cdk/aws-lookoutequipment/package.json b/packages/@aws-cdk/aws-lookoutequipment/package.json index 673dce8a9e5db..26dc399bb1d3d 100644 --- a/packages/@aws-cdk/aws-lookoutequipment/package.json +++ b/packages/@aws-cdk/aws-lookoutequipment/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-lookoutequipment", "module": "aws_cdk.aws_lookoutequipment" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-lookoutmetrics/package.json b/packages/@aws-cdk/aws-lookoutmetrics/package.json index dd6721487fb9e..257c1ccad03c5 100644 --- a/packages/@aws-cdk/aws-lookoutmetrics/package.json +++ b/packages/@aws-cdk/aws-lookoutmetrics/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-lookoutmetrics", "module": "aws_cdk.aws_lookoutmetrics" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-lookoutvision/package.json b/packages/@aws-cdk/aws-lookoutvision/package.json index da277979b41e2..3722553279a80 100644 --- a/packages/@aws-cdk/aws-lookoutvision/package.json +++ b/packages/@aws-cdk/aws-lookoutvision/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-lookoutvision", "module": "aws_cdk.aws_lookoutvision" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-macie/package.json b/packages/@aws-cdk/aws-macie/package.json index cf5dcf3ddc027..0db2eaf329169 100644 --- a/packages/@aws-cdk/aws-macie/package.json +++ b/packages/@aws-cdk/aws-macie/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-managedblockchain/package.json b/packages/@aws-cdk/aws-managedblockchain/package.json index 2a56972081f67..8f8f77907edb4 100644 --- a/packages/@aws-cdk/aws-managedblockchain/package.json +++ b/packages/@aws-cdk/aws-managedblockchain/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-mediaconnect/package.json b/packages/@aws-cdk/aws-mediaconnect/package.json index 4fa2d509bc28f..ca2d9a593ea5a 100644 --- a/packages/@aws-cdk/aws-mediaconnect/package.json +++ b/packages/@aws-cdk/aws-mediaconnect/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-mediaconnect", "module": "aws_cdk.aws_mediaconnect" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-mediaconvert/package.json b/packages/@aws-cdk/aws-mediaconvert/package.json index a65ece075fb44..0ee4ec64dcbe7 100644 --- a/packages/@aws-cdk/aws-mediaconvert/package.json +++ b/packages/@aws-cdk/aws-mediaconvert/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-medialive/package.json b/packages/@aws-cdk/aws-medialive/package.json index a857dc5e4280a..f87426fc1fd81 100644 --- a/packages/@aws-cdk/aws-medialive/package.json +++ b/packages/@aws-cdk/aws-medialive/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-mediapackage/package.json b/packages/@aws-cdk/aws-mediapackage/package.json index 0a3ca05146fb9..31e2b39fe0c9a 100644 --- a/packages/@aws-cdk/aws-mediapackage/package.json +++ b/packages/@aws-cdk/aws-mediapackage/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.MediaPackage", diff --git a/packages/@aws-cdk/aws-mediastore/package.json b/packages/@aws-cdk/aws-mediastore/package.json index 34b83f88e975b..ece435e1c014f 100644 --- a/packages/@aws-cdk/aws-mediastore/package.json +++ b/packages/@aws-cdk/aws-mediastore/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-memorydb/package.json b/packages/@aws-cdk/aws-memorydb/package.json index aaeacc2305d5f..b85421f3713f9 100644 --- a/packages/@aws-cdk/aws-memorydb/package.json +++ b/packages/@aws-cdk/aws-memorydb/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-memorydb", "module": "aws_cdk.aws_memorydb" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-mwaa/package.json b/packages/@aws-cdk/aws-mwaa/package.json index e0c7e0c26544e..f0a0cec6a6877 100644 --- a/packages/@aws-cdk/aws-mwaa/package.json +++ b/packages/@aws-cdk/aws-mwaa/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-mwaa", "module": "aws_cdk.aws_mwaa" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-neptune/README.md b/packages/@aws-cdk/aws-neptune/README.md index c4f131b27d550..77f50a7f6192d 100644 --- a/packages/@aws-cdk/aws-neptune/README.md +++ b/packages/@aws-cdk/aws-neptune/README.md @@ -36,7 +36,7 @@ To set up a Neptune database, define a `DatabaseCluster`. You must always launch ```ts const cluster = new neptune.DatabaseCluster(this, 'Database', { vpc, - instanceType: neptune.InstanceType.R5_LARGE + instanceType: neptune.InstanceType.R5_LARGE, }); ``` @@ -92,7 +92,7 @@ const clusterParams = new neptune.ClusterParameterGroup(this, 'ClusterParams', { const dbParams = new neptune.ParameterGroup(this, 'DbParams', { description: 'Db parameter group', parameters: { - neptune_query_timeout: '120000' + neptune_query_timeout: '120000', }, }); @@ -113,7 +113,7 @@ attribute. const cluster = new neptune.DatabaseCluster(this, 'Database', { vpc, instanceType: neptune.InstanceType.R5_LARGE, - instances: 2 + instances: 2, }); ``` @@ -122,7 +122,7 @@ Additionally it is also possible to add replicas using `DatabaseInstance` for an ```ts fixture=with-cluster const replica1 = new neptune.DatabaseInstance(this, 'Instance', { cluster, - instanceType: neptune.InstanceType.R5_LARGE + instanceType: neptune.InstanceType.R5_LARGE, }); ``` @@ -133,9 +133,9 @@ the engine of the entire cluster to the latest minor version after a stabilizati window of 2 to 3 weeks. ```ts -new neptune.DatabaseCluster(stack, 'Cluster', { - vpc, - instanceType: InstanceType.R5_LARGE, - autoMinorVersionUpgrade: true - }); +new neptune.DatabaseCluster(this, 'Cluster', { + vpc, + instanceType: neptune.InstanceType.R5_LARGE, + autoMinorVersionUpgrade: true, +}); ``` diff --git a/packages/@aws-cdk/aws-neptune/package.json b/packages/@aws-cdk/aws-neptune/package.json index 0f524965e61f5..27c8c35f6e980 100644 --- a/packages/@aws-cdk/aws-neptune/package.json +++ b/packages/@aws-cdk/aws-neptune/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-networkfirewall/package.json b/packages/@aws-cdk/aws-networkfirewall/package.json index 0fb6559679080..91e96fd0b3a46 100644 --- a/packages/@aws-cdk/aws-networkfirewall/package.json +++ b/packages/@aws-cdk/aws-networkfirewall/package.json @@ -28,6 +28,13 @@ "distName": "aws-cdk.aws-networkfirewall", "module": "aws_cdk.aws_networkfirewall" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-networkmanager/package.json b/packages/@aws-cdk/aws-networkmanager/package.json index e86cd346d6a3c..696efca60f8eb 100644 --- a/packages/@aws-cdk/aws-networkmanager/package.json +++ b/packages/@aws-cdk/aws-networkmanager/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-nimblestudio/package.json b/packages/@aws-cdk/aws-nimblestudio/package.json index 4a760f3ca04f2..c033e0066f94e 100644 --- a/packages/@aws-cdk/aws-nimblestudio/package.json +++ b/packages/@aws-cdk/aws-nimblestudio/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-nimblestudio", "module": "aws_cdk.aws_nimblestudio" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-opsworks/package.json b/packages/@aws-cdk/aws-opsworks/package.json index ab1b39f13d8d6..838ff7003ff62 100644 --- a/packages/@aws-cdk/aws-opsworks/package.json +++ b/packages/@aws-cdk/aws-opsworks/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-opsworkscm/package.json b/packages/@aws-cdk/aws-opsworkscm/package.json index 1b32bd96310aa..1220456e3ff0b 100644 --- a/packages/@aws-cdk/aws-opsworkscm/package.json +++ b/packages/@aws-cdk/aws-opsworkscm/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-panorama/package.json b/packages/@aws-cdk/aws-panorama/package.json index b49f7b48cfb52..37658a09b0c61 100644 --- a/packages/@aws-cdk/aws-panorama/package.json +++ b/packages/@aws-cdk/aws-panorama/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.Panorama", @@ -30,6 +37,13 @@ "distName": "aws-cdk.aws-panorama", "module": "aws_cdk.aws_panorama" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-pinpoint/package.json b/packages/@aws-cdk/aws-pinpoint/package.json index fd77ed7f9c3c3..30801027c4e06 100644 --- a/packages/@aws-cdk/aws-pinpoint/package.json +++ b/packages/@aws-cdk/aws-pinpoint/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-pinpointemail/package.json b/packages/@aws-cdk/aws-pinpointemail/package.json index d50d8720e25e3..26486ee72627b 100644 --- a/packages/@aws-cdk/aws-pinpointemail/package.json +++ b/packages/@aws-cdk/aws-pinpointemail/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-qldb/package.json b/packages/@aws-cdk/aws-qldb/package.json index 584ee48921e4c..a35637354c0c1 100644 --- a/packages/@aws-cdk/aws-qldb/package.json +++ b/packages/@aws-cdk/aws-qldb/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-quicksight/package.json b/packages/@aws-cdk/aws-quicksight/package.json index 495c2e3fa0a5f..270c171d3b5cb 100644 --- a/packages/@aws-cdk/aws-quicksight/package.json +++ b/packages/@aws-cdk/aws-quicksight/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-quicksight", "module": "aws_cdk.aws_quicksight" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-ram/package.json b/packages/@aws-cdk/aws-ram/package.json index c66d84681500c..1064bb16fc89f 100644 --- a/packages/@aws-cdk/aws-ram/package.json +++ b/packages/@aws-cdk/aws-ram/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts index 310d071ec226b..01f2f62d7a4da 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts @@ -799,6 +799,11 @@ export class PostgresEngineVersion { * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 */ public static readonly VER_9_6_23 = PostgresEngineVersion.of('9.6.23', '9.6'); + /** + * Version "9.6.24". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ + public static readonly VER_9_6_24 = PostgresEngineVersion.of('9.6.24', '9.6'); /** Version "10" (only a major version, without a specific minor version). */ public static readonly VER_10 = PostgresEngineVersion.of('10', '10'); @@ -834,6 +839,8 @@ export class PostgresEngineVersion { public static readonly VER_10_17 = PostgresEngineVersion.of('10.17', '10', { s3Import: true, s3Export: true }); /** Version "10.18". */ public static readonly VER_10_18 = PostgresEngineVersion.of('10.18', '10', { s3Import: true, s3Export: true }); + /** Version "10.19". */ + public static readonly VER_10_19 = PostgresEngineVersion.of('10.19', '10', { s3Import: true, s3Export: true }); /** Version "11" (only a major version, without a specific minor version). */ public static readonly VER_11 = PostgresEngineVersion.of('11', '11', { s3Import: true }); @@ -861,6 +868,8 @@ export class PostgresEngineVersion { public static readonly VER_11_12 = PostgresEngineVersion.of('11.12', '11', { s3Import: true, s3Export: true }); /** Version "11.13". */ public static readonly VER_11_13 = PostgresEngineVersion.of('11.13', '11', { s3Import: true, s3Export: true }); + /** Version "11.14". */ + public static readonly VER_11_14 = PostgresEngineVersion.of('11.14', '11', { s3Import: true, s3Export: true }); /** Version "12" (only a major version, without a specific minor version). */ public static readonly VER_12 = PostgresEngineVersion.of('12', '12', { s3Import: true }); @@ -878,6 +887,8 @@ export class PostgresEngineVersion { public static readonly VER_12_7 = PostgresEngineVersion.of('12.7', '12', { s3Import: true, s3Export: true }); /** Version "12.8". */ public static readonly VER_12_8 = PostgresEngineVersion.of('12.8', '12', { s3Import: true, s3Export: true }); + /** Version "12.9". */ + public static readonly VER_12_9 = PostgresEngineVersion.of('12.9', '12', { s3Import: true, s3Export: true }); /** Version "13" (only a major version, without a specific minor version). */ public static readonly VER_13 = PostgresEngineVersion.of('13', '13', { s3Import: true, s3Export: true }); @@ -889,6 +900,13 @@ export class PostgresEngineVersion { public static readonly VER_13_3 = PostgresEngineVersion.of('13.3', '13', { s3Import: true, s3Export: true }); /** Version "13.4". */ public static readonly VER_13_4 = PostgresEngineVersion.of('13.4', '13', { s3Import: true, s3Export: true }); + /** Version "13.5". */ + public static readonly VER_13_5 = PostgresEngineVersion.of('13.5', '13', { s3Import: true, s3Export: true }); + + /** Version "14" (only a major version, without a specific minor version). */ + public static readonly VER_14 = PostgresEngineVersion.of('14', '14', { s3Import: true, s3Export: true }); + /** Version "14.1". */ + public static readonly VER_14_1 = PostgresEngineVersion.of('14.1', '14', { s3Import: true, s3Export: true }); /** * Create a new PostgresEngineVersion with an arbitrary version. diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index bd56c7dd94539..0da51c0d59691 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-events-targets": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts b/packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts index 826c988688c24..f6e3824092442 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import { AuroraEngineVersion, AuroraMysqlEngineVersion, AuroraPostgresEngineVersion, DatabaseClusterEngine } from '../lib'; describe('cluster engine', () => { @@ -11,8 +10,6 @@ describe('cluster engine', () => { // THEN expect(family).toEqual('aurora5.6'); - - }); test("default parameterGroupFamily for versionless Aurora MySQL cluster engine is 'aurora-mysql5.7'", () => { @@ -24,8 +21,6 @@ describe('cluster engine', () => { // THEN expect(family).toEqual('aurora-mysql5.7'); - - }); test('default parameterGroupFamily for versionless Aurora PostgreSQL is not defined', () => { @@ -37,8 +32,6 @@ describe('cluster engine', () => { // THEN expect(family).toEqual(undefined); - - }); test('cluster parameter group correctly determined for AURORA and given version', () => { @@ -52,8 +45,6 @@ describe('cluster engine', () => { // THEN expect(family).toEqual('aurora5.6'); - - }); test('cluster parameter group correctly determined for AURORA_MYSQL and given version', () => { @@ -67,8 +58,6 @@ describe('cluster engine', () => { // THEN expect(family).toEqual('aurora-mysql5.7'); - - }); test('cluster parameter group correctly determined for AURORA_MYSQL and given version 3', () => { @@ -95,8 +84,6 @@ describe('cluster engine', () => { // THEN expect(family).toEqual('aurora-postgresql11'); - - }); test('parameter group family', () => { @@ -117,8 +104,6 @@ describe('cluster engine', () => { 'aurora-postgresql9.6'); expect(DatabaseClusterEngine.auroraPostgres({ version: AuroraPostgresEngineVersion.of('10.0', '10') }).parameterGroupFamily).toEqual( 'aurora-postgresql10'); - - }); test('supported log types', () => { @@ -126,6 +111,5 @@ describe('cluster engine', () => { expect(DatabaseClusterEngine.aurora({ version: AuroraEngineVersion.VER_1_22_2 }).supportedLogTypes).toEqual(mysqlLogTypes); expect(DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_2_08_1 }).supportedLogTypes).toEqual(mysqlLogTypes); expect(DatabaseClusterEngine.auroraPostgres({ version: AuroraPostgresEngineVersion.VER_9_6_9 }).supportedLogTypes).toEqual(['postgresql']); - }); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/aws-rds/test/cluster.test.ts b/packages/@aws-cdk/aws-rds/test/cluster.test.ts index b3dbdfc81d007..aa72d19f4f7d9 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, ResourcePart, SynthUtils } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import { ManagedPolicy, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; @@ -34,7 +33,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { Properties: { Engine: 'aurora', DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' }, @@ -46,13 +45,13 @@ describe('cluster', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toCountResources('AWS::RDS::DBInstance', 2); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBInstance', 2); + Template.fromStack(stack).hasResource('AWS::RDS::DBInstance', { DeletionPolicy: 'Delete', UpdateReplacePolicy: 'Delete', - }, ResourcePart.CompleteDefinition); + }); }); test('validates that the number of instances is not a deploy-time value', () => { @@ -91,7 +90,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { Engine: 'aurora', DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' }, MasterUsername: 'admin', @@ -146,7 +145,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { Engine: 'aurora', DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' }, MasterUsername: 'admin', @@ -182,7 +181,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBClusterParameterGroupName: { Ref: 'ParamsA8366201' }, }); }); @@ -201,10 +200,10 @@ describe('cluster', () => { removalPolicy: cdk.RemovalPolicy.RETAIN, }); - expect(stack).toHaveResourceLike('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResource('AWS::RDS::DBSubnetGroup', { DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition); + }); }); test('creates a secret when master credentials are not specified', () => { @@ -226,7 +225,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { MasterUsername: { 'Fn::Join': [ '', @@ -253,7 +252,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { GenerateSecretString: { ExcludeCharacters: '\"@/\\', GenerateStringKey: 'password', @@ -282,7 +281,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { KmsKeyId: { 'Fn::GetAtt': [ 'Key961B73FD', @@ -316,7 +315,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBParameterGroupName: { Ref: 'ParameterGroup5E32DECB', }, @@ -343,7 +342,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { EnablePerformanceInsights: true, PerformanceInsightsRetentionPeriod: 731, PerformanceInsightsKMSKeyId: { 'Fn::GetAtt': ['Key961B73FD', 'Arn'] }, @@ -367,7 +366,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { EnablePerformanceInsights: true, PerformanceInsightsRetentionPeriod: 731, }); @@ -408,7 +407,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { AutoMinorVersionUpgrade: false, }); }); @@ -427,7 +426,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { AllowMajorVersionUpgrade: true, }); }); @@ -446,7 +445,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DeleteAutomatedBackups: false, }); }); @@ -471,7 +470,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { Engine: 'aurora-mysql', EngineVersion: '5.7.mysql_aurora.2.04.4', }); @@ -497,12 +496,10 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { Engine: 'aurora-postgresql', EngineVersion: '10.7', }); - - }); test('cluster exposes different read and write endpoints', () => { @@ -546,7 +543,7 @@ describe('cluster', () => { cluster.connections.allowToAnyIpv4(ec2.Port.tcp(443)); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { GroupId: 'sg-123456789', }); }); @@ -559,8 +556,6 @@ describe('cluster', () => { }); expect(cluster.clusterIdentifier).toEqual('identifier'); - - }); test('minimal imported cluster throws on accessing attributes for unprovided parameters', () => { @@ -621,8 +616,6 @@ describe('cluster', () => { account: '12345', region: 'us-test-1', }); - - }); test('cluster with enabled monitoring', () => { @@ -645,14 +638,14 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { MonitoringInterval: 60, MonitoringRoleArn: { 'Fn::GetAtt': ['DatabaseMonitoringRole576991DA', 'Arn'], }, - }, ResourcePart.Properties); + }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -680,8 +673,6 @@ describe('cluster', () => { }, ], }); - - }); test('create a cluster with imported monitoring role', () => { @@ -712,14 +703,12 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { MonitoringInterval: 60, MonitoringRoleArn: { 'Fn::GetAtt': ['MonitoringRole90457BF9', 'Arn'], }, - }, ResourcePart.Properties); - - + }); }); test('addRotationSingleUser()', () => { @@ -737,7 +726,7 @@ describe('cluster', () => { // WHEN cluster.addRotationSingleUser(); - expect(stack).toHaveResource('AWS::SecretsManager::RotationSchedule', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { SecretId: { Ref: 'DatabaseSecretAttachmentE5D1B020', }, @@ -768,7 +757,7 @@ describe('cluster', () => { const userSecret = new DatabaseSecret(stack, 'UserSecret', { username: 'user' }); cluster.addRotationMultiUser('user', { secret: userSecret.attach(cluster) }); - expect(stack).toHaveResource('AWS::SecretsManager::RotationSchedule', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { SecretId: { Ref: 'UserSecretAttachment16ACBE6D', }, @@ -783,7 +772,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Parameters: { masterSecretArn: { Ref: 'DatabaseSecretAttachmentE5D1B020', @@ -822,13 +811,13 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::RotationSchedule', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { AutomaticallyAfterDays: 15, }, }); - expect(stack).toHaveResource('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Parameters: { endpoint: { 'Fn::Join': ['', [ @@ -882,7 +871,7 @@ describe('cluster', () => { // Rotation in isolated subnet with access to Secrets Manager API via endpoint cluster.addRotationSingleUser({ endpoint }); - expect(stack).toHaveResource('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Parameters: { endpoint: { 'Fn::Join': ['', [ @@ -933,8 +922,6 @@ describe('cluster', () => { // THEN expect(() => cluster.addRotationSingleUser()).toThrow(/without secret/); - - }); test('throws when trying to add single user rotation multiple times', () => { @@ -955,8 +942,6 @@ describe('cluster', () => { // THEN expect(() => cluster.addRotationSingleUser()).toThrow(/A single user rotation was already added to this cluster/); - - }); test('create a cluster with s3 import role', () => { @@ -983,7 +968,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -994,7 +979,7 @@ describe('cluster', () => { }], }); - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Family: 'aurora5.6', Parameters: { aurora_load_from_s3_role: { @@ -1005,8 +990,6 @@ describe('cluster', () => { }, }, }); - - }); test('create a cluster with s3 import buckets', () => { @@ -1031,7 +1014,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1042,7 +1025,7 @@ describe('cluster', () => { }], }); - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Family: 'aurora5.6', Parameters: { aurora_load_from_s3_role: { @@ -1054,7 +1037,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -1091,8 +1074,6 @@ describe('cluster', () => { Version: '2012-10-17', }, }); - - }); test('cluster with s3 import bucket adds supported feature name to IAM role', () => { @@ -1119,7 +1100,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1130,8 +1111,6 @@ describe('cluster', () => { FeatureName: 's3Import', }], }); - - }); test('throws when s3 import bucket or s3 export bucket is supplied for a Postgres version that does not support it', () => { @@ -1175,8 +1154,6 @@ describe('cluster', () => { s3ExportBuckets: [bucket], }); }).toThrow(/s3Export is not supported for Postgres version: 10.4. Use a version that supports the s3Export feature./); - - }); test('cluster with s3 export bucket adds supported feature name to IAM role', () => { @@ -1203,7 +1180,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1214,8 +1191,6 @@ describe('cluster', () => { FeatureName: 's3Export', }], }); - - }); test('create a cluster with s3 export role', () => { @@ -1242,7 +1217,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1253,7 +1228,7 @@ describe('cluster', () => { }], }); - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Family: 'aurora5.6', Parameters: { aurora_select_into_s3_role: { @@ -1264,8 +1239,6 @@ describe('cluster', () => { }, }, }); - - }); testFutureBehavior('create a cluster with s3 export buckets', { [cxapi.S3_GRANT_WRITE_WITHOUT_ACL]: true }, cdk.App, (app) => { @@ -1290,7 +1263,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1301,7 +1274,7 @@ describe('cluster', () => { }], }); - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Family: 'aurora5.6', Parameters: { aurora_select_into_s3_role: { @@ -1313,7 +1286,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -1323,6 +1296,10 @@ describe('cluster', () => { 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], Effect: 'Allow', @@ -1353,8 +1330,6 @@ describe('cluster', () => { Version: '2012-10-17', }, }); - - }); test('create a cluster with s3 import and export buckets', () => { @@ -1381,7 +1356,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1400,7 +1375,7 @@ describe('cluster', () => { }], }); - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Family: 'aurora5.6', Parameters: { aurora_load_from_s3_role: { @@ -1417,8 +1392,6 @@ describe('cluster', () => { }, }, }); - - }); test('create a cluster with s3 import and export buckets and custom parameter group', () => { @@ -1453,7 +1426,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1472,7 +1445,7 @@ describe('cluster', () => { }], }); - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Family: 'aurora5.6', Parameters: { key: 'value', @@ -1490,8 +1463,6 @@ describe('cluster', () => { }, }, }); - - }); test('PostgreSQL cluster with s3 export buckets does not generate custom parameter group and specifies the correct port', () => { @@ -1518,7 +1489,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [{ RoleArn: { 'Fn::GetAtt': [ @@ -1531,9 +1502,7 @@ describe('cluster', () => { Port: 5432, }); - expect(stack).not.toHaveResource('AWS::RDS::DBClusterParameterGroup'); - - + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBClusterParameterGroup', 0); }); test('unversioned PostgreSQL cluster can be used with s3 import and s3 export buckets', () => { @@ -1560,7 +1529,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { AssociatedRoles: [ { FeatureName: 's3Import', @@ -1582,8 +1551,6 @@ describe('cluster', () => { }, ], }); - - }); test("Aurora PostgreSQL cluster uses a different default master username than 'admin', which is a reserved word", () => { @@ -1600,13 +1567,11 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { GenerateSecretString: { SecretStringTemplate: '{"username":"postgres"}', }, }); - - }); test('MySQL cluster without S3 exports or imports references the correct default ParameterGroup', () => { @@ -1628,13 +1593,11 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBClusterParameterGroupName: 'default.aurora-mysql5.7', }); - expect(stack).not.toHaveResource('AWS::RDS::DBClusterParameterGroup'); - - + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBClusterParameterGroup', 0); }); test('throws when s3ExportRole and s3ExportBuckets properties are both specified', () => { @@ -1661,8 +1624,6 @@ describe('cluster', () => { s3ExportRole: exportRole, s3ExportBuckets: [exportBucket], })).toThrow(); - - }); test('throws when s3ImportRole and s3ImportBuckets properties are both specified', () => { @@ -1689,8 +1650,6 @@ describe('cluster', () => { s3ImportRole: importRole, s3ImportBuckets: [importBucket], })).toThrow(); - - }); test('can set CloudWatch log exports', () => { @@ -1713,11 +1672,9 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { EnableCloudwatchLogsExports: ['error', 'general', 'slowquery', 'audit'], }); - - }); test('can set CloudWatch log retention', () => { @@ -1741,7 +1698,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { ServiceToken: { 'Fn::GetAtt': [ 'LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A', @@ -1751,7 +1708,7 @@ describe('cluster', () => { LogGroupName: { 'Fn::Join': ['', ['/aws/rds/cluster/', { Ref: 'DatabaseB269D8BB' }, '/error']] }, RetentionInDays: 90, }); - expect(stack).toHaveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { ServiceToken: { 'Fn::GetAtt': [ 'LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A', @@ -1761,8 +1718,6 @@ describe('cluster', () => { LogGroupName: { 'Fn::Join': ['', ['/aws/rds/cluster/', { Ref: 'DatabaseB269D8BB' }, '/general']] }, RetentionInDays: 90, }); - - }); test('throws if given unsupported CloudWatch log exports', () => { @@ -1784,8 +1739,6 @@ describe('cluster', () => { cloudwatchLogsExports: ['error', 'general', 'slowquery', 'audit', 'thislogdoesnotexist', 'neitherdoesthisone'], }); }).toThrow(/Unsupported logs for the current engine type: thislogdoesnotexist,neitherdoesthisone/); - - }); test('can set deletion protection', () => { @@ -1808,16 +1761,15 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DeletionProtection: true, }); - - }); test('does not throw (but adds a node error) if a (dummy) VPC does not have sufficient subnets', () => { // GIVEN - const stack = testStack(); + const app = new cdk.App(); + const stack = testStack(app, 'TestStack'); const vpc = ec2.Vpc.fromLookup(stack, 'VPC', { isDefault: true }); // WHEN @@ -1837,11 +1789,9 @@ describe('cluster', () => { }); // THEN - const art = SynthUtils.synthesize(stack); + const art = app.synth().getStackArtifact('TestStack'); const meta = art.findMetadataByType('aws:cdk:error'); expect(meta[0].data).toEqual('Cluster requires at least 2 subnets, got 0'); - - }); test('create a cluster from a snapshot', () => { @@ -1859,7 +1809,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { Properties: { Engine: 'aurora', EngineVersion: '5.6.mysql_aurora.1.22.2', @@ -1870,9 +1820,9 @@ describe('cluster', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toCountResources('AWS::RDS::DBInstance', 2); + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBInstance', 2); expect(cluster.instanceIdentifiers).toHaveLength(2); expect(stack.resolve(cluster.instanceIdentifiers[0])).toEqual({ @@ -1915,12 +1865,10 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBSubnetGroupName: 'my-subnet-group', }); - expect(stack).toCountResources('AWS::RDS::DBSubnetGroup', 0); - - + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBSubnetGroup', 0); }); test('defaultChild returns the DB Cluster', () => { @@ -1941,8 +1889,6 @@ describe('cluster', () => { // THEN expect(cluster.node.defaultChild instanceof CfnDBCluster).toBeTruthy(); - - }); test('fromGeneratedSecret', () => { @@ -1960,7 +1906,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { MasterUsername: 'admin', // username is a string MasterUserPassword: { 'Fn::Join': [ @@ -1994,7 +1940,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { ReplicaRegions: [ { Region: 'eu-west-1', @@ -2023,7 +1969,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Name: secretName, }); }); @@ -2044,7 +1990,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Name: secretName, }); }); @@ -2066,12 +2012,10 @@ describe('cluster', () => { }, }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Engine: 'aurora', PubliclyAccessible: true, }); - - }); test('can set public accessibility for database cluster with instances in public subnet', () => { @@ -2091,12 +2035,10 @@ describe('cluster', () => { }, }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Engine: 'aurora', PubliclyAccessible: false, }); - - }); test('database cluster instances in public subnet should by default have publiclyAccessible set to true', () => { @@ -2115,12 +2057,10 @@ describe('cluster', () => { }, }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Engine: 'aurora', PubliclyAccessible: true, }); - - }); test('changes the case of the cluster identifier if the lowercaseDbIdentifier feature flag is enabled', () => { @@ -2140,7 +2080,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBClusterIdentifier: clusterIdentifier.toLowerCase(), }); }); @@ -2160,7 +2100,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBClusterIdentifier: clusterIdentifier, }); }); @@ -2179,7 +2119,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { CopyTagsToSnapshot: true, }); }); @@ -2199,7 +2139,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { CopyTagsToSnapshot: false, }); }); @@ -2219,7 +2159,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { CopyTagsToSnapshot: true, }); }); @@ -2239,7 +2179,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { BacktrackWindow: 24 * 60 * 60, }); }); @@ -2247,8 +2187,8 @@ describe('cluster', () => { test.each([ [cdk.RemovalPolicy.RETAIN, 'Retain', 'Retain', 'Retain'], - [cdk.RemovalPolicy.SNAPSHOT, 'Snapshot', 'Delete', ABSENT], - [cdk.RemovalPolicy.DESTROY, 'Delete', 'Delete', ABSENT], + [cdk.RemovalPolicy.SNAPSHOT, 'Snapshot', 'Delete', Match.absent()], + [cdk.RemovalPolicy.DESTROY, 'Delete', 'Delete', Match.absent()], ])('if Cluster RemovalPolicy is \'%s\', the DBCluster has DeletionPolicy \'%s\', the DBInstance has \'%s\' and the DBSubnetGroup has \'%s\'', (clusterRemovalPolicy, clusterValue, instanceValue, subnetValue) => { const stack = new cdk.Stack(); @@ -2264,25 +2204,25 @@ test.each([ }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { DeletionPolicy: clusterValue, UpdateReplacePolicy: clusterValue, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResource('AWS::RDS::DBInstance', { DeletionPolicy: instanceValue, UpdateReplacePolicy: instanceValue, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResourceLike('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResource('AWS::RDS::DBSubnetGroup', { DeletionPolicy: subnetValue, - }, ResourcePart.CompleteDefinition); + }); }); test.each([ [cdk.RemovalPolicy.RETAIN, 'Retain', 'Retain', 'Retain'], - [cdk.RemovalPolicy.SNAPSHOT, 'Snapshot', 'Delete', ABSENT], - [cdk.RemovalPolicy.DESTROY, 'Delete', 'Delete', ABSENT], + [cdk.RemovalPolicy.SNAPSHOT, 'Snapshot', 'Delete', Match.absent()], + [cdk.RemovalPolicy.DESTROY, 'Delete', 'Delete', Match.absent()], ])('if Cluster RemovalPolicy is \'%s\', the DBCluster has DeletionPolicy \'%s\', the DBInstance has \'%s\' and the DBSubnetGroup has \'%s\'', (clusterRemovalPolicy, clusterValue, instanceValue, subnetValue) => { const stack = new cdk.Stack(); @@ -2298,25 +2238,24 @@ test.each([ }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { DeletionPolicy: clusterValue, UpdateReplacePolicy: clusterValue, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResource('AWS::RDS::DBInstance', { DeletionPolicy: instanceValue, UpdateReplacePolicy: instanceValue, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResourceLike('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResource('AWS::RDS::DBSubnetGroup', { DeletionPolicy: subnetValue, UpdateReplacePolicy: subnetValue, - }, ResourcePart.CompleteDefinition); + }); }); - -function testStack(app?: cdk.App) { - const stack = new cdk.Stack(app, undefined, { env: { account: '12345', region: 'us-test-1' } }); +function testStack(app?: cdk.App, stackId?: string) { + const stack = new cdk.Stack(app, stackId, { env: { account: '12345', region: 'us-test-1' } }); stack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']); return stack; } diff --git a/packages/@aws-cdk/aws-rds/test/database-secret.test.ts b/packages/@aws-cdk/aws-rds/test/database-secret.test.ts index 9fe7793536a1c..424fe4164fd74 100644 --- a/packages/@aws-cdk/aws-rds/test/database-secret.test.ts +++ b/packages/@aws-cdk/aws-rds/test/database-secret.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { CfnResource, Stack } from '@aws-cdk/core'; import { DatabaseSecret } from '../lib'; import { DEFAULT_PASSWORD_EXCLUDE_CHARS } from '../lib/private/util'; @@ -14,7 +14,7 @@ describe('database secret', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Description: { 'Fn::Join': [ '', @@ -35,8 +35,6 @@ describe('database secret', () => { }); expect(getSecretLogicalId(dbSecret, stack)).toEqual('SecretA720EF05'); - - }); test('with master secret', () => { @@ -54,7 +52,7 @@ describe('database secret', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { GenerateSecretString: { ExcludeCharacters: '"@/\\', GenerateStringKey: 'password', @@ -73,8 +71,6 @@ describe('database secret', () => { }, }, }); - - }); test('replace on password critera change', () => { @@ -106,8 +102,6 @@ describe('database secret', () => { replaceOnPasswordCriteriaChanges: true, }); expect(dbSecretlogicalId).not.toEqual(getSecretLogicalId(otherSecret2, stack)); - - }); }); diff --git a/packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts b/packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts index 135704a893f75..8d1da9e04b1b6 100644 --- a/packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts +++ b/packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as cdk from '@aws-cdk/core'; @@ -21,7 +20,7 @@ describe('database secret manager', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { Properties: { Engine: 'aurora-postgresql', DBClusterParameterGroupName: 'default.aurora-postgresql10', @@ -65,9 +64,7 @@ describe('database secret manager', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); - - + }); }); }); diff --git a/packages/@aws-cdk/aws-rds/test/instance-engine.test.ts b/packages/@aws-cdk/aws-rds/test/instance-engine.test.ts index 99ed162c4f5eb..f9a93f3bca867 100644 --- a/packages/@aws-cdk/aws-rds/test/instance-engine.test.ts +++ b/packages/@aws-cdk/aws-rds/test/instance-engine.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as rds from '../lib'; @@ -10,8 +10,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless MySQL instance engine is not defined', () => { @@ -20,8 +18,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless PostgreSQL instance engine is not defined', () => { @@ -30,8 +26,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test("default parameterGroupFamily for versionless Oracle SE instance engine is 'oracle-se-11.2'", () => { @@ -40,8 +34,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual('oracle-se-11.2'); - - }); test("default parameterGroupFamily for versionless Oracle SE 1 instance engine is 'oracle-se1-11.2'", () => { @@ -50,8 +42,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual('oracle-se1-11.2'); - - }); test('default parameterGroupFamily for versionless Oracle SE 2 instance engine is not defined', () => { @@ -60,8 +50,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless Oracle EE instance engine is not defined', () => { @@ -70,8 +58,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless SQL Server SE instance engine is not defined', () => { @@ -80,8 +66,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless SQL Server EX instance engine is not defined', () => { @@ -90,8 +74,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless SQL Server Web instance engine is not defined', () => { @@ -100,8 +82,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); test('default parameterGroupFamily for versionless SQL Server EE instance engine is not defined', () => { @@ -110,8 +90,6 @@ describe('instance engine', () => { const family = engine.parameterGroupFamily; expect(family).toEqual(undefined); - - }); describe('Oracle engine bindToInstance', () => { @@ -122,8 +100,6 @@ describe('instance engine', () => { const engineConfig = engine.bindToInstance(new cdk.Stack(), {}); expect(engineConfig.features?.s3Import).toEqual('S3_INTEGRATION'); expect(engineConfig.features?.s3Export).toEqual('S3_INTEGRATION'); - - }); test('s3 import/export - creates an option group if needed', () => { @@ -136,15 +112,13 @@ describe('instance engine', () => { }); expect(engineConfig.optionGroup).toBeDefined(); - expect(stack).toHaveResourceLike('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { EngineName: 'oracle-se2', OptionConfigurations: [{ OptionName: 'S3_INTEGRATION', OptionVersion: '1.0', }], }); - - }); test('s3 import/export - appends to an existing option group if it exists', () => { @@ -163,7 +137,7 @@ describe('instance engine', () => { }); expect(engineConfig.optionGroup).toEqual(optionGroup); - expect(stack).toHaveResourceLike('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { EngineName: 'oracle-se2', OptionConfigurations: [{ OptionName: 'MY_OPTION_CONFIG', @@ -173,8 +147,6 @@ describe('instance engine', () => { OptionVersion: '1.0', }], }); - - }); }); @@ -185,8 +157,6 @@ describe('instance engine', () => { const engineConfig = engine.bindToInstance(new cdk.Stack(), {}); expect(engineConfig.features?.s3Import).toEqual('S3_INTEGRATION'); expect(engineConfig.features?.s3Export).toEqual('S3_INTEGRATION'); - - }); test('s3 import/export - throws if roles are not equal', () => { @@ -200,8 +170,6 @@ describe('instance engine', () => { expect(() => engine.bindToInstance(new cdk.Stack(), { s3ImportRole })).not.toThrow(); expect(() => engine.bindToInstance(new cdk.Stack(), { s3ExportRole })).not.toThrow(); expect(() => engine.bindToInstance(new cdk.Stack(), { s3ImportRole, s3ExportRole: s3ImportRole })).not.toThrow(); - - }); test('s3 import/export - creates an option group if needed', () => { @@ -214,7 +182,7 @@ describe('instance engine', () => { }); expect(engineConfig.optionGroup).toBeDefined(); - expect(stack).toHaveResourceLike('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { EngineName: 'sqlserver-se', OptionConfigurations: [{ OptionName: 'SQLSERVER_BACKUP_RESTORE', @@ -224,8 +192,6 @@ describe('instance engine', () => { }], }], }); - - }); test('s3 import/export - appends to an existing option group if it exists', () => { @@ -244,7 +210,7 @@ describe('instance engine', () => { }); expect(engineConfig.optionGroup).toEqual(optionGroup); - expect(stack).toHaveResourceLike('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { EngineName: 'sqlserver-se', OptionConfigurations: [{ OptionName: 'MY_OPTION_CONFIG', @@ -257,8 +223,6 @@ describe('instance engine', () => { }], }], }); - - }); }); @@ -269,8 +233,6 @@ describe('instance engine', () => { const engineConfig = engineNewerVersion.bindToInstance(new cdk.Stack(), {}); expect(engineConfig.features?.s3Import).toEqual(undefined); expect(engineConfig.features?.s3Export).toEqual(undefined); - - }); test('returns s3 import/export feature if the version supports it', () => { @@ -279,8 +241,6 @@ describe('instance engine', () => { const engineConfig = engineNewerVersion.bindToInstance(new cdk.Stack(), {}); expect(engineConfig.features?.s3Import).toEqual('s3Import'); expect(engineConfig.features?.s3Export).toEqual('s3Export'); - - }); }); }); diff --git a/packages/@aws-cdk/aws-rds/test/instance.test.ts b/packages/@aws-cdk/aws-rds/test/instance.test.ts index 484ad765cb790..6681f7e77aab7 100644 --- a/packages/@aws-cdk/aws-rds/test/instance.test.ts +++ b/packages/@aws-cdk/aws-rds/test/instance.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, ResourcePart, anything } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as targets from '@aws-cdk/aws-events-targets'; import { ManagedPolicy, Role, ServicePrincipal, AccountPrincipal } from '@aws-cdk/aws-iam'; @@ -49,7 +48,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResource('AWS::RDS::DBInstance', { Properties: { DBInstanceClass: 'db.t2.medium', AllocatedStorage: '100', @@ -117,9 +116,9 @@ describe('instance', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupDescription: 'Subnet group for Instance database', SubnetIds: [ { @@ -131,11 +130,11 @@ describe('instance', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Security group for Instance database', }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -164,7 +163,7 @@ describe('instance', () => { ], }); - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Description: { 'Fn::Join': [ '', @@ -184,7 +183,7 @@ describe('instance', () => { }, }); - expect(stack).toHaveResource('AWS::SecretsManager::SecretTargetAttachment', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::SecretTargetAttachment', { SecretId: { Ref: 'InstanceSecret478E0A47', }, @@ -194,9 +193,7 @@ describe('instance', () => { TargetType: 'AWS::RDS::DBInstance', }); - expect(stack).toCountResources('Custom::LogRetention', 4); - - + Template.fromStack(stack).resourceCountIs('Custom::LogRetention', 4); }); test('throws when create database with specific AZ and multiAZ enabled', () => { @@ -239,7 +236,7 @@ describe('instance', () => { parameterGroup, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBParameterGroupName: { Ref: 'ParameterGroup5E32DECB', }, @@ -247,8 +244,6 @@ describe('instance', () => { Ref: 'OptionGroupACA43DC1', }, }); - - }); test('can specify subnet type', () => { @@ -263,13 +258,13 @@ describe('instance', () => { }, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBSubnetGroupName: { Ref: 'InstanceSubnetGroupF2CBA54F', }, PubliclyAccessible: false, }); - expect(stack).toHaveResource('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupDescription: 'Subnet group for Instance database', SubnetIds: [ { @@ -280,8 +275,6 @@ describe('instance', () => { }, ], }); - - }); describe('DatabaseInstanceFromSnapshot', () => { @@ -293,11 +286,9 @@ describe('instance', () => { vpc, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBSnapshotIdentifier: 'my-snapshot', }); - - }); test('can generate a new snapshot password', () => { @@ -310,8 +301,8 @@ describe('instance', () => { }), }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { - MasterUsername: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { + MasterUsername: Match.absent(), MasterUserPassword: { 'Fn::Join': ['', [ '{{resolve:secretsmanager:', @@ -320,7 +311,7 @@ describe('instance', () => { ]], }, }); - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Description: { 'Fn::Join': ['', ['Generated by the CDK for stack: ', { Ref: 'AWS::StackName' }]], }, @@ -331,8 +322,6 @@ describe('instance', () => { SecretStringTemplate: '{"username":"admin"}', }, }); - - }); test('fromGeneratedSecret with replica regions', () => { @@ -345,7 +334,7 @@ describe('instance', () => { }), }); - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { ReplicaRegions: [ { Region: 'eu-west-1', @@ -361,8 +350,6 @@ describe('instance', () => { vpc, credentials: { generatePassword: true }, })).toThrow(/`credentials` `username` must be specified when `generatePassword` is set to true/); - - }); test('can set a new snapshot password from an existing SecretValue', () => { @@ -374,12 +361,10 @@ describe('instance', () => { }); // TODO - Expect this to be broken - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { - MasterUsername: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { + MasterUsername: Match.absent(), MasterUserPassword: 'mysecretpassword', }); - - }); test('can set a new snapshot password from an existing Secret', () => { @@ -394,14 +379,12 @@ describe('instance', () => { credentials: rds.SnapshotCredentials.fromSecret(secret), }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { - MasterUsername: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { + MasterUsername: Match.absent(), MasterUserPassword: { 'Fn::Join': ['', ['{{resolve:secretsmanager:', { Ref: 'DBSecretD58955BC' }, ':SecretString:password::}}']], }, }); - - }); test('can create a new database instance with fromDatabaseInstanceAttributes using a token for the port', () => { @@ -425,9 +408,9 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveOutput({ - exportName: 'databaseUrl', - outputValue: { + Template.fromStack(stack).hasOutput('portOutput', { + Export: { Name: 'databaseUrl' }, + Value: { Ref: 'DatabasePort', }, }); @@ -449,7 +432,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { SourceDBInstanceIdentifier: { 'Fn::Join': ['', [ 'arn:', @@ -466,8 +449,6 @@ describe('instance', () => { Ref: 'ReadReplicaSubnetGroup680C605C', }, }); - - }); test('on event', () => { @@ -485,7 +466,7 @@ describe('instance', () => { instance.onEvent('InstanceEvent', { target: new targets.LambdaFunction(fn) }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventPattern: { source: [ 'aws.rds', @@ -528,8 +509,6 @@ describe('instance', () => { }, ], }); - - }); test('on event without target', () => { @@ -542,7 +521,7 @@ describe('instance', () => { instance.onEvent('InstanceEvent'); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventPattern: { source: [ 'aws.rds', @@ -574,8 +553,6 @@ describe('instance', () => { ], }, }); - - }); test('can use metricCPUUtilization', () => { @@ -593,8 +570,6 @@ describe('instance', () => { period: cdk.Duration.minutes(5), statistic: 'Average', }); - - }); test('can resolve endpoint port and socket address', () => { @@ -618,8 +593,6 @@ describe('instance', () => { ], ], }); - - }); test('can deactivate backup', () => { @@ -631,11 +604,9 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { BackupRetentionPeriod: 0, }); - - }); test('imported instance with imported security group with allowAllOutbound set to false', () => { @@ -652,11 +623,9 @@ describe('instance', () => { instance.connections.allowToAnyIpv4(ec2.Port.tcp(443)); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { GroupId: 'sg-123456789', }); - - }); test('create an instance with imported monitoring role', () => { @@ -676,14 +645,12 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { MonitoringInterval: 60, MonitoringRoleArn: { 'Fn::GetAtt': ['MonitoringRole90457BF9', 'Arn'], }, - }, ResourcePart.Properties); - - + }); }); test('create an instance with an existing security group', () => { @@ -700,11 +667,11 @@ describe('instance', () => { instance.connections.allowDefaultPortFromAnyIpv4(); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { VPCSecurityGroups: ['sg-123456789'], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { FromPort: { 'Fn::GetAtt': [ 'InstanceC1063A87', @@ -719,8 +686,6 @@ describe('instance', () => { ], }, }); - - }); test('addRotationSingleUser()', () => { @@ -734,7 +699,7 @@ describe('instance', () => { instance.addRotationSingleUser(); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::RotationSchedule', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { SecretId: { Ref: 'DatabaseSecretAttachmentE5D1B020', }, @@ -762,7 +727,7 @@ describe('instance', () => { instance.addRotationMultiUser('user', { secret: userSecret.attach(instance) }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::RotationSchedule', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { SecretId: { Ref: 'UserSecretAttachment16ACBE6D', }, @@ -777,7 +742,7 @@ describe('instance', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Parameters: { masterSecretArn: { Ref: 'DatabaseSecretAttachmentE5D1B020', @@ -812,13 +777,13 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::RotationSchedule', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { RotationRules: { AutomaticallyAfterDays: 15, }, }); - expect(stack).toHaveResource('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Parameters: { endpoint: { 'Fn::Join': ['', [ @@ -870,7 +835,7 @@ describe('instance', () => { instance.addRotationSingleUser({ endpoint }); // THEN - expect(stack).toHaveResource('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Parameters: { endpoint: { 'Fn::Join': ['', [ @@ -910,8 +875,6 @@ describe('instance', () => { // THEN expect(() => instance.addRotationSingleUser()).toThrow(/without secret/); - - }); test('throws when trying to add single user rotation multiple times', () => { @@ -927,8 +890,6 @@ describe('instance', () => { // THEN expect(() => instance.addRotationSingleUser()).toThrow(/A single user rotation was already added to this instance/); - - }); test('throws when timezone is set for non-sqlserver database engine', () => { @@ -953,8 +914,6 @@ describe('instance', () => { vpc, })).toThrow(/timezone property can not be configured for/); }); - - }); test('create an instance from snapshot with maximum allocated storage', () => { @@ -967,12 +926,10 @@ describe('instance', () => { maxAllocatedStorage: 200, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBSnapshotIdentifier: 'my-snapshot', MaxAllocatedStorage: 200, }); - - }); test('create a DB instance with maximum allocated storage', () => { @@ -985,12 +942,10 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { BackupRetentionPeriod: 0, MaxAllocatedStorage: 250, }); - - }); test('iam authentication - off by default', () => { @@ -999,11 +954,9 @@ describe('instance', () => { vpc, }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { - EnableIAMDatabaseAuthentication: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { + EnableIAMDatabaseAuthentication: Match.absent(), }); - - }); test('createGrant - creates IAM policy and enables IAM auth', () => { @@ -1016,10 +969,10 @@ describe('instance', () => { }); instance.grantConnect(role); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { EnableIAMDatabaseAuthentication: true, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [{ Effect: 'Allow', @@ -1031,8 +984,6 @@ describe('instance', () => { Version: '2012-10-17', }, }); - - }); test('createGrant - throws if IAM auth disabled', () => { @@ -1046,8 +997,6 @@ describe('instance', () => { }); expect(() => { instance.grantConnect(role); }).toThrow(/Cannot grant connect when IAM authentication is disabled/); - - }); test('domain - sets domain property', () => { @@ -1061,11 +1010,9 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Domain: domain, }); - - }); test('domain - uses role if provided', () => { @@ -1081,12 +1028,10 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Domain: domain, DomainIAMRoleName: stack.resolve(role.roleName), }); - - }); test('domain - creates role if not provided', () => { @@ -1100,12 +1045,12 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Domain: domain, - DomainIAMRoleName: anything(), + DomainIAMRoleName: Match.anyValue(), }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -1133,8 +1078,6 @@ describe('instance', () => { }, ], }); - - }); test('throws when domain is set for mariadb database engine', () => { @@ -1161,8 +1104,6 @@ describe('instance', () => { vpc, })).toThrow(expectedError); }); - - }); describe('performance insights', () => { @@ -1175,13 +1116,11 @@ describe('instance', () => { performanceInsightEncryptionKey: new kms.Key(stack, 'Key'), }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { EnablePerformanceInsights: true, PerformanceInsightsRetentionPeriod: 731, PerformanceInsightsKMSKeyId: { 'Fn::GetAtt': ['Key961B73FD', 'Arn'] }, }); - - }); test('setting performance insights fields enables performance insights', () => { @@ -1191,12 +1130,10 @@ describe('instance', () => { performanceInsightRetention: rds.PerformanceInsightRetention.LONG_TERM, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { EnablePerformanceInsights: true, PerformanceInsightsRetentionPeriod: 731, }); - - }); test('throws if performance insights fields are set but performance insights is disabled', () => { @@ -1208,8 +1145,6 @@ describe('instance', () => { performanceInsightRetention: rds.PerformanceInsightRetention.DEFAULT, }); }).toThrow(/`enablePerformanceInsights` disabled, but `performanceInsightRetention` or `performanceInsightEncryptionKey` was set/); - - }); }); @@ -1220,12 +1155,10 @@ describe('instance', () => { subnetGroup: rds.SubnetGroup.fromSubnetGroupName(stack, 'SubnetGroup', 'my-subnet-group'), }); - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBSubnetGroupName: 'my-subnet-group', }); - expect(stack).toCountResources('AWS::RDS::DBSubnetGroup', 0); - - + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBSubnetGroup', 0); }); test('defaultChild returns the DB Instance', () => { @@ -1236,8 +1169,6 @@ describe('instance', () => { // THEN expect(instance.node.defaultChild instanceof rds.CfnDBInstance).toBeTruthy(); - - }); test("PostgreSQL database instance uses a different default master username than 'admin', which is a reserved word", () => { @@ -1249,13 +1180,11 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { GenerateSecretString: { SecretStringTemplate: '{"username":"postgres"}', }, }); - - }); describe('S3 Import/Export', () => { @@ -1269,7 +1198,7 @@ describe('instance', () => { s3ExportBuckets: [new s3.Bucket(stack, 'S3Export')], }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { AssociatedRoles: [ { FeatureName: 'S3_INTEGRATION', @@ -1280,7 +1209,7 @@ describe('instance', () => { }); // Can read from import bucket, and read/write from export bucket - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [{ Action: [ @@ -1301,6 +1230,10 @@ describe('instance', () => { 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], Effect: 'Allow', @@ -1312,8 +1245,6 @@ describe('instance', () => { Version: '2012-10-17', }, }); - - }); test('throws if using s3 import on unsupported engine', () => { @@ -1335,8 +1266,6 @@ describe('instance', () => { s3ImportRole, }); }).toThrow(/Engine 'mysql-8.0.19' does not support S3 import/); - - }); test('throws if using s3 export on unsupported engine', () => { @@ -1358,8 +1287,6 @@ describe('instance', () => { s3ExportRole: s3ExportRole, }); }).toThrow(/Engine 'mysql-8.0.19' does not support S3 export/); - - }); test('throws if provided two different roles for import/export', () => { @@ -1378,8 +1305,6 @@ describe('instance', () => { s3ExportRole, }); }).toThrow(/S3 import and export roles must be the same/); - - }); }); @@ -1392,7 +1317,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { MasterUsername: 'postgres', // username is a string MasterUserPassword: { 'Fn::Join': [ @@ -1420,7 +1345,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { ReplicaRegions: [ { Region: 'eu-west-1', @@ -1438,7 +1363,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { MasterUsername: 'postgres', // username is a string MasterUserPassword: '{{resolve:ssm-secure:/dbPassword:1}}', // reference to SSM }); @@ -1460,7 +1385,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Name: secretName, }); }); @@ -1477,7 +1402,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Name: secretName, }); }); @@ -1494,11 +1419,9 @@ describe('instance', () => { publiclyAccessible: false, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { PubliclyAccessible: false, }); - - }); test('can set publiclyAccessible to true with private subnets', () => { @@ -1513,7 +1436,7 @@ describe('instance', () => { publiclyAccessible: true, }); - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { PubliclyAccessible: true, }); }); @@ -1537,7 +1460,7 @@ describe('instance', () => { } ); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBInstanceIdentifier: instanceIdentifier.toLowerCase(), }); }); @@ -1559,7 +1482,7 @@ describe('instance', () => { } ); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBInstanceIdentifier: instanceIdentifier, }); }); @@ -1605,7 +1528,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { DBParameterGroupName: { Ref: 'ParameterGroup5E32DECB', }, @@ -1622,7 +1545,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Port: '3306', }); }); @@ -1642,7 +1565,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBInstance', { Port: { Ref: 'Port', }, @@ -1652,8 +1575,8 @@ describe('instance', () => { test.each([ [cdk.RemovalPolicy.RETAIN, 'Retain', 'Retain'], - [cdk.RemovalPolicy.SNAPSHOT, 'Snapshot', ABSENT], - [cdk.RemovalPolicy.DESTROY, 'Delete', ABSENT], + [cdk.RemovalPolicy.SNAPSHOT, 'Snapshot', Match.absent()], + [cdk.RemovalPolicy.DESTROY, 'Delete', Match.absent()], ])('if Instance RemovalPolicy is \'%s\', the instance has DeletionPolicy \'%s\' and the DBSubnetGroup has \'%s\'', (instanceRemovalPolicy, instanceValue, subnetValue) => { // GIVEN stack = new cdk.Stack(); @@ -1670,15 +1593,15 @@ test.each([ }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBInstance', { + Template.fromStack(stack).hasResource('AWS::RDS::DBInstance', { DeletionPolicy: instanceValue, UpdateReplacePolicy: instanceValue, - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResourceLike('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResource('AWS::RDS::DBSubnetGroup', { DeletionPolicy: subnetValue, UpdateReplacePolicy: subnetValue, - }, ResourcePart.CompleteDefinition); + }); }); describe('cross-account instance', () => { @@ -1707,7 +1630,7 @@ describe('cross-account instance', () => { value: instance.instanceIdentifier, }); - expect(outputStack).toMatchTemplate({ + Template.fromStack(outputStack).templateMatches({ Outputs: { DatabaseInstanceArn: { Value: { diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json b/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json index a2631bd5991c8..d3127f0b8fdd9 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json @@ -548,6 +548,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance-s3-postgres.expected.json b/packages/@aws-cdk/aws-rds/test/integ.instance-s3-postgres.expected.json index c623afaf92464..f811978275863 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance-s3-postgres.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.instance-s3-postgres.expected.json @@ -455,6 +455,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance-s3.expected.json b/packages/@aws-cdk/aws-rds/test/integ.instance-s3.expected.json index 5b851784caef0..f379bde6663f7 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance-s3.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.instance-s3.expected.json @@ -454,6 +454,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-rds/test/option-group.test.ts b/packages/@aws-cdk/aws-rds/test/option-group.test.ts index a0ab91ac0dfdf..5e8fb5b9f5261 100644 --- a/packages/@aws-cdk/aws-rds/test/option-group.test.ts +++ b/packages/@aws-cdk/aws-rds/test/option-group.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import { DatabaseInstanceEngine, OptionGroup, OracleEngineVersion } from '../lib'; @@ -21,7 +21,7 @@ describe('option group', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { EngineName: 'oracle-se2', MajorEngineVersion: '12.1', OptionConfigurations: [ @@ -30,8 +30,6 @@ describe('option group', () => { }, ], }); - - }); test('option group with new security group', () => { @@ -55,7 +53,7 @@ describe('option group', () => { optionGroup.optionConnections.OEM.connections.allowDefaultPortFromAnyIpv4(); // THEN - expect(stack).toHaveResource('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { OptionConfigurations: [ { OptionName: 'OEM', @@ -72,7 +70,7 @@ describe('option group', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Security group for OEM option', SecurityGroupIngress: [ { @@ -87,8 +85,6 @@ describe('option group', () => { Ref: 'VPCB9E5F0B4', }, }); - - }); test('option group with existing security group', () => { @@ -113,7 +109,7 @@ describe('option group', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { OptionConfigurations: [ { OptionName: 'OEM', @@ -129,8 +125,6 @@ describe('option group', () => { }, ], }); - - }); test('throws when using an option with port and no vpc', () => { @@ -149,7 +143,5 @@ describe('option group', () => { }, ], })).toThrow(/`port`.*`vpc`/); - - }); }); diff --git a/packages/@aws-cdk/aws-rds/test/parameter-group.test.ts b/packages/@aws-cdk/aws-rds/test/parameter-group.test.ts index bf8e789aee0ad..594fab914310a 100644 --- a/packages/@aws-cdk/aws-rds/test/parameter-group.test.ts +++ b/packages/@aws-cdk/aws-rds/test/parameter-group.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import { DatabaseClusterEngine, ParameterGroup } from '../lib'; @@ -17,10 +17,8 @@ describe('parameter group', () => { }); // THEN - expect(stack).toCountResources('AWS::RDS::DBParameterGroup', 0); - expect(stack).toCountResources('AWS::RDS::DBClusterParameterGroup', 0); - - + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBParameterGroup', 0); + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBClusterParameterGroup', 0); }); test('create a parameter group when bound to an instance', () => { @@ -38,15 +36,13 @@ describe('parameter group', () => { parameterGroup.bindToInstance({}); // THEN - expect(stack).toHaveResource('AWS::RDS::DBParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBParameterGroup', { Description: 'desc', Family: 'aurora5.6', Parameters: { key: 'value', }, }); - - }); test('create a parameter group when bound to a cluster', () => { @@ -64,15 +60,13 @@ describe('parameter group', () => { parameterGroup.bindToCluster({}); // THEN - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Description: 'desc', Family: 'aurora5.6', Parameters: { key: 'value', }, }); - - }); test('creates 2 parameter groups when bound to a cluster and an instance', () => { @@ -91,10 +85,8 @@ describe('parameter group', () => { parameterGroup.bindToInstance({}); // THEN - expect(stack).toCountResources('AWS::RDS::DBParameterGroup', 1); - expect(stack).toCountResources('AWS::RDS::DBClusterParameterGroup', 1); - - + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBParameterGroup', 1); + Template.fromStack(stack).resourceCountIs('AWS::RDS::DBClusterParameterGroup', 1); }); test('Add an additional parameter to an existing parameter group', () => { @@ -114,7 +106,7 @@ describe('parameter group', () => { clusterParameterGroup.addParameter('key2', 'value2'); // THEN - expect(stack).toHaveResource('AWS::RDS::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBClusterParameterGroup', { Description: 'desc', Family: 'aurora5.6', Parameters: { @@ -122,7 +114,5 @@ describe('parameter group', () => { key2: 'value2', }, }); - - }); }); diff --git a/packages/@aws-cdk/aws-rds/test/proxy.test.ts b/packages/@aws-cdk/aws-rds/test/proxy.test.ts index 9cd48e7686dd9..f98e36bdf3647 100644 --- a/packages/@aws-cdk/aws-rds/test/proxy.test.ts +++ b/packages/@aws-cdk/aws-rds/test/proxy.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import { AccountPrincipal, Role } from '@aws-cdk/aws-iam'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; @@ -15,8 +14,6 @@ describe('proxy', () => { beforeEach(() => { stack = new cdk.Stack(); vpc = new ec2.Vpc(stack, 'VPC'); - - }); test('create a DB proxy from an instance', () => { @@ -36,7 +33,7 @@ describe('proxy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBProxy', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxy', { Auth: [ { AuthScheme: 'SECRETS', @@ -66,7 +63,7 @@ describe('proxy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBProxyTargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxyTargetGroup', { DBProxyName: { Ref: 'ProxyCB0DFB71', }, @@ -78,8 +75,6 @@ describe('proxy', () => { ], TargetGroupName: 'default', }); - - }); test('create a DB proxy from a cluster', () => { @@ -99,7 +94,7 @@ describe('proxy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBProxy', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxy', { Auth: [ { AuthScheme: 'SECRETS', @@ -127,7 +122,7 @@ describe('proxy', () => { }, ], }); - expect(stack).toHaveResourceLike('AWS::RDS::DBProxyTargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxyTargetGroup', { DBProxyName: { Ref: 'ProxyCB0DFB71', }, @@ -137,10 +132,10 @@ describe('proxy', () => { Ref: 'DatabaseB269D8BB', }, ], - DBInstanceIdentifiers: ABSENT, + DBInstanceIdentifiers: Match.absent(), TargetGroupName: 'default', }); - expect(stack).toHaveResourceLike('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { IpProtocol: 'tcp', Description: 'Allow connections to the database Cluster from the Proxy', FromPort: { @@ -156,8 +151,6 @@ describe('proxy', () => { 'Fn::GetAtt': ['DatabaseB269D8BB', 'Endpoint.Port'], }, }); - - }); test('One or more secrets are required.', () => { @@ -175,8 +168,6 @@ describe('proxy', () => { vpc, }); }).toThrow('One or more secrets are required.'); - - }); test('fails when trying to create a proxy for a target without an engine', () => { @@ -191,8 +182,6 @@ describe('proxy', () => { secrets: [new secretsmanager.Secret(stack, 'Secret')], }); }).toThrow(/Could not determine engine for proxy target 'Default\/Cluster'\. Please provide it explicitly when importing the resource/); - - }); test("fails when trying to create a proxy for a target with an engine that doesn't have engineFamily", () => { @@ -213,8 +202,6 @@ describe('proxy', () => { secrets: [new secretsmanager.Secret(stack, 'Secret')], }); }).toThrow(/Engine 'mariadb-10\.0\.24' does not support proxies/); - - }); test('correctly creates a proxy for an imported Cluster if its engine is known', () => { @@ -232,20 +219,18 @@ describe('proxy', () => { secrets: [new secretsmanager.Secret(stack, 'Secret')], }); - expect(stack).toHaveResourceLike('AWS::RDS::DBProxy', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxy', { EngineFamily: 'POSTGRESQL', }); - expect(stack).toHaveResourceLike('AWS::RDS::DBProxyTargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBProxyTargetGroup', { DBClusterIdentifiers: [ 'my-cluster', ], }); - expect(stack).toHaveResourceLike('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'SecurityGroup for Database Proxy', VpcId: { Ref: 'VPCB9E5F0B4' }, }); - - }); describe('imported Proxies', () => { @@ -256,8 +241,6 @@ describe('proxy', () => { endpoint: 'my-endpoint', securityGroups: [], }); - - }); test('grant rds-db:connect in grantConnect() with a dbUser explicitly passed', () => { @@ -269,7 +252,7 @@ describe('proxy', () => { importedDbProxy.grantConnect(role, databaseUser); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [{ Effect: 'Allow', @@ -289,8 +272,6 @@ describe('proxy', () => { Version: '2012-10-17', }, }); - - }); test('throws when grantConnect() is used without a dbUser', () => { @@ -303,8 +284,6 @@ describe('proxy', () => { expect(() => { importedDbProxy.grantConnect(role); }).toThrow(/For imported Database Proxies, the dbUser is required in grantConnect/); - - }); }); @@ -328,7 +307,7 @@ describe('proxy', () => { proxy.grantConnect(role); // THEN - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [{ Effect: 'Allow', @@ -362,8 +341,6 @@ describe('proxy', () => { Version: '2012-10-17', }, }); - - }); test('new Proxy with multiple Secrets cannot use grantConnect() without a dbUser passed', () => { @@ -391,8 +368,6 @@ describe('proxy', () => { expect(() => { proxy.grantConnect(role); }).toThrow(/When the Proxy contains multiple Secrets, you must pass a dbUser explicitly to grantConnect/); - - }); test('DBProxyTargetGroup should have dependency on the proxy targets', () => { @@ -412,7 +387,7 @@ describe('proxy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBProxyTargetGroup', { + Template.fromStack(stack).hasResource('AWS::RDS::DBProxyTargetGroup', { Properties: { DBProxyName: { Ref: 'proxy3A1DA9C7', @@ -429,8 +404,6 @@ describe('proxy', () => { 'clusterSecurityGroupF441DCEA', 'clusterSubnets81E3593F', ], - }, ResourcePart.CompleteDefinition); - - + }); }); }); diff --git a/packages/@aws-cdk/aws-rds/test/serverless-cluster-from-snapshot.test.ts b/packages/@aws-cdk/aws-rds/test/serverless-cluster-from-snapshot.test.ts index 4953fa6da7a71..489cd91ae861d 100644 --- a/packages/@aws-cdk/aws-rds/test/serverless-cluster-from-snapshot.test.ts +++ b/packages/@aws-cdk/aws-rds/test/serverless-cluster-from-snapshot.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; import * as cdk from '@aws-cdk/core'; @@ -18,7 +17,7 @@ describe('serverless cluster from snapshot', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { Properties: { Engine: 'aurora-mysql', DBClusterParameterGroupName: 'default.aurora-mysql5.7', @@ -39,7 +38,7 @@ describe('serverless cluster from snapshot', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); + }); }); test('can generate a new snapshot password', () => { @@ -57,8 +56,8 @@ describe('serverless cluster from snapshot', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { - MasterUsername: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { + MasterUsername: Match.absent(), MasterUserPassword: { 'Fn::Join': ['', [ '{{resolve:secretsmanager:', @@ -67,7 +66,7 @@ describe('serverless cluster from snapshot', () => { ]], }, }); - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Description: { 'Fn::Join': ['', ['Generated by the CDK for stack: ', { Ref: 'AWS::StackName' }]], }, @@ -95,7 +94,7 @@ describe('serverless cluster from snapshot', () => { }); // THEN - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { ReplicaRegions: [ { Region: 'eu-west-1', @@ -130,8 +129,8 @@ describe('serverless cluster from snapshot', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { - MasterUsername: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { + MasterUsername: Match.absent(), MasterUserPassword: 'mysecretpassword', }); }); @@ -153,8 +152,8 @@ describe('serverless cluster from snapshot', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { - MasterUsername: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { + MasterUsername: Match.absent(), MasterUserPassword: { 'Fn::Join': ['', ['{{resolve:secretsmanager:', { Ref: 'DBSecretD58955BC' }, ':SecretString:password::}}']], }, @@ -162,8 +161,8 @@ describe('serverless cluster from snapshot', () => { }); }); -function testStack(app?: cdk.App, id?: string): cdk.Stack { - const stack = new cdk.Stack(app, id, { env: { account: '12345', region: 'us-test-1' } }); +function testStack(): cdk.Stack { + const stack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); stack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']); return stack; } diff --git a/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts b/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts index dff8d035b78a3..3849a99cdeb02 100644 --- a/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts +++ b/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart, SynthUtils } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; @@ -25,7 +24,7 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { Properties: { Engine: 'aurora-postgresql', DBClusterParameterGroupName: 'default.aurora-postgresql10', @@ -47,9 +46,7 @@ describe('serverless cluster', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); - - + }); }); test('can create a Serverless Cluster with Aurora Mysql database engine', () => { @@ -64,7 +61,7 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResource('AWS::RDS::DBCluster', { Properties: { Engine: 'aurora-mysql', DBClusterParameterGroupName: 'default.aurora-mysql5.7', @@ -108,8 +105,7 @@ describe('serverless cluster', () => { }, DeletionPolicy: 'Snapshot', UpdateReplacePolicy: 'Snapshot', - }, ResourcePart.CompleteDefinition); - + }); }); test('can create a Serverless cluster with imported vpc and security group', () => { @@ -129,7 +125,7 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { Engine: 'aurora-postgresql', DBClusterParameterGroupName: 'default.aurora-postgresql10', EngineMode: 'serverless', @@ -160,8 +156,6 @@ describe('serverless cluster', () => { }, VpcSecurityGroupIds: ['SecurityGroupId12345'], }); - - }); test("sets the retention policy of the SubnetGroup to 'Retain' if the Serverless Cluster is created with 'Retain'", () => { @@ -174,12 +168,10 @@ describe('serverless cluster', () => { removalPolicy: cdk.RemovalPolicy.RETAIN, }); - expect(stack).toHaveResourceLike('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResource('AWS::RDS::DBSubnetGroup', { DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition); - - + }); }); test('creates a secret when master credentials are not specified', () => { @@ -198,7 +190,7 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { MasterUsername: { 'Fn::Join': [ '', @@ -225,7 +217,7 @@ describe('serverless cluster', () => { }, }); - expect(stack).toHaveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { GenerateSecretString: { ExcludeCharacters: '"@/\\', GenerateStringKey: 'password', @@ -233,8 +225,6 @@ describe('serverless cluster', () => { SecretStringTemplate: '{"username":"myuser"}', }, }); - - }); test('create an Serverless cluster with custom KMS key for storage', () => { @@ -250,7 +240,7 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { KmsKeyId: { 'Fn::GetAtt': [ 'Key961B73FD', @@ -258,8 +248,6 @@ describe('serverless cluster', () => { ], }, }); - - }); test('create a cluster using a specific version of Postgresql', () => { @@ -276,13 +264,11 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { Engine: 'aurora-postgresql', EngineMode: 'serverless', EngineVersion: '10.7', }); - - }); test('cluster exposes different read and write endpoints', () => { @@ -302,8 +288,6 @@ describe('serverless cluster', () => { // THEN expect(stack.resolve(cluster.clusterEndpoint)).not .toEqual(stack.resolve(cluster.clusterReadEndpoint)); - - }); test('imported cluster with imported security group honors allowAllOutbound', () => { @@ -324,11 +308,9 @@ describe('serverless cluster', () => { cluster.connections.allowToAnyIpv4(ec2.Port.tcp(443)); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { GroupId: 'sg-123456789', }); - - }); test('can import a serverless cluster with minimal attributes', () => { @@ -339,8 +321,6 @@ describe('serverless cluster', () => { }); expect(cluster.clusterIdentifier).toEqual('identifier'); - - }); test('minimal imported cluster throws on accessing attributes for missing parameters', () => { @@ -352,8 +332,6 @@ describe('serverless cluster', () => { expect(() => cluster.clusterEndpoint).toThrow(/Cannot access `clusterEndpoint` of an imported cluster/); expect(() => cluster.clusterReadEndpoint).toThrow(/Cannot access `clusterReadEndpoint` of an imported cluster/); - - }); test('imported cluster can access properties if attributes are provided', () => { @@ -371,8 +349,6 @@ describe('serverless cluster', () => { expect(cluster.clusterEndpoint.socketAddress).toEqual('addr:3306'); expect(cluster.clusterReadEndpoint.socketAddress).toEqual('reader-address:3306'); - - }); test('throws when trying to add rotation to a serverless cluster without secret', () => { @@ -392,8 +368,6 @@ describe('serverless cluster', () => { // THEN expect(() => cluster.addRotationSingleUser()).toThrow(/without secret/); - - }); test('throws when trying to add single user rotation multiple times', () => { @@ -411,8 +385,6 @@ describe('serverless cluster', () => { // THEN expect(() => cluster.addRotationSingleUser()).toThrow(/A single user rotation was already added to this cluster/); - - }); test('can set deletion protection', () => { @@ -428,11 +400,9 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DeletionProtection: true, }); - - }); test('can set backup retention', () => { @@ -448,16 +418,15 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { BackupRetentionPeriod: 2, }); - - }); test('does not throw (but adds a node error) if a (dummy) VPC does not have sufficient subnets', () => { // GIVEN - const stack = testStack(); + const app = new cdk.App(); + const stack = testStack(app, 'TestStack'); const vpc = ec2.Vpc.fromLookup(stack, 'VPC', { isDefault: true }); // WHEN @@ -470,11 +439,9 @@ describe('serverless cluster', () => { }); // THEN - const art = SynthUtils.synthesize(stack); + const art = app.synth().getStackArtifact('TestStack'); const meta = art.findMetadataByType('aws:cdk:error'); expect(meta[0].data).toEqual('Cluster requires at least 2 subnets, got 0'); - - }); test('can set scaling configuration', () => { @@ -494,7 +461,7 @@ describe('serverless cluster', () => { }); //THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { ScalingConfiguration: { AutoPause: true, MaxCapacity: 128, @@ -502,8 +469,6 @@ describe('serverless cluster', () => { SecondsUntilAutoPause: 600, }, }); - - }); test('can enable Data API', () => { @@ -519,11 +484,9 @@ describe('serverless cluster', () => { }); //THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { EnableHttpEndpoint: true, }); - - }); test('default scaling options', () => { @@ -539,13 +502,11 @@ describe('serverless cluster', () => { }); //THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { ScalingConfiguration: { AutoPause: true, }, }); - - }); test('auto pause is disabled if a time of zero is specified', () => { @@ -563,13 +524,11 @@ describe('serverless cluster', () => { }); //THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { ScalingConfiguration: { AutoPause: false, }, }); - - }); test('throws when invalid auto pause time is specified', () => { @@ -595,8 +554,6 @@ describe('serverless cluster', () => { autoPause: cdk.Duration.days(2), }, })).toThrow(/auto pause time must be between 5 minutes and 1 day./); - - }); test('throws when invalid backup retention period is specified', () => { @@ -618,8 +575,6 @@ describe('serverless cluster', () => { vpc, backupRetention: cdk.Duration.days(36), })).toThrow(/backup retention period must be between 1 and 35 days. received: 36/); - - }); test('throws error when min capacity is greater than max capacity', () => { @@ -637,8 +592,6 @@ describe('serverless cluster', () => { maxCapacity: AuroraCapacityUnit.ACU_1, }, })).toThrow(/maximum capacity must be greater than or equal to minimum capacity./); - - }); test('check that clusterArn property works', () => { @@ -669,7 +622,6 @@ describe('serverless cluster', () => { ], ], }); - }); test('can grant Data API access', () => { @@ -687,7 +639,7 @@ describe('serverless cluster', () => { cluster.grantDataApiAccess(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -721,8 +673,6 @@ describe('serverless cluster', () => { }, ], }); - - }); test('can grant Data API access on imported cluster with given secret', () => { @@ -742,7 +692,7 @@ describe('serverless cluster', () => { cluster.grantDataApiAccess(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -776,8 +726,6 @@ describe('serverless cluster', () => { }, ], }); - - }); test('grant Data API access enables the Data API', () => { @@ -794,11 +742,9 @@ describe('serverless cluster', () => { cluster.grantDataApiAccess(user); //THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { EnableHttpEndpoint: true, }); - - }); test('grant Data API access throws if the Data API is disabled', () => { @@ -814,8 +760,6 @@ describe('serverless cluster', () => { // WHEN expect(() => cluster.grantDataApiAccess(user)).toThrow(/Cannot grant Data API access when the Data API is disabled/); - - }); test('changes the case of the cluster identifier if the lowercaseDbIdentifier feature flag is enabled', () => { @@ -835,11 +779,9 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBClusterIdentifier: clusterIdentifier.toLowerCase(), }); - - }); test('does not change the case of the cluster identifier if the lowercaseDbIdentifier feature flag is disabled', () => { @@ -857,11 +799,9 @@ describe('serverless cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBCluster', { DBClusterIdentifier: clusterIdentifier, }); - - }); }); diff --git a/packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts b/packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts index 72615d08f2093..a591dfd4a7bf1 100644 --- a/packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts +++ b/packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as core from '@aws-cdk/core'; import * as rds from '../../lib'; @@ -12,11 +12,9 @@ describe('sql server instance engine', () => { }), }).bindToInstance({}); - expect(stack).toHaveResourceLike('AWS::RDS::DBParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBParameterGroup', { Family: 'sqlserver-web-11.0', }); - - }); test("has MajorEngineVersion ending in '11.00' for major version 11", () => { @@ -35,11 +33,9 @@ describe('sql server instance engine', () => { ], }); - expect(stack).toHaveResourceLike('AWS::RDS::OptionGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { MajorEngineVersion: '11.00', }); - - }); }); }); diff --git a/packages/@aws-cdk/aws-rds/test/subnet-group.test.ts b/packages/@aws-cdk/aws-rds/test/subnet-group.test.ts index 1dc9718258deb..44fd4e24482a8 100644 --- a/packages/@aws-cdk/aws-rds/test/subnet-group.test.ts +++ b/packages/@aws-cdk/aws-rds/test/subnet-group.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import * as rds from '../lib'; @@ -10,7 +10,6 @@ describe('subnet group', () => { beforeEach(() => { stack = new cdk.Stack(); vpc = new ec2.Vpc(stack, 'VPC'); - }); test('creates a subnet group from minimal properties', () => { @@ -19,15 +18,13 @@ describe('subnet group', () => { vpc, }); - expect(stack).toHaveResource('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupDescription: 'MyGroup', SubnetIds: [ { Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' }, { Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A' }, ], }); - - }); test('creates a subnet group from all properties', () => { @@ -38,7 +35,7 @@ describe('subnet group', () => { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE }, }); - expect(stack).toHaveResource('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupDescription: 'My Shared Group', DBSubnetGroupName: 'sharedgroup', SubnetIds: [ @@ -46,8 +43,6 @@ describe('subnet group', () => { { Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A' }, ], }); - - }); test('correctly creates a subnet group with a deploy-time value for its name', () => { @@ -59,13 +54,11 @@ describe('subnet group', () => { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE }, }); - expect(stack).toHaveResourceLike('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupName: { Ref: 'Parameter', }, }); - - }); describe('subnet selection', () => { @@ -75,15 +68,13 @@ describe('subnet group', () => { vpc, }); - expect(stack).toHaveResource('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupDescription: 'MyGroup', SubnetIds: [ { Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' }, { Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A' }, ], }); - - }); test('can specify subnet type', () => { @@ -93,14 +84,13 @@ describe('subnet group', () => { vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }, }); - expect(stack).toHaveResource('AWS::RDS::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::RDS::DBSubnetGroup', { DBSubnetGroupDescription: 'MyGroup', SubnetIds: [ { Ref: 'VPCPublicSubnet1SubnetB4246D30' }, { Ref: 'VPCPublicSubnet2Subnet74179F39' }, ], }); - }); }); @@ -108,8 +98,5 @@ describe('subnet group', () => { const subnetGroup = rds.SubnetGroup.fromSubnetGroupName(stack, 'Group', 'my-subnet-group'); expect(subnetGroup.subnetGroupName).toEqual('my-subnet-group'); - - }); - }); diff --git a/packages/@aws-cdk/aws-rekognition/package.json b/packages/@aws-cdk/aws-rekognition/package.json index b7371d019400b..9cf5d2e0d407d 100644 --- a/packages/@aws-cdk/aws-rekognition/package.json +++ b/packages/@aws-cdk/aws-rekognition/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.Rekognition", @@ -30,6 +37,13 @@ "distName": "aws-cdk.aws-rekognition", "module": "aws_cdk.aws_rekognition" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-resourcegroups/package.json b/packages/@aws-cdk/aws-resourcegroups/package.json index 67dc38bf37492..caf23fb138d50 100644 --- a/packages/@aws-cdk/aws-resourcegroups/package.json +++ b/packages/@aws-cdk/aws-resourcegroups/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-robomaker/package.json b/packages/@aws-cdk/aws-robomaker/package.json index 6748a1e52affc..d40dc417d86d6 100644 --- a/packages/@aws-cdk/aws-robomaker/package.json +++ b/packages/@aws-cdk/aws-robomaker/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 849968f30b2f2..8290edcc15e60 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "aws-sdk": "^2.848.0", "jest": "^27.4.7" diff --git a/packages/@aws-cdk/aws-route53recoverycontrol/package.json b/packages/@aws-cdk/aws-route53recoverycontrol/package.json index 316ce7ec6b312..0ec2546ada38d 100644 --- a/packages/@aws-cdk/aws-route53recoverycontrol/package.json +++ b/packages/@aws-cdk/aws-route53recoverycontrol/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-route53recoverycontrol", "module": "aws_cdk.aws_route53recoverycontrol" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-route53recoveryreadiness/package.json b/packages/@aws-cdk/aws-route53recoveryreadiness/package.json index f0a1538967e6b..6dcaed6cb8433 100644 --- a/packages/@aws-cdk/aws-route53recoveryreadiness/package.json +++ b/packages/@aws-cdk/aws-route53recoveryreadiness/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-route53recoveryreadiness", "module": "aws_cdk.aws_route53recoveryreadiness" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts index 9507cdb5dace7..b017aa4e3a535 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts +++ b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts @@ -665,6 +665,10 @@ testFutureBehavior('lambda execution role gets permissions to read from the sour 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], Effect: 'Allow', diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json index ae66a97b871bc..e50759499b6cc 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json @@ -417,6 +417,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json index 26209e97d225e..51473300ac82b 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json @@ -378,6 +378,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -411,6 +415,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -444,6 +452,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1528,6 +1540,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 4f2592b9c633a..47138a3d30ec6 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -249,6 +249,33 @@ const bucket = s3.Bucket.fromBucketAttributes(this, 'ImportedBucket', { bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SnsDestination(topic)); ``` +When you add an event notification to a bucket, a custom resource is created to +manage the notifications. By default, a new role is created for the Lambda +function that implements this feature. If you want to use your own role instead, +you should provide it in the `Bucket` constructor: + +```ts +declare const myRole: iam.IRole; +const bucket = new s3.Bucket(this, 'MyBucket', { + notificationsHandlerRole: myRole, +}); +``` + +Whatever role you provide, the CDK will try to modify it by adding the +permissions from `AWSLambdaBasicExecutionRole` (an AWS managed policy) as well +as the permissions `s3:PutBucketNotification` and `s3:GetBucketNotification`. +If you’re passing an imported role, and you don’t want this to happen, configure +it to be immutable: + +```ts +const importedRole = iam.Role.fromRoleArn(this, 'role', 'arn:aws:iam::123456789012:role/RoleName', { + mutable: false, +}); +``` + +> If you provide an imported immutable role, make sure that it has at least all +> the permissions mentioned above. Otherwise, the deployment will fail! + [S3 Bucket Notifications]: https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index ea25e2c01467b..42d6a84bdc18b 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -356,9 +356,7 @@ export interface IBucket extends IResource { } /** - * A reference to a bucket. The easiest way to instantiate is to call - * `bucket.export()`. Then, the consumer can use `Bucket.import(this, ref)` and - * get a `Bucket`. + * A reference to a bucket outside this stack */ export interface BucketAttributes { /** @@ -429,6 +427,13 @@ export interface BucketAttributes { * @default - it's assumed the bucket is in the same region as the scope it's being imported into */ readonly region?: string; + + /** + * The role to be used by the notifications handler + * + * @default - a new role will be created. + */ + readonly notificationsHandlerRole?: iam.IRole; } /** @@ -486,14 +491,12 @@ export abstract class BucketBase extends Resource implements IBucket { */ protected abstract disallowPublicAccess?: boolean; - private readonly notifications: BucketNotifications; + private notifications?: BucketNotifications; + + protected notificationsHandlerRole?: iam.IRole; constructor(scope: Construct, id: string, props: ResourceProps = {}) { super(scope, id, props); - - // defines a BucketNotifications construct. Notice that an actual resource will only - // be added if there are notifications added, so we don't need to condition this. - this.notifications = new BucketNotifications(this, 'Notifications', { bucket: this }); } /** @@ -838,7 +841,17 @@ export abstract class BucketBase extends Resource implements IBucket { * https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html */ public addEventNotification(event: EventType, dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) { - this.notifications.addNotification(event, dest, ...filters); + this.withNotifications(notifications => notifications.addNotification(event, dest, ...filters)); + } + + private withNotifications(cb: (notifications: BucketNotifications) => void) { + if (!this.notifications) { + this.notifications = new BucketNotifications(this, 'Notifications', { + bucket: this, + handlerRole: this.notificationsHandlerRole, + }); + } + cb(this.notifications); } /** @@ -1089,7 +1102,7 @@ export enum InventoryFormat { */ PARQUET = 'Parquet', /** - * Generate the inventory list as Parquet. + * Generate the inventory list as ORC. */ ORC = 'ORC', } @@ -1461,6 +1474,13 @@ export interface BucketProps { */ readonly transferAcceleration?: boolean; + /** + * The role to be used by the notifications handler + * + * @default - a new role will be created. + */ + readonly notificationsHandlerRole?: iam.IRole; + /** * Inteligent Tiering Configurations * @@ -1544,6 +1564,7 @@ export class Bucket extends BucketBase { public policy?: BucketPolicy = undefined; protected autoCreatePolicy = false; protected disallowPublicAccess = false; + protected notificationsHandlerRole = attrs.notificationsHandlerRole; /** * Exports this bucket from the stack. @@ -1631,6 +1652,8 @@ export class Bucket extends BucketBase { physicalName: props.bucketName, }); + this.notificationsHandlerRole = props.notificationsHandlerRole; + const { bucketEncryption, encryptionKey } = this.parseEncryption(props); Bucket.validateBucketName(this.physicalName); diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts index c093487a8f105..76edb141a3cd0 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts @@ -7,6 +7,10 @@ import * as cdk from '@aws-cdk/core'; // eslint-disable-next-line no-duplicate-imports, import/order import { Construct } from '@aws-cdk/core'; +export class NotificationsResourceHandlerProps { + role?: iam.IRole; +} + /** * A Lambda-based custom resource handler that provisions S3 bucket * notifications for a bucket. @@ -31,14 +35,14 @@ export class NotificationsResourceHandler extends Construct { * * @returns The ARN of the custom resource lambda function. */ - public static singleton(context: Construct) { + public static singleton(context: Construct, props: NotificationsResourceHandlerProps = {}) { const root = cdk.Stack.of(context); // well-known logical id to ensure stack singletonity const logicalId = 'BucketNotificationsHandler050a0587b7544547bf325f094a3db834'; let lambda = root.node.tryFindChild(logicalId) as NotificationsResourceHandler; if (!lambda) { - lambda = new NotificationsResourceHandler(root, logicalId); + lambda = new NotificationsResourceHandler(root, logicalId, props); } return lambda; @@ -53,19 +57,19 @@ export class NotificationsResourceHandler extends Construct { /** * The role of the handler's lambda function. */ - public readonly role: iam.Role; + public readonly role: iam.IRole; - constructor(scope: Construct, id: string) { + constructor(scope: Construct, id: string, props: NotificationsResourceHandlerProps = {}) { super(scope, id); - this.role = new iam.Role(this, 'Role', { + this.role = props.role ?? new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), - managedPolicies: [ - iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'), - ], }); - this.role.addToPolicy(new iam.PolicyStatement({ + this.role.addManagedPolicy( + iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'), + ); + this.role.addToPrincipalPolicy(new iam.PolicyStatement({ actions: ['s3:PutBucketNotification'], resources: ['*'], })); @@ -80,11 +84,17 @@ export class NotificationsResourceHandler extends Construct { return properties; } } + + const handlerSource = fs.readFileSync(path.join(__dirname, 'lambda/index.py'), 'utf8'); + if (handlerSource.length > 4096) { + throw new Error(`Source of Notifications Resource Handler is too large (${handlerSource.length} > 4096)`); + } + const resource = new InLineLambda(this, 'Resource', { type: resourceType, properties: { Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)', - Code: { ZipFile: fs.readFileSync(path.join(__dirname, 'lambda/index.py'), 'utf8') }, + Code: { ZipFile: handlerSource }, Handler: 'index.handler', Role: this.role.roleArn, Runtime: 'python3.7', @@ -95,4 +105,8 @@ export class NotificationsResourceHandler extends Construct { this.functionArn = resource.getAtt('Arn').toString(); } + + public addToRolePolicy(statement: iam.PolicyStatement) { + this.role.addToPrincipalPolicy(statement); + } } diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts index d5190f1a6a913..6bc50ec5b6064 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -13,6 +13,11 @@ interface NotificationsProps { * The bucket to manage notifications for. */ bucket: IBucket; + + /** + * The role to be used by the lambda handler + */ + handlerRole?: iam.IRole; } /** @@ -36,10 +41,12 @@ export class BucketNotifications extends Construct { private readonly topicNotifications = new Array(); private resource?: cdk.CfnResource; private readonly bucket: IBucket; + private readonly handlerRole?: iam.IRole; constructor(scope: Construct, id: string, props: NotificationsProps) { super(scope, id); this.bucket = props.bucket; + this.handlerRole = props.handlerRole; } /** @@ -102,12 +109,14 @@ export class BucketNotifications extends Construct { */ private createResourceOnce() { if (!this.resource) { - const handler = NotificationsResourceHandler.singleton(this); + const handler = NotificationsResourceHandler.singleton(this, { + role: this.handlerRole, + }); const managed = this.bucket instanceof Bucket; if (!managed) { - handler.role.addToPolicy(new iam.PolicyStatement({ + handler.addToRolePolicy(new iam.PolicyStatement({ actions: ['s3:GetBucketNotification'], resources: ['*'], })); diff --git a/packages/@aws-cdk/aws-s3/lib/perms.ts b/packages/@aws-cdk/aws-s3/lib/perms.ts index f57b97153f27c..abfb00d852f6e 100644 --- a/packages/@aws-cdk/aws-s3/lib/perms.ts +++ b/packages/@aws-cdk/aws-s3/lib/perms.ts @@ -16,11 +16,16 @@ export const LEGACY_BUCKET_PUT_ACTIONS = [ export const BUCKET_PUT_ACTIONS = [ 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ]; export const BUCKET_PUT_ACL_ACTIONS = [ 's3:PutObjectAcl', + 's3:PutObjectVersionAcl', ]; export const BUCKET_DELETE_ACTIONS = [ diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 2c7ea2bc6b9f2..7e3a9f89448e0 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/aws-s3/test/bucket.test.ts b/packages/@aws-cdk/aws-s3/test/bucket.test.ts index 574d0d38e4755..dbbd49420c493 100644 --- a/packages/@aws-cdk/aws-s3/test/bucket.test.ts +++ b/packages/@aws-cdk/aws-s3/test/bucket.test.ts @@ -809,6 +809,10 @@ describe('bucket', () => { 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', @@ -1084,6 +1088,10 @@ describe('bucket', () => { 's3:List*', 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', @@ -1117,6 +1125,10 @@ describe('bucket', () => { 'Action': [ 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', @@ -1184,6 +1196,10 @@ describe('bucket', () => { 'Action': [ 's3:DeleteObject*', 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', @@ -1217,6 +1233,10 @@ describe('bucket', () => { { 'Action': [ 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', 's3:Abort*', ], 'Effect': 'Allow', @@ -1247,8 +1267,23 @@ describe('bucket', () => { const resources = Template.fromStack(stack).toJSON().Resources; const actions = (id: string) => resources[id].Properties.PolicyDocument.Statement[0].Action; - expect(actions('WriterDefaultPolicyDC585BCE')).toEqual(['s3:DeleteObject*', 's3:PutObject', 's3:Abort*']); - expect(actions('PutterDefaultPolicyAB138DD3')).toEqual(['s3:PutObject', 's3:Abort*']); + expect(actions('WriterDefaultPolicyDC585BCE')).toEqual([ + 's3:DeleteObject*', + 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', + 's3:Abort*', + ]); + expect(actions('PutterDefaultPolicyAB138DD3')).toEqual([ + 's3:PutObject', + 's3:PutObjectLegalHold', + 's3:PutObjectRetention', + 's3:PutObjectTagging', + 's3:PutObjectVersionTagging', + 's3:Abort*', + ]); expect(actions('DeleterDefaultPolicyCD33B8A0')).toEqual('s3:DeleteObject*'); }); diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket-auto-delete-objects.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket-auto-delete-objects.expected.json index f5cf756e8a75d..da2c8cf503fe6 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket-auto-delete-objects.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket-auto-delete-objects.expected.json @@ -110,7 +110,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3Bucket2C6C817C" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3Bucket09A62232" }, "S3Key": { "Fn::Join": [ @@ -123,7 +123,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE" } ] } @@ -136,7 +136,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE" } ] } @@ -228,7 +228,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abfS3BucketE1985B35" + "Ref": "AssetParameters31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6S3BucketB51EC107" }, "S3Key": { "Fn::Join": [ @@ -241,7 +241,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abfS3VersionKey610C6DE2" + "Ref": "AssetParameters31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6S3VersionKey2B267DB5" } ] } @@ -254,7 +254,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abfS3VersionKey610C6DE2" + "Ref": "AssetParameters31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6S3VersionKey2B267DB5" } ] } @@ -297,29 +297,29 @@ } }, "Parameters": { - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3Bucket2C6C817C": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3Bucket09A62232": { "Type": "String", - "Description": "S3 bucket for asset \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "S3 bucket for asset \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE": { "Type": "String", - "Description": "S3 key for asset version \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "S3 key for asset version \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709ArtifactHash17D48178": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824ArtifactHash76F8FCF2": { "Type": "String", - "Description": "Artifact hash for asset \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "Artifact hash for asset \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abfS3BucketE1985B35": { + "AssetParameters31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6S3BucketB51EC107": { "Type": "String", - "Description": "S3 bucket for asset \"618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abf\"" + "Description": "S3 bucket for asset \"31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6\"" }, - "AssetParameters618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abfS3VersionKey610C6DE2": { + "AssetParameters31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6S3VersionKey2B267DB5": { "Type": "String", - "Description": "S3 key for asset version \"618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abf\"" + "Description": "S3 key for asset version \"31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6\"" }, - "AssetParameters618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abfArtifactHash467DFC33": { + "AssetParameters31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6ArtifactHashEE982197": { "Type": "String", - "Description": "Artifact hash for asset \"618bbe9863c0edd5c4ca2e24b5063762f020fafec018cd06f57e2bd9f2f48abf\"" + "Description": "Artifact hash for asset \"31552cb1c5c4cdb0d9502dc59c3cd63cb519dcb7e320e60965c75940297ae3b6\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket-inventory.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket-inventory.expected.json index f5610756ad71e..a142de99be8b0 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket-inventory.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket-inventory.expected.json @@ -155,4 +155,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket-sharing.lit.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket-sharing.lit.expected.json index 4197e9179b4ff..d3f68abaf02b5 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket-sharing.lit.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket-sharing.lit.expected.json @@ -38,6 +38,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -71,4 +75,4 @@ } } } -] +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json index 58cd3c5760961..4352395e831c2 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json @@ -89,6 +89,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -173,4 +177,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json index 37a7d24a40029..6ab5dcbfef26e 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json @@ -44,7 +44,10 @@ [ "https://", { - "Fn::GetAtt": ["MyBucketF68F3FF0", "RegionalDomainName"] + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "RegionalDomainName" + ] }, "/myfolder/myfile.txt" ] @@ -58,7 +61,10 @@ [ "https://", { - "Fn::GetAtt": ["MyBucketF68F3FF0", "DomainName"] + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "DomainName" + ] }, "/myfolder/myfile.txt" ] @@ -80,4 +86,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/notification.test.ts b/packages/@aws-cdk/aws-s3/test/notification.test.ts index fbc8e1aa45a49..411852018d081 100644 --- a/packages/@aws-cdk/aws-s3/test/notification.test.ts +++ b/packages/@aws-cdk/aws-s3/test/notification.test.ts @@ -1,4 +1,5 @@ import { Template } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as s3 from '../lib'; @@ -30,6 +31,29 @@ describe('notification', () => { }); }); + test('can specify a custom role for the notifications handler of imported buckets', () => { + const stack = new cdk.Stack(); + + const importedRole = iam.Role.fromRoleArn(stack, 'role', 'arn:aws:iam::111111111111:role/DevsNotAllowedToTouch'); + + const bucket = s3.Bucket.fromBucketAttributes(stack, 'MyBucket', { + bucketName: 'foo-bar', + notificationsHandlerRole: importedRole, + }); + + bucket.addEventNotification(s3.EventType.OBJECT_CREATED, { + bind: () => ({ + arn: 'ARN', + type: s3.BucketNotificationDestinationType.TOPIC, + }), + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)', + Role: 'arn:aws:iam::111111111111:role/DevsNotAllowedToTouch', + }); + }); + test('can specify prefix and suffix filter rules', () => { const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-s3objectlambda/package.json b/packages/@aws-cdk/aws-s3objectlambda/package.json index 3e7849ee2de72..0e1020f5e6dc1 100644 --- a/packages/@aws-cdk/aws-s3objectlambda/package.json +++ b/packages/@aws-cdk/aws-s3objectlambda/package.json @@ -30,6 +30,13 @@ "distName": "aws-cdk.aws-s3objectlambda", "module": "aws_cdk.aws_s3objectlambda" } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } } }, "repository": { diff --git a/packages/@aws-cdk/aws-s3outposts/package.json b/packages/@aws-cdk/aws-s3outposts/package.json index 230241444e76d..9c1b9dc830771 100644 --- a/packages/@aws-cdk/aws-s3outposts/package.json +++ b/packages/@aws-cdk/aws-s3outposts/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.S3Outposts", diff --git a/packages/@aws-cdk/aws-sagemaker/package.json b/packages/@aws-cdk/aws-sagemaker/package.json index 49d7b6d54f5ac..9741c8b8d96a3 100644 --- a/packages/@aws-cdk/aws-sagemaker/package.json +++ b/packages/@aws-cdk/aws-sagemaker/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-sam/package.json b/packages/@aws-cdk/aws-sam/package.json index 37ddba18cf79b..7b955ade95a3f 100644 --- a/packages/@aws-cdk/aws-sam/package.json +++ b/packages/@aws-cdk/aws-sam/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-sdb/package.json b/packages/@aws-cdk/aws-sdb/package.json index 466c7fc72acf1..4636ae5160146 100644 --- a/packages/@aws-cdk/aws-sdb/package.json +++ b/packages/@aws-cdk/aws-sdb/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts index 3656f0d55ba57..7322148e2a245 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts @@ -92,7 +92,7 @@ export class RotationSchedule extends Resource { 'secretsmanager:PutSecretValue', 'secretsmanager:UpdateSecretVersionStage', ], - resources: [props.secret.secretArn], + resources: [props.secret.secretFullArn ? props.secret.secretFullArn : `${props.secret.secretArn}-??????`], }), ); props.rotationLambda.addToRolePolicy( diff --git a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts index fab58e80e82d2..caa6543ec42f1 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts @@ -101,6 +101,57 @@ test('assign permissions for rotation schedule with a rotation Lambda', () => { }); }); +test('grants correct permissions for secret imported by name', () => { + // GIVEN + const secret = secretsmanager.Secret.fromSecretNameV2(stack, 'Secret', 'mySecretName'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_10_X, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: Match.arrayWith([ + { + Action: [ + 'secretsmanager:DescribeSecret', + 'secretsmanager:GetSecretValue', + 'secretsmanager:PutSecretValue', + 'secretsmanager:UpdateSecretVersionStage', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':secretsmanager:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':secret:mySecretName-??????', + ]], + }, + }, + ]), + Version: '2012-10-17', + }, + PolicyName: 'LambdaServiceRoleDefaultPolicyDAE46E21', + Roles: [ + { + Ref: 'LambdaServiceRoleA8ED4D3B', + }, + ], + }); +}); + test('assign kms permissions for rotation schedule with a rotation Lambda', () => { // GIVEN const encryptionKey = new kms.Key(stack, 'Key'); diff --git a/packages/@aws-cdk/aws-servicecatalog/README.md b/packages/@aws-cdk/aws-servicecatalog/README.md index 436fe376f9624..12e57cbc200ee 100644 --- a/packages/@aws-cdk/aws-servicecatalog/README.md +++ b/packages/@aws-cdk/aws-servicecatalog/README.md @@ -201,21 +201,24 @@ portfolio.addProduct(product); ## Tag Options TagOptions allow administrators to easily manage tags on provisioned products by creating a selection of tags for end users to choose from. -For example, an end user can choose an `ec2` for the instance type size. -TagOptions are created by specifying a key with a selection of values and can be associated with both portfolios and products. +TagOptions are created by specifying a tag key with a selection of allowed values and can be associated with both portfolios and products. When launching a product, both the TagOptions associated with the product and the containing portfolio are made available. At the moment, TagOptions can only be disabled in the console. ```ts fixture=portfolio-product -const tagOptionsForPortfolio = new servicecatalog.TagOptions({ - costCenter: ['Data Insights', 'Marketing'], +const tagOptionsForPortfolio = new servicecatalog.TagOptions(this, 'OrgTagOptions', { + allowedValuesForTags: { + Group: ['finance', 'engineering', 'marketing', 'research'], + CostCenter: ['01', '02','03'], + }, }); portfolio.associateTagOptions(tagOptionsForPortfolio); -const tagOptionsForProduct = new servicecatalog.TagOptions({ - ec2InstanceType: ['A1', 'M4'], - ec2InstanceSize: ['medium', 'large'], +const tagOptionsForProduct = new servicecatalog.TagOptions(this, 'ProductTagOptions', { + allowedValuesForTags: { + Environment: ['dev', 'alpha', 'prod'], + }, }); product.associateTagOptions(tagOptionsForProduct); ``` 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 e1e4ee8de38da..dd44ef0f022fc 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/private/association-manager.ts @@ -9,7 +9,7 @@ import { IPortfolio } from '../portfolio'; import { IProduct } from '../product'; import { CfnLaunchNotificationConstraint, CfnLaunchRoleConstraint, CfnLaunchTemplateConstraint, CfnPortfolioProductAssociation, - CfnResourceUpdateConstraint, CfnStackSetConstraint, CfnTagOption, CfnTagOptionAssociation, + CfnResourceUpdateConstraint, CfnStackSetConstraint, CfnTagOptionAssociation, } from '../servicecatalog.generated'; import { TagOptions } from '../tag-options'; import { hashValues } from './util'; @@ -139,33 +139,16 @@ export class AssociationManager { } } - public static associateTagOptions(resource: cdk.IResource, resourceId: string, tagOptions: TagOptions): void { - const resourceStack = cdk.Stack.of(resource); - for (const [key, tagOptionsList] of Object.entries(tagOptions.tagOptionsMap)) { - InputValidator.validateLength(resource.node.addr, 'TagOption key', 1, 128, key); - tagOptionsList.forEach((value: string) => { - InputValidator.validateLength(resource.node.addr, 'TagOption value', 1, 256, value); - const tagOptionKey = hashValues(key, value, resourceStack.node.addr); - const tagOptionConstructId = `TagOption${tagOptionKey}`; - let cfnTagOption = resourceStack.node.tryFindChild(tagOptionConstructId) as CfnTagOption; - if (!cfnTagOption) { - cfnTagOption = new CfnTagOption(resourceStack, tagOptionConstructId, { - key: key, - value: value, - active: true, - }); - } - const tagAssocationKey = hashValues(key, value, resource.node.addr); - const tagAssocationConstructId = `TagOptionAssociation${tagAssocationKey}`; - if (!resource.node.tryFindChild(tagAssocationConstructId)) { - new CfnTagOptionAssociation(resource as cdk.Resource, tagAssocationConstructId, { - resourceId: resourceId, - tagOptionId: cfnTagOption.ref, - }); - } - }); - }; + for (const cfnTagOption of tagOptions._cfnTagOptions) { + const tagAssocationConstructId = `TagOptionAssociation${hashValues(cfnTagOption.key, cfnTagOption.value, resource.node.addr)}`; + if (!resource.node.tryFindChild(tagAssocationConstructId)) { + new CfnTagOptionAssociation(resource as cdk.Resource, tagAssocationConstructId, { + resourceId: resourceId, + tagOptionId: cfnTagOption.ref, + }); + } + } } private static setLaunchRoleConstraint( diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/product.ts b/packages/@aws-cdk/aws-servicecatalog/lib/product.ts index 29a47fc6932a9..3c4e8bd9fb59f 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/product.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/product.ts @@ -1,11 +1,11 @@ import { ArnFormat, IResource, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { TagOptions } from '.'; import { CloudFormationTemplate } from './cloudformation-template'; import { MessageLanguage } from './common'; import { AssociationManager } from './private/association-manager'; import { InputValidator } from './private/validation'; import { CfnCloudFormationProduct } from './servicecatalog.generated'; +import { TagOptions } from './tag-options'; /** * A Service Catalog product, currently only supports type CloudFormationProduct @@ -137,7 +137,7 @@ export interface CloudFormationProductProps { * * @default - No tagOptions provided */ - readonly tagOptions?: TagOptions + readonly tagOptions?: TagOptions; } /** diff --git a/packages/@aws-cdk/aws-servicecatalog/lib/tag-options.ts b/packages/@aws-cdk/aws-servicecatalog/lib/tag-options.ts index 808ea78add4a3..b0342c6336bd3 100644 --- a/packages/@aws-cdk/aws-servicecatalog/lib/tag-options.ts +++ b/packages/@aws-cdk/aws-servicecatalog/lib/tag-options.ts @@ -1,14 +1,70 @@ +import * as cdk from '@aws-cdk/core'; +import { hashValues } from './private/util'; +import { InputValidator } from './private/validation'; +import { CfnTagOption } from './servicecatalog.generated'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from 'constructs'; + +/** + * Properties for TagOptions. + */ +export interface TagOptionsProps { + /** + * The values that are allowed to be set for specific tags. + * The keys of the map represent the tag keys, + * and the values of the map are a list of allowed values for that particular tag key. + */ + readonly allowedValuesForTags: { [tagKey: string]: string[] }; +} + /** - * Defines a Tag Option, which are similar to tags - * but have multiple values per key. + * Defines a set of TagOptions, which are a list of key-value pairs managed in AWS Service Catalog. + * It is not an AWS tag, but serves as a template for creating an AWS tag based on the TagOption. + * See https://docs.aws.amazon.com/servicecatalog/latest/adminguide/tagoptions.html + * + * @resource AWS::ServiceCatalog::TagOption */ -export class TagOptions { +export class TagOptions extends cdk.Resource { /** - * List of CfnTagOption - */ - public readonly tagOptionsMap: { [key: string]: string[] }; + * List of underlying CfnTagOption resources. + * + * @internal + */ + public _cfnTagOptions: CfnTagOption[]; - constructor(tagOptionsMap: { [key: string]: string[]} ) { - this.tagOptionsMap = { ...tagOptionsMap }; + constructor(scope: Construct, id: string, props: TagOptionsProps) { + super(scope, id); + + this._cfnTagOptions = this.createUnderlyingTagOptions(props.allowedValuesForTags); + } + + private createUnderlyingTagOptions(allowedValuesForTags: { [tagKey: string]: string[] }): CfnTagOption[] { + if (Object.keys(allowedValuesForTags).length === 0) { + throw new Error(`No tag option keys or values were provided for resource ${this.node.path}`); + } + var tagOptions: CfnTagOption[] = []; + + for (const [tagKey, tagValues] of Object.entries(allowedValuesForTags)) { + InputValidator.validateLength(this.node.addr, 'TagOption key', 1, 128, tagKey); + + const uniqueTagValues = new Set(tagValues); + if (uniqueTagValues.size === 0) { + throw new Error(`No tag option values were provided for tag option key ${tagKey} for resource ${this.node.path}`); + } + uniqueTagValues.forEach((tagValue: string) => { + InputValidator.validateLength(this.node.addr, 'TagOption value', 1, 256, tagValue); + const tagOptionIdentifier = hashValues(tagKey, tagValue); + const tagOption = new CfnTagOption(this, tagOptionIdentifier, { + key: tagKey, + value: tagValue, + active: true, + }); + tagOptions.push(tagOption); + }); + } + return tagOptions; } } + diff --git a/packages/@aws-cdk/aws-servicecatalog/package.json b/packages/@aws-cdk/aws-servicecatalog/package.json index 13567c9c5c88d..d5493a3813974 100644 --- a/packages/@aws-cdk/aws-servicecatalog/package.json +++ b/packages/@aws-cdk/aws-servicecatalog/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", @@ -105,7 +112,13 @@ "props-physical-name:@aws-cdk/aws-servicecatalog.CloudFormationProductProps", "resource-attribute:@aws-cdk/aws-servicecatalog.Portfolio.portfolioName", "props-physical-name:@aws-cdk/aws-servicecatalog.PortfolioProps", - "props-physical-name:@aws-cdk/aws-servicecatalog.ProductStack" + "props-physical-name:@aws-cdk/aws-servicecatalog.ProductStack", + "props-struct-name:@aws-cdk/aws-servicecatalog.ITagOptions", + "props-physical-name:@aws-cdk/aws-servicecatalog.TagOptionsProps", + "ref-via-interface:@aws-cdk/aws-servicecatalog.CloudFormationProductProps.tagOptions", + "ref-via-interface:@aws-cdk/aws-servicecatalog.IProduct.associateTagOptions.tagOptions", + "ref-via-interface:@aws-cdk/aws-servicecatalog.IPortfolio.associateTagOptions.tagOptions", + "ref-via-interface:@aws-cdk/aws-servicecatalog.PortfolioProps.tagOptions" ] }, "maturity": "experimental", 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 c298f292d039d..c25d867209591 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.expected.json +++ b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.expected.json @@ -81,7 +81,7 @@ "Ref": "TestPortfolio4AC794EB" }, "TagOptionId": { - "Ref": "TagOptionc0d88a3c4b8b" + "Ref": "TagOptions5f31c54ba705F110F743" } } }, @@ -92,7 +92,7 @@ "Ref": "TestPortfolio4AC794EB" }, "TagOptionId": { - "Ref": "TagOption9b16df08f83d" + "Ref": "TagOptions8d263919cebb6764AC10" } } }, @@ -103,7 +103,7 @@ "Ref": "TestPortfolio4AC794EB" }, "TagOptionId": { - "Ref": "TagOptiondf34c1c83580" + "Ref": "TagOptionsa260cbbd99c416C40F73" } } }, @@ -217,7 +217,7 @@ "TestPortfolioPortfolioProductAssociationa0185761d231B0D998A7" ] }, - "TagOptionc0d88a3c4b8b": { + "TagOptions5f31c54ba705F110F743": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { "Key": "key1", @@ -225,7 +225,7 @@ "Active": true } }, - "TagOption9b16df08f83d": { + "TagOptions8d263919cebb6764AC10": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { "Key": "key1", @@ -233,7 +233,7 @@ "Active": true } }, - "TagOptiondf34c1c83580": { + "TagOptionsa260cbbd99c416C40F73": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { "Key": "key2", @@ -263,7 +263,7 @@ "Ref": "TestProduct7606930B" }, "TagOptionId": { - "Ref": "TagOptionc0d88a3c4b8b" + "Ref": "TagOptions5f31c54ba705F110F743" } } }, @@ -274,7 +274,7 @@ "Ref": "TestProduct7606930B" }, "TagOptionId": { - "Ref": "TagOption9b16df08f83d" + "Ref": "TagOptions8d263919cebb6764AC10" } } }, @@ -285,7 +285,7 @@ "Ref": "TestProduct7606930B" }, "TagOptionId": { - "Ref": "TagOptiondf34c1c83580" + "Ref": "TagOptionsa260cbbd99c416C40F73" } } }, diff --git a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts index 669016f35be2a..c5941dec42062 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/integ.portfolio.ts @@ -22,9 +22,11 @@ const portfolio = new servicecatalog.Portfolio(stack, 'TestPortfolio', { portfolio.giveAccessToRole(role); portfolio.giveAccessToGroup(group); -const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], +const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); portfolio.associateTagOptions(tagOptions); diff --git a/packages/@aws-cdk/aws-servicecatalog/test/integ.product.expected.json b/packages/@aws-cdk/aws-servicecatalog/test/integ.product.expected.json index fb51ec2ad0df4..05ea621ec10fd 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/integ.product.expected.json +++ b/packages/@aws-cdk/aws-servicecatalog/test/integ.product.expected.json @@ -226,7 +226,7 @@ "Ref": "TestProduct7606930B" }, "TagOptionId": { - "Ref": "TagOptionab501c9aef99" + "Ref": "TagOptions5f31c54ba705F110F743" } } }, @@ -237,7 +237,7 @@ "Ref": "TestProduct7606930B" }, "TagOptionId": { - "Ref": "TagOptiona453ac93ee6f" + "Ref": "TagOptions8d263919cebb6764AC10" } } }, @@ -248,11 +248,11 @@ "Ref": "TestProduct7606930B" }, "TagOptionId": { - "Ref": "TagOptiona006431604cb" + "Ref": "TagOptionsa260cbbd99c416C40F73" } } }, - "TagOptionab501c9aef99": { + "TagOptions5f31c54ba705F110F743": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { "Key": "key1", @@ -260,7 +260,7 @@ "Active": true } }, - "TagOptiona453ac93ee6f": { + "TagOptions8d263919cebb6764AC10": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { "Key": "key1", @@ -268,7 +268,7 @@ "Active": true } }, - "TagOptiona006431604cb": { + "TagOptionsa260cbbd99c416C40F73": { "Type": "AWS::ServiceCatalog::TagOption", "Properties": { "Key": "key2", diff --git a/packages/@aws-cdk/aws-servicecatalog/test/integ.product.ts b/packages/@aws-cdk/aws-servicecatalog/test/integ.product.ts index e1e08105ee3ce..22429b3ddbf83 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/integ.product.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/integ.product.ts @@ -38,9 +38,11 @@ const product = new servicecatalog.CloudFormationProduct(stack, 'TestProduct', { ], }); -const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], +const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); product.associateTagOptions(tagOptions); diff --git a/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts b/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts index 43a283c157f80..e53062e0a6752 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/portfolio.test.ts @@ -304,9 +304,11 @@ describe('portfolio associations and product constraints', () => { }), test('add tag options to portfolio', () => { - const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); portfolio.associateTagOptions(tagOptions); @@ -316,9 +318,11 @@ describe('portfolio associations and product constraints', () => { }), test('add tag options to portfolio as prop', () => { - const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); portfolio = new servicecatalog.Portfolio(stack, 'MyPortfolioWithTag', { @@ -331,53 +335,21 @@ describe('portfolio associations and product constraints', () => { Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOptionAssociation', 3); }), - test('adding identical tag options to portfolio is idempotent', () => { - const tagOptions1 = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], - }); - - const tagOptions2 = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], + test('adding tag options to portfolio multiple times is idempotent', () => { + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); - portfolio.associateTagOptions(tagOptions1); - portfolio.associateTagOptions(tagOptions2); // If not idempotent this would fail + portfolio.associateTagOptions(tagOptions); + portfolio.associateTagOptions(tagOptions); // If not idempotent this would fail Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOption', 3); //Generates a resource for each unique key-value pair Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOptionAssociation', 3); }), - test('fails to add tag options with invalid minimum key length', () => { - const tagOptions = new servicecatalog.TagOptions({ - '': ['value1', 'value2'], - 'key2': ['value1'], - }); - expect(() => { - portfolio.associateTagOptions(tagOptions); - }).toThrowError(/Invalid TagOption key for resource/); - }); - - test('fails to add tag options with invalid maxium key length', () => { - const tagOptions = new servicecatalog.TagOptions({ - ['key1'.repeat(1000)]: ['value1', 'value2'], - key2: ['value1'], - }); - expect(() => { - portfolio.associateTagOptions(tagOptions); - }).toThrowError(/Invalid TagOption key for resource/); - }), - - test('fails to add tag options with invalid value length', () => { - const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1'.repeat(1000), 'value2'], - key2: ['value1'], - }); - expect(() => { - portfolio.associateTagOptions(tagOptions); - }).toThrowError(/Invalid TagOption value for resource/); - }), - test('add tag update constraint', () => { portfolio.addProduct(product); portfolio.constrainTagUpdates(product, { diff --git a/packages/@aws-cdk/aws-servicecatalog/test/product.test.ts b/packages/@aws-cdk/aws-servicecatalog/test/product.test.ts index 0afc91ce86153..d27e18a1c3358 100644 --- a/packages/@aws-cdk/aws-servicecatalog/test/product.test.ts +++ b/packages/@aws-cdk/aws-servicecatalog/test/product.test.ts @@ -271,7 +271,7 @@ describe('Product', () => { productVersions: [], }); }).toThrowError(/Invalid product versions for resource Default\/MyProduct/); - }), + }); describe('adding and associating TagOptions to a product', () => { let product: servicecatalog.IProduct; @@ -286,12 +286,14 @@ describe('Product', () => { }, ], }); - }), + }); test('add tag options to product', () => { - const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); product.associateTagOptions(tagOptions); @@ -301,9 +303,11 @@ describe('Product', () => { }), test('add tag options as input to product in props', () => { - const tagOptions = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); new servicecatalog.CloudFormationProduct(stack, 'MyProductWithTagOptions', { @@ -321,44 +325,19 @@ describe('Product', () => { Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOptionAssociation', 3); }), - test('adding identical tag options to product is idempotent', () => { - const tagOptions1 = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], - }); - - const tagOptions2 = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], + test('adding tag options to product multiple times is idempotent', () => { + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, }); - product.associateTagOptions(tagOptions1); - product.associateTagOptions(tagOptions2); // If not idempotent this would fail + product.associateTagOptions(tagOptions); + product.associateTagOptions(tagOptions); // If not idempotent this would fail Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOption', 3); //Generates a resource for each unique key-value pair Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOptionAssociation', 3); - }), - - test('adding duplicate tag options to portfolio and product creates unique tag options and enumerated associations', () => { - const tagOptions1 = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value1'], - }); - - const tagOptions2 = new servicecatalog.TagOptions({ - key1: ['value1', 'value2'], - key2: ['value2'], - }); - - const portfolio = new servicecatalog.Portfolio(stack, 'MyPortfolio', { - displayName: 'testPortfolio', - providerName: 'testProvider', - }); - - portfolio.associateTagOptions(tagOptions1); - product.associateTagOptions(tagOptions2); // If not idempotent this would fail - - Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOption', 4); //Generates a resource for each unique key-value pair - Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOptionAssociation', 6); }); }); }); diff --git a/packages/@aws-cdk/aws-servicecatalog/test/tag-option.test.ts b/packages/@aws-cdk/aws-servicecatalog/test/tag-option.test.ts new file mode 100644 index 0000000000000..54e0464da467e --- /dev/null +++ b/packages/@aws-cdk/aws-servicecatalog/test/tag-option.test.ts @@ -0,0 +1,194 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as servicecatalog from '../lib'; + +describe('TagOptions', () => { + let app: cdk.App; + let stack: cdk.Stack; + + beforeEach(() => { + app = new cdk.App(); + stack = new cdk.Stack(app); + }); + + describe('creating tagOption(s)', () => { + test('default tagOptions creation', () => { + new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1', 'value2', 'value3'], + }, + }); + + Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOption', 5); + }), + + test('fails to create tag option with invalid minimum key length', () => { + expect(() => { + new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + '': ['value1', 'value2'], + }, + }); + }).toThrowError(/Invalid TagOption key for resource/); + }), + + test('fails to create tag option with invalid maxium key length', () => { + expect(() => { + new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + ['longKey'.repeat(1000)]: ['value1', 'value2'], + }, + }); + }).toThrowError(/Invalid TagOption key for resource/); + }), + + test('fails to create tag option with invalid value length', () => { + expect(() => { + new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key: ['tagOptionValue'.repeat(1000)], + }, + }); + }).toThrowError(/Invalid TagOption value for resource/); + }), + + test('fails to create tag options with no tag keys or values', () => { + expect(() => { + new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: {}, + }); + }).toThrowError(/No tag option keys or values were provided/); + }), + + test('fails to create tag options for tag key with no values', () => { + expect(() => { + new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: [], + }, + }); + }).toThrowError(/No tag option values were provided for tag option key/); + }), + + test('associate tag options', () => { + const portfolio = new servicecatalog.Portfolio(stack, 'MyPortfolio', { + displayName: 'testPortfolio', + providerName: 'testProvider', + }); + + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1', 'value2', 'value3'], + }, + }); + portfolio.associateTagOptions(tagOptions); + + Template.fromStack(stack).hasResource('AWS::ServiceCatalog::TagOption', 5); + Template.fromStack(stack).hasResource('AWS::ServiceCatalog::TagOptionAssociation', 5); + }), + + test('creating tag options with duplicate values is idempotent', () => { + const portfolio = new servicecatalog.Portfolio(stack, 'MyPortfolio', { + displayName: 'testPortfolio', + providerName: 'testProvider', + }); + + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2', 'value2'], + key2: ['value1', 'value2', 'value3', 'value3'], + }, + }); + portfolio.associateTagOptions(tagOptions); + + Template.fromStack(stack).hasResource('AWS::ServiceCatalog::TagOption', 5); + Template.fromStack(stack).hasResource('AWS::ServiceCatalog::TagOptionAssociation', 5); + }), + + test('create and associate tag options to different resources', () => { + const portfolio1 = new servicecatalog.Portfolio(stack, 'MyPortfolio1', { + displayName: 'testPortfolio1', + providerName: 'testProvider1', + }); + + const portfolio2 = new servicecatalog.Portfolio(stack, 'MyPortfolio2', { + displayName: 'testPortfolio2', + providerName: 'testProvider2', + }); + + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1', 'value2', 'value3'], + }, + }); + + portfolio1.associateTagOptions(tagOptions); + portfolio2.associateTagOptions(tagOptions); + + Template.fromStack(stack).hasResource('AWS::ServiceCatalog::TagOption', 5); + Template.fromStack(stack).hasResource('AWS::ServiceCatalog::TagOptionAssociation', 10); + }), + + test('adding tag options to portfolio and product creates unique tag options and enumerated associations', () => { + const tagOptions = new servicecatalog.TagOptions(stack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1'], + }, + }); + + const portfolio = new servicecatalog.Portfolio(stack, 'MyPortfolio', { + displayName: 'testPortfolio', + providerName: 'testProvider', + }); + + const product = new servicecatalog.CloudFormationProduct(stack, 'MyProduct', { + productName: 'testProduct', + owner: 'testOwner', + productVersions: [ + { + cloudFormationTemplate: servicecatalog.CloudFormationTemplate.fromUrl('https://awsdocs.s3.amazonaws.com/servicecatalog/development-environment.template'), + }, + ], + tagOptions: tagOptions, + }); + + portfolio.associateTagOptions(tagOptions); + product.associateTagOptions(tagOptions); + + Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOption', 3); //Generates a resource for each unique key-value pair + Template.fromStack(stack).resourceCountIs('AWS::ServiceCatalog::TagOptionAssociation', 6); + }); + + test('create and associate tag options in another stack', () => { + const tagOptionsStack = new cdk.Stack(app, 'TagOptionsStack'); + const productStack = new cdk.Stack(app, 'ProductStack'); + + const tagOptions = new servicecatalog.TagOptions(tagOptionsStack, 'TagOptions', { + allowedValuesForTags: { + key1: ['value1', 'value2'], + key2: ['value1', 'value2', 'value3'], + }, + }); + + new servicecatalog.CloudFormationProduct(productStack, 'MyProduct', { + productName: 'testProduct', + owner: 'testOwner', + productVersions: [ + { + cloudFormationTemplate: servicecatalog.CloudFormationTemplate.fromUrl('https://awsdocs.s3.amazonaws.com/servicecatalog/development-environment.template'), + }, + ], + tagOptions: tagOptions, + }); + + Template.fromStack(tagOptionsStack).hasResource('AWS::ServiceCatalog::TagOption', 5); + Template.fromStack(productStack).resourceCountIs('AWS::ServiceCatalog::TagOption', 0); + Template.fromStack(productStack).hasResource('AWS::ServiceCatalog::TagOptionAssociation', 5); + }); + }); +}); diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index 3fcee22ba4a66..ba4ab174a807e 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", @@ -156,7 +163,9 @@ "docs-public-apis:@aws-cdk/aws-servicediscovery.PrivateDnsNamespaceAttributes", "docs-public-apis:@aws-cdk/aws-servicediscovery.PrivateDnsNamespaceProps", "docs-public-apis:@aws-cdk/aws-servicediscovery.PublicDnsNamespaceAttributes", - "docs-public-apis:@aws-cdk/aws-servicediscovery.PublicDnsNamespaceProps" + "docs-public-apis:@aws-cdk/aws-servicediscovery.PublicDnsNamespaceProps", + "resource-attribute:@aws-cdk/aws-servicediscovery.PrivateDnsNamespace.privateDnsNamespaceHostedZoneId", + "resource-attribute:@aws-cdk/aws-servicediscovery.PublicDnsNamespace.publicDnsNamespaceHostedZoneId" ] }, "stability": "stable", diff --git a/packages/@aws-cdk/aws-ses-actions/package.json b/packages/@aws-cdk/aws-ses-actions/package.json index 2d42ecb26c71b..0165754b44065 100644 --- a/packages/@aws-cdk/aws-ses-actions/package.json +++ b/packages/@aws-cdk/aws-ses-actions/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 9b49c6d9b0933..f7419dafe0ba7 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/aws-signer/README.md b/packages/@aws-cdk/aws-signer/README.md index 2de7797eb344f..6c95da4668e7a 100644 --- a/packages/@aws-cdk/aws-signer/README.md +++ b/packages/@aws-cdk/aws-signer/README.md @@ -27,7 +27,7 @@ to sign a zip file. For more information go to [Signing Platforms in AWS Signer] AWS Signer provides a pre-defined set of signing platforms. They are available in the CDK as - -```ts +```text Platform.AWS_IOT_DEVICE_MANAGEMENT_SHA256_ECDSA Platform.AWS_LAMBDA_SHA384_ECDSA Platform.AMAZON_FREE_RTOS_TI_CC3220SF @@ -43,11 +43,9 @@ For more information, visit [Signing Profiles in AWS Signer](https://docs.aws.am The following code sets up a signing profile for signing lambda code bundles - ```ts -import * as signer from '@aws-cdk/aws-signer'; - const signingProfile = new signer.SigningProfile(this, 'SigningProfile', { platform: signer.Platform.AWS_LAMBDA_SHA384_ECDSA, -} ); +}); ``` A signing profile is valid by default for 135 months. This can be modified by specifying the `signatureValidityPeriod` property. diff --git a/packages/@aws-cdk/aws-signer/package.json b/packages/@aws-cdk/aws-signer/package.json index 064c381b6a88a..7f44205727a9b 100644 --- a/packages/@aws-cdk/aws-signer/package.json +++ b/packages/@aws-cdk/aws-signer/package.json @@ -7,6 +7,13 @@ "jsii": { "outdir": "dist", "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, "targets": { "dotnet": { "namespace": "Amazon.CDK.AWS.Signer", diff --git a/packages/@aws-cdk/aws-signer/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-signer/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..443e1e88b241a --- /dev/null +++ b/packages/@aws-cdk/aws-signer/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as signer from '@aws-cdk/aws-signer'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md index 59adc3517e704..17948bc1112f4 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md @@ -208,6 +208,7 @@ and invokes it asynchronously. ```ts declare const fn: lambda.Function; + const submitJob = new tasks.LambdaInvoke(this, 'Invoke Handler', { lambdaFunction: fn, payload: sfn.TaskInput.fromJsonPathAt('$.input'), @@ -215,12 +216,12 @@ 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()`. +You can also use [intrinsic functions](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html) available on `JsonPath`, for example `JsonPath.format()`. 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)"), + queryString: sfn.JsonPath.format('select contacts where year={};', sfn.JsonPath.stringAt('$.year')), queryExecutionContext: { databaseName: 'interactions', }, @@ -305,6 +306,25 @@ const invokeTask = new tasks.CallApiGatewayRestApiEndpoint(this, 'Call REST API' }); ``` +Be aware that the header values must be arrays. When passing the Task Token +in the headers field `WAIT_FOR_TASK_TOKEN` integration, use +`JsonPath.array()` to wrap the token in an array: + +```ts +import * as apigateway from '@aws-cdk/aws-apigateway'; +declare const api: apigateway.RestApi; + +new tasks.CallApiGatewayRestApiEndpoint(this, 'Endpoint', { + api, + stageName: 'Stage', + method: tasks.HttpMethod.PUT, + integrationPattern: sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, + headers: sfn.TaskInput.fromObject({ + TaskToken: sfn.JsonPath.array(sfn.JsonPath.taskToken), + }), +}); +``` + ### Call HTTP API Endpoint The `CallApiGatewayHttpApiEndpoint` calls the HTTP API endpoint. @@ -798,7 +818,7 @@ The service integration APIs correspond to Amazon EMR on EKS APIs, but differ in ### Create Virtual Cluster -The [CreateVirtualCluster](https://docs.aws.amazon.com/emr-on-eks/latest/APIReference/API_CreateVirtualCluster.html) API creates a single virtual cluster that's mapped to a single Kubernetes namespace. +The [CreateVirtualCluster](https://docs.aws.amazon.com/emr-on-eks/latest/APIReference/API_CreateVirtualCluster.html) API creates a single virtual cluster that's mapped to a single Kubernetes namespace. The EKS cluster containing the Kubernetes namespace where the virtual cluster will be mapped can be passed in from the task input. diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/apigateway/call-rest-api.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/apigateway/call-rest-api.ts index 0352777e9c06a..153331cb6dafb 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/apigateway/call-rest-api.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/apigateway/call-rest-api.ts @@ -24,6 +24,25 @@ export interface CallApiGatewayRestApiEndpointProps extends CallApiGatewayEndpoi /** * Call REST API endpoint as a Task * + * Be aware that the header values must be arrays. When passing the Task Token + * in the headers field `WAIT_FOR_TASK_TOKEN` integration, use + * `JsonPath.array()` to wrap the token in an array: + * + * ```ts + * import * as apigateway from '@aws-cdk/aws-apigateway'; + * declare const api: apigateway.RestApi; + * + * new tasks.CallApiGatewayRestApiEndpoint(this, 'Endpoint', { + * api, + * stageName: 'Stage', + * method: tasks.HttpMethod.PUT, + * integrationPattern: sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, + * headers: sfn.TaskInput.fromObject({ + * TaskToken: sfn.JsonPath.array(sfn.JsonPath.taskToken), + * }), + * }); + * ``` + * * @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-api-gateway.html */ export class CallApiGatewayRestApiEndpoint extends CallApiGatewayEndpointBase { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts index 4cac7c7180bde..9c4f53ca3b927 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts @@ -202,7 +202,7 @@ export class EmrCreateCluster extends sfn.TaskStateBase { this.taskPolicies = this.createPolicyStatements(this._serviceRole, this._clusterRole, this._autoScalingRole); - if (this.props.releaseLabel !== undefined) { + if (this.props.releaseLabel !== undefined && !cdk.Token.isUnresolved(this.props.releaseLabel)) { this.validateReleaseLabel(this.props.releaseLabel); } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/call-rest-api.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/call-rest-api.test.ts index 37a083fb2cc95..f47ca69e0dc0a 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/call-rest-api.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/call-rest-api.test.ts @@ -69,7 +69,7 @@ describe('CallApiGatewayRestApiEndpoint', () => { method: HttpMethod.GET, stageName: 'dev', integrationPattern: sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, - headers: sfn.TaskInput.fromObject({ TaskToken: sfn.JsonPath.taskToken }), + headers: sfn.TaskInput.fromObject({ TaskToken: sfn.JsonPath.array(sfn.JsonPath.taskToken) }), }); // THEN @@ -97,7 +97,7 @@ describe('CallApiGatewayRestApiEndpoint', () => { }, AuthType: 'NO_AUTH', Headers: { - 'TaskToken.$': '$$.Task.Token', + 'TaskToken.$': 'States.Array($$.Task.Token)', }, Method: 'GET', Stage: 'dev', diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/emr/emr-create-cluster.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/emr/emr-create-cluster.test.ts index 3d846ec1d06d2..4e185710e2413 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/emr/emr-create-cluster.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/emr/emr-create-cluster.test.ts @@ -34,6 +34,14 @@ beforeEach(() => { ); }); +test('Create Cluster with an unresolved release label', () => { + new EmrCreateCluster(stack, 'Task', { + instances: {}, + name: 'Cluster', + releaseLabel: cdk.Token.asString({}), + }); +}); + test('Create Cluster with FIRE_AND_FORGET integrationPattern', () => { // WHEN const task = new EmrCreateCluster(stack, 'Task', { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/emrcontainers/integ.job-submission-workflow.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/emrcontainers/integ.job-submission-workflow.expected.json index ec1551086d626..ad853185040b4 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/emrcontainers/integ.job-submission-workflow.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/emrcontainers/integ.job-submission-workflow.expected.json @@ -1342,6 +1342,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json index 107fd16780144..942f72ba3d41b 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json @@ -148,6 +148,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json index 0cd3166d73138..3e069a953f03a 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json @@ -148,6 +148,10 @@ "Action": [ "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md index eae413c7c3574..94129f56a38f9 100644 --- a/packages/@aws-cdk/aws-stepfunctions/README.md +++ b/packages/@aws-cdk/aws-stepfunctions/README.md @@ -95,6 +95,51 @@ properly (for example, permissions to invoke any Lambda functions you add to your workflow). A role will be created by default, but you can supply an existing one as well. +## Accessing State (the JsonPath class) + +Every State Machine execution has [State Machine +Data](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-state-machine-data.html): +a JSON document containing keys and values that is fed into the state machine, +gets modified as the state machine progresses, and finally is produced as output. + +You can pass fragments of this State Machine Data into Tasks of the state machine. +To do so, use the static methods on the `JsonPath` class. For example, to pass +the value that's in the data key of `OrderId` to a Lambda function as you invoke +it, use `JsonPath.stringAt('$.OrderId')`, like so: + +```ts +import * as lambda from '@aws-cdk/aws-lambda'; + +declare const orderFn: lambda.Function; + +const submitJob = new tasks.LambdaInvoke(this, 'InvokeOrderProcessor', { + lambdaFunction: orderFn, + payload: sfn.TaskInput.fromObject({ + OrderId: sfn.JsonPath.stringAt('$.OrderId'), + }), +}); +``` + +The following methods are available: + +| Method | Purpose | +|--------|---------| +| `JsonPath.stringAt('$.Field')` | reference a field, return the type as a `string`. | +| `JsonPath.listAt('$.Field')` | reference a field, return the type as a list of strings. | +| `JsonPath.numberAt('$.Field')` | reference a field, return the type as a number. Use this for functions that expect a number argument. | +| `JsonPath.objectAt('$.Field')` | reference a field, return the type as an `IResolvable`. Use this for functions that expect an object argument. | +| `JsonPath.entirePayload` | reference the entire data object (equivalent to a path of `$`). | +| `JsonPath.taskToken` | reference the [Task Token](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token), used for integration patterns that need to run for a long time. | + +You can also call [intrinsic functions](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html) using the methods on `JsonPath`: + +| Method | Purpose | +|--------|---------| +| `JsonPath.array(JsonPath.stringAt('$.Field'), ...)` | make an array from other elements. | +| `JsonPath.format('The value is {}.', JsonPath.stringAt('$.Value'))` | insert elements into a format string. | +| `JsonPath.stringToJson(JsonPath.stringAt('$.ObjStr'))` | parse a JSON string to an object | +| `JsonPath.jsonToString(JsonPath.objectAt('$.Obj'))` | stringify an object to a JSON string | + ## Amazon States Language This library comes with a set of classes that model the [Amazon States @@ -603,8 +648,8 @@ new cloudwatch.Alarm(this, 'ThrottledAlarm', { ## Error names -Step Functions identifies errors in the Amazon States Language using case-sensitive strings, known as error names. -The Amazon States Language defines a set of built-in strings that name well-known errors, all beginning with the `States.` prefix. +Step Functions identifies errors in the Amazon States Language using case-sensitive strings, known as error names. +The Amazon States Language defines a set of built-in strings that name well-known errors, all beginning with the `States.` prefix. * `States.ALL` - A wildcard that matches any known error name. * `States.Runtime` - An execution failed due to some exception that could not be processed. Often these are caused by errors at runtime, such as attempting to apply InputPath or OutputPath on a null JSON payload. A `States.Runtime` error is not retriable, and will always cause the execution to fail. A retry or catch on `States.ALL` will NOT catch States.Runtime errors. diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts b/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts index 990a2542d4fea..e78c026293798 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/fields.ts @@ -1,5 +1,5 @@ -import { Token } from '@aws-cdk/core'; -import { findReferencedPaths, jsonPathString, JsonPathToken, renderObject } from './json-path'; +import { Token, IResolvable } from '@aws-cdk/core'; +import { findReferencedPaths, jsonPathString, JsonPathToken, renderObject, renderInExpression, jsonPathFromAny } from './private/json-path'; /** * Extract a field from the State Machine data or context @@ -38,6 +38,14 @@ export class JsonPath { return Token.asNumber(new JsonPathToken(path)); } + /** + * Reference a complete (complex) object in a JSON path location + */ + public static objectAt(path: string): IResolvable { + validateJsonPath(path); + return new JsonPathToken(path); + } + /** * Use the entire data structure * @@ -78,6 +86,82 @@ export class JsonPath { return new JsonPathToken('$$').toString(); } + /** + * Make an intrinsic States.Array expression + * + * Combine any number of string literals or JsonPath expressions into an array. + * + * Use this function if the value of an array element directly has to come + * from a JSON Path expression (either the State object or the Context object). + * + * If the array contains object literals whose values come from a JSON path + * expression, you do not need to use this function. + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html + */ + public static array(...values: string[]): string { + return new JsonPathToken(`States.Array(${values.map(renderInExpression).join(', ')})`).toString(); + } + + /** + * Make an intrinsic States.Format expression + * + * This can be used to embed JSON Path variables inside a format string. + * + * For example: + * + * ```ts + * sfn.JsonPath.format('Hello, my name is {}.', sfn.JsonPath.stringAt('$.name')) + * ``` + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html + */ + public static format(formatString: string, ...values: string[]): string { + const allArgs = [formatString, ...values]; + return new JsonPathToken(`States.Format(${allArgs.map(renderInExpression).join(', ')})`).toString(); + } + + /** + * Make an intrinsic States.StringToJson expression + * + * During the execution of the Step Functions state machine, parse the given + * argument as JSON into its object form. + * + * For example: + * + * ```ts + * sfn.JsonPath.stringToJson(sfn.JsonPath.stringAt('$.someJsonBody')) + * ``` + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html + */ + public static stringToJson(jsonString: string): IResolvable { + return new JsonPathToken(`States.StringToJson(${renderInExpression(jsonString)})`); + } + + /** + * Make an intrinsic States.JsonToString expression + * + * During the execution of the Step Functions state machine, encode the + * given object into a JSON string. + * + * For example: + * + * ```ts + * sfn.JsonPath.jsonToString(sfn.JsonPath.objectAt('$.someObject')) + * ``` + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html + */ + public static jsonToString(value: any): string { + const path = jsonPathFromAny(value); + if (!path) { + throw new Error('Argument to JsonPath.jsonToString() must be a JsonPath object'); + } + + return new JsonPathToken(`States.JsonToString(${path})`).toString(); + } + private constructor() {} } diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/private/intrinstics.ts b/packages/@aws-cdk/aws-stepfunctions/lib/private/intrinstics.ts new file mode 100644 index 0000000000000..f10c81ffb115b --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions/lib/private/intrinstics.ts @@ -0,0 +1,262 @@ +export type IntrinsicExpression = StringLiteralExpression | PathExpression | FnCallExpression; +export type TopLevelIntrinsic = PathExpression | FnCallExpression; + +export interface StringLiteralExpression { + readonly type: 'string-literal'; + readonly literal: string; +} + +export interface PathExpression { + readonly type: 'path'; + readonly path: string; +} + +export interface FnCallExpression { + readonly type: 'fncall'; + readonly functionName: string; + readonly arguments: IntrinsicExpression[]; +} + + +/** + * LL(1) parser for StepFunctions intrinsics + * + * The parser implements a state machine over a cursor into an expression + * string. The cusor gets moved, the character at the cursor gets inspected + * and based on the character we accumulate some value and potentially move + * to a different state. + * + * Literal strings are not allowed at the top level, but are allowed inside + * function calls. + */ +export class IntrinsicParser { + private i: number = 0; + + constructor(private readonly expression: string) { + } + + public parseTopLevelIntrinsic(): TopLevelIntrinsic { + this.ws(); + + let ret; + if (this.char() === '$') { + ret = this.parsePath(); + } else if (isAlphaNum(this.char())) { + ret = this.parseFnCall(); + } else { + this.raiseError("expected '$' or a function call"); + } + + this.ws(); + + if (!this.eof) { + this.raiseError('unexpected trailing characters'); + } + + return ret; + } + + private parseIntrinsic(): IntrinsicExpression { + this.ws(); + + if (this.char() === '$') { + return this.parsePath(); + } + + if (isAlphaNum(this.char())) { + return this.parseFnCall(); + } + + if (this.char() === "'") { + return this.parseStringLiteral(); + } + + this.raiseError('expected $, function or single-quoted string'); + } + + /** + * Simplified path parsing + * + * JSON path can actually be quite complicated, but we don't need to validate + * it precisely. We just need to know how far it extends. + * + * Therefore, we only care about: + * + * - Starts with a $ + * - Accept ., $ and alphanums + * - Accept single-quoted strings ('...') + * - Accept anything between matched square brackets ([...]) + */ + private parsePath(): PathExpression { + const pathString = new Array(); + if (this.char() !== '$') { + this.raiseError('expected \'$\''); + } + pathString.push(this.consume()); + + let done = false; + while (!done && !this.eof) { + switch (this.char()) { + case '.': + case '$': + pathString.push(this.consume()); + break; + case "'": + const { quoted } = this.consumeQuotedString(); + pathString.push(quoted); + break; + + case '[': + pathString.push(this.consumeBracketedExpression(']')); + break; + + default: + if (isAlphaNum(this.char())) { + pathString.push(this.consume()); + break; + } + + // Not alphanum, end of path expression + done = true; + } + } + + return { type: 'path', path: pathString.join('') }; + } + + /** + * Parse a fncall + * + * Cursor should be on call identifier. Afterwards, cursor will be on closing + * quote. + */ + private parseFnCall(): FnCallExpression { + const name = new Array(); + while (this.char() !== '(') { + name.push(this.consume()); + } + + this.next(); // Consume the '(' + this.ws(); + + const args = []; + while (this.char() !== ')') { + args.push(this.parseIntrinsic()); + this.ws(); + + if (this.char() === ',') { + this.next(); + continue; + } else if (this.char() === ')') { + continue; + } else { + this.raiseError('expected , or )'); + } + } + this.next(); // Consume ')' + + return { + type: 'fncall', + arguments: args, + functionName: name.join(''), + }; + } + + /** + * Parse a string literal + * + * Cursor is expected to be on the first opening quote. Afterwards, + * cursor will be after the closing quote. + */ + private parseStringLiteral(): StringLiteralExpression { + const { unquoted } = this.consumeQuotedString(); + return { type: 'string-literal', literal: unquoted }; + } + + /** + * Parse a bracketed expression + * + * Cursor is expected to be on the opening brace. Afterwards, + * the cursor will be after the closing brace. + */ + private consumeBracketedExpression(closingBrace: string): string { + const ret = new Array(); + ret.push(this.consume()); + while (this.char() !== closingBrace) { + if (this.char() === '[') { + ret.push(this.consumeBracketedExpression(']')); + } else if (this.char() === '{') { + ret.push(this.consumeBracketedExpression('}')); + } else { + ret.push(this.consume()); + } + } + ret.push(this.consume()); + return ret.join(''); + } + + /** + * Parse a string literal + * + * Cursor is expected to be on the first opening quote. Afterwards, + * cursor will be after the closing quote. + */ + private consumeQuotedString(): { readonly quoted: string; unquoted: string } { + const quoted = new Array(); + const unquoted = new Array(); + + quoted.push(this.consume()); + while (this.char() !== "'") { + if (this.char() === '\\') { + // Advance and add next character literally, whatever it is + quoted.push(this.consume()); + } + quoted.push(this.char()); + unquoted.push(this.char()); + this.next(); + } + quoted.push(this.consume()); + return { quoted: quoted.join(''), unquoted: unquoted.join('') }; + } + + /** + * Consume whitespace if it exists + * + * Move the cursor to the next non-whitespace character. + */ + private ws() { + while (!this.eof && [' ', '\t', '\n'].includes(this.char())) { + this.next(); + } + } + + private get eof() { + return this.i >= this.expression.length; + } + + private char(): string { + if (this.eof) { + this.raiseError('unexpected end of string'); + } + + return this.expression[this.i]; + } + + private next() { + this.i++; + } + + private consume() { + const ret = this.char(); + this.next(); + return ret; + } + + private raiseError(message: string): never { + throw new Error(`Invalid JSONPath expression: ${message} at index ${this.i} in ${JSON.stringify(this.expression)}`); + } +} + +function isAlphaNum(x: string) { + return x.match(/^[a-zA-Z0-9]$/); +} diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts b/packages/@aws-cdk/aws-stepfunctions/lib/private/json-path.ts similarity index 70% rename from packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts rename to packages/@aws-cdk/aws-stepfunctions/lib/private/json-path.ts index b4602b5e887d0..7263da227261f 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/private/json-path.ts @@ -1,4 +1,5 @@ import { captureStackTrace, IResolvable, IResolveContext, Token, Tokenization } from '@aws-cdk/core'; +import { IntrinsicParser, IntrinsicExpression } from './intrinstics'; const JSON_PATH_TOKEN_SYMBOL = Symbol.for('@aws-cdk/aws-stepfunctions.JsonPathToken'); @@ -38,6 +39,7 @@ export function renderObject(obj: object | undefined): object | undefined { handleList: renderStringList, handleNumber: renderNumber, handleBoolean: renderBoolean, + handleResolvable: renderResolvable, }); } @@ -49,36 +51,79 @@ export function findReferencedPaths(obj: object | undefined): Set { recurseObject(obj, { handleString(_key: string, x: string) { - const path = jsonPathString(x); - if (path !== undefined) { found.add(path); } + for (const p of findPathsInIntrinsicFunctions(jsonPathString(x))) { + found.add(p); + } return {}; }, handleList(_key: string, x: string[]) { - const path = jsonPathStringList(x); - if (path !== undefined) { found.add(path); } + for (const p of findPathsInIntrinsicFunctions(jsonPathStringList(x))) { + found.add(p); + } return {}; }, handleNumber(_key: string, x: number) { - const path = jsonPathNumber(x); - if (path !== undefined) { found.add(path); } + for (const p of findPathsInIntrinsicFunctions(jsonPathNumber(x))) { + found.add(p); + } return {}; }, handleBoolean(_key: string, _x: boolean) { return {}; }, + + handleResolvable(_key: string, x: IResolvable) { + for (const p of findPathsInIntrinsicFunctions(jsonPathFromAny(x))) { + found.add(p); + } + return {}; + }, }); return found; } +/** + * From an expression, return the list of JSON paths referenced in it + */ +function findPathsInIntrinsicFunctions(expression?: string): string[] { + if (!expression) { return []; } + + const ret = new Array(); + + try { + const parsed = new IntrinsicParser(expression).parseTopLevelIntrinsic(); + recurse(parsed); + return ret; + } catch (e) { + // Not sure that our parsing is 100% correct. We don't want to break anyone, so + // fall back to legacy behavior if we can't parse this string. + return [expression]; + } + + function recurse(p: IntrinsicExpression) { + switch (p.type) { + case 'path': + ret.push(p.path); + break; + + case 'fncall': + for (const arg of p.arguments) { + recurse(arg); + } + } + } +} + interface FieldHandlers { handleString(key: string, x: string): {[key: string]: string}; handleList(key: string, x: string[]): {[key: string]: string[] | string }; handleNumber(key: string, x: number): {[key: string]: number | string}; handleBoolean(key: string, x: boolean): {[key: string]: boolean}; + handleResolvable(key: string, x: IResolvable): {[key: string]: any}; } export function recurseObject(obj: object | undefined, handlers: FieldHandlers, visited: object[] = []): object | undefined { @@ -108,7 +153,11 @@ export function recurseObject(obj: object | undefined, handlers: FieldHandlers, } else if (value === null || value === undefined) { // Nothing } else if (typeof value === 'object') { - ret[key] = recurseObject(value, handlers, visited); + if (Tokenization.isResolvable(value)) { + Object.assign(ret, handlers.handleResolvable(key, value)); + } else { + ret[key] = recurseObject(value, handlers, visited); + } } } @@ -166,6 +215,21 @@ function renderString(key: string, value: string): {[key: string]: string} { } } +/** + * Render a resolvable + * + * If we can extract a Path from it, render as a path string, otherwise as itself (will + * be resolved later + */ +function renderResolvable(key: string, value: IResolvable): {[key: string]: any} { + const path = jsonPathFromAny(value); + if (path !== undefined) { + return { [key + '.$']: path }; + } else { + return { [key]: value }; + } +} + /** * Render a parameter string list * @@ -219,6 +283,12 @@ export function jsonPathString(x: string): string | undefined { return undefined; } +export function jsonPathFromAny(x: any) { + if (!x) { return undefined; } + if (typeof x === 'string') { return jsonPathString(x); } + return pathFromToken(Tokenization.reverse(x)); +} + /** * If the indicated string list is an encoded JSON path, return the path * @@ -240,3 +310,35 @@ function jsonPathNumber(x: number): string | undefined { function pathFromToken(token: IResolvable | undefined) { return token && (JsonPathToken.isJsonPathToken(token) ? token.path : undefined); } + +/** + * Render the string in a valid JSON Path expression. + * + * If the string is a Tokenized JSON path reference -- return the JSON path reference inside it. + * Otherwise, single-quote it. + * + * Call this function whenever you're building compound JSONPath expressions, in + * order to avoid having tokens-in-tokens-in-tokens which become very hard to parse. + */ +export function renderInExpression(x: string) { + const path = jsonPathString(x); + return path ?? singleQuotestring(x); +} + +function singleQuotestring(x: string) { + const ret = new Array(); + ret.push("'"); + for (const c of x) { + if (c === "'") { + ret.push("\\'"); + } else if (c === '\\') { + ret.push('\\\\'); + } else if (c === '\n') { + ret.push('\\n'); + } else { + ret.push(c); + } + } + ret.push("'"); + return ret.join(''); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts index fb5e52d2831b2..bff92d431cbab 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts @@ -116,7 +116,7 @@ export interface PassProps { /** * Define a Pass in the state machine * - * A Pass state can be used to transform the current exeuction's state. + * A Pass state can be used to transform the current execution's state. */ export class Pass extends State implements INextable { public readonly endStates: INextable[]; diff --git a/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts index 0e6e3e9c25175..eec3864f938e6 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/fields.test.ts @@ -212,3 +212,52 @@ describe('Fields', () => { }); }); }); + +describe('intrinsics constructors', () => { + test('array', () => { + expect(FieldUtils.renderObject({ + Field: JsonPath.array('asdf', JsonPath.stringAt('$.Id')), + })).toEqual({ + 'Field.$': "States.Array('asdf', $.Id)", + }); + }); + + test('format', () => { + expect(FieldUtils.renderObject({ + Field: JsonPath.format('Hi my name is {}.', JsonPath.stringAt('$.Name')), + })).toEqual({ + 'Field.$': "States.Format('Hi my name is {}.', $.Name)", + }); + + expect(FieldUtils.renderObject({ + Field: JsonPath.format(JsonPath.stringAt('$.Format'), JsonPath.stringAt('$.Name')), + })).toEqual({ + 'Field.$': 'States.Format($.Format, $.Name)', + }); + }); + + test('stringToJson', () => { + expect(FieldUtils.renderObject({ + Field: JsonPath.stringToJson(JsonPath.stringAt('$.Str')), + })).toEqual({ + 'Field.$': 'States.StringToJson($.Str)', + }); + }); + + test('jsonToString', () => { + expect(FieldUtils.renderObject({ + Field: JsonPath.jsonToString(JsonPath.objectAt('$.Obj')), + })).toEqual({ + 'Field.$': 'States.JsonToString($.Obj)', + }); + }); +}); + +test('find task token even if nested in intrinsic functions', () => { + expect(FieldUtils.containsTaskToken({ x: JsonPath.array(JsonPath.taskToken) })).toEqual(true); + + expect(FieldUtils.containsTaskToken({ x: JsonPath.array('nope') })).toEqual(false); + + // Even if it's a hand-written literal and doesn't use our constructors + expect(FieldUtils.containsTaskToken({ x: JsonPath.stringAt('States.Array($$.Task.Token)') })).toEqual(true); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions/test/private/intrinsics.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/private/intrinsics.test.ts new file mode 100644 index 0000000000000..5534ad5509b20 --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions/test/private/intrinsics.test.ts @@ -0,0 +1,91 @@ +import { IntrinsicParser } from '../../lib/private/intrinstics'; + + +test('parse JSON path', () => { + expect(parse('$.Path')).toEqual({ type: 'path', path: '$.Path' }); + expect(parse('$[\'complex\'].Path')).toEqual({ type: 'path', path: '$[\'complex\'].Path' }); + + // Can add whitespace + expect(parse(' $.Path')).toEqual({ type: 'path', path: '$.Path' }); + expect(parse('$.Path ')).toEqual({ type: 'path', path: '$.Path' }); +}); + +test('JSON path with quoted literal', () => { + expect(parse("$['I\\'m'].Path")).toEqual({ type: 'path', path: "$['I\\'m'].Path" }); +}); + +test('Complex JSON path between square brackets', () => { + expect(parse("$[?('Eva Green' in @['starring'])]")).toEqual({ type: 'path', path: "$[?('Eva Green' in @['starring'])]" }); +}); + +test('JSON path must be contiguous', () => { + expect(() => parse('$.Path AndThen')).toThrow(/Invalid JSONPath expression/); +}); + +test('parse fncall with path', () => { + expect(parse('States.Array($$.Context.Token)')).toEqual({ + type: 'fncall', + functionName: 'States.Array', + arguments: [{ + type: 'path', + path: '$$.Context.Token', + }], + }); +}); + +test('parse fncall with string and path', () => { + expect(parse("States.Format('Hi my name is {}.', $.Name)")).toEqual({ + type: 'fncall', + functionName: 'States.Format', + arguments: [ + { + type: 'string-literal', + literal: 'Hi my name is {}.', + }, + { + type: 'path', + path: '$.Name', + }, + ], + }); +}); + +test('parse string literal with escaped quotes', () => { + expect(parse("States.Format('Hi I\\'m cool')")).toEqual({ + type: 'fncall', + functionName: 'States.Format', + arguments: [ + { + type: 'string-literal', + literal: "Hi I'm cool", + }, + ], + }); +}); + +test('nested function calls', () => { + expect(parse("States.Format('{}', States.JsonToString($.Obj))")).toEqual({ + type: 'fncall', + functionName: 'States.Format', + arguments: [ + { + type: 'string-literal', + literal: '{}', + }, + { + type: 'fncall', + functionName: 'States.JsonToString', + arguments: [ + { + type: 'path', + path: '$.Obj', + }, + ], + }, + ], + }); +}); + +function parse(x: string) { + return new IntrinsicParser(x).parseTopLevelIntrinsic(); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-synthetics/lib/canary.ts b/packages/@aws-cdk/aws-synthetics/lib/canary.ts index 1a4ebaa7326ab..c078fd0718ce7 100644 --- a/packages/@aws-cdk/aws-synthetics/lib/canary.ts +++ b/packages/@aws-cdk/aws-synthetics/lib/canary.ts @@ -299,9 +299,13 @@ export class Canary extends cdk.Resource { resources: ['*'], actions: ['s3:ListAllMyBuckets'], }), + new iam.PolicyStatement({ + resources: [this.artifactsBucket.bucketArn], + actions: ['s3:GetBucketLocation'], + }), new iam.PolicyStatement({ resources: [this.artifactsBucket.arnForObjects(`${prefix ? prefix+'/*' : '*'}`)], - actions: ['s3:PutObject', 's3:GetBucketLocation'], + actions: ['s3:PutObject'], }), new iam.PolicyStatement({ resources: ['*'], diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.asset.expected.json b/packages/@aws-cdk/aws-synthetics/test/integ.asset.expected.json index 7d614f08201b7..256de95e7be25 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.asset.expected.json +++ b/packages/@aws-cdk/aws-synthetics/test/integ.asset.expected.json @@ -41,10 +41,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyCanaryArtifactsBucket89975E6D", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ @@ -197,10 +204,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyCanaryArtifactsBucket89975E6D", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.canary.expected.json b/packages/@aws-cdk/aws-synthetics/test/integ.canary.expected.json index 5411264651330..2425fad01de67 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.canary.expected.json +++ b/packages/@aws-cdk/aws-synthetics/test/integ.canary.expected.json @@ -30,10 +30,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "mytestbucket8DC16178", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ @@ -210,10 +217,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyCanaryOneArtifactsBucketDF4A487D", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ @@ -424,10 +438,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyCanaryTwoArtifactsBucket79B179B6", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ @@ -638,10 +659,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyCanaryThreeArtifactsBucket894E857E", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ @@ -852,10 +880,17 @@ "Resource": "*" }, { - "Action": [ - "s3:PutObject", - "s3:GetBucketLocation" - ], + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPythonCanaryArtifactsBucket7AE88133", + "Arn" + ] + } + }, + { + "Action": "s3:PutObject", "Effect": "Allow", "Resource": { "Fn::Join": [ diff --git a/packages/@aws-cdk/aws-waf/package.json b/packages/@aws-cdk/aws-waf/package.json index d1f980c9a985b..8aaf46f4b625f 100644 --- a/packages/@aws-cdk/aws-waf/package.json +++ b/packages/@aws-cdk/aws-waf/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-wafregional/package.json b/packages/@aws-cdk/aws-wafregional/package.json index c3aff82a3a269..485d9962f8424 100644 --- a/packages/@aws-cdk/aws-wafregional/package.json +++ b/packages/@aws-cdk/aws-wafregional/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/aws-workspaces/package.json b/packages/@aws-cdk/aws-workspaces/package.json index e652fa57e8549..95758a0601e75 100644 --- a/packages/@aws-cdk/aws-workspaces/package.json +++ b/packages/@aws-cdk/aws-workspaces/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/cdk-assets-schema/package.json b/packages/@aws-cdk/cdk-assets-schema/package.json index a020985f920a7..682764dae229f 100644 --- a/packages/@aws-cdk/cdk-assets-schema/package.json +++ b/packages/@aws-cdk/cdk-assets-schema/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "scripts": { "build": "cdk-build", diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 57eaad18b6af9..9e9151fd8b014 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,102 @@ +# CloudFormation Resource Specification v54.0.0 + +## New Resource Types + +* AWS::KafkaConnect::Connector +* AWS::Rekognition::Collection + +## Attribute Changes + + +## Property Changes + +* AWS::IVS::RecordingConfiguration ThumbnailConfiguration (__added__) +* AWS::Location::GeofenceCollection PricingPlan.Required (__changed__) + * Old: true + * New: false +* AWS::Location::Map PricingPlan.Required (__changed__) + * Old: true + * New: false +* AWS::Location::PlaceIndex PricingPlan.Required (__changed__) + * Old: true + * New: false +* AWS::Location::RouteCalculator PricingPlan.Required (__changed__) + * Old: true + * New: false +* AWS::Location::Tracker PricingPlan.Required (__changed__) + * Old: true + * New: false +* AWS::SecretsManager::RotationSchedule RotateImmediatelyOnUpdate (__added__) +* AWS::Timestream::Table MagneticStoreWriteProperties (__added__) + +## Property Type Changes + +* AWS::GuardDuty::Detector.CFNKubernetesAuditLogsConfiguration (__added__) +* AWS::GuardDuty::Detector.CFNKubernetesConfiguration (__added__) +* AWS::IVS::RecordingConfiguration.ThumbnailConfiguration (__added__) +* AWS::MSK::Cluster.ProvisionedThroughput (__added__) +* AWS::ECS::TaskDefinition.EFSVolumeConfiguration AuthorizationConfig.PrimitiveType (__deleted__) +* AWS::GuardDuty::Detector.CFNDataSourceConfigurations Kubernetes (__added__) +* AWS::MSK::Cluster.BrokerNodeGroupInfo StorageInfo.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::MSK::Cluster.EBSStorageInfo ProvisionedThroughput (__added__) +* AWS::MSK::Cluster.EBSStorageInfo VolumeSize.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::MSK::Cluster.StorageInfo EBSStorageInfo.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::Route53::RecordSetGroup.RecordSet Comment (__deleted__) +* AWS::SecretsManager::RotationSchedule.RotationRules Duration (__added__) +* AWS::SecretsManager::RotationSchedule.RotationRules ScheduleExpression (__added__) + +## Unapplied changes + +* AWS::AppIntegrations is at 53.1.0 + +# CloudFormation Resource Specification v53.1.0 + +## New Resource Types + +* AWS::Lightsail::Certificate +* AWS::Lightsail::Container +* AWS::Lightsail::Distribution + +## Attribute Changes + +* AWS::ServiceDiscovery::PrivateDnsNamespace HostedZoneId (__added__) +* AWS::ServiceDiscovery::PublicDnsNamespace HostedZoneId (__added__) + +## Property Changes + +* AWS::DMS::Endpoint GcpMySQLSettings (__added__) +* AWS::SageMaker::Device Device.PrimitiveType (__deleted__) +* AWS::SageMaker::Pipeline ParallelismConfiguration (__added__) + +## Property Type Changes + +* AWS::ApplicationInsights::Application.HAClusterPrometheusExporter (__added__) +* AWS::ApplicationInsights::Application.HANAPrometheusExporter (__added__) +* AWS::DMS::Endpoint.GcpMySQLSettings (__added__) +* AWS::SSM::MaintenanceWindowTask.CloudWatchOutputConfig (__added__) +* AWS::ApplicationInsights::Application.ConfigurationDetails HAClusterPrometheusExporter (__added__) +* AWS::ApplicationInsights::Application.ConfigurationDetails HANAPrometheusExporter (__added__) +* AWS::DataBrew::Job.OutputLocation BucketOwner (__added__) +* AWS::DataBrew::Job.S3Location BucketOwner (__added__) +* AWS::RoboMaker::SimulationApplication.RobotSoftwareSuite Version.Required (__changed__) + * Old: true + * New: false +* AWS::RoboMaker::SimulationApplication.SimulationSoftwareSuite Version.Required (__changed__) + * Old: true + * New: false +* AWS::SSM::MaintenanceWindowTask.MaintenanceWindowRunCommandParameters CloudWatchOutputConfig (__added__) +* AWS::SSM::MaintenanceWindowTask.MaintenanceWindowRunCommandParameters DocumentVersion (__added__) + +## Unapplied changes + +* AWS::ECS is at 51.0.0 + ## Unapplied changes diff --git a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts index 8d06fe1148967..6c0ac50aa9ee9 100644 --- a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts +++ b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts @@ -295,12 +295,11 @@ async function main() { await addDependencyToMegaPackage(path.join('@aws-cdk', 'cloudformation-include'), module.packageName, version, ['dependencies', 'peerDependencies']); await addDependencyToMegaPackage('aws-cdk-lib', module.packageName, version, ['devDependencies']); await addDependencyToMegaPackage('monocdk', module.packageName, version, ['devDependencies']); - await addDependencyToMegaPackage('decdk', module.packageName, version, ['dependencies']); } } /** - * A few of our packages (e.g., decdk, aws-cdk-lib) require a dependency on every service package. + * A few of our packages (e.g., aws-cdk-lib) require a dependency on every service package. * This automates adding the dependency (and peer dependency) to the package.json. */ async function addDependencyToMegaPackage(megaPackageName: string, packageName: string, version: string, dependencyTypes: string[]) { diff --git a/packages/@aws-cdk/cfnspec/build-tools/update.sh b/packages/@aws-cdk/cfnspec/build-tools/update.sh index 470b4d4de4d6a..449825636182d 100755 --- a/packages/@aws-cdk/cfnspec/build-tools/update.sh +++ b/packages/@aws-cdk/cfnspec/build-tools/update.sh @@ -61,13 +61,17 @@ update-spec \ spec-source/specification/000_cfn/000_official \ true true +old_version=$(cat cfn.version) +new_version=$(node -p "require('${scriptdir}/../spec-source/specification/000_cfn/000_official/001_Version.json').ResourceSpecificationVersion") echo >&2 "Recording new version..." rm -f cfn.version -node -p "require('${scriptdir}/../spec-source/specification/000_cfn/000_official/001_Version.json').ResourceSpecificationVersion" > cfn.version +echo "$new_version" > cfn.version - -echo >&2 "Reporting outdated specs..." -node build-tools/report-issues spec-source/specification/000_cfn/000_official/ outdated >> CHANGELOG.md.new +# Only report outdated specs if we made changes, otherwise we're stuck reporting changes every time. +if [[ "$new_version" != "$old_version" ]]; then + echo >&2 "Reporting outdated specs..." + node build-tools/report-issues spec-source/specification/000_cfn/000_official/ outdated >> CHANGELOG.md.new +fi update-spec \ "Serverless Application Model (SAM) Resource Specification" \ @@ -85,8 +89,7 @@ node ${scriptdir}/create-missing-libraries.js || { exit 1 } -# update decdk dep list -(cd ${scriptdir}/../../../decdk && node ./deps.js || true) +# update monocdk dep list (cd ${scriptdir}/../../../monocdk && yarn gen || true) # append old changelog after new and replace as the last step because otherwise we will not be idempotent diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index b0fa2fdea909c..89d18917ebe4d 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -53.0.0 +54.0.0 diff --git a/packages/@aws-cdk/cfnspec/package.json b/packages/@aws-cdk/cfnspec/package.json index 9b4b2b0670b04..c9ed729376890 100644 --- a/packages/@aws-cdk/cfnspec/package.json +++ b/packages/@aws-cdk/cfnspec/package.json @@ -45,7 +45,8 @@ }, "repository": { "url": "https://github.com/aws/aws-cdk.git", - "type": "git" + "type": "git", + "directory": "packages/@aws-cdk/cfnspec" }, "license": "Apache-2.0", "author": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json b/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json index 584d3f0218535..f5cb2f56051fa 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json +++ b/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json @@ -647,13 +647,13 @@ }, "description": "The AWS::AmplifyUIBuilder::Component resource specifies a component within an Amplify app. A component is a user interface (UI) element that you can customize. Use `ComponentChild` to configure an instance of a `Component` . A `ComponentChild` instance inherits the configuration of the main `Component` .", "properties": { - "BindingProperties": "The information to connect a component's properties to data at runtime.", + "BindingProperties": "The information to connect a component's properties to data at runtime. You can't specify `tags` as a valid property for `bindingProperties` .", "Children": "A list of the component's `ComponentChild` instances.", - "CollectionProperties": "The data binding configuration for the component's properties. Use this for a collection component.", + "CollectionProperties": "The data binding configuration for the component's properties. Use this for a collection component. You can't specify `tags` as a valid property for `collectionProperties` .", "ComponentType": "The type of the component. This can be an Amplify custom UI component or another custom component.", "Name": "The name of the component.", - "Overrides": "Describes the component's properties that can be overriden in a customized instance of the component.", - "Properties": "Describes the component's properties.", + "Overrides": "Describes the component's properties that can be overriden in a customized instance of the component. You can't specify `tags` as a valid property for `overrides` .", + "Properties": "Describes the component's properties. You can't specify `tags` as a valid property for `properties` .", "SourceId": "The unique ID of the component in its original source system, such as Figma.", "Tags": "One or more key-value pairs to use when tagging the component.", "Variants": "A list of the component's variants. A variant is a unique style configuration of a main component." @@ -688,7 +688,7 @@ "Children": "The list of `ComponentChild` instances for this component.", "ComponentType": "The type of the child component.", "Name": "The name of the child component.", - "Properties": "Describes the properties of the child component." + "Properties": "Describes the properties of the child component. You can't specify `tags` as a valid property for `properties` ." } }, "AWS::AmplifyUIBuilder::Component.ComponentConditionProperty": { @@ -759,7 +759,7 @@ "attributes": {}, "description": "The `ComponentVariant` property specifies the style configuration of a unique variation of a main component.", "properties": { - "Overrides": "The properties of the component variant that can be overriden when customizing an instance of the component.", + "Overrides": "The properties of the component variant that can be overriden when customizing an instance of the component. You can't specify `tags` as a valid property for `overrides` .", "VariantValues": "The combination of variants that comprise this variant." } }, @@ -827,7 +827,7 @@ }, "AWS::ApiGateway::Account": { "attributes": { - "Id": "", + "Id": "The ID for the account. For example: `abc123` .", "Ref": "`Ref` returns the ID of the resource, such as `mysta-accou-01234b567890example` ." }, "description": "The `AWS::ApiGateway::Account` resource specifies the IAM role that Amazon API Gateway uses to write API logs to Amazon CloudWatch Logs.\n\n> If an API Gateway resource has never been created in your AWS account , you must add a dependency on another API Gateway resource, such as an [AWS::ApiGateway::RestApi](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html) or [AWS::ApiGateway::ApiKey](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html) resource.\n> \n> If an API Gateway resource has been created in your AWS account , no dependency is required (even if the resource was deleted).", @@ -862,7 +862,7 @@ }, "AWS::ApiGateway::Authorizer": { "attributes": { - "AuthorizerId": "", + "AuthorizerId": "The ID for the authorizer. For example: `abc123` .", "Ref": "`Ref` returns the authorizer's ID, such as `abcde1` ." }, "description": "The `AWS::ApiGateway::Authorizer` resource creates an authorization layer that API Gateway activates for methods that have authorization enabled. API Gateway activates the authorizer when a client calls those methods.", @@ -902,7 +902,7 @@ }, "AWS::ApiGateway::Deployment": { "attributes": { - "DeploymentId": "", + "DeploymentId": "The ID for the deployment. For example: `abc123` .", "Ref": "`Ref` returns the deployment ID, such as `123abc` ." }, "description": "The `AWS::ApiGateway::Deployment` resource deploys an API Gateway `RestApi` resource to a stage so that clients can call the API over the internet. The stage acts as an environment.", @@ -976,7 +976,7 @@ "MetricsEnabled": "Indicates whether Amazon CloudWatch metrics are enabled for methods in the stage.", "Tags": "An array of arbitrary tags (key-value pairs) to associate with the stage.", "ThrottlingBurstLimit": "The target request burst rate limit. This allows more requests through for a period of time than the target rate limit. For more information, see [Manage API Request Throttling](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) in the *API Gateway Developer Guide* .", - "ThrottlingRateLimit": "The Atarget request steady-state rate limit. For more information, see [Manage API Request Throttling](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) in the *API Gateway Developer Guide* .", + "ThrottlingRateLimit": "The target request steady-state rate limit. For more information, see [Manage API Request Throttling](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) in the *API Gateway Developer Guide* .", "TracingEnabled": "Specifies whether active tracing with X-ray is enabled for this stage.\n\nFor more information, see [Trace API Gateway API Execution with AWS X-Ray](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-xray.html) in the *API Gateway Developer Guide* .", "Variables": "A map that defines the stage variables. Variable names must consist of alphanumeric characters, and the values must match the following regular expression: `[A-Za-z0-9-._~:/?#&=,]+` ." } @@ -1049,7 +1049,7 @@ }, "AWS::ApiGateway::GatewayResponse": { "attributes": { - "Id": "" + "Id": "The ID for the gateway response. For example: `abc123` ." }, "description": "The `AWS::ApiGateway::GatewayResponse` resource creates a gateway response for your API. For more information, see [API Gateway Responses](https://docs.aws.amazon.com/apigateway/latest/developerguide/customize-gateway-responses.html#api-gateway-gatewayResponse-definition) in the *API Gateway Developer Guide* .", "properties": { @@ -1150,7 +1150,7 @@ "AWS::ApiGateway::Resource": { "attributes": { "Ref": "`Ref` returns the resource ID, such as `abc123` .", - "ResourceId": "" + "ResourceId": "The ID for the resource. For example: `abc123` ." }, "description": "The `AWS::ApiGateway::Resource` resource creates a resource in an API.", "properties": { @@ -1259,7 +1259,7 @@ }, "AWS::ApiGateway::UsagePlan": { "attributes": { - "Id": "", + "Id": "The ID for the usage plan. For example: `abc123` .", "Ref": "`Ref` returns the usage plan ID, such as `abc123` ." }, "description": "The `AWS::ApiGateway::UsagePlan` resource creates a usage plan for deployed APIs. A usage plan sets a target for the throttling and quota limits on individual client API keys. For more information, see [Creating and Using API Usage Plans in Amazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html) in the *API Gateway Developer Guide* .\n\nIn some cases clients can exceed the targets that you set. Don\u2019t rely on usage plans to control costs. Consider using [AWS Budgets](https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-managing-costs.html) to monitor costs and [AWS WAF](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html) to manage API requests.", @@ -1318,7 +1318,7 @@ "properties": { "Description": "A description of the VPC link.", "Name": "A name for the VPC link.", - "Tags": "", + "Tags": "An array of arbitrary tags (key-value pairs) to associate with the VPC link.", "TargetArns": "The ARN of network load balancer of the VPC targeted by the VPC link. The network load balancer must be owned by the same AWS account of the API owner." } }, @@ -1721,7 +1721,7 @@ "Name": "A name for the configuration profile.", "RetrievalRoleArn": "The ARN of an IAM role with permission to access the configuration at the specified `LocationUri` .\n\n> A retrieval role ARN is not required for configurations stored in the AWS AppConfig hosted configuration store. It is required for all other sources that store your configuration.", "Tags": "Metadata to assign to the configuration profile. Tags help organize and categorize your AWS AppConfig resources. Each tag consists of a key and an optional value, both of which you define.", - "Type": "The type of configurations that the configuration profile contains. A configuration can be a feature flag used for enabling or disabling new features or a freeform configuration used to introduce changes to your application.", + "Type": "The type of configurations contained in the profile. AWS AppConfig supports `feature flags` and `freeform` configurations. We recommend you create feature flag configurations to enable or disable new features and freeform configurations to distribute configurations to an application. When calling this API, enter one of the following values for `Type` :\n\n`AWS.AppConfig.FeatureFlags`\n\n`AWS.Freeform`", "Validators": "A list of methods for validating the configuration." } }, @@ -1821,7 +1821,7 @@ "attributes": { "Ref": "`Ref` returns the version number." }, - "description": "Create a new configuration in the AWS AppConfig hosted configuration store. Configurations must be 64 KB or smaller. The AWS AppConfig hosted configuration store provides the following benefits over other configuration store options.\n\n- You don't need to set up and configure other services such as Amazon Simple Storage Service ( Amazon S3 ) or Parameter Store.\n- You don't need to configure AWS Identity and Access Management ( IAM ) permissions to use the configuration store.\n- You can store configurations in any content type.\n- There is no cost to use the store.\n- You can create a configuration and add it to the store when you create a configuration profile.", + "description": "Create a new configuration in the AWS AppConfig hosted configuration store. Configurations must be 1 MB or smaller. The AWS AppConfig hosted configuration store provides the following benefits over other configuration store options.\n\n- You don't need to set up and configure other services such as Amazon Simple Storage Service ( Amazon S3 ) or Parameter Store.\n- You don't need to configure AWS Identity and Access Management ( IAM ) permissions to use the configuration store.\n- You can store configurations in any content type.\n- There is no cost to use the store.\n- You can create a configuration and add it to the store when you create a configuration profile.", "properties": { "ApplicationId": "The application ID.", "ConfigurationProfileId": "The configuration profile ID.", @@ -2522,6 +2522,31 @@ "Object": "The object specified in the Zendesk flow source." } }, + "AWS::AppIntegrations::DataIntegration": { + "attributes": { + "DataIntegrationArn": "The Amazon Resource Name (ARN) for the DataIntegration.", + "Id": "A unique identifier.", + "Ref": "`Ref` returns the DataIntegration name. For example:\n\n`{ \"Ref\": \"myDataIntegrationName\" }`" + }, + "description": "Creates and persists a DataIntegration resource.", + "properties": { + "Description": "A description of the DataIntegration.", + "KmsKey": "The KMS key for the DataIntegration.", + "Name": "The name of the DataIntegration.", + "ScheduleConfig": "The name of the data and how often it should be pulled from the source.", + "SourceURI": "The URI of the data source.", + "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) ." + } + }, + "AWS::AppIntegrations::DataIntegration.ScheduleConfig": { + "attributes": {}, + "description": "The name of the data and how often it should be pulled from the source.", + "properties": { + "FirstExecutionFrom": "The start date for objects to import in the first flow run as an Unix/epoch timestamp in milliseconds or in ISO-8601 format.", + "Object": "The name of the object to pull from the data source.", + "ScheduleExpression": "How often the data should be pulled from data source." + } + }, "AWS::AppIntegrations::EventIntegration": { "attributes": { "Associations": "The association status of the event integration, returned as an array of EventIntegrationAssociation objects.", @@ -4696,6 +4721,8 @@ "properties": { "AlarmMetrics": "A list of metrics to monitor for the component. All component types can use `AlarmMetrics` .", "Alarms": "A list of alarms to monitor for the component. All component types can use `Alarm` .", + "HAClusterPrometheusExporter": "", + "HANAPrometheusExporter": "", "JMXPrometheusExporter": "A list of Java metrics to monitor for the component.", "Logs": "A list of logs to monitor for the component. Only Amazon EC2 instances can use `Logs` .", "WindowsEvents": "A list of Windows Events to monitor for the component. Only Amazon EC2 instances running on Windows can use `WindowsEvents` ." @@ -4709,6 +4736,24 @@ "ResourceList": "The list of resource ARNs that belong to the component." } }, + "AWS::ApplicationInsights::Application.HAClusterPrometheusExporter": { + "attributes": {}, + "description": "", + "properties": { + "PrometheusPort": "" + } + }, + "AWS::ApplicationInsights::Application.HANAPrometheusExporter": { + "attributes": {}, + "description": "", + "properties": { + "AgreeToInstallHANADBClient": "", + "HANAPort": "", + "HANASID": "", + "HANASecretName": "", + "PrometheusPort": "" + } + }, "AWS::ApplicationInsights::Application.JMXPrometheusExporter": { "attributes": {}, "description": "The `AWS::ApplicationInsights::Application JMXPrometheusExporter` property type defines the JMXPrometheus Exporter configuration. For more information, see the [component configuration](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/component-config-sections.html#component-configuration-prometheus) in the CloudWatch Application Insights documentation.", @@ -4980,7 +5025,7 @@ "Cooldown": "The amount of time, in seconds, after a scaling activity completes before another scaling activity can start. The default value is `300` . This setting applies when using simple scaling policies, but not when using other scaling policies or scheduled scaling. For more information, see [Scaling cooldowns for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/Cooldown.html) in the *Amazon EC2 Auto Scaling User Guide* .", "DesiredCapacity": "The desired capacity is the initial capacity of the Auto Scaling group at the time of its creation and the capacity it attempts to maintain. It can scale beyond this capacity if you configure automatic scaling.\n\nThe number must be greater than or equal to the minimum size of the group and less than or equal to the maximum size of the group. If you do not specify a desired capacity when creating the stack, the default is the minimum size of the group.\n\nCloudFormation marks the Auto Scaling group as successful (by setting its status to CREATE_COMPLETE) when the desired capacity is reached. However, if a maximum Spot price is set in the launch template or launch configuration that you specified, then desired capacity is not used as a criteria for success. Whether your request is fulfilled depends on Spot Instance capacity and your maximum price.", "DesiredCapacityType": "The unit of measurement for the value specified for desired capacity. Amazon EC2 Auto Scaling supports `DesiredCapacityType` for attribute-based instance type selection only. For more information, see [Creating an Auto Scaling group using attribute-based instance type selection](https://docs.aws.amazon.com/autoscaling/ec2/userguide/create-asg-instance-type-requirements.html) in the *Amazon EC2 Auto Scaling User Guide* .\n\nBy default, Amazon EC2 Auto Scaling specifies `units` , which translates into number of instances.\n\nValid values: `units` | `vcpu` | `memory-mib`", - "HealthCheckGracePeriod": "The amount of time, in seconds, that Amazon EC2 Auto Scaling waits before checking the health status of an EC2 instance that has come into service. The default value is `0` . For more information, see [Health checks for Auto Scaling instances](https://docs.aws.amazon.com/autoscaling/ec2/userguide/healthcheck.html) in the *Amazon EC2 Auto Scaling User Guide* .\n\nIf you are adding an `ELB` health check, you must specify this property.", + "HealthCheckGracePeriod": "The amount of time, in seconds, that Amazon EC2 Auto Scaling waits before checking the health status of an EC2 instance that has come into service and marking it unhealthy due to a failed health check. The default value is `0` . For more information, see [Health checks for Auto Scaling instances](https://docs.aws.amazon.com/autoscaling/ec2/userguide/healthcheck.html) in the *Amazon EC2 Auto Scaling User Guide* .\n\nIf you are adding an `ELB` health check, you must specify this property.", "HealthCheckType": "The service to use for the health checks. The valid values are `EC2` (default) and `ELB` . If you configure an Auto Scaling group to use load balancer (ELB) health checks, it considers the instance unhealthy if it fails either the EC2 status checks or the load balancer health checks. For more information, see [Health checks for Auto Scaling instances](https://docs.aws.amazon.com/autoscaling/ec2/userguide/healthcheck.html) in the *Amazon EC2 Auto Scaling User Guide* .", "InstanceId": "The ID of the instance used to base the launch configuration on. If specified, Amazon EC2 Auto Scaling uses the configuration values from the specified instance to create a new launch configuration. For more information, see [Creating an Auto Scaling group using an EC2 instance](https://docs.aws.amazon.com/autoscaling/ec2/userguide/create-asg-from-instance.html) in the *Amazon EC2 Auto Scaling User Guide* .\n\nTo get the instance ID, use the EC2 [DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) API operation.\n\nIf you specify `LaunchTemplate` , `MixedInstancesPolicy` , or `LaunchConfigurationName` , don't specify `InstanceId` .", "LaunchConfigurationName": "The name of the [launch configuration](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-launchconfig.html) to use to launch instances.\n\nIf you specify `LaunchTemplate` , `MixedInstancesPolicy` , or `InstanceId` , don't specify `LaunchConfigurationName` .", @@ -5241,7 +5286,7 @@ "attributes": { "Ref": "When the logical ID of this resource is provided to the `Ref` intrinsic function, `Ref` returns the resource name. For example: `mylifecyclehook` .\n\nFor more information about using the `Ref` function, see [Ref](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html) ." }, - "description": "The `AWS::AutoScaling::LifecycleHook` resource specifies lifecycle hooks for an Auto Scaling group. These hooks enable an Auto Scaling group to be aware of events in the Auto Scaling instance lifecycle, and then perform a custom action when the corresponding lifecycle event occurs. A lifecycle hook provides a specified amount of time (one hour by default) to complete the lifecycle action before the instance transitions to the next state.\n\nThere are two types of lifecycle hooks that can be implemented: launch lifecycle hooks and termination lifecycle hooks. Use a launch lifecycle hook to prepare instances for use or to delay instances from registering behind the load balancer before their configuration has been applied completely. Use a termination lifecycle hook to prepare running instances to be shut down.\n\nFor more information, see [Amazon EC2 Auto Scaling lifecycle hooks](https://docs.aws.amazon.com/autoscaling/ec2/userguide/lifecycle-hooks.html) in the *Amazon EC2 Auto Scaling User Guide* and [PutLifecycleHook](https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_PutLifecycleHook.html) in the *Amazon EC2 Auto Scaling API Reference* .", + "description": "The `AWS::AutoScaling::LifecycleHook` resource specifies lifecycle hooks for an Auto Scaling group. These hooks let you create solutions that are aware of events in the Auto Scaling instance lifecycle, and then perform a custom action on instances when the corresponding lifecycle event occurs. A lifecycle hook provides a specified amount of time (one hour by default) to wait for the action to complete before the instance transitions to the next state.\n\nUse lifecycle hooks to prepare new instances for use or to delay them from being registered behind a load balancer before their configuration has been applied completely. You can also use lifecycle hooks to prepare running instances to be terminated by, for example, downloading logs or other data.\n\nFor more information, see [Amazon EC2 Auto Scaling lifecycle hooks](https://docs.aws.amazon.com/autoscaling/ec2/userguide/lifecycle-hooks.html) in the *Amazon EC2 Auto Scaling User Guide* and [PutLifecycleHook](https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_PutLifecycleHook.html) in the *Amazon EC2 Auto Scaling API Reference* .", "properties": { "AutoScalingGroupName": "The name of the Auto Scaling group for the lifecycle hook.", "DefaultResult": "The action the Auto Scaling group takes when the lifecycle hook timeout elapses or if an unexpected failure occurs. The valid values are `CONTINUE` and `ABANDON` (default).\n\nFor more information, see [Adding lifecycle hooks](https://docs.aws.amazon.com/autoscaling/ec2/userguide/adding-lifecycle-hooks.html) in the *Amazon EC2 Auto Scaling User Guide* .", @@ -5384,11 +5429,19 @@ "description": "The `AWS::AutoScaling::WarmPool` resource creates a pool of pre-initialized EC2 instances that sits alongside the Auto Scaling group. Whenever your application needs to scale out, the Auto Scaling group can draw on the warm pool to meet its new desired capacity.\n\nWhen you create a warm pool, you can define a minimum size. When your Auto Scaling group scales out and the size of the warm pool shrinks, Amazon EC2 Auto Scaling launches new instances into the warm pool to maintain its minimum size.\n\nFor more information, see [Warm pools for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html) in the *Amazon EC2 Auto Scaling User Guide* .\n\n> CloudFormation supports the `UpdatePolicy` attribute for Auto Scaling groups. During an update, if `UpdatePolicy` is set to `AutoScalingRollingUpdate` , CloudFormation replaces `InService` instances only. Instances in the warm pool are not replaced. The difference in which instances are replaced can potentially result in different instance configurations after the stack update completes. If `UpdatePolicy` is set to `AutoScalingReplacingUpdate` , you do not encounter this issue because CloudFormation replaces both the Auto Scaling group and the warm pool.", "properties": { "AutoScalingGroupName": "The name of the Auto Scaling group.", + "InstanceReusePolicy": "", "MaxGroupPreparedCapacity": "Specifies the maximum number of instances that are allowed to be in the warm pool or in any state except `Terminated` for the Auto Scaling group. This is an optional property. Specify it only if you do not want the warm pool size to be determined by the difference between the group's maximum capacity and its desired capacity.\n\n> If a value for `MaxGroupPreparedCapacity` is not specified, Amazon EC2 Auto Scaling launches and maintains the difference between the group's maximum capacity and its desired capacity. If you specify a value for `MaxGroupPreparedCapacity` , Amazon EC2 Auto Scaling uses the difference between the `MaxGroupPreparedCapacity` and the desired capacity instead.\n> \n> The size of the warm pool is dynamic. Only when `MaxGroupPreparedCapacity` and `MinSize` are set to the same value does the warm pool have an absolute size. \n\nIf the desired capacity of the Auto Scaling group is higher than the `MaxGroupPreparedCapacity` , the capacity of the warm pool is 0, unless you specify a value for `MinSize` . To remove a value that you previously set, include the property but specify -1 for the value.", "MinSize": "Specifies the minimum number of instances to maintain in the warm pool. This helps you to ensure that there is always a certain number of warmed instances available to handle traffic spikes. Defaults to 0 if not specified.", "PoolState": "Sets the instance state to transition to after the lifecycle actions are complete. Default is `Stopped` ." } }, + "AWS::AutoScaling::WarmPool.InstanceReusePolicy": { + "attributes": {}, + "description": "", + "properties": { + "ReuseOnScaleIn": "" + } + }, "AWS::AutoScalingPlans::ScalingPlan": { "attributes": { "Ref": "When you pass the logical ID of an `AWS::AutoScalingPlans::ScalingPlan` resource to the intrinsic `Ref` function, the function returns the Amazon Resource Name (ARN) of the scaling plan. The format of the ARN is as follows:\n\n`arn:aws:autoscaling: *region* : *123456789012:* scalingPlan:scalingPlanName/ *plan-name* :scalingPlanVersion/ *plan-version*`\n\nFor more information about using the `Ref` function, see [Ref](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html) ." @@ -5501,7 +5554,7 @@ "Ref": "`Ref` returns `BackupPlanId` .", "VersionId": "Unique, randomly generated, Unicode, UTF-8 encoded strings that are at most 1,024 bytes long. Version Ids cannot be edited." }, - "description": "Contains an optional backup plan display name and an array of `BackupRule` objects, each of which specifies a backup rule. Each rule in a backup plan is a separate scheduled task and can back up a different selection of AWS resources.", + "description": "Contains an optional backup plan display name and an array of `BackupRule` objects, each of which specifies a backup rule. Each rule in a backup plan is a separate scheduled task and can back up a different selection of AWS resources.\n\nFor a sample AWS CloudFormation template, see the [AWS Backup Developer Guide](https://docs.aws.amazon.com/aws-backup/latest/devguide/assigning-resources.html#assigning-resources-cfn) .", "properties": { "BackupPlan": "Uniquely identifies the backup plan to be associated with the selection of resources.", "BackupPlanTags": "To help organize your resources, you can assign your own metadata to the resources that you create. Each tag is a key-value pair. The specified tags are assigned to all backups created with this plan." @@ -5869,7 +5922,7 @@ }, "description": "The `AWS::CE::CostCategory` resource creates groupings of cost that you can use across products in the AWS Billing and Cost Management console, such as Cost Explorer and AWS Budgets. For more information, see [Managing Your Costs with Cost Categories](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/manage-cost-categories.html) in the *AWS Billing and Cost Management User Guide* .", "properties": { - "DefaultValue": "", + "DefaultValue": "The default value for the cost category.", "Name": "The unique name of the Cost Category.", "RuleVersion": "The rule schema version in this particular Cost Category.", "Rules": "The array of CostCategoryRule in JSON array format.\n\n> Rules are processed in order. If there are multiple rules that match the line item, then the first rule to match is used to determine that Cost Category value.", @@ -8711,7 +8764,7 @@ }, "AWS::Config::AggregationAuthorization": { "attributes": { - "AggregationAuthorizationArn": "", + "AggregationAuthorizationArn": "The Amazon Resource Name (ARN) of the aggregation object.", "Ref": "`Ref` returns the ARN of the AggregationAuthorization, such as `arn:aws:config:us-east-1:123456789012:aggregation-authorization/987654321012/us-west-2` ." }, "description": "An object that represents the authorizations granted to aggregator accounts and regions.", @@ -8864,7 +8917,7 @@ "attributes": { "Ref": "`Ref` returns the OrganizationConfigRuleName." }, - "description": "An organization config rule that has information about config rules that AWS Config creates in member accounts. Only a master account can create or update an organization config rule.\n\n`OrganizationConfigRule` resource enables organization service access through `EnableAWSServiceAccess` action and creates a service linked role in the master account of your organization. The service linked role is created only when the role does not exist in the master account. AWS Config verifies the existence of role with `GetRole` action.\n\nWhen creating custom organization config rules using a centralized Lambda function, you will need to allow Lambda permissions to sub-accounts and you will need to create an IAM role will to pass to the Lambda function. For more information, see [How to Centrally Manage AWS Config Rules across Multiple AWS Accounts](https://docs.aws.amazon.com/devops/how-to-centrally-manage-aws-config-rules-across-multiple-aws-accounts/) .", + "description": "An organization config rule that has information about config rules that AWS Config creates in member accounts. Only a master account and a delegated administrator can create or update an organization config rule.\n\n`OrganizationConfigRule` resource enables organization service access through `EnableAWSServiceAccess` action and creates a service linked role in the master account of your organization. The service linked role is created only when the role does not exist in the master account. AWS Config verifies the existence of role with `GetRole` action.\n\nWhen creating custom organization config rules using a centralized Lambda function, you will need to allow Lambda permissions to sub-accounts and you will need to create an IAM role will to pass to the Lambda function. For more information, see [How to Centrally Manage AWS Config Rules across Multiple AWS Accounts](https://docs.aws.amazon.com/devops/how-to-centrally-manage-aws-config-rules-across-multiple-aws-accounts/) .", "properties": { "ExcludedAccounts": "A comma-separated list of accounts excluded from organization config rule.", "OrganizationConfigRuleName": "The name that you assign to organization config rule.", @@ -9643,6 +9696,7 @@ "EndpointType": "The type of endpoint. Valid values are `source` and `target` .", "EngineName": "The type of engine for the endpoint. Valid values, depending on the `EndpointType` value, include `\"mysql\"` , `\"oracle\"` , `\"postgres\"` , `\"mariadb\"` , `\"aurora\"` , `\"aurora-postgresql\"` , `\"opensearch\"` , `\"redshift\"` , `\"s3\"` , `\"db2\"` , `\"azuredb\"` , `\"sybase\"` , `\"dynamodb\"` , `\"mongodb\"` , `\"kinesis\"` , `\"kafka\"` , `\"elasticsearch\"` , `\"docdb\"` , `\"sqlserver\"` , and `\"neptune\"` .", "ExtraConnectionAttributes": "Additional attributes associated with the connection. Each attribute is specified as a name-value pair associated by an equal sign (=). Multiple attributes are separated by a semicolon (;) with no additional white space. For information on the attributes available for connecting your source or target endpoint, see [Working with AWS DMS Endpoints](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Endpoints.html) in the *AWS Database Migration Service User Guide.*", + "GcpMySQLSettings": "Settings in JSON format for the source GCP MySQL endpoint.", "IbmDb2Settings": "Not currently supported by AWS CloudFormation .", "KafkaSettings": "Settings in JSON format for the target Apache Kafka endpoint. For more information about the available settings, see [Using object mapping to migrate data to a Kafka topic](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.Kafka.html#CHAP_Target.Kafka.ObjectMapping) in the *AWS Database Migration Service User Guide.*", "KinesisSettings": "Settings in JSON format for the target endpoint for Amazon Kinesis Data Streams. For more information about the available settings, see [Using Amazon Kinesis Data Streams as a Target for AWS Database Migration Service](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.Kinesis.html) in the *AWS Database Migration Service User Guide.*", @@ -9691,6 +9745,25 @@ "ServiceAccessRoleArn": "The Amazon Resource Name (ARN) used by the service to access the IAM role. The role must allow the `iam:PassRole` action." } }, + "AWS::DMS::Endpoint.GcpMySQLSettings": { + "attributes": {}, + "description": "Settings in JSON format for the source GCP MySQL endpoint.", + "properties": { + "AfterConnectScript": "Specifies a script to run immediately after AWS DMS connects to the endpoint. The migration task continues running regardless if the SQL statement succeeds or fails.\n\nFor this parameter, provide the code of the script itself, not the name of a file containing the script.", + "CleanSourceMetadataOnMismatch": "Adjusts the behavior of AWS DMS when migrating from an SQL Server source database that is hosted as part of an Always On availability group cluster. If you need AWS DMS to poll all the nodes in the Always On cluster for transaction backups, set this attribute to `false` .", + "DatabaseName": "Database name for the endpoint. For a MySQL source or target endpoint, don't explicitly specify the database using the `DatabaseName` request parameter on either the `CreateEndpoint` or `ModifyEndpoint` API call. Specifying `DatabaseName` when you create or modify a MySQL endpoint replicates all the task tables to this single database. For MySQL endpoints, you specify the database only when you specify the schema in the table-mapping rules of the AWS DMS task.", + "EventsPollInterval": "Specifies how often to check the binary log for new changes/events when the database is idle. The default is five seconds.\n\nExample: `eventsPollInterval=5;`\n\nIn the example, AWS DMS checks for changes in the binary logs every five seconds.", + "MaxFileSize": "Specifies the maximum size (in KB) of any .csv file used to transfer data to a MySQL-compatible database.\n\nExample: `maxFileSize=512`", + "ParallelLoadThreads": "Improves performance when loading data into the MySQL-compatible target database. Specifies how many threads to use to load the data into the MySQL-compatible target database. Setting a large number of threads can have an adverse effect on database performance, because a separate connection is required for each thread. The default is one.\n\nExample: `parallelLoadThreads=1`", + "Password": "Endpoint connection password.", + "Port": "", + "SecretsManagerAccessRoleArn": "The full Amazon Resource Name (ARN) of the IAM role that specifies AWS DMS as the trusted entity and grants the required permissions to access the value in `SecretsManagerSecret.` The role must allow the `iam:PassRole` action. `SecretsManagerSecret` has the value of the AWS Secrets Manager secret that allows access to the MySQL endpoint.\n\n> You can specify one of two sets of values for these permissions. You can specify the values for this setting and `SecretsManagerSecretId` . Or you can specify clear-text values for `UserName` , `Password` , `ServerName` , and `Port` . You can't specify both. For more information on creating this `SecretsManagerSecret` and the `SecretsManagerAccessRoleArn` and `SecretsManagerSecretId` required to access it, see [Using secrets to access AWS Database Migration Service resources](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Security.html#security-iam-secretsmanager) in the AWS Database Migration Service User Guide.", + "SecretsManagerSecretId": "The full ARN, partial ARN, or friendly name of the `SecretsManagerSecret` that contains the MySQL endpoint connection details.", + "ServerName": "Endpoint TCP port.", + "ServerTimezone": "Specifies the time zone for the source MySQL database.\n\nExample: `serverTimezone=US/Pacific;`\n\nNote: Do not enclose time zones in single quotes.", + "Username": "Endpoint connection user name." + } + }, "AWS::DMS::Endpoint.IbmDb2Settings": { "attributes": {}, "description": "Not currently supported by AWS CloudFormation .", @@ -10536,7 +10609,7 @@ "StreamArn": "The ARN of the DynamoDB stream, such as `arn:aws:dynamodb:us-east-1:123456789012:table/testddbstack-myDynamoDBTable-012A1SL7SMP5Q/stream/2015-11-30T20:10:00.000` . The `StreamArn` returned is that of the replica in the region the stack is deployed to.\n\n> You must specify the `StreamSpecification` property to use this attribute.", "TableId": "Unique identifier for the table, such as `a123b456-01ab-23cd-123a-111222aaabbb` . The `TableId` returned is that of the replica in the region the stack is deployed to." }, - "description": "The `AWS::DynamoDB::GlobalTable` resource enables you to create and manage a Version 2019.11.21 global table. This resource cannot be used to create or manage a Version 2017.11.29 global table.\n\n> You cannot convert a resource of type `AWS::DynamoDB::Table` into a resource of type `AWS::DynamoDB::GlobalTable` by changing its type in your template. *Doing so might result in the deletion of your DynamoDB table.* \n\nYou should be aware of the following behaviors when working with DynamoDB global tables.\n\n- The IAM Principal executing the stack operation must have the permissions listed below in all regions where you plan to have a global table replica. The IAM Principal's permissions should not have restrictions based on IP source address. Some global tables operations (for example, adding a replica) are asynchronous, and require that the IAM Principal is valid until they complete. You should not delete the Principal (user or IAM role) until CloudFormation has finished updating your stack.\n\n- `dynamodb:CreateTable`\n- `dynamodb:UpdateTable`\n- `dynamodb:DeleteTable`\n- `dynamodb:DescribeContinuousBackups`\n- `dynamodb:DescribeContributorInsights`\n- `dynamodb:DescribeTable`\n- `dynamodb:DescribeTableReplicaAutoScaling`\n- `dynamodb:DescribeTimeToLive`\n- `dynamodb:ListTables`\n- `dynamodb:UpdateTimeToLive`\n- `dynamodb:UpdateContributorInsights`\n- `dynamodb:UpdateContinuousBackups`\n- `dynamodb:ListTagsOfResource`\n- `dynamodb:TagResource`\n- `dynamodb:UntagResource`\n- `dynamodb:BatchWriteItem`\n- `dynamodb:CreateTableReplica`\n- `dynamodb:DeleteItem`\n- `dynamodb:DeleteTableReplica`\n- `dynamodb:DisableKinesisStreamingDestination`\n- `dynamodb:EnableKinesisStreamingDestination`\n- `dynamodb:GetItem`\n- `dynamodb:PutItem`\n- `dynamodb:Query`\n- `dynamodb:Scan`\n- `dynamodb:UpdateItem`\n- `dynamodb:DescribeTableReplicaAutoScaling`\n- `dynamodb:UpdateTableReplicaAutoScaling`\n- `iam:CreateServiceLinkedRole`\n- `kms:CreateGrant`\n- `kms:DescribeKey`\n- `application-autoscaling:DeleteScalingPolicy`\n- `application-autoscaling:DeleteScheduledAction`\n- `application-autoscaling:DeregisterScalableTarget`\n- `application-autoscaling:DescribeScalingPolicies`\n- `application-autoscaling:DescribeScalableTargets`\n- `application-autoscaling:PutScalingPolicy`\n- `application-autoscaling:PutScheduledAction`\n- `application-autoscaling:RegisterScalableTarget`\n- When using provisioned billing mode, CloudFormation will create an auto scaling policy on each of your replicas to control their write capacities. You must configure this policy using the `WriteProvisionedThroughputSettings` property. CloudFormation will ensure that all replicas have the same write capacity auto scaling property. You cannot directly specify a value for write capacity for a global table.\n- If your table uses provisioned capacity, you must configure auto scaling directly in the `AWS::DynamoDB::GlobalTable` resource. You should not configure additional auto scaling policies on any of the table replicas or global secondary indexes, either via API or via `AWS::ApplicationAutoScaling::ScalableTarget` or `AWS::ApplicationAutoScaling::ScalingPolicy` . Doing so might result in unexpected behavior and is unsupported.\n- In AWS CloudFormation , each global table is controlled by a single stack, in a single region, regardless of the number of replicas. When you deploy your template, CloudFormation will create/update all replicas as part of a single stack operation. You should not deploy the same `AWS::DynamoDB::GlobalTable` resource in multiple regions. Doing so will result in errors, and is unsupported. If you deploy your application template in multiple regions, you can use conditions to only create the resource in a single region. Alternatively, you can choose to define your `AWS::DynamoDB::GlobalTable` resources in a stack separate from your application stack, and make sure it is only deployed to a single region.", + "description": "The `AWS::DynamoDB::GlobalTable` resource enables you to create and manage a Version 2019.11.21 global table. This resource cannot be used to create or manage a Version 2017.11.29 global table.\n\n> You cannot convert a resource of type `AWS::DynamoDB::Table` into a resource of type `AWS::DynamoDB::GlobalTable` by changing its type in your template. *Doing so might result in the deletion of your DynamoDB table.*\n> \n> You can instead use the GlobalTable resource to create a new table in a single Region. This will be billed the same as a single Region table. If you later update the stack to add other Regions then Global Tables pricing will apply. \n\nYou should be aware of the following behaviors when working with DynamoDB global tables.\n\n- The IAM Principal executing the stack operation must have the permissions listed below in all regions where you plan to have a global table replica. The IAM Principal's permissions should not have restrictions based on IP source address. Some global tables operations (for example, adding a replica) are asynchronous, and require that the IAM Principal is valid until they complete. You should not delete the Principal (user or IAM role) until CloudFormation has finished updating your stack.\n\n- `dynamodb:CreateTable`\n- `dynamodb:UpdateTable`\n- `dynamodb:DeleteTable`\n- `dynamodb:DescribeContinuousBackups`\n- `dynamodb:DescribeContributorInsights`\n- `dynamodb:DescribeTable`\n- `dynamodb:DescribeTableReplicaAutoScaling`\n- `dynamodb:DescribeTimeToLive`\n- `dynamodb:ListTables`\n- `dynamodb:UpdateTimeToLive`\n- `dynamodb:UpdateContributorInsights`\n- `dynamodb:UpdateContinuousBackups`\n- `dynamodb:ListTagsOfResource`\n- `dynamodb:TagResource`\n- `dynamodb:UntagResource`\n- `dynamodb:BatchWriteItem`\n- `dynamodb:CreateTableReplica`\n- `dynamodb:DeleteItem`\n- `dynamodb:DeleteTableReplica`\n- `dynamodb:DisableKinesisStreamingDestination`\n- `dynamodb:EnableKinesisStreamingDestination`\n- `dynamodb:GetItem`\n- `dynamodb:PutItem`\n- `dynamodb:Query`\n- `dynamodb:Scan`\n- `dynamodb:UpdateItem`\n- `dynamodb:DescribeTableReplicaAutoScaling`\n- `dynamodb:UpdateTableReplicaAutoScaling`\n- `iam:CreateServiceLinkedRole`\n- `kms:CreateGrant`\n- `kms:DescribeKey`\n- `application-autoscaling:DeleteScalingPolicy`\n- `application-autoscaling:DeleteScheduledAction`\n- `application-autoscaling:DeregisterScalableTarget`\n- `application-autoscaling:DescribeScalingPolicies`\n- `application-autoscaling:DescribeScalableTargets`\n- `application-autoscaling:PutScalingPolicy`\n- `application-autoscaling:PutScheduledAction`\n- `application-autoscaling:RegisterScalableTarget`\n- When using provisioned billing mode, CloudFormation will create an auto scaling policy on each of your replicas to control their write capacities. You must configure this policy using the `WriteProvisionedThroughputSettings` property. CloudFormation will ensure that all replicas have the same write capacity auto scaling property. You cannot directly specify a value for write capacity for a global table.\n- If your table uses provisioned capacity, you must configure auto scaling directly in the `AWS::DynamoDB::GlobalTable` resource. You should not configure additional auto scaling policies on any of the table replicas or global secondary indexes, either via API or via `AWS::ApplicationAutoScaling::ScalableTarget` or `AWS::ApplicationAutoScaling::ScalingPolicy` . Doing so might result in unexpected behavior and is unsupported.\n- In AWS CloudFormation , each global table is controlled by a single stack, in a single region, regardless of the number of replicas. When you deploy your template, CloudFormation will create/update all replicas as part of a single stack operation. You should not deploy the same `AWS::DynamoDB::GlobalTable` resource in multiple regions. Doing so will result in errors, and is unsupported. If you deploy your application template in multiple regions, you can use conditions to only create the resource in a single region. Alternatively, you can choose to define your `AWS::DynamoDB::GlobalTable` resources in a stack separate from your application stack, and make sure it is only deployed to a single region.", "properties": { "AttributeDefinitions": "A list of attributes that describe the key schema for the global table and indexes.", "BillingMode": "Specifies how you are charged for read and write throughput and how you manage capacity. Valid values are:\n\n- `PAY_PER_REQUEST`\n- `PROVISIONED`\n\nAll replicas in your global table will have the same billing mode. If you use `PROVISIONED` billing mode, you must provide an auto scaling configuration via the `WriteProvisionedThroughputSettings` property. The default value of this property is `PROVISIONED` .", @@ -10547,7 +10620,7 @@ "SSESpecification": "Specifies the settings to enable server-side encryption. These settings will be applied to all replicas. If you plan to use customer-managed KMS keys, you must provide a key for each replica using the `ReplicaSpecification.ReplicaSSESpecification` property.", "StreamSpecification": "Specifies the streams settings on your global table. You must provide a value for this property if your global table contains more than one replica. You can only change the streams settings if your global table has only one replica.", "TableName": "A name for the global table. If you don't specify a name, AWS CloudFormation generates a unique ID and uses that ID as the table name. For more information, see [Name type](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-name.html) .\n\n> If you specify a name, you cannot perform updates that require replacement of this resource. You can perform updates that require no or some interruption. If you must replace the resource, specify a new name.", - "TimeToLiveSpecification": "Specifies the Time to Live (TTL) settings for the table. This setting will be applied to all replicas.\n\n> For detailed information about the TTL feature of DynamoDB, see [Expiring Items with Time to Live](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) in the Amazon DynamoDB Developer Guide.", + "TimeToLiveSpecification": "Specifies the time to live (TTL) settings for the table. This setting will be applied to all replicas.", "WriteProvisionedThroughputSettings": "Specifies an auto scaling policy for write capacity. This policy will be applied to all replicas. This setting must be specified if `BillingMode` is set to `PROVISIONED` ." } }, @@ -10717,7 +10790,7 @@ "TableClass": "The table class of the new table. Valid values are `STANDARD` and `STANDARD_INFREQUENT_ACCESS` .", "TableName": "A name for the table. If you don't specify a name, AWS CloudFormation generates a unique physical ID and uses that ID for the table name. For more information, see [Name Type](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-name.html) .\n\n> If you specify a name, you cannot perform updates that require replacement of this resource. You can perform updates that require no or some interruption. If you must replace the resource, specify a new name.", "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) .", - "TimeToLiveSpecification": "Specifies the Time to Live (TTL) settings for the table.\n\n> For detailed information about the TTL feature of DynamoDB, see [Expiring Items with Time to Live](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) in the Amazon DynamoDB Developer Guide." + "TimeToLiveSpecification": "Specifies the Time to Live (TTL) settings for the table.\n\n> For detailed information about the limits in DynamoDB, see [Limits in Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html) in the Amazon DynamoDB Developer Guide." } }, "AWS::DynamoDB::Table.AttributeDefinition": { @@ -10787,7 +10860,7 @@ }, "AWS::DynamoDB::Table.ProvisionedThroughput": { "attributes": {}, - "description": "Throughput for the specified table, which consists of values for `ReadCapacityUnits` and `WriteCapacityUnits` .", + "description": "Throughput for the specified table, which consists of values for `ReadCapacityUnits` and `WriteCapacityUnits` . For more information about the contents of a provisioned throughput structure, see [Amazon DynamoDB Table ProvisionedThroughput](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-provisionedthroughput.html) .", "properties": { "ReadCapacityUnits": "The maximum number of strongly consistent reads consumed per second before DynamoDB returns a `ThrottlingException` . For more information, see [Specifying Read and Write Requirements](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html#ProvisionedThroughput) in the *Amazon DynamoDB Developer Guide* .\n\nIf read/write capacity mode is `PAY_PER_REQUEST` the value is set to 0.", "WriteCapacityUnits": "The maximum number of writes consumed per second before DynamoDB returns a `ThrottlingException` . For more information, see [Specifying Read and Write Requirements](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html#ProvisionedThroughput) in the *Amazon DynamoDB Developer Guide* .\n\nIf read/write capacity mode is `PAY_PER_REQUEST` the value is set to 0." @@ -10923,12 +10996,14 @@ "AuthenticationOptions": "Information about the authentication method to be used to authenticate clients.", "ClientCidrBlock": "The IPv4 address range, in CIDR notation, from which to assign client IP addresses. The address range cannot overlap with the local CIDR of the VPC in which the associated subnet is located, or the routes that you add manually. The address range cannot be changed after the Client VPN endpoint has been created. The CIDR block should be /22 or greater.", "ClientConnectOptions": "The options for managing connection authorization for new client connections.", + "ClientLoginBannerOptions": "Options for enabling a customizable text banner that will be displayed on AWS provided clients when a VPN session is established.", "ConnectionLogOptions": "Information about the client connection logging options.\n\nIf you enable client connection logging, data about client connections is sent to a Cloudwatch Logs log stream. The following information is logged:\n\n- Client connection requests\n- Client connection results (successful and unsuccessful)\n- Reasons for unsuccessful client connection requests\n- Client connection termination time", "Description": "A brief description of the Client VPN endpoint.", "DnsServers": "Information about the DNS servers to be used for DNS resolution. A Client VPN endpoint can have up to two DNS servers. If no DNS server is specified, the DNS address configured on the device is used for the DNS server.", "SecurityGroupIds": "The IDs of one or more security groups to apply to the target network. You must also specify the ID of the VPC that contains the security groups.", "SelfServicePortal": "Specify whether to enable the self-service portal for the Client VPN endpoint.\n\nDefault Value: `enabled`", "ServerCertificateArn": "The ARN of the server certificate. For more information, see the [AWS Certificate Manager User Guide](https://docs.aws.amazon.com/acm/latest/userguide/) .", + "SessionTimeoutHours": "The maximum VPN session duration time in hours.\n\nValid values: `8 | 10 | 12 | 24`\n\nDefault value: `24`", "SplitTunnel": "Indicates whether split-tunnel is enabled on the AWS Client VPN endpoint.\n\nBy default, split-tunnel on a VPN endpoint is disabled.\n\nFor information about split-tunnel VPN endpoints, see [Split-tunnel AWS Client VPN endpoint](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/split-tunnel-vpn.html) in the *AWS Client VPN Administrator Guide* .", "TagSpecifications": "The tags to apply to the Client VPN endpoint during creation.", "TransportProtocol": "The transport protocol to be used by the VPN session.\n\nDefault value: `udp`", @@ -10961,6 +11036,14 @@ "LambdaFunctionArn": "The Amazon Resource Name (ARN) of the AWS Lambda function used for connection authorization." } }, + "AWS::EC2::ClientVpnEndpoint.ClientLoginBannerOptions": { + "attributes": {}, + "description": "Options for enabling a customizable text banner that will be displayed on AWS provided clients when a VPN session is established.", + "properties": { + "BannerText": "Customizable text that will be displayed in a banner on AWS provided clients when a VPN session is established. UTF-8 encoded characters only. Maximum of 1400 characters.", + "Enabled": "Enable or disable a customizable text banner that will be displayed on AWS provided clients when a VPN session is established.\n\nValid values: `true | false`\n\nDefault value: `false`" + } + }, "AWS::EC2::ClientVpnEndpoint.ConnectionLogOptions": { "attributes": {}, "description": "Describes the client connection logging options for the Client VPN endpoint.", @@ -11346,6 +11429,7 @@ }, "AWS::EC2::Host": { "attributes": { + "HostId": "The ID of the host.", "Ref": "`Ref` returns the host ID, such as `h-0ab123c45d67ef89` ." }, "description": "Allocates a fully dedicated physical server for launching EC2 instances. Because the host is fully dedicated for your use, it can help you address compliance requirements and reduce costs by allowing you to use your existing server-bound software licenses. For more information, see [Dedicated Hosts](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-overview.html) in the *Amazon EC2 User Guide for Linux Instances* .", @@ -11433,6 +11517,7 @@ "Arn": "The ARN of the scope.", "IpamArn": "The ARN of an IPAM.", "IpamScopeId": "The ID of an IPAM scope.", + "IpamScopeType": "The type of the scope.", "IsDefault": "Defines if the scope is the default scope or not.", "PoolCount": "The number of pools in a scope.", "Ref": "`Ref` returns the IPAM scope ID." @@ -11441,7 +11526,6 @@ "properties": { "Description": "The description of the scope.", "IpamId": "The ID of the IPAM for which you're creating this scope.", - "IpamScopeType": "The type of the scope.", "Tags": "The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value. For example, to find all resources that have a tag with the key `Owner` and the value `TeamA` , specify `tag:Owner` for the filter name and `TeamA` for the filter value." } }, @@ -11599,7 +11683,6 @@ "attributes": {}, "description": "Specifies a network interface that is to be attached to an instance.\n\nYou can create a network interface when launching an instance. For an example, see the [AWS::EC2::Instance examples](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html#aws-properties-ec2-instance--examples--Automatically_assign_a_public_IP_address) .\n\nAlternatively, you can attach an existing network interface when launching an instance. For an example, see the [AWS::EC2:NetworkInterface examples](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-network-interface.html#aws-resource-ec2-network-interface--examples--Basic_network_interface) .", "properties": { - "AssociateCarrierIpAddress": "", "AssociatePublicIpAddress": "Indicates whether to assign a public IPv4 address to an instance. Applies only if creating a network interface when launching an instance. The network interface must be the primary network interface. If launching into a default subnet, the default value is `true` .", "DeleteOnTermination": "Indicates whether the network interface is deleted when the instance is terminated. Applies only if creating a network interface when launching an instance.", "Description": "The description of the network interface. Applies only if creating a network interface when launching an instance.", @@ -13223,8 +13306,6 @@ "EnableDnsHostnames": "Indicates whether the instances launched in the VPC get DNS hostnames. If enabled, instances in the VPC get DNS hostnames; otherwise, they do not. Disabled by default for nondefault VPCs. For more information, see [DNS attributes in your VPC](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#vpc-dns-support) .\n\nYou can only enable DNS hostnames if you've enabled DNS support.", "EnableDnsSupport": "Indicates whether the DNS resolution is supported for the VPC. If enabled, queries to the Amazon provided DNS server at the 169.254.169.253 IP address, or the reserved IP address at the base of the VPC network range \"plus two\" succeed. If disabled, the Amazon provided DNS service in the VPC that resolves public DNS hostnames to IP addresses is not enabled. Enabled by default. For more information, see [DNS attributes in your VPC](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#vpc-dns-support) .", "InstanceTenancy": "The allowed tenancy of instances launched into the VPC.\n\n- `\"default\"` : An instance launched into the VPC runs on shared hardware by default, unless you explicitly specify a different tenancy during instance launch.\n- `\"dedicated\"` : An instance launched into the VPC is a Dedicated Instance by default, unless you explicitly specify a tenancy of host during instance launch. You cannot specify a tenancy of default during instance launch.\n\nUpdating `InstanceTenancy` requires no replacement only if you are updating its value from `\"dedicated\"` to `\"default\"` . Updating `InstanceTenancy` from `\"default\"` to `\"dedicated\"` requires replacement.", - "Ipv4IpamPoolId": "The ID of an IPv4 IPAM pool you want to use for allocating this VPC's CIDR. For more information, see [What is IPAM?](https://docs.aws.amazon.com//vpc/latest/ipam/what-is-it-ipam.html) in the *Amazon VPC IPAM User Guide* .", - "Ipv4NetmaskLength": "The netmask length of the IPv4 CIDR you want to allocate to this VPC from an Amazon VPC IP Address Manager (IPAM) pool. For more information about IPAM, see [What is IPAM?](https://docs.aws.amazon.com//vpc/latest/ipam/what-is-it-ipam.html) in the *Amazon VPC IPAM User Guide* .", "Tags": "The tags for the VPC." } }, @@ -13236,11 +13317,7 @@ "properties": { "AmazonProvidedIpv6CidrBlock": "Requests an Amazon-provided IPv6 CIDR block with a /56 prefix length for the VPC. You cannot specify the range of IPv6 addresses, or the size of the CIDR block.", "CidrBlock": "An IPv4 CIDR block to associate with the VPC.", - "Ipv4IpamPoolId": "Associate a CIDR allocated from an IPv4 IPAM pool to a VPC. For more information about Amazon VPC IP Address Manager (IPAM), see [What is IPAM?](https://docs.aws.amazon.com//vpc/latest/ipam/what-is-it-ipam.html) in the *Amazon VPC IPAM User Guide* .", - "Ipv4NetmaskLength": "The netmask length of the IPv4 CIDR you would like to associate from an Amazon VPC IP Address Manager (IPAM) pool. For more information about IPAM, see [What is IPAM?](https://docs.aws.amazon.com//vpc/latest/ipam/what-is-it-ipam.html) in the *Amazon VPC IPAM User Guide* .", "Ipv6CidrBlock": "An IPv6 CIDR block from the IPv6 address pool. You must also specify `Ipv6Pool` in the request.\n\nTo let Amazon choose the IPv6 CIDR block for you, omit this parameter.", - "Ipv6IpamPoolId": "Associates a CIDR allocated from an IPv6 IPAM pool to a VPC. For more information about Amazon VPC IP Address Manager (IPAM), see [What is IPAM?](https://docs.aws.amazon.com//vpc/latest/ipam/what-is-it-ipam.html) in the *Amazon VPC IPAM User Guide* .", - "Ipv6NetmaskLength": "The netmask length of the IPv6 CIDR you would like to associate from an Amazon VPC IP Address Manager (IPAM) pool. For more information about IPAM, see [What is IPAM?](https://docs.aws.amazon.com//vpc/latest/ipam/what-is-it-ipam.html) in the *Amazon VPC IPAM User Guide* .", "Ipv6Pool": "The ID of an IPv6 address pool from which to allocate the IPv6 CIDR block.", "VpcId": "The ID of the VPC." } @@ -13380,6 +13457,7 @@ }, "AWS::EC2::VPNGatewayRoutePropagation": { "attributes": { + "Id": "The ID of the VPN gateway.", "Ref": "`Ref` returns the ID of the VPN gateway." }, "description": "Enables a virtual private gateway (VGW) to propagate routes to the specified route table of a VPC.\n\nIf you reference a VPN gateway that is in the same template as your VPN gateway route propagation, you must explicitly declare a dependency on the VPN gateway attachment. The `AWS::EC2::VPNGatewayRoutePropagation` resource cannot use the VPN gateway until it has successfully attached to the VPC. Add a [DependsOn Attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html) in the `AWS::EC2::VPNGatewayRoutePropagation` resource to explicitly declare a dependency on the VPN gateway attachment.", @@ -14305,9 +14383,9 @@ "attributes": {}, "description": "The Kubernetes network configuration for the cluster.", "properties": { - "IpFamily": "Specify which IP family is used to assign Kubernetes Pod and Service IP addresses. If you don't specify a value, `ipv4` is used by default. You can only specify an IP family when you create a cluster and can't change this value once the cluster is created. If you specify `ipv6` , the VPC and subnets that you specify for cluster creation must have both IPv4 and IPv6 CIDR blocks assigned to them.\n\nYou can only specify `ipv6` for 1.21 and later clusters that use version 1.10.1 or later of the Amazon VPC CNI add-on. If you specify `ipv6` , then ensure that your VPC meets the requirements listed in the considerations listed in [Assigning IPv6 addresses to Pods and Services](https://docs.aws.amazon.com/eks/latest/userguide/cni-ipv6.html) in the Amazon EKS User Guide. Kubernetes assigns Services IPv6 addresses from the unique local address range (fc00::/7). You can't specify a custom IPv6 CIDR block. Pod addresses are assigned from the subnet's IPv6 CIDR.", + "IpFamily": "Specify which IP family is used to assign Kubernetes pod and service IP addresses. If you don't specify a value, `ipv4` is used by default. You can only specify an IP family when you create a cluster and can't change this value once the cluster is created. If you specify `ipv6` , the VPC and subnets that you specify for cluster creation must have both IPv4 and IPv6 CIDR blocks assigned to them.\n\nYou can only specify `ipv6` for 1.21 and later clusters that use version 1.10.1 or later of the Amazon VPC CNI add-on. If you specify `ipv6` , then ensure that your VPC meets the requirements listed in the considerations listed in [Assigning IPv6 addresses to pods and services](https://docs.aws.amazon.com/eks/latest/userguide/cni-ipv6.html) in the Amazon EKS User Guide. Kubernetes assigns services IPv6 addresses from the unique local address range (fc00::/7). You can't specify a custom IPv6 CIDR block. Pod addresses are assigned from the subnet's IPv6 CIDR.", "ServiceIpv4Cidr": "Don't specify a value if you select `ipv6` for *ipFamily* . The CIDR block to assign Kubernetes service IP addresses from. If you don't specify a block, Kubernetes assigns addresses from either the 10.100.0.0/16 or 172.20.0.0/16 CIDR blocks. We recommend that you specify a block that does not overlap with resources in other networks that are peered or connected to your VPC. The block must meet the following requirements:\n\n- Within one of the following private IP address blocks: 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16.\n- Doesn't overlap with any CIDR block assigned to the VPC that you selected for VPC.\n- Between /24 and /12.\n\n> You can only specify a custom CIDR block when you create a cluster and can't change this value once the cluster is created.", - "ServiceIpv6Cidr": "The CIDR block that Kubernetes Pod and Service IP addresses are assigned from if you created a 1.21 or later cluster with version 1.10.1 or later of the Amazon VPC CNI add-on and specified `ipv6` for *ipFamily* when you created the cluster. Kubernetes assigns Service addresses from the unique local address range ( `fc00::/7` ) because you can't specify a custom IPv6 CIDR block when you create the cluster." + "ServiceIpv6Cidr": "The CIDR block that Kubernetes pod and service IP addresses are assigned from if you created a 1.21 or later cluster with version 1.10.1 or later of the Amazon VPC CNI add-on and specified `ipv6` for *ipFamily* when you created the cluster. Kubernetes assigns service addresses from the unique local address range ( `fc00::/7` ) because you can't specify a custom IPv6 CIDR block when you create the cluster." } }, "AWS::EKS::Cluster.Logging": { @@ -14748,9 +14826,9 @@ "attributes": {}, "description": "`VolumeSpecification` is a subproperty of the `EbsBlockDeviceConfig` property type. `VolumeSecification` determines the volume type, IOPS, and size (GiB) for EBS volumes attached to EC2 instances.", "properties": { - "Iops": "The number of I/O operations per second (IOPS) that the volume supports. IOPS parameters are supported for volumes: io1 and gp3. Among them, IOPS parameters are required for volumes io1 but optional for volumes gp3 which default to 3000 IOPS. IOPS parameters are not supported for volumes: gp2, standard, st1 and sc1.", + "Iops": "The number of I/O operations per second (IOPS) that the volume supports.", "SizeInGB": "The volume size, in gibibytes (GiB). This can be a number from 1 - 1024. If the volume type is EBS-optimized, the minimum value is 10.", - "VolumeType": "The volume type. Volume types supported are gp2, io1, standard sc1, st1 and gp3. For gp3, customer will be able to configure IOPs but not throughput. Throughput will default to 125 MiB/s." + "VolumeType": "The volume type. Volume types supported are gp2, io1, and standard." } }, "AWS::EMR::InstanceFleetConfig": { @@ -14835,9 +14913,9 @@ "attributes": {}, "description": "`VolumeSpecification` is a subproperty of the `EbsBlockDeviceConfig` property type. `VolumeSecification` determines the volume type, IOPS, and size (GiB) for EBS volumes attached to EC2 instances.", "properties": { - "Iops": "The number of I/O operations per second (IOPS) that the volume supports. IOPS parameters are supported for volumes: io1 and gp3. Among them, IOPS parameters are required for volumes io1 but optional for volumes gp3 which default to 3000 IOPS. IOPS parameters are not supported for volumes: gp2, standard, st1 and sc1.", + "Iops": "The number of I/O operations per second (IOPS) that the volume supports.", "SizeInGB": "The volume size, in gibibytes (GiB). This can be a number from 1 - 1024. If the volume type is EBS-optimized, the minimum value is 10.", - "VolumeType": "The volume type. Volume types supported are gp2, io1, standard sc1, st1 and gp3. For gp3, customer will be able to configure IOPs but not throughput. Throughput will default to 125 MiB/s." + "VolumeType": "The volume type. Volume types supported are gp2, io1, and standard." } }, "AWS::EMR::InstanceGroupConfig": { @@ -14961,9 +15039,9 @@ "attributes": {}, "description": "`VolumeSpecification` is a subproperty of the `EbsBlockDeviceConfig` property type. `VolumeSecification` determines the volume type, IOPS, and size (GiB) for EBS volumes attached to EC2 instances.", "properties": { - "Iops": "The number of I/O operations per second (IOPS) that the volume supports. IOPS parameters are supported for volumes: io1 and gp3. Among them, IOPS parameters are required for volumes io1 but optional for volumes gp3 which default to 3000 IOPS. IOPS parameters are not supported for volumes: gp2, standard, st1 and sc1.", + "Iops": "The number of I/O operations per second (IOPS) that the volume supports.", "SizeInGB": "The volume size, in gibibytes (GiB). This can be a number from 1 - 1024. If the volume type is EBS-optimized, the minimum value is 10.", - "VolumeType": "The volume type. Volume types supported are gp2, io1, standard sc1, st1 and gp3. For gp3, customer will be able to configure IOPs but not throughput. Throughput will default to 125 MiB/s." + "VolumeType": "The volume type. Volume types supported are gp2, io1, and standard." } }, "AWS::EMR::SecurityConfiguration": { @@ -16536,7 +16614,7 @@ "EventPattern": "The EventBridge event pattern that defines how the metric is recorded.\n\nFor more information about EventBridge event patterns, see [Amazon EventBridge event patterns](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html) .", "MetricName": "A name for the metric. It can include up to 255 characters.", "UnitLabel": "A label for the units that the metric is measuring.", - "ValueKey": "A label for the units that the metric is measuring." + "ValueKey": "The JSON path to reference the numerical metric value in the event." } }, "AWS::Evidently::Experiment.OnlineAbConfigObject": { @@ -17868,6 +17946,14 @@ "Path": "The path of the JDBC target." } }, + "AWS::Glue::Crawler.MongoDBTarget": { + "attributes": {}, + "description": "Specifies an Amazon DocumentDB or MongoDB data store to crawl.", + "properties": { + "ConnectionName": "The name of the connection to use to connect to the Amazon DocumentDB or MongoDB target.", + "Path": "The path of the Amazon DocumentDB or MongoDB target (database/collection)." + } + }, "AWS::Glue::Crawler.RecrawlPolicy": { "attributes": {}, "description": "When crawling an Amazon S3 data source after the first crawl is complete, specifies whether to crawl the entire dataset again or to crawl only folders that were added since the last crawler run. For more information, see [Incremental Crawls in AWS Glue](https://docs.aws.amazon.com/glue/latest/dg/incremental-crawls.html) in the developer guide.", @@ -17880,8 +17966,11 @@ "description": "Specifies a data store in Amazon Simple Storage Service (Amazon S3).", "properties": { "ConnectionName": "The name of a connection which allows a job or crawler to access data in Amazon S3 within an Amazon Virtual Private Cloud environment (Amazon VPC).", + "DlqEventQueueArn": "", + "EventQueueArn": "", "Exclusions": "A list of glob patterns used to exclude from the crawl. For more information, see [Catalog Tables with a Crawler](https://docs.aws.amazon.com/glue/latest/dg/add-crawler.html) .", - "Path": "The path to the Amazon S3 target." + "Path": "The path to the Amazon S3 target.", + "SampleSize": "Sets the number of files in each leaf folder to be crawled when crawling sample files in a dataset. If not set, all the files are crawled. A valid value is an integer between 1 and 249." } }, "AWS::Glue::Crawler.Schedule": { @@ -17906,6 +17995,7 @@ "CatalogTargets": "Specifies AWS Glue Data Catalog targets.", "DynamoDBTargets": "Specifies Amazon DynamoDB targets.", "JdbcTargets": "Specifies JDBC targets.", + "MongoDBTargets": "A list of Mongo DB targets.", "S3Targets": "Specifies Amazon Simple Storage Service (Amazon S3) targets." } }, @@ -19518,11 +19608,26 @@ }, "AWS::GuardDuty::Detector.CFNDataSourceConfigurations": { "attributes": {}, - "description": "Describes whether S3 data event logs will be enabled as a data source when the detector is created.", + "description": "Describes whether S3 data event logs or Kubernetes audit logs will be enabled as a data source when the detector is created.", "properties": { + "Kubernetes": "Describes which Kuberentes data sources are enabled for a detector.", "S3Logs": "Describes whether S3 data event logs are enabled as a data source." } }, + "AWS::GuardDuty::Detector.CFNKubernetesAuditLogsConfiguration": { + "attributes": {}, + "description": "Describes which optional data sources are enabled for a detector.", + "properties": { + "Enable": "Describes whether Kubernetes audit logs are enabled as a data source for the detector." + } + }, + "AWS::GuardDuty::Detector.CFNKubernetesConfiguration": { + "attributes": {}, + "description": "Describes which Kubernetes protection data sources are enabled for the detector.", + "properties": { + "AuditLogs": "Describes whether Kubernetes audit logs are enabled as a data source for the detector." + } + }, "AWS::GuardDuty::Detector.CFNS3LogsConfiguration": { "attributes": {}, "description": "Describes whether S3 data event logs will be enabled as a data source when the detector is created.", @@ -19541,7 +19646,7 @@ "DetectorId": "The ID of the detector belonging to the GuardDuty account that you want to create a filter for.", "FindingCriteria": "Represents the criteria to be used in the filter for querying findings.", "Name": "The name of the filter. Minimum length of 3. Maximum length of 64. Valid characters include alphanumeric characters, dot (.), underscore (_), and dash (-). Spaces are not allowed.", - "Rank": "Specifies the position of the filter in the list of current filters. Also specifies the order in which this filter is applied to the findings.\n\n> By default filters may not be created in the same order as they are ranked. To ensure filters are created in the correct order you can use the optional `DependsOn` attribute with the following syntax: `\"DependsOn\":[ \"ObjectName\" ]` . You can find more information on using this attribute [here](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html) ." + "Rank": "" } }, "AWS::GuardDuty::Filter.Condition": { @@ -19898,7 +20003,8 @@ "properties": { "DestinationConfiguration": "A destination configuration contains information about where recorded video will be stored. See the [DestinationConfiguration](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-destinationconfiguration.html) property type for more information.", "Name": "Recording-configuration name. The value does not need to be unique.", - "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) ." + "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) .", + "ThumbnailConfiguration": "A thumbnail configuration enables/disables the recording of thumbnails for a live session and controls the interval at which thumbnails are generated for the live session. See the [ThumbnailConfiguration](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thunbnailconfiguration.html) property type for more information." } }, "AWS::IVS::RecordingConfiguration.DestinationConfiguration": { @@ -19915,6 +20021,14 @@ "BucketName": "Location (S3 bucket name) where recorded videos will be stored." } }, + "AWS::IVS::RecordingConfiguration.ThumbnailConfiguration": { + "attributes": {}, + "description": "The ThumbnailConfiguration property type describes a configuration of thumbnails for recorded video.", + "properties": { + "RecordingMode": "Thumbnail recording mode. Valid values:\n\n- `DISABLED` : Use DISABLED to disable the generation of thumbnails for recorded video.\n- `INTERVAL` : Use INTERVAL to enable the generation of thumbnails for recorded video at a time interval controlled by the [TargetIntervalSeconds](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thumbnailconfiguration.html#cfn-ivs-recordingconfiguration-thumbnailconfiguration-targetintervalseconds) property.\n\n*Default* : `INTERVAL`", + "TargetIntervalSeconds": "The targeted thumbnail-generation interval in seconds. This is configurable (and required) only if [RecordingMode](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thumbnailconfiguration.html#cfn-ivs-recordingconfiguration-thumbnailconfiguration-recordingmode) is `INTERVAL` .\n\n> Setting a value for `TargetIntervalSeconds` does not guarantee that thumbnails are generated at the specified interval. For thumbnails to be generated at the `TargetIntervalSeconds` interval, the `IDR/Keyframe` value for the input video must be less than the `TargetIntervalSeconds` value. See [Amazon IVS Streaming Configuration](https://docs.aws.amazon.com/ivs/latest/userguide/streaming-config.html) for information on setting `IDR/Keyframe` to the recommended value in video-encoder settings. \n\n*Default* : 60\n\n*Valid Range* : Minumum value of 5. Maximum value of 60." + } + }, "AWS::IVS::StreamKey": { "attributes": { "Arn": "The stream-key ARN. For example: `arn:aws:ivs:us-west-2:123456789012:stream-key/g1H2I3j4k5L6`", @@ -20424,7 +20538,7 @@ "properties": { "DisplayName": "Field that represents a friendly name in the console for the custom metric; it doesn't have to be unique. Don't use this name as the metric identifier in the device metric report. Can be updated.", "MetricName": "The name of the custom metric. This will be used in the metric report submitted from the device/thing. It shouldn't begin with `aws:` . Cannot be updated once it's defined.", - "MetricType": "The type of the custom metric. Types include `string-list` , `ip-address-list` , `number-list` , and `number` .", + "MetricType": "The type of the custom metric. Types include `string-list` , `ip-address-list` , and `number-list` .", "Tags": "Metadata that can be used to manage the custom metric." } }, @@ -20529,13 +20643,13 @@ }, "AWS::IoT::Logging": { "attributes": { - "Ref": "" + "Ref": "`Ref` returns the log ID. For example:\n\n`{\"Ref\": \"Log-12345\"}`" }, - "description": "", + "description": "Configure logging.", "properties": { - "AccountId": "", - "DefaultLogLevel": "", - "RoleArn": "" + "AccountId": "The account ID.", + "DefaultLogLevel": "The default log level.Valid Values: `DEBUG | INFO | ERROR | WARN | DISABLED`", + "RoleArn": "The role ARN used for the log." } }, "AWS::IoT::MitigationAction": { @@ -20653,14 +20767,14 @@ }, "AWS::IoT::ResourceSpecificLogging": { "attributes": { - "Ref": "", - "TargetId": "" + "Ref": "`Ref` returns the resource-specific log ID. For example:\n\n`{\"Ref\": \"MyResourceLog-12345\" }`", + "TargetId": "The target Id." }, - "description": "", + "description": "Configure resource-specific logging.", "properties": { - "LogLevel": "", - "TargetName": "", - "TargetType": "" + "LogLevel": "The default log level.Valid Values: `DEBUG | INFO | ERROR | WARN | DISABLED`", + "TargetName": "The target name.", + "TargetType": "The target type. Valid Values: `DEFAULT | THING_GROUP`" } }, "AWS::IoT::ScheduledAudit": { @@ -20755,10 +20869,10 @@ "properties": { "Cidrs": "If the `comparisonOperator` calls for a set of CIDRs, use this to specify that set to be compared with the `metric` .", "Count": "If the `comparisonOperator` calls for a numeric value, use this to specify that numeric value to be compared with the `metric` .", - "Number": "", - "Numbers": "", + "Number": "The numeric values of a metric.", + "Numbers": "The numeric value of a metric.", "Ports": "If the `comparisonOperator` calls for a set of ports, use this to specify that set to be compared with the `metric` .", - "Strings": "" + "Strings": "The string values of a metric." } }, "AWS::IoT::SecurityProfile.StatisticalThreshold": { @@ -20810,7 +20924,7 @@ "description": "Describes the actions associated with a rule.", "properties": { "CloudwatchAlarm": "Change the state of a CloudWatch alarm.", - "CloudwatchLogs": "", + "CloudwatchLogs": "Sends data to CloudWatch.", "CloudwatchMetric": "Capture a CloudWatch metric.", "DynamoDB": "Write to a DynamoDB table.", "DynamoDBv2": "Write to a DynamoDB table. This is a new version of the DynamoDB action. It allows you to write each attribute in an MQTT message payload into a separate DynamoDB column.", @@ -20927,7 +21041,7 @@ "attributes": {}, "description": "Describes an action that writes data to an Amazon Kinesis Firehose stream.", "properties": { - "BatchMode": "", + "BatchMode": "Whether to deliver the Kinesis Data Firehose stream as a batch by using [`PutRecordBatch`](https://docs.aws.amazon.com/firehose/latest/APIReference/API_PutRecordBatch.html) . The default value is `false` .\n\nWhen `batchMode` is `true` and the rule's SQL statement evaluates to an Array, each Array element forms one record in the [`PutRecordBatch`](https://docs.aws.amazon.com/firehose/latest/APIReference/API_PutRecordBatch.html) request. The resulting array can't have more than 500 records.", "DeliveryStreamName": "The delivery stream name.", "RoleArn": "The IAM role that grants access to the Amazon Kinesis Firehose stream.", "Separator": "A character separator that will be used to separate records written to the Firehose stream. Valid values are: '\\n' (newline), '\\t' (tab), '\\r\\n' (Windows newline), ',' (comma)." @@ -20962,7 +21076,7 @@ "attributes": {}, "description": "Sends message data to an AWS IoT Analytics channel.", "properties": { - "BatchMode": "", + "BatchMode": "Whether to process the action as a batch. The default value is `false` .\n\nWhen `batchMode` is `true` and the rule SQL statement evaluates to an Array, each Array element is delivered as a separate message when passed by [`BatchPutMessage`](https://docs.aws.amazon.com/iotanalytics/latest/APIReference/API_BatchPutMessage.html) The resulting array can't have more than 100 messages.", "ChannelName": "The name of the IoT Analytics channel to which message data will be sent.", "RoleArn": "The ARN of the role which has a policy that grants IoT Analytics permission to send message data via IoT Analytics (iotanalytics:BatchPutMessage)." } @@ -20971,7 +21085,7 @@ "attributes": {}, "description": "Sends an input to an AWS IoT Events detector.", "properties": { - "BatchMode": "", + "BatchMode": "Whether to process the event actions as a batch. The default value is `false` .\n\nWhen `batchMode` is `true` , you can't specify a `messageId` .\n\nWhen `batchMode` is `true` and the rule SQL statement evaluates to an Array, each Array element is treated as a separate message when Events by calling [`BatchPutMessage`](https://docs.aws.amazon.com/iotevents/latest/apireference/API_iotevents-data_BatchPutMessage.html) . The resulting array can't have more than 10 messages.", "InputName": "The name of the AWS IoT Events input.", "MessageId": "The ID of the message. The default `messageId` is a new UUID value.\n\nWhen `batchMode` is `true` , you can't specify a `messageId` --a new UUID value will be assigned.\n\nAssign a value to this property to ensure that only one input (message) with a given `messageId` will be processed by an AWS IoT Events detector.", "RoleArn": "The ARN of the role that grants AWS IoT permission to send an input to an AWS IoT Events detector. (\"Action\":\"iotevents:BatchPutMessage\")." @@ -21100,7 +21214,7 @@ "attributes": {}, "description": "Describes an action that writes records into an Amazon Timestream table.", "properties": { - "BatchMode": "", + "BatchMode": "Whether to process the action as a batch.", "DatabaseName": "The name of an Amazon Timestream database that has the table to write records into.", "Dimensions": "Metadata attributes of the time series that are written in each measure record.", "RoleArn": "The Amazon Resource Name (ARN) of the role that grants AWS IoT permission to write to the Timestream database table.", @@ -21140,7 +21254,7 @@ "attributes": { "Arn": "The topic rule destination URL.", "Ref": "`Ref` returns the topic rule destination. For example:\n\n`{ \"Ref\": \"TopicRuleDestination\" }`\n\nA value similar to the following is returned:\n\n`a1234567b89c012d3e4fg567hij8k9l01mno1p23q45678901rs234567890t1u2`", - "StatusReason": "" + "StatusReason": "Additional details or reason why the topic rule destination is in the current status." }, "description": "A topic rule destination.", "properties": { @@ -22190,7 +22304,8 @@ "attributes": {}, "description": "Contains a gateway's platform information.", "properties": { - "Greengrass": "A gateway that runs on AWS IoT Greengrass ." + "Greengrass": "A gateway that runs on AWS IoT Greengrass .", + "GreengrassV2": "" } }, "AWS::IoTSiteWise::Gateway.Greengrass": { @@ -22200,6 +22315,13 @@ "GroupArn": "The [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) of the Greengrass group. For more information about how to find a group's ARN, see [ListGroups](https://docs.aws.amazon.com/greengrass/latest/apireference/listgroups-get.html) and [GetGroup](https://docs.aws.amazon.com/greengrass/latest/apireference/getgroup-get.html) in the *AWS IoT Greengrass API Reference* ." } }, + "AWS::IoTSiteWise::Gateway.GreengrassV2": { + "attributes": {}, + "description": "", + "properties": { + "CoreDeviceThingName": "" + } + }, "AWS::IoTSiteWise::Portal": { "attributes": { "PortalArn": "The [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) of the portal, which has the following format.\n\n`arn:${Partition}:iotsitewise:${Region}:${Account}:portal/${PortalId}`", @@ -22605,7 +22727,7 @@ "attributes": { "Ref": "`Ref` returns the alias name, such as `alias/exampleAlias` ." }, - "description": "The `AWS::KMS::Alias` resource specifies a display name for a [KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#kms_keys) . You can use an alias to identify a KMS key in the AWS KMS console, in the [DescribeKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html) operation, and in [cryptographic operations](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#cryptographic-operations) , such as [Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) and [GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) .\n\n> Adding, deleting, or updating an alias can allow or deny permission to the KMS key. For details, see [Using ABAC in AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/abac.html) in the *AWS Key Management Service Developer Guide* . \n\nUsing an alias to refer to a KMS key can help you simplify key management. For example, an alias in your code can be associated with different KMS keys in different AWS Regions . For more information, see [Using aliases](https://docs.aws.amazon.com/kms/latest/developerguide/kms-alias.html) in the *AWS Key Management Service Developer Guide* .\n\nWhen specifying an alias, observe the following rules.\n\n- Each alias is associated with one KMS key, but multiple aliases can be associated with the same KMS key.\n- The alias and its associated KMS key must be in the same AWS account and Region.\n- The alias name must be unique in the AWS account and Region. However, you can create aliases with the same name in different AWS Regions . For example, you can have an `alias/projectKey` in multiple Regions, each of which is associated with a KMS key in its Region.\n- Each alias name must begin with `alias/` followed by a name, such as `alias/exampleKey` . The alias name can contain only alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). Alias names cannot begin with `alias/aws/` . That alias name prefix is reserved for [AWS managed keys](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk) .", + "description": "The `AWS::KMS::Alias` resource specifies a display name for a [KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#kms_keys) . You can use an alias to identify a KMS key in the AWS KMS console, in the [DescribeKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html) operation, and in [cryptographic operations](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#cryptographic-operations) , such as [Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) and [GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) .\n\n> Adding, deleting, or updating an alias can allow or deny permission to the KMS key. For details, see [ABAC for AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/abac.html) in the *AWS Key Management Service Developer Guide* . \n\nUsing an alias to refer to a KMS key can help you simplify key management. For example, an alias in your code can be associated with different KMS keys in different AWS Regions . For more information, see [Using aliases](https://docs.aws.amazon.com/kms/latest/developerguide/kms-alias.html) in the *AWS Key Management Service Developer Guide* .\n\nWhen specifying an alias, observe the following rules.\n\n- Each alias is associated with one KMS key, but multiple aliases can be associated with the same KMS key.\n- The alias and its associated KMS key must be in the same AWS account and Region.\n- The alias name must be unique in the AWS account and Region. However, you can create aliases with the same name in different AWS Regions . For example, you can have an `alias/projectKey` in multiple Regions, each of which is associated with a KMS key in its Region.\n- Each alias name must begin with `alias/` followed by a name, such as `alias/exampleKey` . The alias name can contain only alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). Alias names cannot begin with `alias/aws/` . That alias name prefix is reserved for [AWS managed keys](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk) .", "properties": { "AliasName": "Specifies the alias name. This value must begin with `alias/` followed by a name, such as `alias/ExampleAlias` .\n\n> If you change the value of a `Replacement` property, such as `AliasName` , the existing alias is deleted and a new alias is created for the specified KMS key. This change can disrupt applications that use the alias. It can also allow or deny access to a KMS key affected by attribute-based access control (ABAC). \n\nThe alias must be string of 1-256 characters. It can contain only alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). The alias name cannot begin with `alias/aws/` . The `alias/aws/` prefix is reserved for [AWS managed keys](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk) .\n\n*Pattern* : `alias/^[a-zA-Z0-9/_-]+$`\n\n*Minimum* : `1`\n\n*Maximum* : `256`", "TargetKeyId": "Associates the alias with the specified [customer managed key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#customer-cmk) . The KMS key must be in the same AWS account and Region.\n\nA valid key ID is required. If you supply a null or empty string value, this operation returns an error.\n\nFor help finding the key ID and ARN, see [Finding the key ID and ARN](https://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html#find-cmk-id-arn) in the *AWS Key Management Service Developer Guide* .\n\nSpecify the key ID or the key ARN of the KMS key.\n\nFor example:\n\n- Key ID: `1234abcd-12ab-34cd-56ef-1234567890ab`\n- Key ARN: `arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab`\n\nTo get the key ID and key ARN for a KMS key, use [ListKeys](https://docs.aws.amazon.com/kms/latest/APIReference/API_ListKeys.html) or [DescribeKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html) ." @@ -22617,7 +22739,7 @@ "KeyId": "The key ID of the KMS key, such as `1234abcd-12ab-34cd-56ef-1234567890ab` .\n\nFor information about the key ID of a KMS key, see [Key ID](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id-key-id) in the *AWS Key Management Service Developer Guide* .", "Ref": "`Ref` returns the key ID, such as `1234abcd-12ab-34cd-56ef-1234567890ab` ." }, - "description": "The `AWS::KMS::Key` resource specifies a [symmetric or asymmetric](https://docs.aws.amazon.com/kms/latest/developerguide/symmetric-asymmetric.html) [KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#kms_keys) in AWS Key Management Service ( AWS KMS ).\n\n> AWS KMS is replacing the term *customer master key (CMK)* with *AWS KMS key* and *KMS key* . The concept has not changed. To prevent breaking changes, AWS KMS is keeping some variations of this term. \n\nYou can use symmetric KMS keys to encrypt and decrypt small amounts of data, but they are more commonly used to generate data keys and data key pairs. You can also use symmetric KMS key to encrypt data stored in AWS services that are [integrated with AWS KMS](https://docs.aws.amazon.com//kms/features/#AWS_Service_Integration) . For more information, see [What is AWS Key Management Service ?](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html) in the *AWS Key Management Service Developer Guide* .\n\nYou can use asymmetric KMS keys to encrypt and decrypt data or sign messages and verify signatures. To create an asymmetric key, you must specify an asymmetric `KeySpec` value and a `KeyUsage` value.\n\n> If you change the value of a `Replacement` property, such as `KeyUsage` or `KeySpec` , on an existing KMS key, the existing KMS key is [scheduled for deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html) and a new KMS key is created with the specified value.\n> \n> While scheduled for deletion, the existing KMS key becomes unusable. If you don't [cancel the scheduled deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html#deleting-keys-scheduling-key-deletion) of the existing KMS key outside of CloudFormation, all data encrypted under the existing KMS key becomes unrecoverable when the KMS key is deleted.", + "description": "The `AWS::KMS::Key` resource specifies a [symmetric or asymmetric](https://docs.aws.amazon.com/kms/latest/developerguide/symmetric-asymmetric.html) [KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#kms_keys) in AWS Key Management Service ( AWS KMS ).\n\nYou can use the `AWS::KMS::Key` resource to specify a symmetric or asymmetric multi-Region primary key. To specify a replica key, use the [AWS::KMS::ReplicaKey](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-replicakey.html) resource. For information about multi-Region keys, see [Multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide* .\n\nYou cannot use the `AWS::KMS::Key` resource to specify a KMS key with [imported key material](https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys.html) or a KMS key in a [custom key store](https://docs.aws.amazon.com/kms/latest/developerguide/custom-key-store-overview.html) .\n\n> AWS KMS is replacing the term *customer master key (CMK)* with *AWS KMS key* and *KMS key* . The concept has not changed. To prevent breaking changes, AWS KMS is keeping some variations of this term. \n\nYou can use symmetric KMS keys to encrypt and decrypt small amounts of data, but they are more commonly used to generate data keys and data key pairs. You can also use symmetric KMS key to encrypt data stored in AWS services that are [integrated with AWS KMS](https://docs.aws.amazon.com//kms/features/#AWS_Service_Integration) . For more information, see [What is AWS Key Management Service ?](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html) in the *AWS Key Management Service Developer Guide* .\n\nYou can use asymmetric KMS keys to encrypt and decrypt data or sign messages and verify signatures. To create an asymmetric key, you must specify an asymmetric `KeySpec` value and a `KeyUsage` value.\n\n> If you change the value of the `KeyUsage` , `KeySpec` , or `MultiRegion` property on an existing KMS key, the existing KMS key is [scheduled for deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html) and a new KMS key is created with the specified value.\n> \n> While scheduled for deletion, the existing KMS key becomes unusable. If you don't [cancel the scheduled deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html#deleting-keys-scheduling-key-deletion) of the existing KMS key outside of CloudFormation, all data encrypted under the existing KMS key becomes unrecoverable when the KMS key is deleted. \n\n*Regions*\n\nAWS KMS CloudFormation resources are supported in all Regions in which AWS CloudFormation is supported. However, in the (ap-southeast-3), you cannot use a CloudFormation template to create or manage asymmetric KMS keys or multi-Region KMS keys (primary or replica).", "properties": { "Description": "A description of the KMS key. Use a description that helps you to distinguish this KMS key from others in the account, such as its intended use.", "EnableKeyRotation": "Enables automatic rotation of the key material for the specified KMS key. By default, automatic key rotation is not enabled.\n\nAWS KMS does not support automatic key rotation on asymmetric KMS keys. For asymmetric KMS keys, omit the `EnableKeyRotation` property or set it to `false` .\n\nWhen you enable automatic rotation, AWS KMS automatically creates new key material for the KMS key 365 days after the enable (or reenable) date and every 365 days thereafter. AWS KMS retains all key material until you delete the KMS key. For detailed information about automatic key rotation, see [Rotating KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) in the *AWS Key Management Service Developer Guide* .", @@ -22625,9 +22747,9 @@ "KeyPolicy": "The key policy that authorizes use of the KMS key. The key policy must conform to the following rules.\n\n- The key policy must allow the caller to make a subsequent [PutKeyPolicy](https://docs.aws.amazon.com/kms/latest/APIReference/API_PutKeyPolicy.html) request on the KMS key. This reduces the risk that the KMS key becomes unmanageable. For more information, refer to the scenario in the [Default key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default-allow-root-enable-iam) section of the **AWS Key Management Service Developer Guide** .\n- Each statement in the key policy must contain one or more principals. The principals in the key policy must exist and be visible to AWS KMS . When you create a new AWS principal (for example, an IAM user or role), you might need to enforce a delay before including the new principal in a key policy because the new principal might not be immediately visible to AWS KMS . For more information, see [Changes that I make are not always immediately visible](https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_eventual-consistency) in the *AWS Identity and Access Management User Guide* .\n- The key policy size limit is 32 kilobytes (32768 bytes).\n\nIf you are unsure of which policy to use, consider the *default key policy* . This is the key policy that AWS KMS applies to KMS keys that are created by using the CreateKey API with no specified key policy. It gives the AWS account that owns the key permission to perform all operations on the key. It also allows you write IAM policies to authorize access to the key. For details, see [Default key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default) in the *AWS Key Management Service Developer Guide* .\n\n*Minimum* : `1`\n\n*Maximum* : `32768`", "KeySpec": "Specifies the type of KMS key to create. The default value, `SYMMETRIC_DEFAULT` , creates a KMS key with a 256-bit symmetric key for encryption and decryption. For help choosing a key spec for your KMS key, see [How to choose Your KMS key configuration](https://docs.aws.amazon.com/kms/latest/developerguide/symm-asymm-choose.html) in the *AWS Key Management Service Developer Guide* .\n\nThe `KeySpec` property determines whether the KMS key contains a symmetric key or an asymmetric key pair. It also determines the encryption algorithms or signing algorithms that the KMS key supports. You can't change the `KeySpec` after the KMS key is created. To further restrict the algorithms that can be used with the KMS key, use a condition key in its key policy or IAM policy. For more information, see [kms:EncryptionAlgorithm](https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-encryption-algorithm) or [kms:Signing Algorithm](https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-signing-algorithm) in the *AWS Key Management Service Developer Guide* .\n\n> If you change the `KeySpec` of an existing KMS key, the existing KMS key is scheduled for deletion and a new KMS key is created with the specified `KeySpec` value. While the scheduled deletion is pending, you can't use the existing KMS key. Unless you [cancel the scheduled deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html#deleting-keys-scheduling-key-deletion) of the KMS key outside of CloudFormation, all data encrypted under the existing KMS key becomes unrecoverable when the KMS key is deleted. > [AWS services that are integrated with AWS KMS](https://docs.aws.amazon.com/kms/features/#AWS_Service_Integration) use symmetric KMS keys to protect your data. These services do not support asymmetric KMS keys. For help determining whether a KMS key is symmetric or asymmetric, see [Identifying Symmetric and Asymmetric KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/find-symm-asymm.html) in the *AWS Key Management Service Developer Guide* . \n\nAWS KMS supports the following key specs for KMS keys:\n\n- Symmetric key (default)\n\n- `SYMMETRIC_DEFAULT` (AES-256-GCM)\n- Asymmetric RSA key pairs\n\n- `RSA_2048`\n- `RSA_3072`\n- `RSA_4096`\n- Asymmetric NIST-recommended elliptic curve key pairs\n\n- `ECC_NIST_P256` (secp256r1)\n- `ECC_NIST_P384` (secp384r1)\n- `ECC_NIST_P521` (secp521r1)\n- Other asymmetric elliptic curve key pairs\n\n- `ECC_SECG_P256K1` (secp256k1), commonly used for cryptocurrencies.", "KeyUsage": "Determines the [cryptographic operations](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#cryptographic-operations) for which you can use the KMS key. The default value is `ENCRYPT_DECRYPT` . This property is required only for asymmetric KMS keys. You can't change the `KeyUsage` value after the KMS key is created.\n\n> If you change the `KeyUsage` of an existing KMS key, the existing KMS key is scheduled for deletion and a new KMS key is created with the specified `KeyUsage` value. While the scheduled deletion is pending, you can't use the existing KMS key. Unless you [cancel the scheduled deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html#deleting-keys-scheduling-key-deletion) of the KMS key outside of CloudFormation, all data encrypted under the existing KMS key becomes unrecoverable when the KMS key is deleted. \n\nSelect only one valid value.\n\n- For symmetric KMS keys, omit the property or specify `ENCRYPT_DECRYPT` .\n- For asymmetric KMS keys with RSA key material, specify `ENCRYPT_DECRYPT` or `SIGN_VERIFY` .\n- For asymmetric KMS keys with ECC key material, specify `SIGN_VERIFY` .", - "MultiRegion": "Creates a multi-Region primary key that you can replicate in other AWS Regions .\n\n> If you change the `MultiRegion` property of an existing KMS key, the existing KMS key is scheduled for deletion and a new KMS key is created with the specified `Multi-Region` value. While the scheduled deletion is pending, you can't use the existing KMS key. Unless you [cancel the scheduled deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html#deleting-keys-scheduling-key-deletion) of the KMS key outside of CloudFormation, all data encrypted under the existing KMS key becomes unrecoverable when the KMS key is deleted. \n\nFor a multi-Region key, set to this property to `true` . For a single-Region key, omit this property or set it to `false` . The default value is `false` .\n\n*Multi-Region keys* are an AWS KMS feature that lets you create multiple interoperable KMS keys in different AWS Regions . Because these KMS keys have the same key ID, key material, and other metadata, you can use them to encrypt data in one AWS Region and decrypt it in a different AWS Region without making a cross-Region call or exposing the plaintext data. For more information, see [Using multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide* .\n\nYou can create a symmetric or asymmetric multi-Region key, and you can create a multi-Region key with imported key material. However, you cannot create a multi-Region key in a custom key store.\n\nTo create a replica of this primary key in a different AWS Region , create an [AWS::KMS::ReplicaKey](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-replicakey.html) resource in a CloudFormation stack in the replica Region. Specify the key ARN of this primary key.", + "MultiRegion": "Creates a multi-Region primary key that you can replicate in other AWS Regions .\n\n> If you change the `MultiRegion` property of an existing KMS key, the existing KMS key is scheduled for deletion and a new KMS key is created with the specified `Multi-Region` value. While the scheduled deletion is pending, you can't use the existing KMS key. Unless you [cancel the scheduled deletion](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html#deleting-keys-scheduling-key-deletion) of the KMS key outside of CloudFormation, all data encrypted under the existing KMS key becomes unrecoverable when the KMS key is deleted. \n\nFor a multi-Region key, set to this property to `true` . For a single-Region key, omit this property or set it to `false` . The default value is `false` .\n\n*Multi-Region keys* are an AWS KMS feature that lets you create multiple interoperable KMS keys in different AWS Regions . Because these KMS keys have the same key ID, key material, and other metadata, you can use them to encrypt data in one AWS Region and decrypt it in a different AWS Region without making a cross-Region call or exposing the plaintext data. For more information, see [Multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide* .\n\nYou can create a symmetric or asymmetric multi-Region key, and you can create a multi-Region key with imported key material. However, you cannot create a multi-Region key in a custom key store.\n\nTo create a replica of this primary key in a different AWS Region , create an [AWS::KMS::ReplicaKey](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-replicakey.html) resource in a CloudFormation stack in the replica Region. Specify the key ARN of this primary key.", "PendingWindowInDays": "Specifies the number of days in the waiting period before AWS KMS deletes a KMS key that has been removed from a CloudFormation stack. Enter a value between 7 and 30 days. The default value is 30 days.\n\nWhen you remove a KMS key from a CloudFormation stack, AWS KMS schedules the KMS key for deletion and starts the mandatory waiting period. The `PendingWindowInDays` property determines the length of waiting period. During the waiting period, the key state of KMS key is `Pending Deletion` or `Pending Replica Deletion` , which prevents the KMS key from being used in cryptographic operations. When the waiting period expires, AWS KMS permanently deletes the KMS key.\n\nAWS KMS will not delete a [multi-Region primary key](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) that has replica keys. If you remove a multi-Region primary key from a CloudFormation stack, its key state changes to `PendingReplicaDeletion` so it cannot be replicated or used in cryptographic operations. This state can persist indefinitely. When the last of its replica keys is deleted, the key state of the primary key changes to `PendingDeletion` and the waiting period specified by `PendingWindowInDays` begins. When this waiting period expires, AWS KMS deletes the primary key. For details, see [Deleting multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-delete.html) in the *AWS Key Management Service Developer Guide* .\n\nYou cannot use a CloudFormation template to cancel deletion of the KMS key after you remove it from the stack, regardless of the waiting period. If you specify a KMS key in your template, even one with the same name, CloudFormation creates a new KMS key. To cancel deletion of a KMS key, use the AWS KMS console or the [CancelKeyDeletion](https://docs.aws.amazon.com/kms/latest/APIReference/API_CancelKeyDeletion.html) operation.\n\nFor information about the `Pending Deletion` and `Pending Replica Deletion` key states, see [Key state: Effect on your KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/key-state.html) in the *AWS Key Management Service Developer Guide* . For more information about deleting KMS keys, see the [ScheduleKeyDeletion](https://docs.aws.amazon.com/kms/latest/APIReference/API_ScheduleKeyDeletion.html) operation in the *AWS Key Management Service API Reference* and [Deleting KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html) in the *AWS Key Management Service Developer Guide* .\n\n*Minimum* : 7\n\n*Maximum* : 30", - "Tags": "Assigns one or more tags to the replica key.\n\n> Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see [Using ABAC in AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/abac.html) in the *AWS Key Management Service Developer Guide* . \n\nFor information about tags in AWS KMS , see [Tagging keys](https://docs.aws.amazon.com/kms/latest/developerguide/tagging-keys.html) in the *AWS Key Management Service Developer Guide* . For information about tags in CloudFormation, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) ." + "Tags": "Assigns one or more tags to the replica key.\n\n> Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see [ABAC for AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/abac.html) in the *AWS Key Management Service Developer Guide* . \n\nFor information about tags in AWS KMS , see [Tagging keys](https://docs.aws.amazon.com/kms/latest/developerguide/tagging-keys.html) in the *AWS Key Management Service Developer Guide* . For information about tags in CloudFormation, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) ." } }, "AWS::KMS::ReplicaKey": { @@ -22636,14 +22758,14 @@ "KeyId": "The key ID of the replica key, such as `mrk-1234abcd12ab34cd56ef1234567890ab` .\n\nRelated multi-Region keys have the same key ID. For information about the key IDs of multi-Region keys, see [How multi-Region keys work](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html#mrk-how-it-works) in the *AWS Key Management Service Developer Guide* .", "Ref": "`Ref` returns the key ID, such as `mrk-1234abcd12ab34cd56ef1234567890ab` ." }, - "description": "The `AWS::KMS::ReplicaKey` resource specifies a multi-Region replica key that is based on a multi-Region primary key.\n\n*Multi-Region keys* are an AWS KMS feature that lets you create multiple interoperable KMS keys in different AWS Regions . Because these KMS keys have the same key ID, key material, and other metadata, you can use them to encrypt data in one AWS Region and decrypt it in a different AWS Region without making a cross-Region call or exposing the plaintext data. For more information, see [Using multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide* .\n\nA multi-Region *primary key* is a fully functional symmetric or asymmetric KMS key that is also the model for replica keys in other AWS Regions . To create a multi-Region primary key, add an [AWS::KMS::Key](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-key.html) resource to your CloudFormation stack. Set its `MultiRegion` property to true.\n\nA multi-Region *replica key* is a fully functional symmetric or asymmetric KMS key that has the same key ID and key material as a multi-Region primary key, but is located in a different AWS Region of the same AWS partition. There can be multiple replicas of a primary key, but each must be in a different AWS Region .\n\nA primary key and its replicas have the same key ID and key material. They also have the same key spec, key usage, key material origin, and automatic key rotation status. These properties are known as *shared properties* . If they change, AWS KMS synchronizes the change to all related multi-Region keys. All other properties of a replica key can differ, including its key policy, tags, aliases, and key state. AWS KMS does not synchronize these properties.", + "description": "The `AWS::KMS::ReplicaKey` resource specifies a multi-Region replica key that is based on a multi-Region primary key.\n\n*Multi-Region keys* are an AWS KMS feature that lets you create multiple interoperable KMS keys in different AWS Regions . Because these KMS keys have the same key ID, key material, and other metadata, you can use them to encrypt data in one AWS Region and decrypt it in a different AWS Region without making a cross-Region call or exposing the plaintext data. For more information, see [Multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide* .\n\nA multi-Region *primary key* is a fully functional symmetric or asymmetric KMS key that is also the model for replica keys in other AWS Regions . To create a multi-Region primary key, add an [AWS::KMS::Key](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-key.html) resource to your CloudFormation stack. Set its `MultiRegion` property to true.\n\nA multi-Region *replica key* is a fully functional symmetric or asymmetric KMS key that has the same key ID and key material as a multi-Region primary key, but is located in a different AWS Region of the same AWS partition. There can be multiple replicas of a primary key, but each must be in a different AWS Region .\n\nA primary key and its replicas have the same key ID and key material. They also have the same key spec, key usage, key material origin, and automatic key rotation status. These properties are known as *shared properties* . If they change, AWS KMS synchronizes the change to all related multi-Region keys. All other properties of a replica key can differ, including its key policy, tags, aliases, and key state. AWS KMS does not synchronize these properties.\n\n*Regions*\n\nAWS KMS CloudFormation resources are supported in all Regions in which AWS CloudFormation is supported. However, in the (ap-southeast-3), you cannot use a CloudFormation template to create or manage multi-Region KMS keys (primary or replica).", "properties": { "Description": "A description of the KMS key.\n\nThe default value is an empty string (no description).\n\nThe description is not a shared property of multi-Region keys. You can specify the same description or a different description for each key in a set of related multi-Region keys. AWS Key Management Service does not synchronize this property.", "Enabled": "Specifies whether the replica key is enabled. Disabled KMS keys cannot be used in cryptographic operations.\n\nWhen `Enabled` is `true` , the *key state* of the KMS key is `Enabled` . When `Enabled` is `false` , the key state of the KMS key is `Disabled` . The default value is `true` .\n\nThe actual key state of the replica might be affected by actions taken outside of CloudFormation, such as running the [EnableKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_EnableKey.html) , [DisableKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_DisableKey.html) , or [ScheduleKeyDeletion](https://docs.aws.amazon.com/kms/latest/APIReference/API_ScheduleKeyDeletion.html) operations. Also, while the replica key is being created, its key state is `Creating` . When the process is complete, the key state of the replica key changes to `Enabled` .\n\nFor information about the key states of a KMS key, see [Key state: Effect on your KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/key-state.html) in the *AWS Key Management Service Developer Guide* .", "KeyPolicy": "The key policy that authorizes use of the replica key.\n\nThe key policy is not a shared property of multi-Region keys. You can specify the same key policy or a different key policy for each key in a set of related multi-Region keys. AWS KMS does not synchronize this property.\n\nThe key policy must conform to the following rules.\n\n- The key policy must give the caller [PutKeyPolicy](https://docs.aws.amazon.com/kms/latest/APIReference/API_PutKeyPolicy.html) permission on the KMS key. This reduces the risk that the KMS key becomes unmanageable. For more information, refer to the scenario in the [Default key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default-allow-root-enable-iam) section of the **AWS Key Management Service Developer Guide** .\n- Each statement in the key policy must contain one or more principals. The principals in the key policy must exist and be visible to AWS KMS . When you create a new AWS principal (for example, an IAM user or role), you might need to enforce a delay before including the new principal in a key policy because the new principal might not be immediately visible to AWS KMS . For more information, see [Changes that I make are not always immediately visible](https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_eventual-consistency) in the *AWS Identity and Access Management User Guide* .\n- The key policy size limit is 32 kilobytes (32768 bytes).\n\n*Minimum* : `1`\n\n*Maximum* : `32768`", "PendingWindowInDays": "Specifies the number of days in the waiting period before AWS KMS deletes a replica key that has been removed from a CloudFormation stack. Enter a value between 7 and 30 days. The default value is 30 days.\n\nWhen you remove a replica key from a CloudFormation stack, AWS KMS schedules the replica key for deletion and starts the mandatory waiting period. The `PendingWindowInDays` property determines the length of waiting period. During the waiting period, the key state of replica key is `Pending Deletion` , which prevents it from being used in cryptographic operations. When the waiting period expires, AWS KMS permanently deletes the replica key.\n\nYou cannot use a CloudFormation template to cancel deletion of the replica after you remove it from the stack, regardless of the waiting period. However, if you specify a replica key in your template that is based on the same primary key as the original replica key, CloudFormation creates a new replica key with the same key ID, key material, and other shared properties of the original replica key. This new replica key can decrypt ciphertext that was encrypted under the original replica key, or any related multi-Region key.\n\nFor detailed information about deleting multi-Region keys, see [Deleting multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-delete.html) in the *AWS Key Management Service Developer Guide* .\n\nFor information about the `PendingDeletion` key state, see [Key state: Effect on your KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/key-state.html) in the *AWS Key Management Service Developer Guide* . For more information about deleting KMS keys, see the [ScheduleKeyDeletion](https://docs.aws.amazon.com/kms/latest/APIReference/API_ScheduleKeyDeletion.html) operation in the *AWS Key Management Service API Reference* and [Deleting KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html) in the *AWS Key Management Service Developer Guide* .\n\n*Minimum* : 7\n\n*Maximum* : 30", "PrimaryKeyArn": "Specifies the multi-Region primary key to replicate. The primary key must be in a different AWS Region of the same AWS partition. You can create only one replica of a given primary key in each AWS Region .\n\n> If you change the `PrimaryKeyArn` value of a replica key, the existing replica key is scheduled for deletion and a new replica key is created based on the specified primary key. While it is scheduled for deletion, the existing replica key becomes unusable. You can cancel the scheduled deletion of the key outside of CloudFormation.\n> \n> However, if you inadvertently delete a replica key, you can decrypt ciphertext encrypted by that replica key by using any related multi-Region key. If necessary, you can recreate the replica in the same Region after the previous one is completely deleted. For details, see [Deleting multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-delete.html) in the *AWS Key Management Service Developer Guide* \n\nSpecify the key ARN of an existing multi-Region primary key. For example, `arn:aws:kms:us-east-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab` .", - "Tags": "Assigns one or more tags to the replica key.\n\n> Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see [Using ABAC in AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/abac.html) in the *AWS Key Management Service Developer Guide* . \n\nTags are not a shared property of multi-Region keys. You can specify the same tags or different tags for each key in a set of related multi-Region keys. AWS KMS does not synchronize this property.\n\nEach tag consists of a tag key and a tag value. Both the tag key and the tag value are required, but the tag value can be an empty (null) string. You cannot have more than one tag on a KMS key with the same tag key. If you specify an existing tag key with a different tag value, AWS KMS replaces the current tag value with the specified one.\n\nWhen you assign tags to an AWS resource, AWS generates a cost allocation report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details, see [Tagging keys](https://docs.aws.amazon.com/kms/latest/developerguide/tagging-keys.html) ." + "Tags": "Assigns one or more tags to the replica key.\n\n> Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see [ABAC for AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/abac.html) in the *AWS Key Management Service Developer Guide* . \n\nTags are not a shared property of multi-Region keys. You can specify the same tags or different tags for each key in a set of related multi-Region keys. AWS KMS does not synchronize this property.\n\nEach tag consists of a tag key and a tag value. Both the tag key and the tag value are required, but the tag value can be an empty (null) string. You cannot have more than one tag on a KMS key with the same tag key. If you specify an existing tag key with a different tag value, AWS KMS replaces the current tag value with the specified one.\n\nWhen you assign tags to an AWS resource, AWS generates a cost allocation report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details, see [Tagging keys](https://docs.aws.amazon.com/kms/latest/developerguide/tagging-keys.html) ." } }, "AWS::Kendra::DataSource": { @@ -23051,7 +23173,7 @@ }, "AWS::Kendra::DataSource.WebCrawlerConfiguration": { "attributes": {}, - "description": "", + "description": "Provides the configuration information required for Amazon Kendra Web Crawler.", "properties": { "AuthenticationConfiguration": "Provides configuration information required to connect to websites using authentication.\n\nYou can connect to websites using basic authentication of user name and password.\n\nYou must provide the website host name and port number. For example, the host name of https://a.example.com/page1.html is \"a.example.com\" and the port is 443, the standard port for HTTPS. You use a secret in [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) to store your authentication credentials.", "CrawlDepth": "Specifies the number of levels in a website that you want to crawl.\n\nThe first level begins from the website seed or starting point URL. For example, if a website has 3 levels \u2013 index level (i.e. seed in this example), sections level, and subsections level \u2013 and you are only interested in crawling information up to the sections level (i.e. levels 0-1), you can set your depth to 1.\n\nThe default crawl depth is set to 2.", @@ -24393,7 +24515,7 @@ "attributes": {}, "description": "A structure for the resource.", "properties": { - "DataLocationResource": "Currently not supported by AWS CloudFormation .", + "DataLocationResource": "A structure for a data location object where permissions are granted or revoked.", "DatabaseResource": "A structure for the database object.", "TableResource": "A structure for the table object. A table is a metadata definition that represents your data. You can Grant and Revoke table privileges to a principal.", "TableWithColumnsResource": "Currently not supported by AWS CloudFormation ." @@ -24406,12 +24528,12 @@ "CatalogId": "", "DatabaseName": "The name of the database for the table. Unique to a Data Catalog. A database is a set of associated table definitions organized into a logical group. You can Grant and Revoke database privileges to a principal.", "Name": "The name of the table.", - "TableWildcard": "" + "TableWildcard": "An empty object representing all tables under a database. If this field is specified instead of the `Name` field, all tables under `DatabaseName` will have permission changes applied." } }, "AWS::LakeFormation::Permissions.TableWildcard": { "attributes": {}, - "description": "", + "description": "A wildcard object representing every table under a database.", "properties": {} }, "AWS::LakeFormation::Permissions.TableWithColumnsResource": { @@ -25433,6 +25555,101 @@ "GetObject": "Specifies the anonymous access to all objects in a bucket.\n\nThe following options can be specified:\n\n- `public` - Sets all objects in the bucket to public (read-only), making them readable by everyone on the internet.\n\nIf the `GetObject` value is set to `public` , then all objects in the bucket default to public regardless of the `allowPublicOverrides` value.\n- `private` - Sets all objects in the bucket to private, making them readable only by you and anyone that you grant access to.\n\nIf the `GetObject` value is set to `private` , and the `allowPublicOverrides` value is set to `true` , then all objects in the bucket default to private unless they are configured with a `public-read` ACL. Individual objects with a `public-read` ACL are readable by everyone on the internet." } }, + "AWS::Lightsail::Certificate": { + "attributes": { + "CertificateArn": "The Amazon Resource Name (ARN) of the certificate.", + "Ref": "", + "Status": "The validation status of the certificate." + }, + "description": "The `AWS::Lightsail::Certificate` resource specifies an SSL/TLS certificate that you can use with a content delivery network (CDN) distribution and a container service.\n\n> For information about certificates that you can use with a load balancer, see [AWS::Lightsail::LoadBalancerTlsCertificate](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-loadbalancertlscertificate.html) .", + "properties": { + "CertificateName": "The name of the certificate.", + "DomainName": "The domain name of the certificate.", + "SubjectAlternativeNames": "An array of strings that specify the alternate domains (such as `example.org` ) and subdomains (such as `blog.example.com` ) of the certificate.", + "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) in the *AWS CloudFormation User Guide* .\n\n> The `Value` of `Tags` is optional for Lightsail resources." + } + }, + "AWS::Lightsail::Container": { + "attributes": { + "ContainerArn": "The Amazon Resource Name (ARN) of the container.", + "Ref": "", + "Url": "The publicly accessible URL of the container service.\n\nIf no public endpoint is specified in the current deployment, this URL returns a 404 response." + }, + "description": "The `AWS::Lightsail::Container` resource specifies a container service.\n\nA Lightsail container service is a compute resource to which you can deploy containers.", + "properties": { + "ContainerServiceDeployment": "An object that describes the current container deployment of the container service.", + "IsDisabled": "A Boolean value indicating whether the container service is disabled.", + "Power": "The power specification of the container service.\n\nThe power specifies the amount of RAM, the number of vCPUs, and the base price of the container service.", + "PublicDomainNames": "The public domain name of the container service, such as `example.com` and `www.example.com` .\n\nYou can specify up to four public domain names for a container service. The domain names that you specify are used when you create a deployment with a container that is configured as the public endpoint of your container service.\n\nIf you don't specify public domain names, then you can use the default domain of the container service.\n\n> You must create and validate an SSL/TLS certificate before you can use public domain names with your container service. Use the [AWS::Lightsail::Certificate](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-certificate.html) resource to create a certificate for the public domain names that you want to use with your container service.", + "Scale": "The scale specification of the container service.\n\nThe scale specifies the allocated compute nodes of the container service.", + "ServiceName": "The name of the container service.", + "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) in the *AWS CloudFormation User Guide* .\n\n> The `Value` of `Tags` is optional for Lightsail resources." + } + }, + "AWS::Lightsail::Container.Container": { + "attributes": {}, + "description": "`Container` is a property of the [ContainerServiceDeployment](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-containerservicedeployment.html) property. It describes the settings of a container that will be launched, or that is launched, to an Amazon Lightsail container service.", + "properties": { + "Command": "The launch command for the container.", + "ContainerName": "The name of the container.", + "Environment": "The environment variables of the container.", + "Image": "The name of the image used for the container.\n\nContainer images that are sourced from (registered and stored on) your container service start with a colon ( `:` ). For example, if your container service name is `container-service-1` , the container image label is `mystaticsite` , and you want to use the third version ( `3` ) of the registered container image, then you should specify `:container-service-1.mystaticsite.3` . To use the latest version of a container image, specify `latest` instead of a version number (for example, `:container-service-1.mystaticsite.latest` ). Your container service will automatically use the highest numbered version of the registered container image.\n\nContainer images that are sourced from a public registry like Docker Hub don\u2019t start with a colon. For example, `nginx:latest` or `nginx` .", + "Ports": "An object that describes the open firewall ports and protocols of the container." + } + }, + "AWS::Lightsail::Container.ContainerServiceDeployment": { + "attributes": {}, + "description": "`ContainerServiceDeployment` is a property of the [AWS::Lightsail::Container](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html) resource. It describes a container deployment configuration of a container service.\n\nA deployment specifies the settings, such as the ports and launch command, of containers that are deployed to your container service.", + "properties": { + "Containers": "An object that describes the configuration for the containers of the deployment.", + "PublicEndpoint": "An object that describes the endpoint of the deployment." + } + }, + "AWS::Lightsail::Container.EnvironmentVariable": { + "attributes": {}, + "description": "`EnvironmentVariable` is a property of the [Container](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html) property. It describes the environment variables of a container on a container service which are key-value parameters that provide dynamic configuration of the application or script run by the container.", + "properties": { + "Value": "The environment variable value.", + "Variable": "The environment variable key." + } + }, + "AWS::Lightsail::Container.HealthCheckConfig": { + "attributes": {}, + "description": "`HealthCheckConfig` is a property of the [PublicEndpoint](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicendpoint.html) property. It describes the healthcheck configuration of a container deployment on a container service.", + "properties": { + "HealthyThreshold": "The number of consecutive health check successes required before moving the container to the `Healthy` state. The default value is `2` .", + "IntervalSeconds": "The approximate interval, in seconds, between health checks of an individual container. You can specify between `5` and `300` seconds. The default value is `5` .", + "Path": "The path on the container on which to perform the health check. The default value is `/` .", + "SuccessCodes": "The HTTP codes to use when checking for a successful response from a container. You can specify values between `200` and `499` . You can specify multiple values (for example, `200,202` ) or a range of values (for example, `200-299` ).", + "TimeoutSeconds": "The amount of time, in seconds, during which no response means a failed health check. You can specify between `2` and `60` seconds. The default value is `2` .", + "UnhealthyThreshold": "The number of consecutive health check failures required before moving the container to the `Unhealthy` state. The default value is `2` ." + } + }, + "AWS::Lightsail::Container.PortInfo": { + "attributes": {}, + "description": "`PortInfo` is a property of the [Container](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html) property. It describes the ports to open and the protocols to use for a container on a Amazon Lightsail container service.", + "properties": { + "Port": "The open firewall ports of the container.", + "Protocol": "The protocol name for the open ports.\n\n*Allowed values* : `HTTP` | `HTTPS` | `TCP` | `UDP`" + } + }, + "AWS::Lightsail::Container.PublicDomainName": { + "attributes": {}, + "description": "`PublicDomainName` is a property of the [AWS::Lightsail::Container](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html) resource. It describes the public domain names to use with a container service, such as `example.com` and `www.example.com` . It also describes the certificates to use with a container service.", + "properties": { + "CertificateName": "The name of the certificate for the public domains.", + "DomainNames": "The public domain names to use with the container service." + } + }, + "AWS::Lightsail::Container.PublicEndpoint": { + "attributes": {}, + "description": "`PublicEndpoint` is a property of the [ContainerServiceDeployment](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-containerservicedeployment.html) property. It describes describes the settings of the public endpoint of a container on a container service.", + "properties": { + "ContainerName": "The name of the container entry of the deployment that the endpoint configuration applies to.", + "ContainerPort": "The port of the specified container to which traffic is forwarded to.", + "HealthCheckConfig": "An object that describes the health check configuration of the container." + } + }, "AWS::Lightsail::Database": { "attributes": { "DatabaseArn": "The Amazon Resource Name (ARN) of the database (for example, `arn:aws:lightsail:us-east-2:123456789101:RelationalDatabase/244ad76f-8aad-4741-809f-12345EXAMPLE` ).", @@ -25509,6 +25726,89 @@ "SnapshotTimeOfDay": "The daily time when an automatic snapshot will be created.\n\nConstraints:\n\n- Must be in `HH:00` format, and in an hourly increment.\n- Specified in Coordinated Universal Time (UTC).\n- The snapshot will be automatically created between the time specified and up to 45 minutes after." } }, + "AWS::Lightsail::Distribution": { + "attributes": { + "AbleToUpdateBundle": "Indicates whether you can update the distribution\u2019s current bundle to another bundle.", + "DistributionArn": "The Amazon Resource Name (ARN) of the distribution.", + "Ref": "", + "Status": "The status of the distribution." + }, + "description": "The `AWS::Lightsail::Distribution` resource specifies a content delivery network (CDN) distribution. You can create distributions only in the `us-east-1` AWS Region.\n\nA distribution is a globally distributed network of caching servers that improve the performance of your website or web application hosted on a Lightsail instance, static content hosted on a Lightsail bucket, or through a Lightsail load balancer.", + "properties": { + "BundleId": "The ID of the bundle applied to the distribution.", + "CacheBehaviorSettings": "An object that describes the cache behavior settings of the distribution.", + "CacheBehaviors": "An array of objects that describe the per-path cache behavior of the distribution.", + "CertificateName": "The name of the SSL/TLS certificate attached to the distribution.", + "DefaultCacheBehavior": "An object that describes the default cache behavior of the distribution.", + "DistributionName": "The name of the distribution", + "IpAddressType": "The IP address type of the distribution.\n\nThe possible values are `ipv4` for IPv4 only, and `dualstack` for IPv4 and IPv6.", + "IsEnabled": "A Boolean value indicating whether the distribution is enabled.", + "Origin": "An object that describes the origin resource of the distribution, such as a Lightsail instance, bucket, or load balancer.\n\nThe distribution pulls, caches, and serves content from the origin.", + "Tags": "An array of key-value pairs to apply to this resource.\n\nFor more information, see [Tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) in the *AWS CloudFormation User Guide* .\n\n> The `Value` of `Tags` is optional for Lightsail resources." + } + }, + "AWS::Lightsail::Distribution.CacheBehavior": { + "attributes": {}, + "description": "`CacheBehavior` is a property of the [AWS::Lightsail::Distribution](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html) resource. It describes the default cache behavior of an Amazon Lightsail content delivery network (CDN) distribution.", + "properties": { + "Behavior": "The cache behavior of the distribution.\n\nThe following cache behaviors can be specified:\n\n- *`cache`* - This option is best for static sites. When specified, your distribution caches and serves your entire website as static content. This behavior is ideal for websites with static content that doesn't change depending on who views it, or for websites that don't use cookies, headers, or query strings to personalize content.\n- *`dont-cache`* - This option is best for sites that serve a mix of static and dynamic content. When specified, your distribution caches and serves only the content that is specified in the distribution\u2019s `CacheBehaviorPerPath` parameter. This behavior is ideal for websites or web applications that use cookies, headers, and query strings to personalize content for individual users." + } + }, + "AWS::Lightsail::Distribution.CacheBehaviorPerPath": { + "attributes": {}, + "description": "`CacheBehaviorPerPath` is a property of the [AWS::Lightsail::Distribution](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html) resource. It describes the per-path cache behavior of an Amazon Lightsail content delivery network (CDN) distribution.\n\nUse a per-path cache behavior to override the default cache behavior of a distribution, or to add an exception to it. For example, if you set the `CacheBehavior` to `cache` , you can use a per-path cache behavior to specify a directory, file, or file type that your distribution will cache. If you don\u2019t want your distribution to cache a specified directory, file, or file type, set the per-path cache behavior to `dont-cache` .", + "properties": { + "Behavior": "The cache behavior for the specified path.\n\nYou can specify one of the following per-path cache behaviors:\n\n- *`cache`* - This behavior caches the specified path.\n- *`dont-cache`* - This behavior doesn't cache the specified path.", + "Path": "The path to a directory or file to cache, or not cache. Use an asterisk symbol to specify wildcard directories ( `path/to/assets/*` ), and file types ( `*.html` , `*jpg` , `*js` ). Directories and file paths are case-sensitive.\n\nExamples:\n\n- Specify the following to cache all files in the document root of an Apache web server running on a instance.\n\n`var/www/html/`\n- Specify the following file to cache only the index page in the document root of an Apache web server.\n\n`var/www/html/index.html`\n- Specify the following to cache only the .html files in the document root of an Apache web server.\n\n`var/www/html/*.html`\n- Specify the following to cache only the .jpg, .png, and .gif files in the images sub-directory of the document root of an Apache web server.\n\n`var/www/html/images/*.jpg`\n\n`var/www/html/images/*.png`\n\n`var/www/html/images/*.gif`\n\nSpecify the following to cache all files in the images subdirectory of the document root of an Apache web server.\n\n`var/www/html/images/`" + } + }, + "AWS::Lightsail::Distribution.CacheSettings": { + "attributes": {}, + "description": "`CacheSettings` is a property of the [AWS::Lightsail::Distribution](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html) resource. It describes the cache settings of an Amazon Lightsail content delivery network (CDN) distribution.\n\nThese settings apply only to your distribution\u2019s `CacheBehaviors` that have a `Behavior` of `cache` . This includes the `DefaultCacheBehavior` .", + "properties": { + "AllowedHTTPMethods": "The HTTP methods that are processed and forwarded to the distribution's origin.\n\nYou can specify the following options:\n\n- `GET,HEAD` - The distribution forwards the `GET` and `HEAD` methods.\n- `GET,HEAD,OPTIONS` - The distribution forwards the `GET` , `HEAD` , and `OPTIONS` methods.\n- `GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE` - The distribution forwards the `GET` , `HEAD` , `OPTIONS` , `PUT` , `PATCH` , `POST` , and `DELETE` methods.\n\nIf you specify `GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE` , you might need to restrict access to your distribution's origin so users can't perform operations that you don't want them to. For example, you might not want users to have permission to delete objects from your origin.", + "CachedHTTPMethods": "The HTTP method responses that are cached by your distribution.\n\nYou can specify the following options:\n\n- `GET,HEAD` - The distribution caches responses to the `GET` and `HEAD` methods.\n- `GET,HEAD,OPTIONS` - The distribution caches responses to the `GET` , `HEAD` , and `OPTIONS` methods.", + "DefaultTTL": "The default amount of time that objects stay in the distribution's cache before the distribution forwards another request to the origin to determine whether the content has been updated.\n\n> The value specified applies only when the origin does not add HTTP headers such as `Cache-Control max-age` , `Cache-Control s-maxage` , and `Expires` to objects.", + "ForwardedCookies": "An object that describes the cookies that are forwarded to the origin. Your content is cached based on the cookies that are forwarded.", + "ForwardedHeaders": "An object that describes the headers that are forwarded to the origin. Your content is cached based on the headers that are forwarded.", + "ForwardedQueryStrings": "An object that describes the query strings that are forwarded to the origin. Your content is cached based on the query strings that are forwarded.", + "MaximumTTL": "The maximum amount of time that objects stay in the distribution's cache before the distribution forwards another request to the origin to determine whether the object has been updated.\n\nThe value specified applies only when the origin adds HTTP headers such as `Cache-Control max-age` , `Cache-Control s-maxage` , and `Expires` to objects.", + "MinimumTTL": "The minimum amount of time that objects stay in the distribution's cache before the distribution forwards another request to the origin to determine whether the object has been updated.\n\nA value of `0` must be specified for `minimumTTL` if the distribution is configured to forward all headers to the origin." + } + }, + "AWS::Lightsail::Distribution.CookieObject": { + "attributes": {}, + "description": "`CookieObject` is a property of the [CacheSettings](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html) property. It describes whether an Amazon Lightsail content delivery network (CDN) distribution forwards cookies to the origin and, if so, which ones.\n\nFor the cookies that you specify, your distribution caches separate versions of the specified content based on the cookie values in viewer requests.", + "properties": { + "CookiesAllowList": "The specific cookies to forward to your distribution's origin.", + "Option": "Specifies which cookies to forward to the distribution's origin for a cache behavior.\n\nUse one of the following configurations for your distribution:\n\n- *`all`* - Forwards all cookies to your origin.\n- *`none`* - Doesn\u2019t forward cookies to your origin.\n- *`allow-list`* - Forwards only the cookies that you specify using the `CookiesAllowList` parameter." + } + }, + "AWS::Lightsail::Distribution.HeaderObject": { + "attributes": {}, + "description": "`HeaderObject` is a property of the [CacheSettings](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html) property. It describes the request headers used by your distribution, which caches your content based on the request headers.\n\nFor the headers that you specify, your distribution caches separate versions of the specified content based on the header values in viewer requests. For example, suppose that viewer requests for logo.jpg contain a custom product header that has a value of either acme or apex. Also, suppose that you configure your distribution to cache your content based on values in the product header. Your distribution forwards the product header to the origin and caches the response from the origin once for each header value.", + "properties": { + "HeadersAllowList": "The specific headers to forward to your distribution's origin.", + "Option": "The headers that you want your distribution to forward to your origin. Your distribution caches your content based on these headers.\n\nUse one of the following configurations for your distribution:\n\n- *`all`* - Forwards all headers to your origin..\n- *`none`* - Forwards only the default headers.\n- *`allow-list`* - Forwards only the headers that you specify using the `HeadersAllowList` parameter." + } + }, + "AWS::Lightsail::Distribution.InputOrigin": { + "attributes": {}, + "description": "`InputOrigin` is a property of the [AWS::Lightsail::Distribution](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html) resource. It describes the origin resource of an Amazon Lightsail content delivery network (CDN) distribution.\n\nAn origin can be a instance, bucket, or load balancer. A distribution pulls content from an origin, caches it, and serves it to viewers through a worldwide network of edge servers.", + "properties": { + "Name": "The name of the origin resource.", + "ProtocolPolicy": "The protocol that your Amazon Lightsail distribution uses when establishing a connection with your origin to pull content.", + "RegionName": "The AWS Region name of the origin resource." + } + }, + "AWS::Lightsail::Distribution.QueryStringObject": { + "attributes": {}, + "description": "`QueryStringObject` is a property of the [CacheSettings](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html) property. It describes the query string parameters that an Amazon Lightsail content delivery network (CDN) distribution to bases caching on.\n\nFor the query strings that you specify, your distribution caches separate versions of the specified content based on the query string values in viewer requests.", + "properties": { + "Option": "Indicates whether the distribution forwards and caches based on query strings.", + "QueryStringsAllowList": "The specific query strings that the distribution forwards to the origin.\n\nYour distribution caches content based on the specified query strings.\n\nIf the `option` parameter is true, then your distribution forwards all query strings, regardless of what you specify using the `QueryStringsAllowList` parameter." + } + }, "AWS::Lightsail::Instance": { "attributes": { "Hardware.CpuCount": "The number of vCPUs the instance has.", @@ -25685,8 +25985,8 @@ "CollectionName": "The name for the geofence collection.", "Description": "An optional description for the geofence collection.", "KmsKeyId": "A key identifier for an [AWS KMS customer managed key](https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html) . Enter a key ID, key ARN, alias name, or alias ARN.", - "PricingPlan": "Specifies the pricing plan for the geofence collection.\n\nFor additional details and restrictions on each pricing plan option, see the [Amazon Location Service pricing page](https://docs.aws.amazon.com/location/pricing/) .", - "PricingPlanDataSource": "Specifies the data provider for the geofence collection.\n\n- Required value for the following pricing plans: `MobileAssetTracking` | `MobileAssetManagement`\n\nFor more information about [Data Providers](https://docs.aws.amazon.com/location/data-providers/) , and [Pricing plans](https://docs.aws.amazon.com/location/pricing/) , see the Amazon Location Service product page.\n\n> Amazon Location Service only uses `PricingPlanDataSource` to calculate billing for your geofence collection. Your data will not be shared with the data provider, and will remain in your AWS account or region unless you move it. \n\nValid Values: `Esri` | `Here`" + "PricingPlan": "No longer used. If included, the only allowed value is `RequestBasedUsage` .\n\n*Allowed Values* : `RequestBasedUsage`", + "PricingPlanDataSource": "This parameter is no longer used." } }, "AWS::Location::Map": { @@ -25703,7 +26003,7 @@ "Configuration": "Specifies the map style selected from an available data provider.", "Description": "An optional description for the map resource.", "MapName": "The name for the map resource.\n\nRequirements:\n\n- Must contain only alphanumeric characters (A\u2013Z, a\u2013z, 0\u20139), hyphens (-), periods (.), and underscores (_).\n- Must be a unique map resource name.\n- No spaces allowed. For example, `ExampleMap` .", - "PricingPlan": "Specifies the pricing plan for your map resource.\n\nFor additional details and restrictions on each pricing plan option, see the [Amazon Location Service pricing page](https://docs.aws.amazon.com/location/pricing/) ." + "PricingPlan": "No longer used. If included, the only allowed value is `RequestBasedUsage` .\n\n*Allowed Values* : `RequestBasedUsage`" } }, "AWS::Location::Map.MapConfiguration": { @@ -25727,7 +26027,7 @@ "DataSourceConfiguration": "Specifies the data storage option for requesting Places.", "Description": "The optional description for the place index resource.", "IndexName": "The name of the place index resource.\n\nRequirements:\n\n- Contain only alphanumeric characters (A\u2013Z, a\u2013z, 0\u20139), hyphens (-), periods (.), and underscores (_).\n- Must be a unique place index resource name.\n- No spaces allowed. For example, `ExamplePlaceIndex` .", - "PricingPlan": "Specifies the pricing plan for your place index resource.\n\nFor additional details and restrictions on each pricing plan option, see the [Amazon Location Service pricing page](https://docs.aws.amazon.com/location/pricing/) ." + "PricingPlan": "No longer used. If included, the only allowed value is `RequestBasedUsage` .\n\n*Allowed Values* : `RequestBasedUsage`" } }, "AWS::Location::PlaceIndex.DataSourceConfiguration": { @@ -25750,7 +26050,7 @@ "CalculatorName": "The name of the route calculator resource.\n\nRequirements:\n\n- Can use alphanumeric characters (A\u2013Z, a\u2013z, 0\u20139) , hyphens (-), periods (.), and underscores (_).\n- Must be a unique route calculator resource name.\n- No spaces allowed. For example, `ExampleRouteCalculator` .", "DataSource": "Specifies the data provider of traffic and road network data.\n\n> This field is case-sensitive. Enter the valid values as shown. For example, entering `HERE` returns an error. \n\nValid values include:\n\n- `Esri`\n- `Here`\n\nFor more information about data providers, see the [Amazon Location Service data providers page](https://docs.aws.amazon.com/location/latest/developerguide/what-is-data-provider.html) .", "Description": "The optional description for the route calculator resource.", - "PricingPlan": "Specifies the pricing plan for your route calculator resource.\n\nFor additional details and restrictions on each pricing plan option, see the [Amazon Location Service pricing page](https://docs.aws.amazon.com/location/pricing/) ." + "PricingPlan": "No longer used. If included, the only allowed value is `RequestBasedUsage` .\n\n*Allowed Values* : `RequestBasedUsage`" } }, "AWS::Location::Tracker": { @@ -25765,9 +26065,9 @@ "properties": { "Description": "An optional description for the tracker resource.", "KmsKeyId": "A key identifier for an [AWS KMS customer managed key](https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html) . Enter a key ID, key ARN, alias name, or alias ARN.", - "PositionFiltering": "Specifies the position filtering for the tracker resource.\n\nValid values:\n\n- `TimeBased` - Location updates are evaluated against linked geofence collections, but not every location update is stored. If your update frequency is more often than 30 seconds, only one update per 30 seconds is stored for each unique device ID.\n- `DistanceBased` - If the device has moved less than 30 m (98.4 ft), location updates are ignored. Location updates within this area are neither evaluated against linked geofence collections, nor stored. This helps control costs by reducing the number of geofence evaluations and historical device positions to paginate through. Distance-based filtering can also reduce the effects of GPS noise when displaying device trajectories on a map.\n\nThis field is optional. If not specified, the default value is `TimeBased` .", - "PricingPlan": "Specifies the pricing plan for the tracker resource.\n\nFor additional details and restrictions on each pricing plan option, see the [Amazon Location Service pricing page](https://docs.aws.amazon.com/location/pricing/) .", - "PricingPlanDataSource": "Specifies the data provider for the tracker resource.\n\n- Required value for the following pricing plans: `MobileAssetTracking` | `MobileAssetManagement`\n\nFor more information about [Data Providers](https://docs.aws.amazon.com/location/data-providers/) , and [Pricing plans](https://docs.aws.amazon.com/location/pricing/) , see the Amazon Location Service product page.\n\n> Amazon Location Service only uses `PricingPlanDataSource` to calculate billing for your tracker resource. Your data will not be shared with the data provider, and will remain in your AWS account or region unless you move it. \n\nValid Values: `Esri` | `Here`", + "PositionFiltering": "Specifies the position filtering for the tracker resource.\n\nValid values:\n\n- `TimeBased` - Location updates are evaluated against linked geofence collections, but not every location update is stored. If your update frequency is more often than 30 seconds, only one update per 30 seconds is stored for each unique device ID.\n- `DistanceBased` - If the device has moved less than 30 m (98.4 ft), location updates are ignored. Location updates within this area are neither evaluated against linked geofence collections, nor stored. This helps control costs by reducing the number of geofence evaluations and historical device positions to paginate through. Distance-based filtering can also reduce the effects of GPS noise when displaying device trajectories on a map.\n- `AccuracyBased` - If the device has moved less than the measured accuracy, location updates are ignored. For example, if two consecutive updates from a device have a horizontal accuracy of 5 m and 10 m, the second update is ignored if the device has moved less than 15 m. Ignored location updates are neither evaluated against linked geofence collections, nor stored. This can reduce the effects of GPS noise when displaying device trajectories on a map, and can help control your costs by reducing the number of geofence evaluations.\n\nThis field is optional. If not specified, the default value is `TimeBased` .", + "PricingPlan": "No longer used. If included, the only allowed value is `RequestBasedUsage` .", + "PricingPlanDataSource": "This parameter is no longer used.", "TrackerName": "The name for the tracker resource.\n\nRequirements:\n\n- Contain only alphanumeric characters (A-Z, a-z, 0-9) , hyphens (-), periods (.), and underscores (_).\n- Must be a unique tracker resource name.\n- No spaces allowed. For example, `ExampleTracker` ." } }, @@ -31370,7 +31670,7 @@ "LastUpdatedTime": "The time this dataset version was last updated.", "OutputColumns": "" }, - "description": "Creates a dataset.", + "description": "Creates a dataset. This operation doesn't support datasets that include uploaded files as a source.", "properties": { "AwsAccountId": "The AWS account ID.", "ColumnGroups": "Groupings of columns that work together in certain Amazon QuickSight features. Currently, only geospatial hierarchy is supported.", @@ -32079,7 +32379,7 @@ "ReadEndpoint.Address": "The reader endpoint for the DB cluster. For example: `mystack-mydbcluster-ro-123456789012.us-east-2.rds.amazonaws.com`", "Ref": "`Ref` returns the name of the DB cluster." }, - "description": "The `AWS::RDS::DBCluster` resource creates an Amazon Aurora DB cluster. For more information, see [Managing an Amazon Aurora DB Cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_Aurora.html) in the *Amazon Aurora User Guide* .\n\n> You can only create this resource in AWS Regions where Amazon Aurora is supported. \n\n*Updating DB clusters*\n\nWhen properties labeled \" *Update requires:* [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement) \" are updated, AWS CloudFormation first creates a replacement DB cluster, then changes references from other dependent resources to point to the replacement DB cluster, and finally deletes the old DB cluster.\n\n> We highly recommend that you take a snapshot of the database before updating the stack. If you don't, you lose the data when AWS CloudFormation replaces your DB cluster. To preserve your data, perform the following procedure:\n> \n> - Deactivate any applications that are using the DB cluster so that there's no activity on the DB instance.\n> - Create a snapshot of the DB cluster. For more information about creating DB snapshots, see [Creating a DB Cluster Snapshot](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_CreateSnapshotCluster.html) .\n> - If you want to restore your DB cluster using a DB cluster snapshot, modify the updated template with your DB cluster changes and add the `SnapshotIdentifier` property with the ID of the DB cluster snapshot that you want to use.\n> \n> After you restore a DB cluster with a `SnapshotIdentifier` property, you must specify the same `SnapshotIdentifier` property for any future updates to the DB cluster. When you specify this property for an update, the DB cluster is not restored from the DB cluster snapshot again, and the data in the database is not changed. However, if you don't specify the `SnapshotIdentifier` property, an empty DB cluster is created, and the original DB cluster is deleted. If you specify a property that is different from the previous snapshot restore property, a new DB cluster is restored from the specified `SnapshotIdentifier` property, and the original DB cluster is deleted.\n> - Update the stack. \n\nCurrently, when you are updating the stack for an Aurora Serverless DB cluster, you can't include changes to any other properties when you specify one of the following properties: `PreferredBackupWindow` , `PreferredMaintenanceWindow` , and `Port` . This limitation doesn't apply to provisioned DB clusters.\n\nFor more information about updating other properties of this resource, see `[ModifyDBCluster](https://docs.aws.amazon.com//AmazonRDS/latest/APIReference/API_ModifyDBCluster.html)` . For more information about updating stacks, see [AWS CloudFormation Stacks Updates](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html) .\n\n*Deleting DB clusters*\n\nThe default `DeletionPolicy` for `AWS::RDS::DBCluster` resources is `Snapshot` . For more information about how AWS CloudFormation deletes resources, see [DeletionPolicy Attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) .", + "description": "The `AWS::RDS::DBCluster` resource creates an Amazon Aurora DB cluster. For more information, see [Managing an Amazon Aurora DB Cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_Aurora.html) in the *Amazon Aurora User Guide* .\n\n> You can only create this resource in AWS Regions where Amazon Aurora is supported. \n\nThis topic covers the resource for Amazon Aurora DB clusters. For the documentation on the resource for Amazon RDS DB instances, see [AWS::RDS::DBInstance](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html) .\n\n*Updating DB clusters*\n\nWhen properties labeled \" *Update requires:* [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement) \" are updated, AWS CloudFormation first creates a replacement DB cluster, then changes references from other dependent resources to point to the replacement DB cluster, and finally deletes the old DB cluster.\n\n> We highly recommend that you take a snapshot of the database before updating the stack. If you don't, you lose the data when AWS CloudFormation replaces your DB cluster. To preserve your data, perform the following procedure:\n> \n> - Deactivate any applications that are using the DB cluster so that there's no activity on the DB instance.\n> - Create a snapshot of the DB cluster. For more information about creating DB snapshots, see [Creating a DB Cluster Snapshot](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_CreateSnapshotCluster.html) .\n> - If you want to restore your DB cluster using a DB cluster snapshot, modify the updated template with your DB cluster changes and add the `SnapshotIdentifier` property with the ID of the DB cluster snapshot that you want to use.\n> \n> After you restore a DB cluster with a `SnapshotIdentifier` property, you must specify the same `SnapshotIdentifier` property for any future updates to the DB cluster. When you specify this property for an update, the DB cluster is not restored from the DB cluster snapshot again, and the data in the database is not changed. However, if you don't specify the `SnapshotIdentifier` property, an empty DB cluster is created, and the original DB cluster is deleted. If you specify a property that is different from the previous snapshot restore property, a new DB cluster is restored from the specified `SnapshotIdentifier` property, and the original DB cluster is deleted.\n> - Update the stack. \n\nCurrently, when you are updating the stack for an Aurora Serverless DB cluster, you can't include changes to any other properties when you specify one of the following properties: `PreferredBackupWindow` , `PreferredMaintenanceWindow` , and `Port` . This limitation doesn't apply to provisioned DB clusters.\n\nFor more information about updating other properties of this resource, see `[ModifyDBCluster](https://docs.aws.amazon.com//AmazonRDS/latest/APIReference/API_ModifyDBCluster.html)` . For more information about updating stacks, see [AWS CloudFormation Stacks Updates](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html) .\n\n*Deleting DB clusters*\n\nThe default `DeletionPolicy` for `AWS::RDS::DBCluster` resources is `Snapshot` . For more information about how AWS CloudFormation deletes resources, see [DeletionPolicy Attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) .", "properties": { "AssociatedRoles": "Provides a list of the AWS Identity and Access Management (IAM) roles that are associated with the DB cluster. IAM roles that are associated with a DB cluster grant permission for the DB cluster to access other Amazon Web Services on your behalf.", "AvailabilityZones": "A list of Availability Zones (AZs) where instances in the DB cluster can be created. For information on AWS Regions and Availability Zones, see [Choosing the Regions and Availability Zones](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.RegionsAndAvailabilityZones.html) in the *Amazon Aurora User Guide* .", @@ -32101,7 +32401,7 @@ "KmsKeyId": "The Amazon Resource Name (ARN) of the AWS Key Management Service master key that is used to encrypt the database instances in the DB cluster, such as `arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef` . If you enable the `StorageEncrypted` property but don't specify this property, the default master key is used. If you specify this property, you must set the `StorageEncrypted` property to `true` .\n\nIf you specify the `SnapshotIdentifier` property, the `StorageEncrypted` property value is inherited from the snapshot, and if the DB cluster is encrypted, the specified `KmsKeyId` property is used.", "MasterUserPassword": "The master password for the DB instance.\n\n> If you specify the `SourceDBClusterIdentifier` or `SnapshotIdentifier` property, don't specify this property. The value is inherited from the source DB instance or snapshot.", "MasterUsername": "The name of the master user for the DB cluster.\n\n> If you specify the `SourceDBClusterIdentifier` or `SnapshotIdentifier` property, don't specify this property. The value is inherited from the source DB instance or snapshot.", - "Port": "The port number on which the DB instances in the DB cluster accept connections.\n\nDefault:\n\n- When `EngineMode` is `provisioned` , `3306` (for both Aurora MySQL and Aurora PostgreSQL)\n- When `EngineMode` is `serverless` :\n\n- `3306` when `Engine` is `aurora` or `aurora-mysql`\n- `5432` when `Engine` is `aurora-postgresql`", + "Port": "The port number on which the DB instances in the DB cluster accept connections.\n\nDefault:\n\n- When `EngineMode` is `provisioned` , `3306` (for both Aurora MySQL and Aurora PostgreSQL)\n- When `EngineMode` is `serverless` :\n\n- `3306` when `Engine` is `aurora` or `aurora-mysql`\n- `5432` when `Engine` is `aurora-postgresql`\n\n> The `No interruption` on update behavior only applies to DB clusters. If you are updating a DB instance, see [Port](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html#cfn-rds-dbinstance-port) for the AWS::RDS::DBInstance resource.", "PreferredBackupWindow": "The daily time range during which automated backups are created. For more information, see [Backup Window](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Managing.Backups.html#Aurora.Managing.Backups.BackupWindow) in the *Amazon Aurora User Guide.*\n\nConstraints:\n\n- Must be in the format `hh24:mi-hh24:mi` .\n- Must be in Universal Coordinated Time (UTC).\n- Must not conflict with the preferred maintenance window.\n- Must be at least 30 minutes.", "PreferredMaintenanceWindow": "The weekly time range during which system maintenance can occur, in Universal Coordinated Time (UTC).\n\nFormat: `ddd:hh24:mi-ddd:hh24:mi`\n\nThe default is a 30-minute window selected at random from an 8-hour block of time for each AWS Region, occurring on a random day of the week. To see the time blocks available, see [Adjusting the Preferred DB Cluster Maintenance Window](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_UpgradeDBInstance.Maintenance.html#AdjustingTheMaintenanceWindow.Aurora) in the *Amazon Aurora User Guide.*\n\nValid Days: Mon, Tue, Wed, Thu, Fri, Sat, Sun.\n\nConstraints: Minimum 30-minute window.", "ReplicationSourceIdentifier": "The Amazon Resource Name (ARN) of the source DB instance or DB cluster if this DB cluster is created as a read replica.", @@ -32152,13 +32452,13 @@ "Endpoint.Port": "The port number on which the database accepts connections. For example: `3306`", "Ref": "`Ref` returns the DB instance name." }, - "description": "The `AWS::RDS::DBInstance` resource creates an Amazon RDS DB instance.\n\nIf you import an existing DB instance, and the template configuration doesn't match the actual configuration of the DB instance, AWS CloudFormation applies the changes in the template during the import operation.\n\n> If a DB instance is deleted or replaced during an update, AWS CloudFormation deletes all automated snapshots. However, it retains manual DB snapshots. During an update that requires replacement, you can apply a stack policy to prevent DB instances from being replaced. For more information, see [Prevent Updates to Stack Resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html) . \n\n*Updating DB instances*\n\nWhen properties labeled \" *Update requires:* [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement) \" are updated, AWS CloudFormation first creates a replacement DB instance, then changes references from other dependent resources to point to the replacement DB instance, and finally deletes the old DB instance.\n\n> We highly recommend that you take a snapshot of the database before updating the stack. If you don't, you lose the data when AWS CloudFormation replaces your DB instance. To preserve your data, perform the following procedure:\n> \n> - Deactivate any applications that are using the DB instance so that there's no activity on the DB instance.\n> - Create a snapshot of the DB instance. For more information about creating DB snapshots, see [Creating a DB Snapshot](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateSnapshot.html) .\n> - If you want to restore your instance using a DB snapshot, modify the updated template with your DB instance changes and add the `DBSnapshotIdentifier` property with the ID of the DB snapshot that you want to use.\n> \n> After you restore a DB instance with a `DBSnapshotIdentifier` property, you must specify the same `DBSnapshotIdentifier` property for any future updates to the DB instance. When you specify this property for an update, the DB instance is not restored from the DB snapshot again, and the data in the database is not changed. However, if you don't specify the `DBSnapshotIdentifier` property, an empty DB instance is created, and the original DB instance is deleted. If you specify a property that is different from the previous snapshot restore property, a new DB instance is restored from the specified `DBSnapshotIdentifier` property, and the original DB instance is deleted.\n> - Update the stack. \n\nFor more information about updating other properties of this resource, see `[ModifyDBInstance](https://docs.aws.amazon.com//AmazonRDS/latest/APIReference/API_ModifyDBInstance.html)` . For more information about updating stacks, see [AWS CloudFormation Stacks Updates](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html) .\n\n*Deleting DB instances*\n\nFor DB instances that are part of an Aurora DB cluster, you can set a deletion policy for your DB instance to control how AWS CloudFormation handles the DB instance when the stack is deleted. For Amazon RDS DB instances, you can choose to *retain* the DB instance, to *delete* the DB instance, or to *create a snapshot* of the DB instance. The default AWS CloudFormation behavior depends on the `DBClusterIdentifier` property:\n\n- For `AWS::RDS::DBInstance` resources that don't specify the `DBClusterIdentifier` property, AWS CloudFormation saves a snapshot of the DB instance.\n- For `AWS::RDS::DBInstance` resources that do specify the `DBClusterIdentifier` property, AWS CloudFormation deletes the DB instance.\n\nFor more information, see [DeletionPolicy Attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) .", + "description": "The `AWS::RDS::DBInstance` resource creates an Amazon RDS DB instance.\n\nIf you import an existing DB instance, and the template configuration doesn't match the actual configuration of the DB instance, AWS CloudFormation applies the changes in the template during the import operation.\n\n> If a DB instance is deleted or replaced during an update, AWS CloudFormation deletes all automated snapshots. However, it retains manual DB snapshots. During an update that requires replacement, you can apply a stack policy to prevent DB instances from being replaced. For more information, see [Prevent Updates to Stack Resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html) . \n\nThis topic covers the resource for Amazon RDS DB instances. For the documentation on the resource for Amazon Aurora DB clusters, see [AWS::RDS::DBCluster](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html) .\n\n*Updating DB instances*\n\nWhen properties labeled \" *Update requires:* [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement) \" are updated, AWS CloudFormation first creates a replacement DB instance, then changes references from other dependent resources to point to the replacement DB instance, and finally deletes the old DB instance.\n\n> We highly recommend that you take a snapshot of the database before updating the stack. If you don't, you lose the data when AWS CloudFormation replaces your DB instance. To preserve your data, perform the following procedure:\n> \n> - Deactivate any applications that are using the DB instance so that there's no activity on the DB instance.\n> - Create a snapshot of the DB instance. For more information about creating DB snapshots, see [Creating a DB Snapshot](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateSnapshot.html) .\n> - If you want to restore your instance using a DB snapshot, modify the updated template with your DB instance changes and add the `DBSnapshotIdentifier` property with the ID of the DB snapshot that you want to use.\n> \n> After you restore a DB instance with a `DBSnapshotIdentifier` property, you must specify the same `DBSnapshotIdentifier` property for any future updates to the DB instance. When you specify this property for an update, the DB instance is not restored from the DB snapshot again, and the data in the database is not changed. However, if you don't specify the `DBSnapshotIdentifier` property, an empty DB instance is created, and the original DB instance is deleted. If you specify a property that is different from the previous snapshot restore property, a new DB instance is restored from the specified `DBSnapshotIdentifier` property, and the original DB instance is deleted.\n> - Update the stack. \n\nFor more information about updating other properties of this resource, see `[ModifyDBInstance](https://docs.aws.amazon.com//AmazonRDS/latest/APIReference/API_ModifyDBInstance.html)` . For more information about updating stacks, see [AWS CloudFormation Stacks Updates](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html) .\n\n*Deleting DB instances*\n\nFor DB instances that are part of an Aurora DB cluster, you can set a deletion policy for your DB instance to control how AWS CloudFormation handles the DB instance when the stack is deleted. For Amazon RDS DB instances, you can choose to *retain* the DB instance, to *delete* the DB instance, or to *create a snapshot* of the DB instance. The default AWS CloudFormation behavior depends on the `DBClusterIdentifier` property:\n\n- For `AWS::RDS::DBInstance` resources that don't specify the `DBClusterIdentifier` property, AWS CloudFormation saves a snapshot of the DB instance.\n- For `AWS::RDS::DBInstance` resources that do specify the `DBClusterIdentifier` property, AWS CloudFormation deletes the DB instance.\n\nFor more information, see [DeletionPolicy Attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) .", "properties": { "AllocatedStorage": "The amount of storage (in gigabytes) to be initially allocated for the database instance.\n\n> If any value is set in the `Iops` parameter, `AllocatedStorage` must be at least 100 GiB, which corresponds to the minimum Iops value of 1,000. If you increase the `Iops` value (in 1,000 IOPS increments), then you must also increase the `AllocatedStorage` value (in 100-GiB increments). \n\n*Amazon Aurora*\n\nNot applicable. Aurora cluster volumes automatically grow as the amount of data in your database increases, though you are only charged for the space that you use in an Aurora cluster volume.\n\n*MySQL*\n\nConstraints to the amount of storage for each storage type are the following:\n\n- General Purpose (SSD) storage (gp2): Must be an integer from 20 to 65536.\n- Provisioned IOPS storage (io1): Must be an integer from 100 to 65536.\n- Magnetic storage (standard): Must be an integer from 5 to 3072.\n\n*MariaDB*\n\nConstraints to the amount of storage for each storage type are the following:\n\n- General Purpose (SSD) storage (gp2): Must be an integer from 20 to 65536.\n- Provisioned IOPS storage (io1): Must be an integer from 100 to 65536.\n- Magnetic storage (standard): Must be an integer from 5 to 3072.\n\n*PostgreSQL*\n\nConstraints to the amount of storage for each storage type are the following:\n\n- General Purpose (SSD) storage (gp2): Must be an integer from 20 to 65536.\n- Provisioned IOPS storage (io1): Must be an integer from 100 to 65536.\n- Magnetic storage (standard): Must be an integer from 5 to 3072.\n\n*Oracle*\n\nConstraints to the amount of storage for each storage type are the following:\n\n- General Purpose (SSD) storage (gp2): Must be an integer from 20 to 65536.\n- Provisioned IOPS storage (io1): Must be an integer from 100 to 65536.\n- Magnetic storage (standard): Must be an integer from 10 to 3072.\n\n*SQL Server*\n\nConstraints to the amount of storage for each storage type are the following:\n\n- General Purpose (SSD) storage (gp2):\n\n- Enterprise and Standard editions: Must be an integer from 20 to 16384.\n- Web and Express editions: Must be an integer from 20 to 16384.\n- Provisioned IOPS storage (io1):\n\n- Enterprise and Standard editions: Must be an integer from 20 to 16384.\n- Web and Express editions: Must be an integer from 20 to 16384.\n- Magnetic storage (standard):\n\n- Enterprise and Standard editions: Must be an integer from 20 to 1024.\n- Web and Express editions: Must be an integer from 20 to 1024.", "AllowMajorVersionUpgrade": "A value that indicates whether major version upgrades are allowed. Changing this parameter doesn't result in an outage and the change is asynchronously applied as soon as possible.\n\nConstraints: Major version upgrades must be allowed when specifying a value for the `EngineVersion` parameter that is a different major version than the DB instance's current version.", "AssociatedRoles": "The AWS Identity and Access Management (IAM) roles associated with the DB instance.", "AutoMinorVersionUpgrade": "A value that indicates whether minor engine upgrades are applied automatically to the DB instance during the maintenance window. By default, minor engine upgrades are applied automatically.", - "AvailabilityZone": "The Availability Zone (AZ) where the database will be created. For information on AWS Regions and Availability Zones, see [Regions and Availability Zones](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html) .\n\n*Amazon Aurora*\n\nNot applicable. Availability Zones are managed by the DB cluster.\n\nDefault: A random, system-chosen Availability Zone in the endpoint's AWS Region.\n\nExample: `us-east-1d`\n\nConstraint: The `AvailabilityZone` parameter can't be specified if the DB instance is a Multi-AZ deployment. The specified Availability Zone must be in the same AWS Region as the current endpoint.\n\n> If you're creating a DB instance in an RDS on VMware environment, specify the identifier of the custom Availability Zone to create the DB instance in.\n> \n> For more information about RDS on VMware, see the [RDS on VMware User Guide.](https://docs.aws.amazon.com/AmazonRDS/latest/RDSonVMwareUserGuide/rds-on-vmware.html)", + "AvailabilityZone": "The Availability Zone that the database instance will be created in.\n\nDefault: A random, system-chosen Availability Zone in the endpoint's region.\n\nExample: `us-east-1d`\n\nConstraint: The AvailabilityZone parameter cannot be specified if the MultiAZ parameter is set to `true` . The specified Availability Zone must be in the same region as the current endpoint.", "BackupRetentionPeriod": "The number of days for which automated backups are retained. Setting this parameter to a positive number enables backups. Setting this parameter to 0 disables automated backups.\n\n*Amazon Aurora*\n\nNot applicable. The retention period for automated backups is managed by the DB cluster.\n\nDefault: 1\n\nConstraints:\n\n- Must be a value from 0 to 35\n- Can't be set to 0 if the DB instance is a source to read replicas", "CACertificateIdentifier": "The identifier of the CA certificate for this DB instance.\n\n> Specifying or updating this property triggers a reboot. \n\nFor more information about CA certificate identifiers for RDS DB engines, see [Rotating Your SSL/TLS Certificate](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL-certificate-rotation.html) in the *Amazon RDS User Guide* .\n\nFor more information about CA certificate identifiers for Aurora DB engines, see [Rotating Your SSL/TLS Certificate](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.SSL-certificate-rotation.html) in the *Amazon Aurora User Guide* .", "CharacterSetName": "For supported engines, indicates that the DB instance should be associated with the specified character set.\n\n*Amazon Aurora*\n\nNot applicable. The character set is managed by the DB cluster. For more information, see [AWS::RDS::DBCluster](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html) .", @@ -32182,7 +32482,7 @@ "EngineVersion": "The version number of the database engine to use.\n\nFor a list of valid engine versions, use the `DescribeDBEngineVersions` action.\n\nThe following are the database engines and links to information about the major and minor versions that are available with Amazon RDS. Not every database engine is available for every AWS Region.\n\n*Amazon Aurora*\n\nNot applicable. The version number of the database engine to be used by the DB instance is managed by the DB cluster.\n\n*MariaDB*\n\nSee [MariaDB on Amazon RDS Versions](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_MariaDB.html#MariaDB.Concepts.VersionMgmt) in the *Amazon RDS User Guide.*\n\n*Microsoft SQL Server*\n\nSee [Microsoft SQL Server Versions on Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_SQLServer.html#SQLServer.Concepts.General.VersionSupport) in the *Amazon RDS User Guide.*\n\n*MySQL*\n\nSee [MySQL on Amazon RDS Versions](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_MySQL.html#MySQL.Concepts.VersionMgmt) in the *Amazon RDS User Guide.*\n\n*Oracle*\n\nSee [Oracle Database Engine Release Notes](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.Oracle.PatchComposition.html) in the *Amazon RDS User Guide.*\n\n*PostgreSQL*\n\nSee [Supported PostgreSQL Database Versions](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.DBVersions) in the *Amazon RDS User Guide.*", "Iops": "The number of I/O operations per second (IOPS) that the database provisions. The value must be equal to or greater than 1000.\n\nIf you specify this property, you must follow the range of allowed ratios of your requested IOPS rate to the amount of storage that you allocate (IOPS to allocated storage). For example, you can provision an Oracle database instance with 1000 IOPS and 200 GiB of storage (a ratio of 5:1), or specify 2000 IOPS with 200 GiB of storage (a ratio of 10:1). For more information, see [Amazon RDS Provisioned IOPS Storage to Improve Performance](https://docs.aws.amazon.com/AmazonRDS/latest/DeveloperGuide/CHAP_Storage.html#USER_PIOPS) in the *Amazon RDS User Guide* .\n\n> If you specify `io1` for the `StorageType` property, then you must also specify the `Iops` property.", "KmsKeyId": "The ARN of the AWS Key Management Service ( AWS KMS) master key that's used to encrypt the DB instance, such as `arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef` . If you enable the StorageEncrypted property but don't specify this property, AWS CloudFormation uses the default master key. If you specify this property, you must set the StorageEncrypted property to true.\n\nIf you specify the `SourceDBInstanceIdentifier` property, the value is inherited from the source DB instance if the read replica is created in the same region.\n\nIf you create an encrypted read replica in a different AWS Region, then you must specify a KMS key for the destination AWS Region. KMS encryption keys are specific to the region that they're created in, and you can't use encryption keys from one region in another region.\n\nIf you specify the `SnapshotIdentifier` property, the `StorageEncrypted` property value is inherited from the snapshot, and if the DB instance is encrypted, the specified `KmsKeyId` property is used.\n\nIf you specify `DBSecurityGroups` , AWS CloudFormation ignores this property. To specify both a security group and this property, you must use a VPC security group. For more information about Amazon RDS and VPC, see [Using Amazon RDS with Amazon VPC](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html) in the *Amazon RDS User Guide* .\n\n*Amazon Aurora*\n\nNot applicable. The KMS key identifier is managed by the DB cluster.", - "LicenseModel": "License model information for this DB instance.\n\nValid values: `license-included` | `bring-your-own-license` | `general-public-license`\n\n> If you've specified `DBSecurityGroups` and then you update the license model, AWS CloudFormation replaces the underlying DB instance. This will incur some interruptions to database availability.", + "LicenseModel": "License model information for this DB instance.\n\nValid values:\n\n- Aurora MySQL - `general-public-license`\n- Aurora PostgreSQL - `postgresql-license`\n- MariaDB - `general-public-license`\n- Microsoft SQL Server - `license-included`\n- MySQL - `general-public-license`\n- Oracle - `bring-your-own-license` or `license-included`\n- PostgreSQL - `postgresql-license`\n\n> If you've specified `DBSecurityGroups` and then you update the license model, AWS CloudFormation replaces the underlying DB instance. This will incur some interruptions to database availability.", "MasterUserPassword": "The password for the master user. The password can include any printable ASCII character except \"/\", \"\"\", or \"@\".\n\n*Amazon Aurora*\n\nNot applicable. The password for the master user is managed by the DB cluster.\n\n*MariaDB*\n\nConstraints: Must contain from 8 to 41 characters.\n\n*Microsoft SQL Server*\n\nConstraints: Must contain from 8 to 128 characters.\n\n*MySQL*\n\nConstraints: Must contain from 8 to 41 characters.\n\n*Oracle*\n\nConstraints: Must contain from 8 to 30 characters.\n\n*PostgreSQL*\n\nConstraints: Must contain from 8 to 128 characters.", "MasterUsername": "The master user name for the DB instance.\n\n> If you specify the `SourceDBInstanceIdentifier` or `DBSnapshotIdentifier` property, don't specify this property. The value is inherited from the source DB instance or snapshot. \n\n*Amazon Aurora*\n\nNot applicable. The name for the master user is managed by the DB cluster.\n\n*MariaDB*\n\nConstraints:\n\n- Required for MariaDB.\n- Must be 1 to 16 letters or numbers.\n- Can't be a reserved word for the chosen database engine.\n\n*Microsoft SQL Server*\n\nConstraints:\n\n- Required for SQL Server.\n- Must be 1 to 128 letters or numbers.\n- The first character must be a letter.\n- Can't be a reserved word for the chosen database engine.\n\n*MySQL*\n\nConstraints:\n\n- Required for MySQL.\n- Must be 1 to 16 letters or numbers.\n- First character must be a letter.\n- Can't be a reserved word for the chosen database engine.\n\n*Oracle*\n\nConstraints:\n\n- Required for Oracle.\n- Must be 1 to 30 letters or numbers.\n- First character must be a letter.\n- Can't be a reserved word for the chosen database engine.\n\n*PostgreSQL*\n\nConstraints:\n\n- Required for PostgreSQL.\n- Must be 1 to 63 letters or numbers.\n- First character must be a letter.\n- Can't be a reserved word for the chosen database engine.", "MaxAllocatedStorage": "The upper limit in gibibytes (GiB) to which Amazon RDS can automatically scale the storage of the DB instance.\n\nFor more information about this setting, including limitations that apply to it, see [Managing capacity automatically with Amazon RDS storage autoscaling](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PIOPS.StorageTypes.html#USER_PIOPS.Autoscaling) in the *Amazon RDS User Guide* .\n\nThis setting doesn't apply to RDS Custom.", @@ -32230,7 +32530,7 @@ }, "description": "The `AWS::RDS::DBParameterGroup` resource creates a custom parameter group for an RDS database family.\n\nThis type can be declared in a template and referenced in the `DBParameterGroupName` property of an `[AWS::RDS::DBInstance](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html)` resource.\n\nFor information about configuring parameters for Amazon RDS DB instances, see [Working with DB parameter groups](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html) in the *Amazon RDS User Guide* .\n\nFor information about configuring parameters for Amazon Aurora DB instances, see [Working with DB parameter groups and DB cluster parameter groups](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.html) in the *Amazon Aurora User Guide* .\n\n> Applying a parameter group to a DB instance may require the DB instance to reboot, resulting in a database outage for the duration of the reboot.", "properties": { - "Description": "Provides the customer-specified description for this DB parameter group.", + "Description": "Provides the customer-specified description for this DB Parameter Group.", "Family": "The DB parameter group family name. A DB parameter group can be associated with one and only one DB parameter group family, and can be applied only to a DB instance running a DB engine and engine version compatible with that DB parameter group family.\n\n> The DB parameter group family can't be changed when updating a DB parameter group. \n\nTo list all of the available parameter group families, use the following command:\n\n`aws rds describe-db-engine-versions --query \"DBEngineVersions[].DBParameterGroupFamily\"`\n\nThe output contains duplicates.\n\nFor more information, see `[CreateDBParameterGroup](https://docs.aws.amazon.com//AmazonRDS/latest/APIReference/API_CreateDBParameterGroup.html)` .", "Parameters": "An array of parameter names and values for the parameter update. At least one parameter name and value must be supplied. Subsequent arguments are optional.\n\nFor more information about DB parameters and DB parameter groups for Amazon RDS DB engines, see [Working with DB Parameter Groups](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html) in the *Amazon RDS User Guide* .\n\nFor more information about DB cluster and DB instance parameters and parameter groups for Amazon Aurora DB engines, see [Working with DB Parameter Groups and DB Cluster Parameter Groups](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.html) in the *Amazon Aurora User Guide* .\n\n> AWS CloudFormation doesn't support specifying an apply method for each individual parameter. The default apply method for each parameter is used.", "Tags": "Tags to assign to the DB parameter group." @@ -32334,7 +32634,7 @@ "properties": { "DBSecurityGroupIngress": "Ingress rules to be applied to the DB security group.", "EC2VpcId": "The identifier of an Amazon VPC. This property indicates the VPC that this DB security group belongs to.\n\n> The `EC2VpcId` property is for backward compatibility with older regions, and is no longer recommended for providing security information to an RDS DB instance.", - "GroupDescription": "Provides the description of the DB security group.", + "GroupDescription": "Provides the description of the DB Security Group.", "Tags": "Tags to assign to the DB security group." } }, @@ -32343,9 +32643,9 @@ "description": "The `Ingress` property type specifies an individual ingress rule within an `AWS::RDS::DBSecurityGroup` resource.", "properties": { "CIDRIP": "The IP range to authorize.", - "EC2SecurityGroupId": "Id of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupName": "Name of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupOwnerId": "AWS account number of the owner of the EC2 security group specified in the `EC2SecurityGroupName` parameter. The AWS access key ID isn't an acceptable value. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." + "EC2SecurityGroupId": "Id of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupName": "Name of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupOwnerId": "AWS Account Number of the owner of the EC2 Security Group specified in the EC2SecurityGroupName parameter. The AWS Access Key ID is not an acceptable value. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." } }, "AWS::RDS::DBSecurityGroupIngress": { @@ -32355,10 +32655,10 @@ "description": "The `AWS::RDS::DBSecurityGroupIngress` resource enables ingress to a DB security group using one of two forms of authorization. First, you can add EC2 or VPC security groups to the DB security group if the application using the database is running on EC2 or VPC instances. Second, IP ranges are available if the application accessing your database is running on the Internet.\n\nThis type supports updates. For more information about updating stacks, see [AWS CloudFormation Stacks Updates](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html) .\n\nFor details about the settings for DB security group ingress, see [AuthorizeDBSecurityGroupIngress](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_AuthorizeDBSecurityGroupIngress.html) .", "properties": { "CIDRIP": "The IP range to authorize.", - "DBSecurityGroupName": "The name of the DB security group to add authorization to.", - "EC2SecurityGroupId": "Id of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupName": "Name of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupOwnerId": "AWS account number of the owner of the EC2 security group specified in the `EC2SecurityGroupName` parameter. The AWS access key ID isn't an acceptable value. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." + "DBSecurityGroupName": "The name of the DB Security Group to add authorization to.", + "EC2SecurityGroupId": "Id of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupName": "Name of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupOwnerId": "AWS Account Number of the owner of the EC2 Security Group specified in the EC2SecurityGroupName parameter. The AWS Access Key ID is not an acceptable value. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." } }, "AWS::RDS::DBSubnetGroup": { @@ -32367,9 +32667,9 @@ }, "description": "The `AWS::RDS::DBSubnetGroup` resource creates a database subnet group. Subnet groups must contain at least two subnets in two different Availability Zones in the same region.\n\nFor more information, see [Working with DB subnet groups](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html#USER_VPC.Subnets) in the *Amazon RDS User Guide* .", "properties": { - "DBSubnetGroupDescription": "The description for the DB subnet group.", + "DBSubnetGroupDescription": "The description for the DB Subnet Group.", "DBSubnetGroupName": "The name for the DB subnet group. This value is stored as a lowercase string.\n\nConstraints: Must contain no more than 255 lowercase alphanumeric characters or hyphens. Must not be \"Default\".\n\nExample: `mysubnetgroup`", - "SubnetIds": "The EC2 Subnet IDs for the DB subnet group.", + "SubnetIds": "The EC2 Subnet IDs for the DB Subnet Group.", "Tags": "Tags to assign to the DB subnet group." } }, @@ -32379,8 +32679,8 @@ }, "description": "The `AWS::RDS::EventSubscription` resource allows you to receive notifications for Amazon Relational Database Service events through the Amazon Simple Notification Service (Amazon SNS). For more information, see [Using Amazon RDS Event Notification](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html) in the *Amazon RDS User Guide* .", "properties": { - "Enabled": "A value that indicates whether to activate the subscription. If the event notification subscription isn't activated, the subscription is created but not active.", - "EventCategories": "A list of event categories for a particular source type ( `SourceType` ) that you want to subscribe to. You can see a list of the categories for a given source type in the \"Amazon RDS event categories and event messages\" section of the [*Amazon RDS User Guide*](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.Messages.html) or the [*Amazon Aurora User Guide*](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_Events.Messages.html) . You can also see this list by using the `DescribeEventCategories` operation.", + "Enabled": "A Boolean value; set to *true* to activate the subscription, set to *false* to create the subscription but not active it.", + "EventCategories": "A list of event categories for a SourceType that you want to subscribe to. You can see a list of the categories for a given SourceType in the [Events](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html) topic in the Amazon RDS User Guide or by using the *DescribeEventCategories* action.", "SnsTopicArn": "The Amazon Resource Name (ARN) of the SNS topic created for event notification. The ARN is created by Amazon SNS when you create a topic and subscribe to it.", "SourceIds": "The list of identifiers of the event sources for which events are returned. If not specified, then all sources are included in the response. An identifier must begin with a letter and must contain only ASCII letters, digits, and hyphens. It can't end with a hyphen or contain two consecutive hyphens.\n\nConstraints:\n\n- If a `SourceIds` value is supplied, `SourceType` must also be provided.\n- If the source type is a DB instance, a `DBInstanceIdentifier` value must be supplied.\n- If the source type is a DB cluster, a `DBClusterIdentifier` value must be supplied.\n- If the source type is a DB parameter group, a `DBParameterGroupName` value must be supplied.\n- If the source type is a DB security group, a `DBSecurityGroupName` value must be supplied.\n- If the source type is a DB snapshot, a `DBSnapshotIdentifier` value must be supplied.\n- If the source type is a DB cluster snapshot, a `DBClusterSnapshotIdentifier` value must be supplied.", "SourceType": "The type of source that is generating the events. For example, if you want to be notified of events generated by a DB instance, set this parameter to `db-instance` . If this value isn't specified, all events are returned.\n\nValid values: `db-instance` | `db-cluster` | `db-parameter-group` | `db-security-group` | `db-snapshot` | `db-cluster-snapshot`" @@ -32439,7 +32739,7 @@ }, "description": "Creates a CloudWatch RUM app monitor, which you can use to collect telemetry data from your application and send it to CloudWatch RUM. The data includes performance and reliability information such as page load time, client-side errors, and user behavior.\n\nAfter you create an app monitor, sign in to the CloudWatch RUM console to get the JavaScript code snippet to add to your web application. For more information, see [How do I find a code snippet that I've already generated?](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-RUM-find-code-snippet.html)", "properties": { - "AppMonitorConfiguration": "A structure that contains much of the configuration data for the app monitor. If you are using Amazon Cognito for authorization, you must include this structure in your request, and it must include the ID of the Amazon Cognito identity pool to use for authorization. If you don't include `AppMonitorConfiguration` , you must set up your own authorization method. For more information, see [Authorize your application to send data to AWS](https://docs.aws.amazon.com/monitoring/CloudWatch-RUM-get-started-authorization.html) .\n\nIf you omit this argument, the sample rate used for CloudWatch RUM is set to 10% of the user sessions.", + "AppMonitorConfiguration": "A structure that contains much of the configuration data for the app monitor. If you are using Amazon Cognito for authorization, you must include this structure in your request, and it must include the ID of the Amazon Cognito identity pool to use for authorization. If you don't include `AppMonitorConfiguration` , you must set up your own authorization method. For more information, see [Authorize your application to send data to AWS](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-RUM-get-started-authorization.html) .\n\nIf you omit this argument, the sample rate used for CloudWatch RUM is set to 10% of the user sessions.", "CwLogEnabled": "Data collected by CloudWatch RUM is kept by RUM for 30 days and then deleted. This parameter specifies whether CloudWatch RUM sends a copy of this telemetry data to Amazon CloudWatch Logs in your account. This enables you to keep the telemetry data for more than 30 days, but it does incur Amazon CloudWatch Logs charges.\n\nIf you omit this parameter, the default is `false` .", "Domain": "The top-level internet domain name for which your application has administrative authority. This parameter is required.", "Name": "A name for the app monitor. This parameter is required.", @@ -32811,6 +33111,17 @@ "Url": "The URL to route traffic to. The URL must be an [rfc3986-formatted URL](https://docs.aws.amazon.com/https://datatracker.ietf.org/doc/html/rfc3986) . If the host is a domain name, the name must be resolvable over the public internet. If the scheme is `https` , the top level domain of the host must be listed in the [IANA root zone database](https://docs.aws.amazon.com/https://www.iana.org/domains/root/db) ." } }, + "AWS::Rekognition::Collection": { + "attributes": { + "Arn": "Returns the Amazon Resource Name of the collection.", + "Ref": "`Ref` returns the collection ID. For example:\n\n`{ \"Ref\": \"MyCollection\" }`" + }, + "description": "The `AWS::Rekognition::Collection` type creates a server-side container called a collection. You can use a collection to store information about detected faces and search for known faces in images, stored videos, and streaming videos.", + "properties": { + "CollectionId": "ID for the collection that you are creating.", + "Tags": "A set of tags (key-value pairs) that you want to attach to the collection." + } + }, "AWS::Rekognition::Project": { "attributes": { "Arn": "Returns the Amazon Resource Name of the project.", @@ -33501,7 +33812,7 @@ "FirewallRuleGroupId": "The unique identifier of the firewall rule group.", "MutationProtection": "If enabled, this setting disallows modification or removal of the association, to help prevent against accidentally altering DNS firewall protections.", "Name": "The name of the association.", - "Priority": "The setting that determines the processing order of the rule group among the rule groups that are associated with a single VPC. DNS Firewall filters VPC traffic starting from rule group with the lowest numeric priority setting.\n\nYou must specify a unique priority for each rule group that you associate with a single VPC. To make it easier to insert rule groups later, leave space between the numbers, for example, use 101, 200, and so on. You can change the priority setting for a rule group association after you create it.\n\nThe allowed values for `Priority` are between 100 and 9900.", + "Priority": "The setting that determines the processing order of the rule group among the rule groups that are associated with a single VPC. DNS Firewall filters VPC traffic starting from rule group with the lowest numeric priority setting.\n\nYou must specify a unique priority for each rule group that you associate with a single VPC. To make it easier to insert rule groups later, leave space between the numbers, for example, use 101, 200, and so on. You can change the priority setting for a rule group association after you create it.\n\nThe allowed values for `Priority` are between 100 and 9900 (excluding 100 and 9900).", "Tags": "A list of the tag keys and values that you want to associate with the rule group.", "VpcId": "The unique identifier of the VPC that is associated with the rule group." } @@ -33875,7 +34186,6 @@ "attributes": {}, "description": "Specifies a metrics configuration for the CloudWatch request metrics (specified by the metrics configuration ID) from an Amazon S3 bucket. If you're updating an existing metrics configuration, note that this is a full replacement of the existing metrics configuration. If you don't include the elements you want to keep, they are erased. For examples, see [AWS::S3::Bucket](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html#aws-properties-s3-bucket--examples) . For more information, see [PUT Bucket metrics](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTMetricConfiguration.html) in the *Amazon S3 API Reference* .", "properties": { - "AccessPointArn": "The access point that was used while performing operations on the object. The metrics configuration only includes objects that meet the filter's criteria.", "Id": "The ID used to identify the metrics configuration. This can be any value you choose that helps you identify your metrics configuration.", "Prefix": "The prefix that an object must have to be included in the metrics results.", "TagFilters": "Specifies a list of tag filters to use as a metrics configuration filter. The metrics configuration includes only objects that meet the filter's criteria." @@ -33902,7 +34212,6 @@ "attributes": {}, "description": "Describes the notification configuration for an Amazon S3 bucket.\n\n> If you create the target resource and related permissions in the same template, you might have a circular dependency.\n> \n> For example, you might use the `AWS::Lambda::Permission` resource to grant the bucket permission to invoke an AWS Lambda function. However, AWS CloudFormation can't create the bucket until the bucket has permission to invoke the function ( AWS CloudFormation checks whether the bucket can invoke the function). If you're using Refs to pass the bucket name, this leads to a circular dependency.\n> \n> To avoid this dependency, you can create all resources without specifying the notification configuration. Then, update the stack with a notification configuration.\n> \n> For more information on permissions, see [AWS::Lambda::Permission](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html) and [Granting Permissions to Publish Event Notification Messages to a Destination](https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#grant-destinations-permissions-to-s3) .", "properties": { - "EventBridgeConfiguration": "Enables delivery of events to Amazon EventBridge.", "LambdaConfigurations": "Describes the AWS Lambda functions to invoke and the events for which to invoke them.", "QueueConfigurations": "The Amazon Simple Queue Service queues to publish messages to and the events for which to publish messages.", "TopicConfigurations": "The topic to which notifications are sent and the events for which notifications are generated." @@ -34183,12 +34492,7 @@ "AWS::S3::Bucket.WebsiteConfiguration": { "attributes": {}, "description": "Specifies website configuration parameters for an Amazon S3 bucket.", - "properties": { - "ErrorDocument": "The name of the error document for the website.", - "IndexDocument": "The name of the index document for the website.", - "RedirectAllRequestsTo": "The redirect behavior for every request to this bucket's website endpoint.\n\n> If you specify this property, you can't specify any other property.", - "RoutingRules": "Rules that define when a redirect is applied and the redirect behavior." - } + "properties": {} }, "AWS::S3::BucketPolicy": { "attributes": {}, @@ -34505,7 +34809,7 @@ "attributes": { "Ref": "`Ref` returns the resource name." }, - "description": "The name of the configuration set.\n\nConfiguration sets let you create groups of rules that you can apply to the emails you send using Amazon SES. For more information about using configuration sets, see [Using Amazon SES Configuration Sets](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/using-configuration-sets.html) in the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/) .", + "description": "The name of the configuration set.\n\nConfiguration sets let you create groups of rules that you can apply to the emails you send using Amazon SES. For more information about using configuration sets, see [Using Amazon SES Configuration Sets](https://docs.aws.amazon.com/ses/latest/dg/using-configuration-sets.html) in the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/) .", "properties": { "Name": "" } @@ -34520,14 +34824,14 @@ }, "AWS::SES::ConfigurationSetEventDestination.CloudWatchDestination": { "attributes": {}, - "description": "Contains information associated with an Amazon CloudWatch event destination to which email sending events are published.\n\nEvent destinations, such as Amazon CloudWatch, are associated with configuration sets, which enable you to publish email sending events. For information about using configuration sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/monitor-sending-activity.html) .", + "description": "Contains information associated with an Amazon CloudWatch event destination to which email sending events are published.\n\nEvent destinations, such as Amazon CloudWatch, are associated with configuration sets, which enable you to publish email sending events. For information about using configuration sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity.html) .", "properties": { "DimensionConfigurations": "A list of dimensions upon which to categorize your emails when you publish email sending events to Amazon CloudWatch." } }, "AWS::SES::ConfigurationSetEventDestination.DimensionConfiguration": { "attributes": {}, - "description": "Contains the dimension configuration to use when you publish email sending events to Amazon CloudWatch.\n\nFor information about publishing email sending events to Amazon CloudWatch, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/monitor-sending-activity.html) .", + "description": "Contains the dimension configuration to use when you publish email sending events to Amazon CloudWatch.\n\nFor information about publishing email sending events to Amazon CloudWatch, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity.html) .", "properties": { "DefaultDimensionValue": "The default value of the dimension that is published to Amazon CloudWatch if you do not provide the value of the dimension when you send an email. The default value must meet the following requirements:\n\n- Contain only ASCII letters (a-z, A-Z), numbers (0-9), underscores (_), dashes (-), at signs (@), or periods (.).\n- Contain 256 characters or fewer.", "DimensionName": "The name of an Amazon CloudWatch dimension associated with an email sending metric. The name must meet the following requirements:\n\n- Contain only ASCII letters (a-z, A-Z), numbers (0-9), underscores (_), or dashes (-).\n- Contain 256 characters or fewer.", @@ -34536,7 +34840,7 @@ }, "AWS::SES::ConfigurationSetEventDestination.EventDestination": { "attributes": {}, - "description": "Contains information about an event destination.\n\n> When you create or update an event destination, you must provide one, and only one, destination. The destination can be Amazon CloudWatch, Amazon Kinesis Firehose or Amazon Simple Notification Service (Amazon SNS). \n\nEvent destinations are associated with configuration sets, which enable you to publish email sending events to Amazon CloudWatch, Amazon Kinesis Firehose, or Amazon Simple Notification Service (Amazon SNS). For information about using configuration sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/monitor-sending-activity.html) .", + "description": "Contains information about an event destination.\n\n> When you create or update an event destination, you must provide one, and only one, destination. The destination can be Amazon CloudWatch, Amazon Kinesis Firehose or Amazon Simple Notification Service (Amazon SNS). \n\nEvent destinations are associated with configuration sets, which enable you to publish email sending events to Amazon CloudWatch, Amazon Kinesis Firehose, or Amazon Simple Notification Service (Amazon SNS). For information about using configuration sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity.html) .", "properties": { "CloudWatchDestination": "An object that contains the names, default values, and sources of the dimensions associated with an Amazon CloudWatch event destination.", "Enabled": "Sets whether Amazon SES publishes events to this destination when you send an email with the associated configuration set. Set to `true` to enable publishing to this destination; set to `false` to prevent publishing to this destination. The default value is `false` .", @@ -34547,7 +34851,7 @@ }, "AWS::SES::ConfigurationSetEventDestination.KinesisFirehoseDestination": { "attributes": {}, - "description": "Contains the delivery stream ARN and the IAM role ARN associated with an Amazon Kinesis Firehose event destination.\n\nEvent destinations, such as Amazon Kinesis Firehose, are associated with configuration sets, which enable you to publish email sending events. For information about using configuration sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/monitor-sending-activity.html) .", + "description": "Contains the delivery stream ARN and the IAM role ARN associated with an Amazon Kinesis Firehose event destination.\n\nEvent destinations, such as Amazon Kinesis Firehose, are associated with configuration sets, which enable you to publish email sending events. For information about using configuration sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/monitor-sending-activity.html) .", "properties": { "DeliveryStreamARN": "The ARN of the Amazon Kinesis Firehose stream that email sending events should be published to.", "IAMRoleARN": "The ARN of the IAM role under which Amazon SES publishes email sending events to the Amazon Kinesis Firehose stream." @@ -34592,7 +34896,7 @@ }, "AWS::SES::ReceiptFilter.IpFilter": { "attributes": {}, - "description": "A receipt IP address filter enables you to specify whether to accept or reject mail originating from an IP address or range of IP addresses.\n\nFor information about setting up IP address filters, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-ip-filters.html) .", + "description": "A receipt IP address filter enables you to specify whether to accept or reject mail originating from an IP address or range of IP addresses.\n\nFor information about setting up IP address filters, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-ip-filtering-console-walkthrough.html) .", "properties": { "Cidr": "A single IP address or a range of IP addresses to block or allow, specified in Classless Inter-Domain Routing (CIDR) notation. An example of a single email address is 10.0.0.1. An example of a range of IP addresses is 10.0.0.1/24. For more information about CIDR notation, see [RFC 2317](https://docs.aws.amazon.com/https://tools.ietf.org/html/rfc2317) .", "Policy": "Indicates whether to block or allow incoming mail from the specified IP addresses." @@ -34611,7 +34915,7 @@ }, "AWS::SES::ReceiptRule.Action": { "attributes": {}, - "description": "An action that Amazon SES can take when it receives an email on behalf of one or more email addresses or domains that you own. An instance of this data type can represent only one action.\n\nFor information about setting up receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-receipt-rules.html) .", + "description": "An action that Amazon SES can take when it receives an email on behalf of one or more email addresses or domains that you own. An instance of this data type can represent only one action.\n\nFor information about setting up receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-receipt-rules-console-walkthrough.html) .", "properties": { "AddHeaderAction": "Adds a header to the received email.", "BounceAction": "Rejects the received email by returning a bounce response to the sender and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).", @@ -34624,7 +34928,7 @@ }, "AWS::SES::ReceiptRule.AddHeaderAction": { "attributes": {}, - "description": "When included in a receipt rule, this action adds a header to the received email.\n\nFor information about adding a header using a receipt rule, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-add-header.html) .", + "description": "When included in a receipt rule, this action adds a header to the received email.\n\nFor information about adding a header using a receipt rule, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-add-header.html) .", "properties": { "HeaderName": "The name of the header to add to the incoming message. The name must contain at least one character, and can contain up to 50 characters. It consists of alphanumeric (a\u2013z, A\u2013Z, 0\u20139) characters and dashes.", "HeaderValue": "The content to include in the header. This value can contain up to 2048 characters. It can't contain newline ( `\\n` ) or carriage return ( `\\r` ) characters." @@ -34632,7 +34936,7 @@ }, "AWS::SES::ReceiptRule.BounceAction": { "attributes": {}, - "description": "When included in a receipt rule, this action rejects the received email by returning a bounce response to the sender and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nFor information about sending a bounce message in response to a received email, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-bounce.html) .", + "description": "When included in a receipt rule, this action rejects the received email by returning a bounce response to the sender and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nFor information about sending a bounce message in response to a received email, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-bounce.html) .", "properties": { "Message": "Human-readable text to include in the bounce message.", "Sender": "The email address of the sender of the bounced email. This is the address from which the bounce message is sent.", @@ -34643,7 +34947,7 @@ }, "AWS::SES::ReceiptRule.LambdaAction": { "attributes": {}, - "description": "When included in a receipt rule, this action calls an AWS Lambda function and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nTo enable Amazon SES to call your AWS Lambda function or to publish to an Amazon SNS topic of another account, Amazon SES must have permission to access those resources. For information about giving permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html) .\n\nFor information about using AWS Lambda actions in receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-lambda.html) .", + "description": "When included in a receipt rule, this action calls an AWS Lambda function and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nTo enable Amazon SES to call your AWS Lambda function or to publish to an Amazon SNS topic of another account, Amazon SES must have permission to access those resources. For information about giving permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-permissions.html) .\n\nFor information about using AWS Lambda actions in receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-lambda.html) .", "properties": { "FunctionArn": "The Amazon Resource Name (ARN) of the AWS Lambda function. An example of an AWS Lambda function ARN is `arn:aws:lambda:us-west-2:account-id:function:MyFunction` . For more information about AWS Lambda, see the [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) .", "InvocationType": "The invocation type of the AWS Lambda function. An invocation type of `RequestResponse` means that the execution of the function immediately results in a response, and a value of `Event` means that the function is invoked asynchronously. The default value is `Event` . For information about AWS Lambda invocation types, see the [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html) .\n\n> There is a 30-second timeout on `RequestResponse` invocations. You should use `Event` invocation in most cases. Use `RequestResponse` only to make a mail flow decision, such as whether to stop the receipt rule or the receipt rule set.", @@ -34652,7 +34956,7 @@ }, "AWS::SES::ReceiptRule.Rule": { "attributes": {}, - "description": "Receipt rules enable you to specify which actions Amazon SES should take when it receives mail on behalf of one or more email addresses or domains that you own.\n\nEach receipt rule defines a set of email addresses or domains that it applies to. If the email addresses or domains match at least one recipient address of the message, Amazon SES executes all of the receipt rule's actions on the message.\n\nFor information about setting up receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-receipt-rules.html) .", + "description": "Receipt rules enable you to specify which actions Amazon SES should take when it receives mail on behalf of one or more email addresses or domains that you own.\n\nEach receipt rule defines a set of email addresses or domains that it applies to. If the email addresses or domains match at least one recipient address of the message, Amazon SES executes all of the receipt rule's actions on the message.\n\nFor information about setting up receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-receipt-rules-console-walkthrough.html) .", "properties": { "Actions": "An ordered list of actions to perform on messages that match at least one of the recipient email addresses or domains specified in the receipt rule.", "Enabled": "If `true` , the receipt rule is active. The default value is `false` .", @@ -34664,17 +34968,17 @@ }, "AWS::SES::ReceiptRule.S3Action": { "attributes": {}, - "description": "When included in a receipt rule, this action saves the received message to an Amazon Simple Storage Service (Amazon S3) bucket and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nTo enable Amazon SES to write emails to your Amazon S3 bucket, use an AWS KMS key to encrypt your emails, or publish to an Amazon SNS topic of another account, Amazon SES must have permission to access those resources. For information about granting permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html) .\n\n> When you save your emails to an Amazon S3 bucket, the maximum email size (including headers) is 30 MB. Emails larger than that bounces. \n\nFor information about specifying Amazon S3 actions in receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-s3.html) .", + "description": "When included in a receipt rule, this action saves the received message to an Amazon Simple Storage Service (Amazon S3) bucket and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nTo enable Amazon SES to write emails to your Amazon S3 bucket, use an AWS KMS key to encrypt your emails, or publish to an Amazon SNS topic of another account, Amazon SES must have permission to access those resources. For information about granting permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-permissions.html) .\n\n> When you save your emails to an Amazon S3 bucket, the maximum email size (including headers) is 30 MB. Emails larger than that bounces. \n\nFor information about specifying Amazon S3 actions in receipt rules, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-s3.html) .", "properties": { "BucketName": "The name of the Amazon S3 bucket for incoming email.", - "KmsKeyArn": "The customer master key that Amazon SES should use to encrypt your emails before saving them to the Amazon S3 bucket. You can use the default master key or a custom master key that you created in AWS KMS as follows:\n\n- To use the default master key, provide an ARN in the form of `arn:aws:kms:REGION:ACCOUNT-ID-WITHOUT-HYPHENS:alias/aws/ses` . For example, if your AWS account ID is 123456789012 and you want to use the default master key in the US West (Oregon) Region, the ARN of the default master key would be `arn:aws:kms:us-west-2:123456789012:alias/aws/ses` . If you use the default master key, you don't need to perform any extra steps to give Amazon SES permission to use the key.\n- To use a custom master key that you created in AWS KMS, provide the ARN of the master key and ensure that you add a statement to your key's policy to give Amazon SES permission to use it. For more information about giving permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html) .\n\nFor more information about key policies, see the [AWS KMS Developer Guide](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html) . If you do not specify a master key, Amazon SES does not encrypt your emails.\n\n> Your mail is encrypted by Amazon SES using the Amazon S3 encryption client before the mail is submitted to Amazon S3 for storage. It is not encrypted using Amazon S3 server-side encryption. This means that you must use the Amazon S3 encryption client to decrypt the email after retrieving it from Amazon S3, as the service has no access to use your AWS KMS keys for decryption. This encryption client is currently available with the [AWS SDK for Java](https://docs.aws.amazon.com/sdk-for-java/) and [AWS SDK for Ruby](https://docs.aws.amazon.com/sdk-for-ruby/) only. For more information about client-side encryption using AWS KMS master keys, see the [Amazon S3 Developer Guide](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingClientSideEncryption.html) .", + "KmsKeyArn": "The customer master key that Amazon SES should use to encrypt your emails before saving them to the Amazon S3 bucket. You can use the default master key or a custom master key that you created in AWS KMS as follows:\n\n- To use the default master key, provide an ARN in the form of `arn:aws:kms:REGION:ACCOUNT-ID-WITHOUT-HYPHENS:alias/aws/ses` . For example, if your AWS account ID is 123456789012 and you want to use the default master key in the US West (Oregon) Region, the ARN of the default master key would be `arn:aws:kms:us-west-2:123456789012:alias/aws/ses` . If you use the default master key, you don't need to perform any extra steps to give Amazon SES permission to use the key.\n- To use a custom master key that you created in AWS KMS, provide the ARN of the master key and ensure that you add a statement to your key's policy to give Amazon SES permission to use it. For more information about giving permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-permissions.html) .\n\nFor more information about key policies, see the [AWS KMS Developer Guide](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html) . If you do not specify a master key, Amazon SES does not encrypt your emails.\n\n> Your mail is encrypted by Amazon SES using the Amazon S3 encryption client before the mail is submitted to Amazon S3 for storage. It is not encrypted using Amazon S3 server-side encryption. This means that you must use the Amazon S3 encryption client to decrypt the email after retrieving it from Amazon S3, as the service has no access to use your AWS KMS keys for decryption. This encryption client is currently available with the [AWS SDK for Java](https://docs.aws.amazon.com/sdk-for-java/) and [AWS SDK for Ruby](https://docs.aws.amazon.com/sdk-for-ruby/) only. For more information about client-side encryption using AWS KMS master keys, see the [Amazon S3 Developer Guide](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingClientSideEncryption.html) .", "ObjectKeyPrefix": "The key prefix of the Amazon S3 bucket. The key prefix is similar to a directory name that enables you to store similar data under the same directory in a bucket.", "TopicArn": "The ARN of the Amazon SNS topic to notify when the message is saved to the Amazon S3 bucket. You can find the ARN of a topic by using the [ListTopics](https://docs.aws.amazon.com/sns/latest/api/API_ListTopics.html) operation in Amazon SNS.\n\nFor more information about Amazon SNS topics, see the [Amazon SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/CreateTopic.html) ." } }, "AWS::SES::ReceiptRule.SNSAction": { "attributes": {}, - "description": "When included in a receipt rule, this action publishes a notification to Amazon Simple Notification Service (Amazon SNS). This action includes a complete copy of the email content in the Amazon SNS notifications. Amazon SNS notifications for all other actions simply provide information about the email. They do not include the email content itself.\n\nIf you own the Amazon SNS topic, you don't need to do anything to give Amazon SES permission to publish emails to it. However, if you don't own the Amazon SNS topic, you need to attach a policy to the topic to give Amazon SES permissions to access it. For information about giving permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html) .\n\n> You can only publish emails that are 150 KB or less (including the header) to Amazon SNS. Larger emails bounce. If you anticipate emails larger than 150 KB, use the S3 action instead. \n\nFor information about using a receipt rule to publish an Amazon SNS notification, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-sns.html) .", + "description": "When included in a receipt rule, this action publishes a notification to Amazon Simple Notification Service (Amazon SNS). This action includes a complete copy of the email content in the Amazon SNS notifications. Amazon SNS notifications for all other actions simply provide information about the email. They do not include the email content itself.\n\nIf you own the Amazon SNS topic, you don't need to do anything to give Amazon SES permission to publish emails to it. However, if you don't own the Amazon SNS topic, you need to attach a policy to the topic to give Amazon SES permissions to access it. For information about giving permissions, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-permissions.html) .\n\n> You can only publish emails that are 150 KB or less (including the header) to Amazon SNS. Larger emails bounce. If you anticipate emails larger than 150 KB, use the S3 action instead. \n\nFor information about using a receipt rule to publish an Amazon SNS notification, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-sns.html) .", "properties": { "Encoding": "The encoding to use for the email within the Amazon SNS notification. UTF-8 is easier to use, but may not preserve all special characters when a message was encoded with a different encoding format. Base64 preserves all special characters. The default value is UTF-8.", "TopicArn": "The Amazon Resource Name (ARN) of the Amazon SNS topic to notify. You can find the ARN of a topic by using the [ListTopics](https://docs.aws.amazon.com/sns/latest/api/API_ListTopics.html) operation in Amazon SNS.\n\nFor more information about Amazon SNS topics, see the [Amazon SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/CreateTopic.html) ." @@ -34682,7 +34986,7 @@ }, "AWS::SES::ReceiptRule.StopAction": { "attributes": {}, - "description": "When included in a receipt rule, this action terminates the evaluation of the receipt rule set and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nFor information about setting a stop action in a receipt rule, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-stop.html) .", + "description": "When included in a receipt rule, this action terminates the evaluation of the receipt rule set and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS).\n\nFor information about setting a stop action in a receipt rule, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-stop.html) .", "properties": { "Scope": "The scope of the StopAction. The only acceptable value is `RuleSet` .", "TopicArn": "The Amazon Resource Name (ARN) of the Amazon SNS topic to notify when the stop action is taken. You can find the ARN of a topic by using the [ListTopics](https://docs.aws.amazon.com/sns/latest/api/API_ListTopics.html) Amazon SNS operation.\n\nFor more information about Amazon SNS topics, see the [Amazon SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/CreateTopic.html) ." @@ -34690,7 +34994,7 @@ }, "AWS::SES::ReceiptRule.WorkmailAction": { "attributes": {}, - "description": "When included in a receipt rule, this action calls Amazon WorkMail and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS). It usually isn't necessary to set this up manually, because Amazon WorkMail adds the rule automatically during its setup procedure.\n\nFor information using a receipt rule to call Amazon WorkMail, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-workmail.html) .", + "description": "When included in a receipt rule, this action calls Amazon WorkMail and, optionally, publishes a notification to Amazon Simple Notification Service (Amazon SNS). It usually isn't necessary to set this up manually, because Amazon WorkMail adds the rule automatically during its setup procedure.\n\nFor information using a receipt rule to call Amazon WorkMail, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-workmail.html) .", "properties": { "OrganizationArn": "The Amazon Resource Name (ARN) of the Amazon WorkMail organization. Amazon WorkMail ARNs use the following format:\n\n`arn:aws:workmail:::organization/`\n\nYou can find the ID of your organization by using the [ListOrganizations](https://docs.aws.amazon.com/workmail/latest/APIReference/API_ListOrganizations.html) operation in Amazon WorkMail. Amazon WorkMail organization IDs begin with \" `m-` \", followed by a string of alphanumeric characters.\n\nFor information about Amazon WorkMail organizations, see the [Amazon WorkMail Administrator Guide](https://docs.aws.amazon.com/workmail/latest/adminguide/organizations_overview.html) .", "TopicArn": "The Amazon Resource Name (ARN) of the Amazon SNS topic to notify when the WorkMail action is called. You can find the ARN of a topic by using the [ListTopics](https://docs.aws.amazon.com/sns/latest/api/API_ListTopics.html) operation in Amazon SNS.\n\nFor more information about Amazon SNS topics, see the [Amazon SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/CreateTopic.html) ." @@ -34700,7 +35004,7 @@ "attributes": { "Ref": "`Ref` returns the resource name. For example:" }, - "description": "Creates an empty receipt rule set.\n\nFor information about setting up receipt rule sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-receipt-rule-set.html) .\n\nYou can execute this operation no more than once per second.", + "description": "Creates an empty receipt rule set.\n\nFor information about setting up receipt rule sets, see the [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-concepts.html#receiving-email-concepts-rules) .\n\nYou can execute this operation no more than once per second.", "properties": { "RuleSetName": "The name of the receipt rule set to reorder." } @@ -34709,12 +35013,12 @@ "attributes": {}, "description": "Specifies an email template. Email templates enable you to send personalized email to one or more destinations in a single API operation.", "properties": { - "Template": "The content of the email, composed of a subject line, an HTML part, and a text-only part." + "Template": "The content of the email, composed of a subject line and either an HTML part or a text-only part." } }, "AWS::SES::Template.Template": { "attributes": {}, - "description": "The content of the email, composed of a subject line, an HTML part, and a text-only part.", + "description": "The content of the email, composed of a subject line and either an HTML part or a text-only part.", "properties": { "HtmlPart": "The HTML body of the email.", "SubjectPart": "The subject line of the email.", @@ -34815,7 +35119,7 @@ "AutomationTargetParameterName": "Choose the parameter that will define how your automation will branch out. This target is required for associations that use an Automation runbook and target resources by using rate controls. Automation is a capability of AWS Systems Manager .", "CalendarNames": "The names or Amazon Resource Names (ARNs) of the Change Calendar type documents your associations are gated under. The associations only run when that Change Calendar is open. For more information, see [AWS Systems Manager Change Calendar](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-change-calendar) .", "ComplianceSeverity": "The severity level that is assigned to the association.", - "DocumentVersion": "The version of the SSM document to associate with the target.", + "DocumentVersion": "The version of the SSM document to associate with the target.\n\n> Note the following important information.\n> \n> - State Manager doesn't support running associations that use a new version of a document if that document is shared from another account. State Manager always runs the `default` version of a document if shared from another account, even though the Systems Manager console shows that a new version was processed. If you want to run an association using a new version of a document shared form another account, you must set the document version to `default` .\n> - `DocumentVersion` is not valid for documents owned by AWS , such as `AWS-RunPatchBaseline` or `AWS-UpdateSSMAgent` . If you specify `DocumentVersion` for an AWS document, the system returns the following error: \"Error occurred during operation 'CreateAssociation'.\" (RequestToken: , HandlerErrorCode: GeneralServiceException).", "InstanceId": "The ID of the instance that the SSM document is associated with. You must specify the `InstanceId` or `Targets` property.\n\n> `InstanceId` has been deprecated. To specify an instance ID for an association, use the `Targets` parameter. If you use the parameter `InstanceId` , you cannot use the parameters `AssociationName` , `DocumentVersion` , `MaxErrors` , `MaxConcurrency` , `OutputLocation` , or `ScheduleExpression` . To use these parameters, you must use the `Targets` parameter.", "MaxConcurrency": "The maximum number of targets allowed to run the association at the same time. You can specify a number, for example 10, or a percentage of the target set, for example 10%. The default value is 100%, which means all targets run the association at the same time.\n\nIf a new managed node starts and attempts to run an association while Systems Manager is running `MaxConcurrency` associations, the association is allowed to run. During the next association interval, the new managed node will process its association within the limit specified for `MaxConcurrency` .", "MaxErrors": "The number of errors that are allowed before the system stops sending requests to run the association on additional targets. You can specify either an absolute number of errors, for example 10, or a percentage of the target set, for example 10%. If you specify 3, for example, the system stops sending requests when the fourth error is received. If you specify 0, then the system stops sending requests after the first error is returned. If you run an association on 50 managed nodes and set `MaxError` to 10%, then the system stops sending the request when the sixth error is received.\n\nExecutions that are already running an association when `MaxErrors` is reached are allowed to complete, but some of these executions may fail as well. If you need to ensure that there won't be more than max-errors failed executions, set `MaxConcurrency` to 1 so that executions proceed one at a time.", @@ -34949,6 +35253,14 @@ "WindowId": "The ID of the maintenance window where the task is registered." } }, + "AWS::SSM::MaintenanceWindowTask.CloudWatchOutputConfig": { + "attributes": {}, + "description": "Configuration options for sending command output to Amazon CloudWatch Logs.", + "properties": { + "CloudWatchLogGroupName": "The name of the CloudWatch Logs log group where you want to send command output. If you don't specify a group name, AWS Systems Manager automatically creates a log group for you. The log group uses the following naming format:\n\n`aws/ssm/ *SystemsManagerDocumentName*`", + "CloudWatchOutputEnabled": "Enables Systems Manager to send command output to CloudWatch Logs." + } + }, "AWS::SSM::MaintenanceWindowTask.LoggingInfo": { "attributes": {}, "description": "The `LoggingInfo` property type specifies information about the Amazon S3 bucket to write instance-level logs to.\n\n`LoggingInfo` is a property of the [AWS::SSM::MaintenanceWindowTask](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindowtask.html) resource.\n\n> `LoggingInfo` has been deprecated. To specify an Amazon S3 bucket to contain logs, instead use the `OutputS3BucketName` and `OutputS3KeyPrefix` options in the `TaskInvocationParameters` structure. For information about how Systems Manager handles these options for the supported maintenance window task types, see [AWS Systems Manager MaintenanceWindowTask TaskInvocationParameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-taskinvocationparameters.html) .", @@ -34979,9 +35291,11 @@ "attributes": {}, "description": "The `MaintenanceWindowRunCommandParameters` property type specifies the parameters for a `RUN_COMMAND` task type for a maintenance window task in AWS Systems Manager . This means that these parameters are the same as those for the `SendCommand` API call. For more information about `SendCommand` parameters, see [SendCommand](https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_SendCommand.html) in the *AWS Systems Manager API Reference* .\n\nFor information about available parameters in SSM Command documents, you can view the content of the document itself in the Systems Manager console. For information, see [Viewing SSM command document content](https://docs.aws.amazon.com/systems-manager/latest/userguide/viewing-ssm-document-content.html) in the *AWS Systems Manager User Guide* .\n\n`MaintenanceWindowRunCommandParameters` is a property of the [TaskInvocationParameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-taskinvocationparameters.html) property type.", "properties": { + "CloudWatchOutputConfig": "", "Comment": "Information about the command or commands to run.", "DocumentHash": "The SHA-256 or SHA-1 hash created by the system when the document was created. SHA-1 hashes have been deprecated.", "DocumentHashType": "The SHA-256 or SHA-1 hash type. SHA-1 hashes are deprecated.", + "DocumentVersion": "The AWS Systems Manager document (SSM document) version to use in the request. You can specify `$DEFAULT` , `$LATEST` , or a specific version number. If you run commands by using the AWS CLI, then you must escape the first two options by using a backslash. If you specify a version number, then you don't need to use the backslash. For example:\n\n`--document-version \"\\$DEFAULT\"`\n\n`--document-version \"\\$LATEST\"`\n\n`--document-version \"3\"`", "NotificationConfig": "Configurations for sending notifications about command status changes on a per-managed node basis.", "OutputS3BucketName": "The name of the Amazon Simple Storage Service (Amazon S3) bucket.", "OutputS3KeyPrefix": "The S3 bucket subfolder.", @@ -36639,6 +36953,7 @@ }, "description": "The `AWS::SageMaker::Pipeline` resource creates shell scripts that run when you create and/or start a SageMaker Pipeline. For information about SageMaker Pipelines, see [SageMaker Pipelines](https://docs.aws.amazon.com/sagemaker/latest/dg/pipelines.html) in the *Amazon SageMaker Developer Guide* .", "properties": { + "ParallelismConfiguration": "The parallelism configuration applied to the pipeline.", "PipelineDefinition": "The definition of the pipeline. This can be either a JSON string or an Amazon S3 location.", "PipelineDescription": "The description of the pipeline.", "PipelineDisplayName": "The display name of the pipeline.", @@ -36810,7 +37125,7 @@ "attributes": {}, "description": "A structure that defines the rotation configuration for the secret.", "properties": { - "AutomaticallyAfterDays": "Specifies the number of days between automatic scheduled rotations of the secret.\n\nSecrets Manager schedules the next rotation when the previous one is complete. Secrets Manager schedules the date by adding the rotation interval (number of days) to the actual date of the last rotation. The service chooses the hour within that 24-hour date window randomly. The minute is also chosen somewhat randomly, but weighted towards the top of the hour and influenced by a variety of factors that help distribute load." + "AutomaticallyAfterDays": "The number of days between automatic scheduled rotations of the secret. You can use this value to check that your secret meets your compliance guidelines for how often secrets must be rotated.\n\nIn `DescribeSecret` and `ListSecrets` , this value is calculated from the rotation schedule after every successful rotation. In `RotateSecret` , you can set the rotation schedule in `RotationRules` with `AutomaticallyAfterDays` or `ScheduleExpression` , but not both." } }, "AWS::SecretsManager::Secret": { @@ -37213,6 +37528,7 @@ "AWS::ServiceDiscovery::PrivateDnsNamespace": { "attributes": { "Arn": "The Amazon Resource Name (ARN) of the private namespace.", + "HostedZoneId": "", "Id": "The ID of the private namespace.", "Ref": "`Ref` returns the value of `Id` for the namespace, such as `ns-e4anhexample0004` ." }, @@ -37249,6 +37565,7 @@ "AWS::ServiceDiscovery::PublicDnsNamespace": { "attributes": { "Arn": "The Amazon Resource Name (ARN) of the public namespace.", + "HostedZoneId": "", "Id": "The ID of the public namespace.", "Ref": "`Ref` returns the value of `Id` for the namespace, such as `ns-e4anhexample0004` ." }, diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json index 622c282c40348..c954de63802e3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ACMPCA.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ACMPCA::Certificate.ApiPassthrough": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-apipassthrough.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json index a5814fff40048..1b8f032ef8741 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_APS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::APS::RuleGroupsNamespace": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json index 1d3ae77b4f266..12bb6a42f0682 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AccessAnalyzer.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AccessAnalyzer::Analyzer.ArchiveRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-accessanalyzer-analyzer-archiverule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json index fa5275d5cd707..766cacc1e71c1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmazonMQ.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AmazonMQ::Broker.ConfigurationId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amazonmq-broker-configurationid.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json index 1b9e895909597..570bee885e55a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Amplify.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Amplify::App.AutoBranchCreationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplify-app-autobranchcreationconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json index 977b1c8006948..82bb81a72378d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AmplifyUIBuilder.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AmplifyUIBuilder::Component.ComponentBindingPropertiesValue": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentbindingpropertiesvalue.html", @@ -175,13 +175,22 @@ } }, "AWS::AmplifyUIBuilder::Component.ComponentOverrides": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentoverrides.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentoverrides.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::AmplifyUIBuilder::Component.ComponentOverridesValue": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentoverridesvalue.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentoverridesvalue.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::AmplifyUIBuilder::Component.ComponentProperties": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentproperties.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentproperties.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::AmplifyUIBuilder::Component.ComponentProperty": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentproperty.html", @@ -302,10 +311,16 @@ } }, "AWS::AmplifyUIBuilder::Component.ComponentVariantValues": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentvariantvalues.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-componentvariantvalues.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::AmplifyUIBuilder::Component.FormBindings": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-formbindings.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-formbindings.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::AmplifyUIBuilder::Component.Predicate": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-amplifyuibuilder-component-predicate.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json index c1bce1262a552..b3af71597e977 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGateway.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ApiGateway::ApiKey.StageKey": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-apikey-stagekey.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json index 630ad7c2d2331..c8ba7463bd9c1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApiGatewayV2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ApiGatewayV2::Api.BodyS3Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-api-bodys3location.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json index 6a7f6be1a2508..c5795ed18b0df 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppConfig.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AppConfig::Application.Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appconfig-application-tags.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json index aa01f96a4f378..0f819602ea8ce 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppFlow.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AppFlow::ConnectorProfile.AmplitudeConnectorProfileCredentials": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-connectorprofile-amplitudeconnectorprofilecredentials.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json index 2fb1e24a92662..fd09492304583 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppIntegrations.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "53.1.0", "PropertyTypes": { "AWS::AppIntegrations::EventIntegration.EventFilter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appintegrations-eventintegration-eventfilter.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json index 123b1581c8a8d..168b4b80d52d3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppMesh.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AppMesh::GatewayRoute.GatewayRouteHostnameMatch": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appmesh-gatewayroute-gatewayroutehostnamematch.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json index 09037684f7a76..f48ec64bd9e4e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppRunner.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AppRunner::Service.AuthenticationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-authenticationconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json index 17545674f1490..c36a435a9567a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppStream.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AppStream::AppBlock.S3Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appstream-appblock-s3location.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json index d52e008624e8c..4adc58cb8c678 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AppSync.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AppSync::DataSource.AuthorizationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-datasource-authorizationconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json index d9755e4891b40..a156a3298ce8b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationAutoScaling.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ApplicationAutoScaling::ScalableTarget.ScalableTargetAction": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationautoscaling-scalabletarget-scalabletargetaction.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json index b09e8af1ba116..e4736061901f1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ApplicationInsights.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ApplicationInsights::Application.Alarm": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-alarm.html", @@ -105,6 +105,18 @@ "Type": "List", "UpdateType": "Mutable" }, + "HAClusterPrometheusExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-configurationdetails.html#cfn-applicationinsights-application-configurationdetails-haclusterprometheusexporter", + "Required": false, + "Type": "HAClusterPrometheusExporter", + "UpdateType": "Mutable" + }, + "HANAPrometheusExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-configurationdetails.html#cfn-applicationinsights-application-configurationdetails-hanaprometheusexporter", + "Required": false, + "Type": "HANAPrometheusExporter", + "UpdateType": "Mutable" + }, "JMXPrometheusExporter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-configurationdetails.html#cfn-applicationinsights-application-configurationdetails-jmxprometheusexporter", "Required": false, @@ -145,6 +157,52 @@ } } }, + "AWS::ApplicationInsights::Application.HAClusterPrometheusExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-haclusterprometheusexporter.html", + "Properties": { + "PrometheusPort": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-haclusterprometheusexporter.html#cfn-applicationinsights-application-haclusterprometheusexporter-prometheusport", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::ApplicationInsights::Application.HANAPrometheusExporter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-hanaprometheusexporter.html", + "Properties": { + "AgreeToInstallHANADBClient": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-hanaprometheusexporter.html#cfn-applicationinsights-application-hanaprometheusexporter-agreetoinstallhanadbclient", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "HANAPort": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-hanaprometheusexporter.html#cfn-applicationinsights-application-hanaprometheusexporter-hanaport", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "HANASID": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-hanaprometheusexporter.html#cfn-applicationinsights-application-hanaprometheusexporter-hanasid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "HANASecretName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-hanaprometheusexporter.html#cfn-applicationinsights-application-hanaprometheusexporter-hanasecretname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "PrometheusPort": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-hanaprometheusexporter.html#cfn-applicationinsights-application-hanaprometheusexporter-prometheusport", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::ApplicationInsights::Application.JMXPrometheusExporter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationinsights-application-jmxprometheusexporter.html", "Properties": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json index b16812d311319..26721afe28a3d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Athena.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Athena::WorkGroup.EncryptionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-encryptionconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json index 61d7a09f12a2f..cb771b7159917 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AuditManager.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AuditManager::Assessment.AWSAccount": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-auditmanager-assessment-awsaccount.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json index 3193038d3ee71..13f54cc4fa820 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScaling.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AutoScaling::AutoScalingGroup.AcceleratorCountRequest": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-acceleratorcountrequest.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json index 14964a920c478..21b6ce1b89da2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_AutoScalingPlans.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::AutoScalingPlans::ScalingPlan.ApplicationSource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscalingplans-scalingplan-applicationsource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json index fef22afe7dd6e..df3e1d221e433 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Backup.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Backup::BackupPlan.AdvancedBackupSettingResourceType": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-backup-backupplan-advancedbackupsettingresourcetype.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json index 17486408ef5a2..ad32c594d50a1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Batch.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Batch::ComputeEnvironment.ComputeResources": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-batch-computeenvironment-computeresources.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json index bd570f257e310..510fcee1ebb79 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Budgets.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Budgets::Budget.BudgetData": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-budgets-budget-budgetdata.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json index 10ec09676d838..c0b06d418c562 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CE.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CE::AnomalySubscription.Subscriber": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ce-anomalysubscription-subscriber.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json index 248d7f45762b8..fcc68e91e0b4f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CUR.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CUR::ReportDefinition": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json index 92e62f2abe62c..9d0386533a34f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cassandra.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Cassandra::Table.BillingMode": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cassandra-table-billingmode.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json index 89c119e8036fa..320a0cf950ae1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CertificateManager.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CertificateManager::Account.ExpiryEventsConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-certificatemanager-account-expiryeventsconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json index 26dbcd5632dc0..59d5d78ce64fb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Chatbot.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Chatbot::SlackChannelConfiguration": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json index b5e0218b50999..1a184c0252d80 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cloud9.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Cloud9::EnvironmentEC2.Repository": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloud9-environmentec2-repository.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json index 505651a0bf526..de2d844bbd13e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFormation.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CloudFormation::ResourceVersion.LoggingConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudformation-resourceversion-loggingconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json index 379507c155bb3..362163e1a04d9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudFront.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CloudFront::CachePolicy.CachePolicyConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-cachepolicy-cachepolicyconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json index 1aceb8d9e912a..36b2a2b807494 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudTrail.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CloudTrail::Trail.DataResource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudtrail-trail-dataresource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json index 8b157900c704b..6a3ab4af9d1da 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CloudWatch.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CloudWatch::Alarm.Dimension": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cw-dimension.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json index 120a64e210246..36cef8075f461 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeArtifact.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CodeArtifact::Domain": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json index fcd07ab290568..6fffba14ea56c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeBuild.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodeBuild::Project.Artifacts": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-project-artifacts.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json index 541242d2ba064..3f4217842ec26 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeCommit.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodeCommit::Repository.Code": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codecommit-repository-code.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json index cfbc56cecd081..49876dabb3a2e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeDeploy.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodeDeploy::DeploymentConfig.MinimumHealthyHosts": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codedeploy-deploymentconfig-minimumhealthyhosts.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json index 9c7735ec4fc0e..3d9a64b957f02 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruProfiler.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodeGuruProfiler::ProfilingGroup.Channel": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codeguruprofiler-profilinggroup-channel.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json index 0f8ec67bec645..2c497e67aa887 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeGuruReviewer.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CodeGuruReviewer::RepositoryAssociation": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json index bd180e3e81fcc..e436756f1f56a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodePipeline.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodePipeline::CustomActionType.ArtifactDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-customactiontype-artifactdetails.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json index 96f200ecd4e04..dba5cdca6949e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStar.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodeStar::GitHubRepository.Code": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codestar-githubrepository-code.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json index d4bc922519cff..31220f28bb919 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarConnections.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::CodeStarConnections::Connection": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json index 9d8fed731a9c8..bae272f9088f6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CodeStarNotifications.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CodeStarNotifications::NotificationRule.Target": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codestarnotifications-notificationrule-target.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json index 5227cc1bbf620..1e9c2059ad79a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Cognito.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Cognito::IdentityPool.CognitoIdentityProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-identitypool-cognitoidentityprovider.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json index cb03eba8824de..b43aafa940f68 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Config.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Config::ConfigRule.Scope": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-config-configrule-scope.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json index 469b6626393cf..78e4de40661f6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Connect.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Connect::HoursOfOperation.HoursOfOperationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-connect-hoursofoperation-hoursofoperationconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json index 23161e2b2dec5..911bcaef0fab8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_CustomerProfiles.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::CustomerProfiles::Integration.ConnectorOperator": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json index 79500a3d00a45..f96e8c9aabc9a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DAX.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DAX::Cluster.SSESpecification": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dax-cluster-ssespecification.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json index cd5782c6e5f5f..e65f23c56b8ec 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DLM.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DLM::LifecyclePolicy.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json index ffe692258dc0d..1655ea27117a8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DMS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DMS::Endpoint.DocDbSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-docdbsettings.html", @@ -58,6 +58,89 @@ } } }, + "AWS::DMS::Endpoint.GcpMySQLSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html", + "Properties": { + "AfterConnectScript": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-afterconnectscript", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CleanSourceMetadataOnMismatch": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-cleansourcemetadataonmismatch", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "DatabaseName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-databasename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "EventsPollInterval": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-eventspollinterval", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "MaxFileSize": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-maxfilesize", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ParallelLoadThreads": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-parallelloadthreads", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Password": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-password", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Port": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-port", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "SecretsManagerAccessRoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-secretsmanageraccessrolearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SecretsManagerSecretId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-secretsmanagersecretid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServerName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-servername", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServerTimezone": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-servertimezone", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Username": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-gcpmysqlsettings.html#cfn-dms-endpoint-gcpmysqlsettings-username", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::DMS::Endpoint.IbmDb2Settings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dms-endpoint-ibmdb2settings.html", "Properties": { @@ -654,6 +737,12 @@ "Required": false, "UpdateType": "Mutable" }, + "GcpMySQLSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dms-endpoint.html#cfn-dms-endpoint-gcpmysqlsettings", + "Required": false, + "Type": "GcpMySQLSettings", + "UpdateType": "Mutable" + }, "IbmDb2Settings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dms-endpoint.html#cfn-dms-endpoint-ibmdb2settings", "Required": false, diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json index a5886268c2d16..7cb2359cbc3c8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataBrew.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DataBrew::Dataset.CsvOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-dataset-csvoptions.html", @@ -586,6 +586,12 @@ "Required": true, "UpdateType": "Mutable" }, + "BucketOwner": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputlocation.html#cfn-databrew-job-outputlocation-bucketowner", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Key": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputlocation.html#cfn-databrew-job-outputlocation-key", "PrimitiveType": "String", @@ -595,7 +601,10 @@ } }, "AWS::DataBrew::Job.ParameterMap": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-parametermap.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-parametermap.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::DataBrew::Job.ProfileConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-profileconfiguration.html", @@ -654,6 +663,12 @@ "Required": true, "UpdateType": "Mutable" }, + "BucketOwner": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-s3location.html#cfn-databrew-job-s3location-bucketowner", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Key": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-s3location.html#cfn-databrew-job-s3location-key", "PrimitiveType": "String", @@ -813,7 +828,10 @@ } }, "AWS::DataBrew::Recipe.ParameterMap": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-recipe-parametermap.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-recipe-parametermap.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::DataBrew::Recipe.RecipeParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-recipe-recipeparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json index 3a4165f28ac4b..4f42b9bc2bc2e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataPipeline.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DataPipeline::Pipeline.Field": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-datapipeline-pipeline-pipelineobjects-fields.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json index 3bea1482dc65b..94179f57eb87f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DataSync.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DataSync::LocationEFS.Ec2Config": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-datasync-locationefs-ec2config.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json index 6f23538af7616..99cdc10501c26 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Detective.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Detective::Graph": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json index 606d19bf8ccda..77fac39d2308f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DevOpsGuru.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DevOpsGuru::NotificationChannel.NotificationChannelConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-devopsguru-notificationchannel-notificationchannelconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json index ff40ca6d13a97..eb4d06f810e15 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DirectoryService.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DirectoryService::MicrosoftAD.VpcSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-directoryservice-microsoftad-vpcsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json index 7724c6b92277a..b4fd41803376b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DocDB.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::DocDB::DBCluster": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json index fce8b13b3c4ab..b4b1d7662ed6c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_DynamoDB.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::DynamoDB::GlobalTable.AttributeDefinition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-attributedefinition.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json index dd378cad166e2..d78df543a7cb8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EC2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::EC2::CapacityReservation.TagSpecification": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-capacityreservation-tagspecification.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json index d252878b0d862..78dc1ff454dd4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECR.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ECR::ReplicationConfiguration.ReplicationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json index 4e28f618b223c..e1a0b139bb734 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ECS.json @@ -1,5 +1,5 @@ { - "$version": "51.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ECS::CapacityProvider.AutoScalingGroupProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-capacityprovider-autoscalinggroupprovider.html", @@ -756,7 +756,6 @@ "Properties": { "AuthorizationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-efsvolumeconfiguration.html#cfn-ecs-taskdefinition-efsvolumeconfiguration-authorizationconfig", - "PrimitiveType": "Json", "Required": false, "Type": "AuthorizationConfig", "UpdateType": "Immutable" diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json index ee85713d3e080..6f210def5abcb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EFS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::EFS::AccessPoint.AccessPointTag": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-efs-accesspoint-accesspointtag.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json index f45918127ad5d..a8f1ec9b85fc6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EKS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::EKS::Cluster.ClusterLogging": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eks-cluster-clusterlogging.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json index d7b325eb529df..189c5c6ac0234 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMR.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::EMR::Cluster.Application": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticmapreduce-cluster-application.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json index f60c93ac0c948..72225dc5a8a36 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EMRContainers.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::EMRContainers::VirtualCluster.ContainerInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-emrcontainers-virtualcluster-containerinfo.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json index f767bf251126a..fcbc2ba991437 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElastiCache.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ElastiCache::CacheCluster.CloudWatchLogsDestinationDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-cachecluster-cloudwatchlogsdestinationdetails.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json index 2096de48abd48..b2d5f972168cf 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticBeanstalk.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ElasticBeanstalk::Application.ApplicationResourceLifecycleConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticbeanstalk-application-applicationresourcelifecycleconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json index c8525a1258f6c..a46d39b68265d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancing.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ElasticLoadBalancing::LoadBalancer.AccessLoggingPolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb-accessloggingpolicy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json index caf56c1ab64a8..ee82559f5550d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ElasticLoadBalancingV2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ElasticLoadBalancingV2::Listener.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticloadbalancingv2-listener-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json index 9d70be4cb4ef5..d70abe3bf848e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Elasticsearch.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Elasticsearch::Domain.AdvancedSecurityOptionsInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticsearch-domain-advancedsecurityoptionsinput.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json index c65bbf2e449a1..87e2b87c1f3e4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_EventSchemas.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::EventSchemas::Discoverer.TagsEntry": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eventschemas-discoverer-tagsentry.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json index 91b7b7e8343b8..b7e2e5a1305cd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Events.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Events::EventBus.TagEntry": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-eventbus-tagentry.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json index efaef09a38170..0e5ff1324d462 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Evidently.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Evidently::Experiment.MetricGoalObject": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-evidently-experiment-metricgoalobject.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json index 00062927b9490..3418a0910e65c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FIS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::FIS::ExperimentTemplate.ExperimentTemplateAction": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplateaction.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json index 533b85ab0a40c..b300508f84e68 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FMS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::FMS::Policy.IEMap": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fms-policy-iemap.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json index 77b7021c2d7bc..241438566d5d0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FSx.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::FSx::FileSystem.AuditLogConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fsx-filesystem-windowsconfiguration-auditlogconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json index 4e608724558b5..39186e23b0362 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FinSpace.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::FinSpace::Environment.FederationParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json index 8c024f102368a..b047d1ceda934 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Forecast.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Forecast::Dataset": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json index 780b6537e27bd..9f7cf99628beb 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_FraudDetector.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::FraudDetector::Detector.EntityType": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json index 024232d7655f7..9f48333d2edc8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GameLift.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::GameLift::Alias.RoutingStrategy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-alias-routingstrategy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json index c26531868dfa8..31900d87c1392 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GlobalAccelerator.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::GlobalAccelerator::EndpointGroup.EndpointConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-globalaccelerator-endpointgroup-endpointconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json index 3195c26628fcf..1da2845059db3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Glue.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Glue::Classifier.CsvClassifier": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-glue-classifier-csvclassifier.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json index ea6290026de34..1acdfb17a2fc0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Greengrass.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Greengrass::ConnectorDefinition.Connector": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-greengrass-connectordefinition-connector.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json index 1cfba354f55cb..98636275d0cc4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GreengrassV2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::GreengrassV2::ComponentVersion.ComponentDependencyRequirement": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-greengrassv2-componentversion-componentdependencyrequirement.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json index ee891e28da1bf..0e2802679940b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GroundStation.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::GroundStation::Config.AntennaDownlinkConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-groundstation-config-antennadownlinkconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json index c74e38b2707b1..8b731ed2c0f5a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_GuardDuty.json @@ -1,9 +1,15 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::GuardDuty::Detector.CFNDataSourceConfigurations": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfndatasourceconfigurations.html", "Properties": { + "Kubernetes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfndatasourceconfigurations.html#cfn-guardduty-detector-cfndatasourceconfigurations-kubernetes", + "Required": false, + "Type": "CFNKubernetesConfiguration", + "UpdateType": "Mutable" + }, "S3Logs": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfndatasourceconfigurations.html#cfn-guardduty-detector-cfndatasourceconfigurations-s3logs", "Required": false, @@ -12,6 +18,28 @@ } } }, + "AWS::GuardDuty::Detector.CFNKubernetesAuditLogsConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfnkubernetesauditlogsconfiguration.html", + "Properties": { + "Enable": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfnkubernetesauditlogsconfiguration.html#cfn-guardduty-detector-cfnkubernetesauditlogsconfiguration-enable", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::GuardDuty::Detector.CFNKubernetesConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfnkubernetesconfiguration.html", + "Properties": { + "AuditLogs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfnkubernetesconfiguration.html#cfn-guardduty-detector-cfnkubernetesconfiguration-auditlogs", + "Required": false, + "Type": "CFNKubernetesAuditLogsConfiguration", + "UpdateType": "Mutable" + } + } + }, "AWS::GuardDuty::Detector.CFNS3LogsConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-guardduty-detector-cfns3logsconfiguration.html", "Properties": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json index 184183403c5eb..53cfda04042af 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_HealthLake.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::HealthLake::FHIRDatastore.KmsEncryptionConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-healthlake-fhirdatastore-kmsencryptionconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json index aea05b1dfe0db..0988acb643955 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IAM.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IAM::Group.Policy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json index 2372f426bbdb3..51ae47df57663 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IVS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IVS::RecordingConfiguration.DestinationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-destinationconfiguration.html", @@ -22,6 +22,23 @@ "UpdateType": "Immutable" } } + }, + "AWS::IVS::RecordingConfiguration.ThumbnailConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thumbnailconfiguration.html", + "Properties": { + "RecordingMode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thumbnailconfiguration.html#cfn-ivs-recordingconfiguration-thumbnailconfiguration-recordingmode", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "TargetIntervalSeconds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thumbnailconfiguration.html#cfn-ivs-recordingconfiguration-thumbnailconfiguration-targetintervalseconds", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Immutable" + } + } } }, "ResourceTypes": { @@ -142,6 +159,12 @@ "Required": false, "Type": "List", "UpdateType": "Mutable" + }, + "ThumbnailConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ivs-recordingconfiguration.html#cfn-ivs-recordingconfiguration-thumbnailconfiguration", + "Required": false, + "Type": "ThumbnailConfiguration", + "UpdateType": "Immutable" } } }, diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json index 34a6a66cf5ed6..de690db974d43 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ImageBuilder.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ImageBuilder::ContainerRecipe.ComponentConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-componentconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json index 8e6d7a60b39f8..e1f0c17c35a28 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Inspector.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::Inspector::AssessmentTarget": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json index f798592860c06..852dee1bf86dd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_InspectorV2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::InspectorV2::Filter.DateFilter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-inspectorv2-filter-datefilter.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json index 832964ade69ad..47efca0476631 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoT::AccountAuditConfiguration.AuditCheckConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json index 8bc663da25fe7..9549e31df153c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoT1Click.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoT1Click::Project.DeviceTemplate": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot1click-project-devicetemplate.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json index dde9b516dd10f..a302abae627f8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTAnalytics.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoTAnalytics::Channel.ChannelStorage": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-channel-channelstorage.html", @@ -59,7 +59,10 @@ } }, "AWS::IoTAnalytics::Channel.ServiceManagedS3": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-channel-servicemanageds3.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-channel-servicemanageds3.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::IoTAnalytics::Dataset.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-dataset-action.html", @@ -584,7 +587,10 @@ } }, "AWS::IoTAnalytics::Datastore.JsonConfiguration": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-datastore-jsonconfiguration.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-datastore-jsonconfiguration.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::IoTAnalytics::Datastore.ParquetConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-datastore-parquetconfiguration.html", @@ -639,7 +645,10 @@ } }, "AWS::IoTAnalytics::Datastore.ServiceManagedS3": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-datastore-servicemanageds3.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-datastore-servicemanageds3.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::IoTAnalytics::Datastore.TimestampPartition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotanalytics-datastore-timestamppartition.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json index 79a0efcceac8e..02481d3619545 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTCoreDeviceAdvisor.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::IoTCoreDeviceAdvisor::SuiteDefinition": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json index 97ae22098ae67..f3037eea17264 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTEvents.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoTEvents::DetectorModel.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotevents-detectormodel-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json index 91e7831e64391..4d382071cf2c4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTFleetHub.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::IoTFleetHub::Application": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json index 4528134d4e651..9b4a104719107 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTSiteWise.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoTSiteWise::AccessPolicy.AccessPolicyIdentity": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotsitewise-accesspolicy-accesspolicyidentity.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json index 808b194446d0a..da1c98bd09539 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTThingsGraph.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoTThingsGraph::FlowTemplate.DefinitionDocument": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotthingsgraph-flowtemplate-definitiondocument.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json index 7fea2730d7263..ed16aeedab453 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_IoTWireless.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::IoTWireless::DeviceProfile.LoRaWANDeviceProfile": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-deviceprofile-lorawandeviceprofile.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json index f9a0ed9ee36ad..983705d9e200e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KMS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::KMS::Alias": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json new file mode 100644 index 0000000000000..4a6915456bcf8 --- /dev/null +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KafkaConnect.json @@ -0,0 +1,390 @@ +{ + "$version": "54.0.0", + "PropertyTypes": { + "AWS::KafkaConnect::Connector.ApacheKafkaCluster": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-apachekafkacluster.html", + "Properties": { + "BootstrapServers": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-apachekafkacluster.html#cfn-kafkaconnect-connector-apachekafkacluster-bootstrapservers", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Vpc": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-apachekafkacluster.html#cfn-kafkaconnect-connector-apachekafkacluster-vpc", + "Required": true, + "Type": "Vpc", + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.AutoScaling": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-autoscaling.html", + "Properties": { + "MaxWorkerCount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-autoscaling.html#cfn-kafkaconnect-connector-autoscaling-maxworkercount", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "McuCount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-autoscaling.html#cfn-kafkaconnect-connector-autoscaling-mcucount", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "MinWorkerCount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-autoscaling.html#cfn-kafkaconnect-connector-autoscaling-minworkercount", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "ScaleInPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-autoscaling.html#cfn-kafkaconnect-connector-autoscaling-scaleinpolicy", + "Required": true, + "Type": "ScaleInPolicy", + "UpdateType": "Mutable" + }, + "ScaleOutPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-autoscaling.html#cfn-kafkaconnect-connector-autoscaling-scaleoutpolicy", + "Required": true, + "Type": "ScaleOutPolicy", + "UpdateType": "Mutable" + } + } + }, + "AWS::KafkaConnect::Connector.Capacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-capacity.html", + "Properties": { + "AutoScaling": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-capacity.html#cfn-kafkaconnect-connector-capacity-autoscaling", + "Required": false, + "Type": "AutoScaling", + "UpdateType": "Mutable" + }, + "ProvisionedCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-capacity.html#cfn-kafkaconnect-connector-capacity-provisionedcapacity", + "Required": false, + "Type": "ProvisionedCapacity", + "UpdateType": "Mutable" + } + } + }, + "AWS::KafkaConnect::Connector.CloudWatchLogsLogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-cloudwatchlogslogdelivery.html", + "Properties": { + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-cloudwatchlogslogdelivery.html#cfn-kafkaconnect-connector-cloudwatchlogslogdelivery-enabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Immutable" + }, + "LogGroup": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-cloudwatchlogslogdelivery.html#cfn-kafkaconnect-connector-cloudwatchlogslogdelivery-loggroup", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.CustomPlugin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-customplugin.html", + "Properties": { + "CustomPluginArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-customplugin.html#cfn-kafkaconnect-connector-customplugin-custompluginarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Revision": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-customplugin.html#cfn-kafkaconnect-connector-customplugin-revision", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.FirehoseLogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-firehoselogdelivery.html", + "Properties": { + "DeliveryStream": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-firehoselogdelivery.html#cfn-kafkaconnect-connector-firehoselogdelivery-deliverystream", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-firehoselogdelivery.html#cfn-kafkaconnect-connector-firehoselogdelivery-enabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.KafkaCluster": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-kafkacluster.html", + "Properties": { + "ApacheKafkaCluster": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-kafkacluster.html#cfn-kafkaconnect-connector-kafkacluster-apachekafkacluster", + "Required": true, + "Type": "ApacheKafkaCluster", + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.KafkaClusterClientAuthentication": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-kafkaclusterclientauthentication.html", + "Properties": { + "AuthenticationType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-kafkaclusterclientauthentication.html#cfn-kafkaconnect-connector-kafkaclusterclientauthentication-authenticationtype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.KafkaClusterEncryptionInTransit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-kafkaclusterencryptionintransit.html", + "Properties": { + "EncryptionType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-kafkaclusterencryptionintransit.html#cfn-kafkaconnect-connector-kafkaclusterencryptionintransit-encryptiontype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.LogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-logdelivery.html", + "Properties": { + "WorkerLogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-logdelivery.html#cfn-kafkaconnect-connector-logdelivery-workerlogdelivery", + "Required": true, + "Type": "WorkerLogDelivery", + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.Plugin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-plugin.html", + "Properties": { + "CustomPlugin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-plugin.html#cfn-kafkaconnect-connector-plugin-customplugin", + "Required": true, + "Type": "CustomPlugin", + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.ProvisionedCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-provisionedcapacity.html", + "Properties": { + "McuCount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-provisionedcapacity.html#cfn-kafkaconnect-connector-provisionedcapacity-mcucount", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "WorkerCount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-provisionedcapacity.html#cfn-kafkaconnect-connector-provisionedcapacity-workercount", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::KafkaConnect::Connector.S3LogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-s3logdelivery.html", + "Properties": { + "Bucket": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-s3logdelivery.html#cfn-kafkaconnect-connector-s3logdelivery-bucket", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-s3logdelivery.html#cfn-kafkaconnect-connector-s3logdelivery-enabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Immutable" + }, + "Prefix": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-s3logdelivery.html#cfn-kafkaconnect-connector-s3logdelivery-prefix", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.ScaleInPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-scaleinpolicy.html", + "Properties": { + "CpuUtilizationPercentage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-scaleinpolicy.html#cfn-kafkaconnect-connector-scaleinpolicy-cpuutilizationpercentage", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::KafkaConnect::Connector.ScaleOutPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-scaleoutpolicy.html", + "Properties": { + "CpuUtilizationPercentage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-scaleoutpolicy.html#cfn-kafkaconnect-connector-scaleoutpolicy-cpuutilizationpercentage", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::KafkaConnect::Connector.Vpc": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-vpc.html", + "Properties": { + "SecurityGroups": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-vpc.html#cfn-kafkaconnect-connector-vpc-securitygroups", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + }, + "Subnets": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-vpc.html#cfn-kafkaconnect-connector-vpc-subnets", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.WorkerConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerconfiguration.html", + "Properties": { + "Revision": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerconfiguration.html#cfn-kafkaconnect-connector-workerconfiguration-revision", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Immutable" + }, + "WorkerConfigurationArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerconfiguration.html#cfn-kafkaconnect-connector-workerconfiguration-workerconfigurationarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::KafkaConnect::Connector.WorkerLogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerlogdelivery.html", + "Properties": { + "CloudWatchLogs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerlogdelivery.html#cfn-kafkaconnect-connector-workerlogdelivery-cloudwatchlogs", + "Required": false, + "Type": "CloudWatchLogsLogDelivery", + "UpdateType": "Immutable" + }, + "Firehose": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerlogdelivery.html#cfn-kafkaconnect-connector-workerlogdelivery-firehose", + "Required": false, + "Type": "FirehoseLogDelivery", + "UpdateType": "Immutable" + }, + "S3": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kafkaconnect-connector-workerlogdelivery.html#cfn-kafkaconnect-connector-workerlogdelivery-s3", + "Required": false, + "Type": "S3LogDelivery", + "UpdateType": "Immutable" + } + } + } + }, + "ResourceTypes": { + "AWS::KafkaConnect::Connector": { + "Attributes": { + "ConnectorArn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html", + "Properties": { + "Capacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-capacity", + "Required": true, + "Type": "Capacity", + "UpdateType": "Mutable" + }, + "ConnectorConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-connectorconfiguration", + "PrimitiveItemType": "String", + "Required": true, + "Type": "Map", + "UpdateType": "Immutable" + }, + "ConnectorDescription": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-connectordescription", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "ConnectorName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-connectorname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "KafkaCluster": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-kafkacluster", + "Required": true, + "Type": "KafkaCluster", + "UpdateType": "Immutable" + }, + "KafkaClusterClientAuthentication": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-kafkaclusterclientauthentication", + "Required": true, + "Type": "KafkaClusterClientAuthentication", + "UpdateType": "Immutable" + }, + "KafkaClusterEncryptionInTransit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-kafkaclusterencryptionintransit", + "Required": true, + "Type": "KafkaClusterEncryptionInTransit", + "UpdateType": "Immutable" + }, + "KafkaConnectVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-kafkaconnectversion", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "LogDelivery": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-logdelivery", + "Required": false, + "Type": "LogDelivery", + "UpdateType": "Immutable" + }, + "Plugins": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-plugins", + "DuplicatesAllowed": false, + "ItemType": "Plugin", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + }, + "ServiceExecutionRoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-serviceexecutionrolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "WorkerConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kafkaconnect-connector.html#cfn-kafkaconnect-connector-workerconfiguration", + "Required": false, + "Type": "WorkerConfiguration", + "UpdateType": "Immutable" + } + } + } + } +} diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json index 2d45b9f78beff..19f882d89c9b9 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kendra.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Kendra::DataSource.AccessControlListConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kendra-datasource-accesscontrollistconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json index c8d7dfddb2376..aa0c979da1eb2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Kinesis.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Kinesis::Stream.StreamEncryption": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesis-stream-streamencryption.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json index 7b3d93fdd7fc1..ddfc7d7a02c9a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalytics.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::KinesisAnalytics::Application.CSVMappingParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisanalytics-application-csvmappingparameters.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json index 09fb40024d528..1bce6e96a9b13 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisAnalyticsV2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::KinesisAnalyticsV2::Application.ApplicationCodeConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisanalyticsv2-application-applicationcodeconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json index e4db8a913d1fd..c1f4d7f31ac08 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisFirehose.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::KinesisFirehose::DeliveryStream.AmazonopensearchserviceBufferingHints": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-kinesisfirehose-deliverystream-amazonopensearchservicebufferinghints.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json index 2b6061aa77a15..e924780705c48 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_KinesisVideo.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::KinesisVideo::SignalingChannel": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json index 5b1a3090c00dd..aa4b985508bce 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LakeFormation.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::LakeFormation::DataLakeSettings.Admins": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lakeformation-datalakesettings-admins.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json index 397ccdb2d5cc8..b3c5b359462ea 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lambda.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Lambda::Alias.AliasRoutingConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-alias-aliasroutingconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json index 3cd46fdcb36e5..08e42b7f235ab 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lex.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Lex::Bot.BotLocale": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lex-bot-botlocale.html", @@ -1128,7 +1128,10 @@ } }, "AWS::Lex::ResourcePolicy.Policy": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lex-resourcepolicy-policy.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lex-resourcepolicy-policy.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" } }, "ResourceTypes": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json index e856b69991447..35615e61f2b9c 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LicenseManager.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::LicenseManager::License.BorrowConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-licensemanager-license-borrowconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json index 7d62d286225e8..f6234c99eacab 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Lightsail.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Lightsail::Bucket.AccessRules": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-bucket-accessrules.html", @@ -18,6 +18,183 @@ } } }, + "AWS::Lightsail::Container.Container": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html", + "Properties": { + "Command": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html#cfn-lightsail-container-container-command", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "ContainerName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html#cfn-lightsail-container-container-containername", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Environment": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html#cfn-lightsail-container-container-environment", + "DuplicatesAllowed": false, + "ItemType": "EnvironmentVariable", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Image": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html#cfn-lightsail-container-container-image", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Ports": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-container.html#cfn-lightsail-container-container-ports", + "DuplicatesAllowed": false, + "ItemType": "PortInfo", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container.ContainerServiceDeployment": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-containerservicedeployment.html", + "Properties": { + "Containers": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-containerservicedeployment.html#cfn-lightsail-container-containerservicedeployment-containers", + "DuplicatesAllowed": false, + "ItemType": "Container", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "PublicEndpoint": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-containerservicedeployment.html#cfn-lightsail-container-containerservicedeployment-publicendpoint", + "Required": false, + "Type": "PublicEndpoint", + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container.EnvironmentVariable": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-environmentvariable.html", + "Properties": { + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-environmentvariable.html#cfn-lightsail-container-environmentvariable-value", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Variable": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-environmentvariable.html#cfn-lightsail-container-environmentvariable-variable", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container.HealthCheckConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html", + "Properties": { + "HealthyThreshold": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html#cfn-lightsail-container-healthcheckconfig-healthythreshold", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "IntervalSeconds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html#cfn-lightsail-container-healthcheckconfig-intervalseconds", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Path": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html#cfn-lightsail-container-healthcheckconfig-path", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SuccessCodes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html#cfn-lightsail-container-healthcheckconfig-successcodes", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "TimeoutSeconds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html#cfn-lightsail-container-healthcheckconfig-timeoutseconds", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "UnhealthyThreshold": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-healthcheckconfig.html#cfn-lightsail-container-healthcheckconfig-unhealthythreshold", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container.PortInfo": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-portinfo.html", + "Properties": { + "Port": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-portinfo.html#cfn-lightsail-container-portinfo-port", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Protocol": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-portinfo.html#cfn-lightsail-container-portinfo-protocol", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container.PublicDomainName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicdomainname.html", + "Properties": { + "CertificateName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicdomainname.html#cfn-lightsail-container-publicdomainname-certificatename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DomainNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicdomainname.html#cfn-lightsail-container-publicdomainname-domainnames", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container.PublicEndpoint": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicendpoint.html", + "Properties": { + "ContainerName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicendpoint.html#cfn-lightsail-container-publicendpoint-containername", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ContainerPort": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicendpoint.html#cfn-lightsail-container-publicendpoint-containerport", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "HealthCheckConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-container-publicendpoint.html#cfn-lightsail-container-publicendpoint-healthcheckconfig", + "Required": false, + "Type": "HealthCheckConfig", + "UpdateType": "Mutable" + } + } + }, "AWS::Lightsail::Database.RelationalDatabaseParameter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-database-relationaldatabaseparameter.html", "Properties": { @@ -105,6 +282,167 @@ } } }, + "AWS::Lightsail::Distribution.CacheBehavior": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachebehavior.html", + "Properties": { + "Behavior": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachebehavior.html#cfn-lightsail-distribution-cachebehavior-behavior", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Distribution.CacheBehaviorPerPath": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachebehaviorperpath.html", + "Properties": { + "Behavior": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachebehaviorperpath.html#cfn-lightsail-distribution-cachebehaviorperpath-behavior", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Path": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachebehaviorperpath.html#cfn-lightsail-distribution-cachebehaviorperpath-path", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Distribution.CacheSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html", + "Properties": { + "AllowedHTTPMethods": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-allowedhttpmethods", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CachedHTTPMethods": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-cachedhttpmethods", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DefaultTTL": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-defaultttl", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ForwardedCookies": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-forwardedcookies", + "Required": false, + "Type": "CookieObject", + "UpdateType": "Mutable" + }, + "ForwardedHeaders": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-forwardedheaders", + "Required": false, + "Type": "HeaderObject", + "UpdateType": "Mutable" + }, + "ForwardedQueryStrings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-forwardedquerystrings", + "Required": false, + "Type": "QueryStringObject", + "UpdateType": "Mutable" + }, + "MaximumTTL": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-maximumttl", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "MinimumTTL": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cachesettings.html#cfn-lightsail-distribution-cachesettings-minimumttl", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Distribution.CookieObject": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cookieobject.html", + "Properties": { + "CookiesAllowList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cookieobject.html#cfn-lightsail-distribution-cookieobject-cookiesallowlist", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Option": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-cookieobject.html#cfn-lightsail-distribution-cookieobject-option", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Distribution.HeaderObject": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-headerobject.html", + "Properties": { + "HeadersAllowList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-headerobject.html#cfn-lightsail-distribution-headerobject-headersallowlist", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Option": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-headerobject.html#cfn-lightsail-distribution-headerobject-option", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Distribution.InputOrigin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-inputorigin.html", + "Properties": { + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-inputorigin.html#cfn-lightsail-distribution-inputorigin-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ProtocolPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-inputorigin.html#cfn-lightsail-distribution-inputorigin-protocolpolicy", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RegionName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-inputorigin.html#cfn-lightsail-distribution-inputorigin-regionname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Distribution.QueryStringObject": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-querystringobject.html", + "Properties": { + "Option": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-querystringobject.html#cfn-lightsail-distribution-querystringobject-option", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "QueryStringsAllowList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-distribution-querystringobject.html#cfn-lightsail-distribution-querystringobject-querystringsallowlist", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::Lightsail::Instance.AddOn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lightsail-instance-addon.html", "Properties": { @@ -492,6 +830,106 @@ } } }, + "AWS::Lightsail::Certificate": { + "Attributes": { + "CertificateArn": { + "PrimitiveType": "String" + }, + "Status": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-certificate.html", + "Properties": { + "CertificateName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-certificate.html#cfn-lightsail-certificate-certificatename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "DomainName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-certificate.html#cfn-lightsail-certificate-domainname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "SubjectAlternativeNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-certificate.html#cfn-lightsail-certificate-subjectalternativenames", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-certificate.html#cfn-lightsail-certificate-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::Lightsail::Container": { + "Attributes": { + "ContainerArn": { + "PrimitiveType": "String" + }, + "Url": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html", + "Properties": { + "ContainerServiceDeployment": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-containerservicedeployment", + "Required": false, + "Type": "ContainerServiceDeployment", + "UpdateType": "Mutable" + }, + "IsDisabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-isdisabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "Power": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-power", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "PublicDomainNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-publicdomainnames", + "DuplicatesAllowed": false, + "ItemType": "PublicDomainName", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Scale": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-scale", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "ServiceName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-servicename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-container.html#cfn-lightsail-container-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::Lightsail::Database": { "Attributes": { "DatabaseArn": { @@ -663,6 +1101,86 @@ } } }, + "AWS::Lightsail::Distribution": { + "Attributes": { + "AbleToUpdateBundle": { + "PrimitiveType": "Boolean" + }, + "DistributionArn": { + "PrimitiveType": "String" + }, + "Status": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html", + "Properties": { + "BundleId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-bundleid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "CacheBehaviorSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-cachebehaviorsettings", + "Required": false, + "Type": "CacheSettings", + "UpdateType": "Mutable" + }, + "CacheBehaviors": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-cachebehaviors", + "DuplicatesAllowed": false, + "ItemType": "CacheBehaviorPerPath", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "CertificateName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-certificatename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DefaultCacheBehavior": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-defaultcachebehavior", + "Required": true, + "Type": "CacheBehavior", + "UpdateType": "Mutable" + }, + "DistributionName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-distributionname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "IpAddressType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-ipaddresstype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "IsEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-isenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "Origin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-origin", + "Required": true, + "Type": "InputOrigin", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lightsail-distribution.html#cfn-lightsail-distribution-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::Lightsail::Instance": { "Attributes": { "Hardware.CpuCount": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json index 0ad633b4df290..f05f1d783dd2a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Location.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Location::Map.MapConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-location-map-mapconfiguration.html", @@ -63,7 +63,7 @@ "PricingPlan": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-location-geofencecollection.html#cfn-location-geofencecollection-pricingplan", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" }, "PricingPlanDataSource": { @@ -115,7 +115,7 @@ "PricingPlan": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-location-map.html#cfn-location-map-pricingplan", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" } } @@ -164,7 +164,7 @@ "PricingPlan": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-location-placeindex.html#cfn-location-placeindex-pricingplan", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" } } @@ -207,7 +207,7 @@ "PricingPlan": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-location-routecalculator.html#cfn-location-routecalculator-pricingplan", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" } } @@ -250,7 +250,7 @@ "PricingPlan": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-location-tracker.html#cfn-location-tracker-pricingplan", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" }, "PricingPlanDataSource": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json index 8e70fa0cee778..f8cc58c2ba516 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Logs.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Logs::MetricFilter.MetricTransformation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-logs-metricfilter-metrictransformation.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json index 6dfa099a6e479..977dccf116172 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutEquipment.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::LookoutEquipment::InferenceScheduler": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json index 67912366eea7e..64113f09559da 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutMetrics.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::LookoutMetrics::Alert.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lookoutmetrics-alert-action.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json index 1c1d4d7d25ca6..924c54b70c981 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_LookoutVision.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::LookoutVision::Project": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json index 20185e02691b9..4bf9608d74f7d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MSK.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MSK::Cluster.BrokerLogs": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-brokerlogs.html", @@ -63,7 +63,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-brokernodegroupinfo.html#cfn-msk-cluster-brokernodegroupinfo-storageinfo", "Required": false, "Type": "StorageInfo", - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, @@ -138,11 +138,17 @@ "AWS::MSK::Cluster.EBSStorageInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-ebsstorageinfo.html", "Properties": { + "ProvisionedThroughput": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-ebsstorageinfo.html#cfn-msk-cluster-ebsstorageinfo-provisionedthroughput", + "Required": false, + "Type": "ProvisionedThroughput", + "UpdateType": "Mutable" + }, "VolumeSize": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-ebsstorageinfo.html#cfn-msk-cluster-ebsstorageinfo-volumesize", "PrimitiveType": "Integer", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, @@ -280,6 +286,23 @@ } } }, + "AWS::MSK::Cluster.ProvisionedThroughput": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-provisionedthroughput.html", + "Properties": { + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-provisionedthroughput.html#cfn-msk-cluster-provisionedthroughput-enabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "VolumeThroughput": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-provisionedthroughput.html#cfn-msk-cluster-provisionedthroughput-volumethroughput", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::MSK::Cluster.PublicAccess": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-publicaccess.html", "Properties": { @@ -349,7 +372,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-storageinfo.html#cfn-msk-cluster-storageinfo-ebsstorageinfo", "Required": false, "Type": "EBSStorageInfo", - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json index bc2e6d1d9e0ec..8ae707abc8efa 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MWAA.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MWAA::Environment.LoggingConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-loggingconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json index e944f609e3dcf..86d02f847b841 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Macie.json @@ -1,8 +1,11 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Macie::FindingsFilter.Criterion": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-macie-findingsfilter-criterion.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-macie-findingsfilter-criterion.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::Macie::FindingsFilter.FindingCriteria": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-macie-findingsfilter-findingcriteria.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json index 7e665fcab8308..7e290c63a0719 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ManagedBlockchain.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ManagedBlockchain::Member.ApprovalThresholdPolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-managedblockchain-member-approvalthresholdpolicy.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json index 0b5b342924b9a..32d43fb883532 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConnect.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MediaConnect::Flow.Encryption": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediaconnect-flow-encryption.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json index 7d37f987f2243..38b03e065b796 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaConvert.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MediaConvert::JobTemplate.AccelerationSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediaconvert-jobtemplate-accelerationsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json index a9575f7be0518..a67f39cd5cce6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaLive.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MediaLive::Channel.AacSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-medialive-channel-aacsettings.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json index 9bdd30e498d88..7032ff61f8f24 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaPackage.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MediaPackage::Asset.EgressEndpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-asset-egressendpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json index a8fedcb4c8ba6..7b19df91bc094 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MediaStore.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MediaStore::Container.CorsRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediastore-container-corsrule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json index 6bb085b310c19..6e96df802afdd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_MemoryDB.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::MemoryDB::Cluster.Endpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-memorydb-cluster-endpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json index 50bc432104720..90e4be807e7cf 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Neptune.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Neptune::DBCluster.DBClusterRole": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-neptune-dbcluster-dbclusterrole.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json index eec6716b217f5..b31acc3856f87 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkFirewall.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::NetworkFirewall::Firewall.SubnetMapping": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-networkfirewall-firewall-subnetmapping.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json index d45f96e89d874..752e5fa3a60c4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NetworkManager.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::NetworkManager::Device.Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-networkmanager-device-location.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json index 468c9bd9c0de3..e87dfc03efa25 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_NimbleStudio.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::NimbleStudio::LaunchProfile.StreamConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-nimblestudio-launchprofile-streamconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json index 19bc22d9b94c5..034dd91ad6a81 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpenSearchService.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::OpenSearchService::Domain.AdvancedSecurityOptionsInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opensearchservice-domain-advancedsecurityoptionsinput.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json index 519d76d6ef930..c610392857550 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorks.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::OpsWorks::App.DataSource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opsworks-app-datasource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json index 570877333c9e0..a8611f7328bc6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_OpsWorksCM.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::OpsWorksCM::Server.EngineAttribute": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opsworkscm-server-engineattribute.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json index 035194219a700..3bcaa350c5331 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Panorama.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Panorama::ApplicationInstance.ManifestOverridesPayload": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-panorama-applicationinstance-manifestoverridespayload.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json index bfa0c21284177..a9403960e338a 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Pinpoint.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Pinpoint::ApplicationSettings.CampaignHook": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pinpoint-applicationsettings-campaignhook.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json index 32bbb1cb6b4c6..11fe0bab279ef 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_PinpointEmail.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::PinpointEmail::ConfigurationSet.DeliveryOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pinpointemail-configurationset-deliveryoptions.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json index cc2db7366f3bc..18f5a2cb96b61 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QLDB.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::QLDB::Stream.KinesisConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-qldb-stream-kinesisconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json index 5a5bdfa8e6c2a..6a4ddae8095be 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_QuickSight.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::QuickSight::Analysis.AnalysisError": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-quicksight-analysis-analysiserror.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json index 819cfadbef10c..f9e3a6cc3c4bc 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RAM.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::RAM::ResourceShare": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json index cd571a339ddde..062e819d037d1 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RDS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::RDS::DBCluster.DBClusterRole": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-dbcluster-dbclusterrole.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json index b983fb62b43a4..da4d66719fa9b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RUM.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::RUM::AppMonitor.AppMonitorConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rum-appmonitor-appmonitorconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json index 9c5f46ad18b47..d5a32b1e84353 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Redshift.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Redshift::Cluster.Endpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-redshift-cluster-endpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json index a40b344e967e8..f9cfdc1277a01 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RefactorSpaces.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::RefactorSpaces::Application.ApiGatewayProxyInput": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-refactorspaces-application-apigatewayproxyinput.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Rekognition.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Rekognition.json index c0c686cdd3ffd..2136056f10114 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Rekognition.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Rekognition.json @@ -1,7 +1,31 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { + "AWS::Rekognition::Collection": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rekognition-collection.html", + "Properties": { + "CollectionId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rekognition-collection.html#cfn-rekognition-collection-collectionid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rekognition-collection.html#cfn-rekognition-collection-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::Rekognition::Project": { "Attributes": { "Arn": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json index 604616dca92c4..126a37ff213be 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResilienceHub.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ResilienceHub::App.PhysicalResourceId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resiliencehub-app-physicalresourceid.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json index 598676fe5f284..9cac2cc809794 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ResourceGroups.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ResourceGroups::Group.ConfigurationItem": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resourcegroups-group-configurationitem.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json index 3722ce17511d9..3eb508d2b1d31 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_RoboMaker.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::RoboMaker::RobotApplication.RobotSoftwareSuite": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-robomaker-robotapplication-robotsoftwaresuite.html", @@ -70,7 +70,7 @@ "Version": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-robomaker-simulationapplication-robotsoftwaresuite.html#cfn-robomaker-simulationapplication-robotsoftwaresuite-version", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Mutable" } } @@ -87,7 +87,7 @@ "Version": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-robomaker-simulationapplication-simulationsoftwaresuite.html#cfn-robomaker-simulationapplication-simulationsoftwaresuite-version", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Mutable" } } diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json index 86a247178c9df..83d16a2e86366 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Route53::HealthCheck.HealthCheckTag": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-healthcheck-healthchecktag.html", @@ -175,12 +175,6 @@ "Type": "AliasTarget", "UpdateType": "Mutable" }, - "Comment": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-recordset.html#cfn-route53-recordset-comment", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "Failover": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-recordset.html#cfn-route53-recordset-failover", "PrimitiveType": "String", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json index 41460af15abff..87f4fb8b14b2d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryControl.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Route53RecoveryControl::Cluster.ClusterEndpoint": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53recoverycontrol-cluster-clusterendpoint.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json index 2b3279a4837de..093e72a573327 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53RecoveryReadiness.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Route53RecoveryReadiness::ResourceSet.DNSTargetResource": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53recoveryreadiness-resourceset-dnstargetresource.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json index 5a7beb7b41b77..e5515847825de 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Route53Resolver.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Route53Resolver::FirewallRuleGroup.FirewallRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53resolver-firewallrulegroup-firewallrule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json index 0de3c71167d87..fe825075468f3 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::S3::AccessPoint.PublicAccessBlockConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-accesspoint-publicaccessblockconfiguration.html", @@ -1425,7 +1425,10 @@ } }, "AWS::S3::StorageLens.Encryption": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-storagelens-encryption.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-storagelens-encryption.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::S3::StorageLens.PrefixLevel": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-storagelens-prefixlevel.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json index bf370b284d998..d8d7c233b3545 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3ObjectLambda.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::S3ObjectLambda::AccessPoint.ObjectLambdaConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3objectlambda-accesspoint-objectlambdaconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json index 6d51b5629d95f..dbb5e545aebbe 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_S3Outposts.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::S3Outposts::AccessPoint.VpcConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-accesspoint-vpcconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json index db00243b9966a..2a7a33a7940fe 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SDB.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::SDB::Domain": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json index a9d0cb754399f..25385c39bede8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SES.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SES::ConfigurationSetEventDestination.CloudWatchDestination": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ses-configurationseteventdestination-cloudwatchdestination.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json index 2180ca857d4c3..5f3a2d58c9a93 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SNS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SNS::Topic.Subscription": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-subscription.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json index 2b450c599cfbe..4cfad3b1715f2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SQS.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::SQS::Queue": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json index 0d805d217d26b..1e81684aaf0fc 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSM.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SSM::Association.InstanceAssociationOutputLocation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-association-instanceassociationoutputlocation.html", @@ -112,6 +112,23 @@ } } }, + "AWS::SSM::MaintenanceWindowTask.CloudWatchOutputConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-cloudwatchoutputconfig.html", + "Properties": { + "CloudWatchLogGroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-cloudwatchoutputconfig.html#cfn-ssm-maintenancewindowtask-cloudwatchoutputconfig-cloudwatchloggroupname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CloudWatchOutputEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-cloudwatchoutputconfig.html#cfn-ssm-maintenancewindowtask-cloudwatchoutputconfig-cloudwatchoutputenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::SSM::MaintenanceWindowTask.LoggingInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-logginginfo.html", "Properties": { @@ -178,6 +195,12 @@ "AWS::SSM::MaintenanceWindowTask.MaintenanceWindowRunCommandParameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-maintenancewindowruncommandparameters.html", "Properties": { + "CloudWatchOutputConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-maintenancewindowruncommandparameters.html#cfn-ssm-maintenancewindowtask-maintenancewindowruncommandparameters-cloudwatchoutputconfig", + "Required": false, + "Type": "CloudWatchOutputConfig", + "UpdateType": "Mutable" + }, "Comment": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-maintenancewindowruncommandparameters.html#cfn-ssm-maintenancewindowtask-maintenancewindowruncommandparameters-comment", "PrimitiveType": "String", @@ -196,6 +219,12 @@ "Required": false, "UpdateType": "Mutable" }, + "DocumentVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-maintenancewindowruncommandparameters.html#cfn-ssm-maintenancewindowtask-maintenancewindowruncommandparameters-documentversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "NotificationConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-maintenancewindowtask-maintenancewindowruncommandparameters.html#cfn-ssm-maintenancewindowtask-maintenancewindowruncommandparameters-notificationconfig", "Required": false, @@ -377,7 +406,10 @@ } }, "AWS::SSM::PatchBaseline.PatchStringDate": { - "PrimitiveType": "String" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-patchbaseline-patchstringdate.html", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" }, "AWS::SSM::PatchBaseline.Rule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssm-patchbaseline-rule.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json index 900a141fa92a9..be8d37188e696 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMContacts.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SSMContacts::Contact.ChannelTargetInfo": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-channeltargetinfo.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json index ae5f675cd73bb..685d8d2f0b454 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSMIncidents.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SSMIncidents::ReplicationSet.RegionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-regionconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json index 4cc7c4cbbbd3d..1efb8f82ef527 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SSO.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SSO::InstanceAccessControlAttributeConfiguration.AccessControlAttribute": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sso-instanceaccesscontrolattributeconfiguration-accesscontrolattribute.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SageMaker.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SageMaker.json index 6c48ebab0d9ae..2496c5195642f 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SageMaker.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SageMaker.json @@ -1,5 +1,5 @@ { - "$version": "51.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SageMaker::App.ResourceSpec": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-app-resourcespec.html", @@ -2674,7 +2674,6 @@ "Properties": { "Device": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-device.html#cfn-sagemaker-device-device", - "PrimitiveType": "Json", "Required": false, "Type": "Device", "UpdateType": "Mutable" @@ -3560,6 +3559,12 @@ "AWS::SageMaker::Pipeline": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-pipeline.html", "Properties": { + "ParallelismConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-pipeline.html#cfn-sagemaker-pipeline-parallelismconfiguration", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, "PipelineDefinition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-pipeline.html#cfn-sagemaker-pipeline-pipelinedefinition", "PrimitiveType": "Json", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json index 9017b0273ba4e..b15e459342175 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecretsManager.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::SecretsManager::RotationSchedule.HostedRotationLambda": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-rotationschedule-hostedrotationlambda.html", @@ -68,6 +68,18 @@ "PrimitiveType": "Integer", "Required": false, "UpdateType": "Mutable" + }, + "Duration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-rotationschedule-rotationrules.html#cfn-secretsmanager-rotationschedule-rotationrules-duration", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ScheduleExpression": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-rotationschedule-rotationrules.html#cfn-secretsmanager-rotationschedule-rotationrules-scheduleexpression", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" } } }, @@ -187,6 +199,12 @@ "Type": "HostedRotationLambda", "UpdateType": "Mutable" }, + "RotateImmediatelyOnUpdate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-rotationschedule.html#cfn-secretsmanager-rotationschedule-rotateimmediatelyonupdate", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "RotationLambdaARN": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-rotationschedule.html#cfn-secretsmanager-rotationschedule-rotationlambdaarn", "PrimitiveType": "String", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json index 588dd5b237b85..6c1abfae959bf 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_SecurityHub.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::SecurityHub::Hub": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json index e099af115a6e0..7990181ba67fa 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalog.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ServiceCatalog::CloudFormationProduct.ProvisioningArtifactProperties": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-servicecatalog-cloudformationproduct-provisioningartifactproperties.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json index 04a430b65b113..c6f35c729e8f6 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceCatalogAppRegistry.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": {}, "ResourceTypes": { "AWS::ServiceCatalogAppRegistry::Application": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json index fbca8eca4ac59..f858bcfa693ad 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_ServiceDiscovery.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::ServiceDiscovery::PrivateDnsNamespace.PrivateDnsPropertiesMutable": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-servicediscovery-privatednsnamespace-privatednspropertiesmutable.html", @@ -204,6 +204,9 @@ "Arn": { "PrimitiveType": "String" }, + "HostedZoneId": { + "PrimitiveType": "String" + }, "Id": { "PrimitiveType": "String" } @@ -248,6 +251,9 @@ "Arn": { "PrimitiveType": "String" }, + "HostedZoneId": { + "PrimitiveType": "String" + }, "Id": { "PrimitiveType": "String" } diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json index ea9c0b912e504..c1786ac0a37b4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Signer.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Signer::SigningProfile.SignatureValidityPeriod": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-signer-signingprofile-signaturevalidityperiod.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json index 9383689adb8e6..f71c89051453e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_StepFunctions.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::StepFunctions::Activity.TagsEntry": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-activity-tagsentry.html", @@ -30,7 +30,10 @@ } }, "AWS::StepFunctions::StateMachine.Definition": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-statemachine-definition.html" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-statemachine-definition.html", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" }, "AWS::StepFunctions::StateMachine.LogDestination": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-statemachine-logdestination.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json index 475928ccf870c..df5002cd3f0d2 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Synthetics.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Synthetics::Canary.ArtifactConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-artifactconfig.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json index 83f96b43ef29c..3bda4e61e2093 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Timestream.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Timestream::ScheduledQuery.DimensionMapping": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-timestream-scheduledquery-dimensionmapping.html", @@ -365,6 +365,12 @@ "Required": true, "UpdateType": "Immutable" }, + "MagneticStoreWriteProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-timestream-table.html#cfn-timestream-table-magneticstorewriteproperties", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, "RetentionProperties": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-timestream-table.html#cfn-timestream-table-retentionproperties", "PrimitiveType": "Json", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json index b2444f54f903f..5a2d7b1758d08 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Transfer.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Transfer::Server.EndpointDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-server-endpointdetails.html", @@ -69,7 +69,10 @@ } }, "AWS::Transfer::Server.Protocol": { - "PrimitiveType": "String" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-server-protocol.html", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" }, "AWS::Transfer::Server.ProtocolDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-server-protocoldetails.html", @@ -159,7 +162,10 @@ } }, "AWS::Transfer::User.SshPublicKey": { - "PrimitiveType": "String" + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-user-sshpublickey.html", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" }, "AWS::Transfer::Workflow.WorkflowStep": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-workflow-workflowstep.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json index cc9564c086725..0e1cee8863433 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAF.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::WAF::ByteMatchSet.ByteMatchTuple": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-waf-bytematchset-bytematchtuples.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json index 46672ee17706f..343a3311b9b30 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFRegional.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::WAFRegional::ByteMatchSet.ByteMatchTuple": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wafregional-bytematchset-bytematchtuple.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json index 92696be1e5e71..b5d7d79371638 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WAFv2.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::WAFv2::LoggingConfiguration.FieldToMatch": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wafv2-loggingconfiguration-fieldtomatch.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json index 41508ba070cb9..3e9e2112198e4 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_Wisdom.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::Wisdom::Assistant.ServerSideEncryptionConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wisdom-assistant-serversideencryptionconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json index e826ef93d5988..4f4cc2a8c95e0 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_WorkSpaces.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::WorkSpaces::ConnectionAlias.ConnectionAliasAssociation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-workspaces-connectionalias-connectionaliasassociation.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json index 27d718a9553d3..e6e6da2087b5b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_AWS_XRay.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "AWS::XRay::Group.InsightsConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-group-insightsconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json index ffcdf5f553add..1727ee77f3d82 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Alexa_ASK.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "Alexa::ASK::Skill.AuthenticationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ask-skill-authenticationconfiguration.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json index bd2527c9ebc66..5188d7d475939 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/000_Tag.json @@ -1,5 +1,5 @@ { - "$version": "53.0.0", + "$version": "54.0.0", "PropertyTypes": { "Tag": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html", diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json index 551a6434fe1f8..8210595ac7f7b 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/000_official/001_Version.json @@ -1,3 +1,3 @@ { - "ResourceSpecificationVersion": "53.0.0" + "ResourceSpecificationVersion": "54.0.0" } diff --git a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/660_Route53_HealthCheck_patch.json b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/660_Route53_HealthCheck_patch.json index b03232b4e72a5..4ff2061df1a22 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/660_Route53_HealthCheck_patch.json +++ b/packages/@aws-cdk/cfnspec/spec-source/specification/000_cfn/660_Route53_HealthCheck_patch.json @@ -118,6 +118,12 @@ "Required": false, "UpdateType": "Mutable" }, + "RoutingControlArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-healthcheck-healthcheckconfig.html#cfn-route53-healthcheck-healthcheckconfig-routingcontrolarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "SearchString": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-healthcheck-healthcheckconfig.html#cfn-route53-healthcheck-healthcheckconfig-searchstring", "PrimitiveType": "String", @@ -154,4 +160,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/cloudformation-include/build.js b/packages/@aws-cdk/cloudformation-include/build.js index 454a10a5014d5..ab4d071b6cf21 100644 --- a/packages/@aws-cdk/cloudformation-include/build.js +++ b/packages/@aws-cdk/cloudformation-include/build.js @@ -1,7 +1,6 @@ /** * This build file has two purposes: - * 1. It adds a dependency on each @aws-cdk/aws-xyz package with L1s to this package, - * similarly to how deps.js does for decdk. + * 1. It adds a dependency on each @aws-cdk/aws-xyz package with L1s to this package. * 2. It generates the file cfn-types-2-classes.json that contains a mapping * between the CloudFormation type and the fully-qualified name of the L1 class, * used in the logic of the CfnInclude class. diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index aadda87535b0b..00a6e48fb8e95 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -175,6 +175,7 @@ "@aws-cdk/aws-iotthingsgraph": "0.0.0", "@aws-cdk/aws-iotwireless": "0.0.0", "@aws-cdk/aws-ivs": "0.0.0", + "@aws-cdk/aws-kafkaconnect": "0.0.0", "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", @@ -363,6 +364,7 @@ "@aws-cdk/aws-iotthingsgraph": "0.0.0", "@aws-cdk/aws-iotwireless": "0.0.0", "@aws-cdk/aws-ivs": "0.0.0", + "@aws-cdk/aws-kafkaconnect": "0.0.0", "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", diff --git a/packages/@aws-cdk/core/lib/secret-value.ts b/packages/@aws-cdk/core/lib/secret-value.ts index 84e668a1b4306..270a70a519065 100644 --- a/packages/@aws-cdk/core/lib/secret-value.ts +++ b/packages/@aws-cdk/core/lib/secret-value.ts @@ -71,8 +71,9 @@ export class SecretValue extends Intrinsic { * latest version of the parameter. */ public static ssmSecure(parameterName: string, version?: string): SecretValue { - const parts = [parameterName, version ?? '']; - return this.cfnDynamicReference(new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, parts.join(':'))); + return this.cfnDynamicReference( + new CfnDynamicReference(CfnDynamicReferenceService.SSM_SECURE, + version ? `${parameterName}:${version}` : parameterName)); } /** diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index 686c71aaba3cf..aa2ee18ee84f0 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -178,7 +178,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/fs-extra": "^8.1.2", "@types/jest": "^27.4.0", "@types/lodash": "^4.14.178", diff --git a/packages/@aws-cdk/core/test/secret-value.test.ts b/packages/@aws-cdk/core/test/secret-value.test.ts index a987cfaff0c87..4a9b7bbe56093 100644 --- a/packages/@aws-cdk/core/test/secret-value.test.ts +++ b/packages/@aws-cdk/core/test/secret-value.test.ts @@ -127,7 +127,7 @@ describe('secret value', () => { const v = SecretValue.ssmSecure('param-name'); // THEN - expect(stack.resolve(v)).toEqual('{{resolve:ssm-secure:param-name:}}'); + expect(stack.resolve(v)).toEqual('{{resolve:ssm-secure:param-name}}'); }); test('cfnDynamicReference', () => { diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 11f7254664669..80a60f9df23b8 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -87,12 +87,12 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.90", + "@types/aws-lambda": "^8.10.92", "@types/fs-extra": "^8.1.2", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "fs-extra": "^9.1.0", "nock": "^13.2.2", "sinon": "^9.2.4" diff --git a/packages/@aws-cdk/example-construct-library/package.json b/packages/@aws-cdk/example-construct-library/package.json index e0d91b4c3a21d..f37f388b24062 100644 --- a/packages/@aws-cdk/example-construct-library/package.json +++ b/packages/@aws-cdk/example-construct-library/package.json @@ -29,7 +29,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/Dockerfile b/packages/@aws-cdk/lambda-layer-awscli/layer/Dockerfile index 7cf1287e2023c..8c5d754c04c03 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/layer/Dockerfile +++ b/packages/@aws-cdk/lambda-layer-awscli/layer/Dockerfile @@ -4,7 +4,8 @@ FROM public.ecr.aws/lambda/provided:latest # versions # -ARG AWSCLI_VERSION=1.18.198 +# This is the last version that still supports Python 2.7 +ARG AWSCLI_VERSION=1.19.112 USER root RUN mkdir -p /opt diff --git a/packages/@aws-cdk/lambda-layer-node-proxy-agent/README.md b/packages/@aws-cdk/lambda-layer-node-proxy-agent/README.md index 45187394cb8b2..f27c51013b3fc 100644 --- a/packages/@aws-cdk/lambda-layer-node-proxy-agent/README.md +++ b/packages/@aws-cdk/lambda-layer-node-proxy-agent/README.md @@ -16,8 +16,11 @@ This module exports a single class called `NodeProxyAgentLayer` which is a `lamb Usage: ```ts -const fn = new lambda.Function(...); -fn.addLayers(new NodeProxyAgentLayer(stack, 'NodeProxyAgentLayer')); +import { NodeProxyAgentLayer } from '@aws-cdk/lambda-layer-node-proxy-agent'; +import * as lambda from '@aws-cdk/aws-lambda'; + +declare const fn: lambda.Function; +fn.addLayers(new NodeProxyAgentLayer(this, 'NodeProxyAgentLayer')); ``` [`proxy-agent`](https://www.npmjs.com/package/proxy-agent) will be installed under `/opt/nodejs/node_modules`. diff --git a/packages/@aws-cdk/lambda-layer-node-proxy-agent/package.json b/packages/@aws-cdk/lambda-layer-node-proxy-agent/package.json index b4e0211270c66..1753c1057db84 100644 --- a/packages/@aws-cdk/lambda-layer-node-proxy-agent/package.json +++ b/packages/@aws-cdk/lambda-layer-node-proxy-agent/package.json @@ -29,7 +29,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/lambda-layer-node-proxy-agent/rosetta/default.ts-fixture b/packages/@aws-cdk/lambda-layer-node-proxy-agent/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..50d86e8a055ce --- /dev/null +++ b/packages/@aws-cdk/lambda-layer-node-proxy-agent/rosetta/default.ts-fixture @@ -0,0 +1,10 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/lib/blueprint/step.ts b/packages/@aws-cdk/pipelines/lib/blueprint/step.ts index 2bd785a7c7640..65f4636b2ecbc 100644 --- a/packages/@aws-cdk/pipelines/lib/blueprint/step.ts +++ b/packages/@aws-cdk/pipelines/lib/blueprint/step.ts @@ -13,6 +13,10 @@ import { FileSet, IFileSetProducer } from './file-set'; export abstract class Step implements IFileSetProducer { /** * Define a sequence of steps to be executed in order. + * + * If you need more fine-grained step ordering, use the `addStepDependency()` + * API. For example, if you want `secondStep` to occur after `firstStep`, call + * `secondStep.addStepDependency(firstStep)`. */ public static sequence(steps: Step[]): Step[] { for (let i = 1; i < steps.length; i++) { diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts index 5d73e23784314..3414554cd5197 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts @@ -5,7 +5,7 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; -import { IDependable, Stack } from '@aws-cdk/core'; +import { IDependable, Stack, Token } from '@aws-cdk/core'; import { Construct, Node } from 'constructs'; import { FileSetLocation, ShellStep, StackOutputReference } from '../blueprint'; import { PipelineQueries } from '../helpers-internal/pipeline-queries'; @@ -271,9 +271,13 @@ export class CodeBuildFactory implements ICodePipelineActionFactory { projectScope = obtainScope(scope, actionName); } + const safePipelineName = Token.isUnresolved(options.pipeline.pipeline.pipelineName) + ? `${Stack.of(options.pipeline).stackName}/${Node.of(options.pipeline.pipeline).id}` + : options.pipeline.pipeline.pipelineName; + const project = new codebuild.PipelineProject(projectScope, this.constructId, { projectName: this.props.projectName, - description: `Pipeline step ${options.pipeline.pipeline.pipelineName}/${stage.stageName}/${actionName}`, + description: `Pipeline step ${safePipelineName}/${stage.stageName}/${actionName}`.substring(0, 255), environment, vpc: projectOptions.vpc, subnetSelection: projectOptions.subnetSelection, diff --git a/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts b/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts index 01069ee7adee0..393f1ffb965ba 100644 --- a/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts +++ b/packages/@aws-cdk/pipelines/test/codepipeline/codebuild-step.test.ts @@ -55,12 +55,7 @@ test('CodeBuild projects have a description', () => { Template.fromStack(pipelineStack).hasResourceProperties( 'AWS::CodeBuild::Project', { - Description: { - 'Fn::Join': [ - '', - ['Pipeline step ', { Ref: 'Pipeline9850B417' }, '/Build/Synth'], - ], - }, + Description: 'Pipeline step PipelineStack/Pipeline/Build/Synth', }, ); }); 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 index 1b6de60c8e589..4bd2e638afb4c 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json @@ -667,6 +667,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1276,6 +1280,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1355,18 +1363,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Build/Synth" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Build/Synth", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ @@ -1954,18 +1951,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/UpdatePipeline/SelfMutate" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/UpdatePipeline/SelfMutate", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ @@ -2308,18 +2294,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Assets/FileAsset1" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Assets/FileAsset1", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ @@ -2421,18 +2396,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Assets/FileAsset2" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Assets/FileAsset2", "EncryptionKey": "alias/aws/s3", "VpcConfig": { "SecurityGroupIds": [ diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json index 6e8c76b176b7f..37cd5d99fd7f8 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json @@ -153,6 +153,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1973,6 +1977,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -2036,18 +2044,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/Build/Synth" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/Build/Synth", "EncryptionKey": "alias/aws/s3" } }, @@ -2347,18 +2344,7 @@ "Cache": { "Type": "NO_CACHE" }, - "Description": { - "Fn::Join": [ - "", - [ - "Pipeline step ", - { - "Ref": "Pipeline9850B417" - }, - "/UpdatePipeline/SelfMutate" - ] - ] - }, + "Description": "Pipeline step PipelineStack/Pipeline/UpdatePipeline/SelfMutate", "EncryptionKey": "alias/aws/s3" } } diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json index 996f6abad6abc..f9f2ec14d2199 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json @@ -215,6 +215,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -1310,6 +1314,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json index 64471fdf6b4c8..26cecb377ee68 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json @@ -215,6 +215,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -746,6 +750,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json index 01eafcfb30eef..c757e3097d633 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json @@ -215,6 +215,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -773,6 +777,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json index 90d937525afc8..ab3e7cbede27a 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json @@ -215,6 +215,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", @@ -704,6 +708,10 @@ "s3:List*", "s3:DeleteObject*", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", "s3:Abort*" ], "Effect": "Allow", diff --git a/packages/@aws-cdk/region-info/README.md b/packages/@aws-cdk/region-info/README.md index 0f1186318ee49..b8d75339c3320 100644 --- a/packages/@aws-cdk/region-info/README.md +++ b/packages/@aws-cdk/region-info/README.md @@ -22,10 +22,8 @@ the form of the `RegionInfo` class. This is the preferred way to interact with the regional information database: ```ts -import { RegionInfo } from '@aws-cdk/region-info'; - // Get the information for "eu-west-1": -const region = RegionInfo.get('eu-west-1'); +const region = regionInfo.RegionInfo.get('eu-west-1'); // Access attributes: region.s3StaticWebsiteEndpoint; // s3-website-eu-west-1.amazonaws.com @@ -44,8 +42,6 @@ a list of known fact names, which can then be used with the `RegionInfo` to retrieve a particular value: ```ts -import * as regionInfo from '@aws-cdk/region-info'; - const codeDeployPrincipal = regionInfo.Fact.find('us-east-1', regionInfo.FactName.servicePrincipal('codedeploy.amazonaws.com')); // => codedeploy.us-east-1.amazonaws.com @@ -60,11 +56,13 @@ missing from the library. In such cases, the `Fact.register` method can be used to inject FactName into the database: ```ts -regionInfo.Fact.register({ - region: 'bermuda-triangle-1', - name: regionInfo.FactName.servicePrincipal('s3.amazonaws.com'), - value: 's3-website.bermuda-triangle-1.nowhere.com', -}); +class MyFact implements regionInfo.IFact { + public readonly region = 'bermuda-triangle-1'; + public readonly name = regionInfo.FactName.servicePrincipal('s3.amazonaws.com'); + public readonly value = 's3-website.bermuda-triangle-1.nowhere.com'; +} + +regionInfo.Fact.register(new MyFact()); ``` ## Overriding incorrect information @@ -74,11 +72,13 @@ overridden using the same `Fact.register` method demonstrated above, simply adding an extra boolean argument: ```ts -regionInfo.Fact.register({ - region: 'us-east-1', - name: regionInfo.FactName.servicePrincipal('service.amazonaws.com'), - value: 'the-correct-principal.amazonaws.com', -}, true /* Allow overriding information */); +class MyFact implements regionInfo.IFact { + public readonly region = 'us-east-1'; + public readonly name = regionInfo.FactName.servicePrincipal('service.amazonaws.com'); + public readonly value = 'the-correct-principal.amazonaws.com'; +} + +regionInfo.Fact.register(new MyFact(), true /* Allow overriding information */); ``` If you happen to have stumbled upon incorrect data built into this library, it 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 b36ff4818838d..c5de5d2e6e785 100644 --- a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts +++ b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts @@ -42,6 +42,7 @@ export const ROUTE_53_BUCKET_WEBSITE_ZONE_IDS: { [region: string]: string } = { 'ap-south-1': 'Z11RGJOFQNVJUP', 'ap-southeast-1': 'Z3O0J2DXBE1FTB', 'ap-southeast-2': 'Z1WCIGYICN2BYD', + 'ap-southeast-3': 'Z01613992JD795ZI93075', 'ca-central-1': 'Z1QDHH18159H29', 'cn-northwest-1': 'Z282HJ1KT0DH03', 'eu-central-1': 'Z21DNDUVLTQW6Q', diff --git a/packages/@aws-cdk/region-info/lib/aws-entities.ts b/packages/@aws-cdk/region-info/lib/aws-entities.ts index 84749afba24b9..9c14e89605607 100644 --- a/packages/@aws-cdk/region-info/lib/aws-entities.ts +++ b/packages/@aws-cdk/region-info/lib/aws-entities.ts @@ -1,17 +1,14 @@ -// Rule prefix -const RULE_ = 'RULE_'; - /** * After this point, SSM only creates regional principals */ -export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = `${RULE_}SSM_PRINCIPALS_ARE_REGIONAL`; +export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = Symbol('SSM_PRINCIPALS_ARE_REGIONAL'); /** * After this point, S3 website domains look like `s3-website.REGION.s3.amazonaws.com` * * Before this point, S3 website domains look like `s3-website-REGION.s3.amazonaws.com`. */ -export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = `${RULE_}S3_WEBSITE_REGIONAL_SUBDOMAIN`; +export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = Symbol('S3_WEBSITE_REGIONAL_SUBDOMAIN'); /** * List of AWS region, ordered by launch date (oldest to newest) @@ -21,13 +18,13 @@ export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = `${RULE_}S3_WEBSITE_REGIONAL_S * regions are left as-is. * * We mix the list of regions with a list of rules that were introduced over - * time (rules are strings starting with `RULE_`). + * time (rules are symbols). * * Therefore, if we want to know if a rule applies to a certain region, we * only need to check its position in the list and compare it to when a * rule was introduced. */ -export const AWS_REGIONS_AND_RULES = [ +export const AWS_REGIONS_AND_RULES: readonly (string | symbol)[] = [ 'us-east-1', // US East (N. Virginia) 'eu-west-1', // Europe (Ireland) 'us-west-1', // US West (N. California) @@ -68,15 +65,15 @@ export const AWS_REGIONS_AND_RULES = [ * Not in the list ==> no built-in data for that region. */ export const AWS_REGIONS = AWS_REGIONS_AND_RULES - .filter((x) => !x.startsWith(RULE_)) - .sort(); + .filter((x) => typeof x === 'string') + .sort() as readonly string[]; /** * Possibly non-exaustive list of all service names, used to locate service principals. * * Not in the list ==> default service principal mappings. */ -export const AWS_SERVICES = [ +export const AWS_SERVICES: readonly string[] = [ 'application-autoscaling', 'autoscaling', 'codedeploy', @@ -96,10 +93,10 @@ export const AWS_SERVICES = [ * * Unknown region => we have to assume no. */ -export function before(region: string, ruleOrRegion: string) { +export function before(region: string, ruleOrRegion: string | symbol) { const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion); if (ruleIx === -1) { - throw new Error(`Unknown rule: ${ruleOrRegion}`); + throw new Error(`Unknown rule: ${String(ruleOrRegion)}`); } const regionIx = AWS_REGIONS_AND_RULES.indexOf(region); return regionIx === -1 ? false : regionIx < ruleIx; @@ -108,17 +105,19 @@ export function before(region: string, ruleOrRegion: string) { /** * Return all regions before a given rule was introduced (or region) */ -export function regionsBefore(ruleOrRegion: string): string[] { +export function regionsBefore(ruleOrRegion: string | symbol): string[] { const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion); if (ruleIx === -1) { - throw new Error(`Unknown rule: ${ruleOrRegion}`); + throw new Error(`Unknown rule: ${String(ruleOrRegion)}`); } - return AWS_REGIONS_AND_RULES.filter((_, i) => i < ruleIx).sort(); + return AWS_REGIONS_AND_RULES.slice(0, ruleIx) + .filter((entry) => typeof entry === 'string') + .sort() as string[]; } -export interface Region { partition: string, domainSuffix: string } +export interface Region { readonly partition: string, readonly domainSuffix: string } -const PARTITION_MAP: { [region: string]: Region } = { +const PARTITION_MAP: {readonly [region: string]: Region } = { 'default': { partition: 'aws', domainSuffix: 'amazonaws.com' }, 'cn-': { partition: 'aws-cn', domainSuffix: 'amazonaws.com.cn' }, 'us-gov-': { partition: 'aws-us-gov', domainSuffix: 'amazonaws.com' }, diff --git a/packages/@aws-cdk/region-info/lib/default.ts b/packages/@aws-cdk/region-info/lib/default.ts index b11aa881f955c..e306bd8c1fc25 100644 --- a/packages/@aws-cdk/region-info/lib/default.ts +++ b/packages/@aws-cdk/region-info/lib/default.ts @@ -23,8 +23,8 @@ export class Default { * @param urlSuffix deprecated and ignored. */ public static servicePrincipal(serviceFqn: string, region: string, urlSuffix: string): string { - const service = extractSimpleName(serviceFqn); - if (!service) { + const serviceName = extractSimpleName(serviceFqn); + if (!serviceName) { // Return "service" if it does not look like any of the following: // - s3 // - s3.amazonaws.com @@ -34,72 +34,86 @@ export class Default { return serviceFqn; } - // Exceptions for Service Principals in us-iso-* - const US_ISO_EXCEPTIONS = new Set([ - 'cloudhsm', - 'config', - 'states', - 'workspaces', - ]); - - // Account for idiosyncratic Service Principals in `us-iso-*` regions - if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) { - switch (service) { - // Services with universal principal - case ('states'): - return `${service}.amazonaws.com`; - - // Services with a partitional principal - default: - return `${service}.${urlSuffix}`; + function determineConfiguration(service: string): (service: string, region: string, urlSuffix: string) => string { + function universal(s: string) { return `${s}.amazonaws.com`; }; + function partitional(s: string, _: string, u: string) { return `${s}.${u}`; }; + function regional(s: string, r: string) { return `${s}.${r}.amazonaws.com`; }; + function regionalPartitional(s: string, r: string, u: string) { return `${s}.${r}.${u}`; }; + + // Exceptions for Service Principals in us-iso-* + const US_ISO_EXCEPTIONS = new Set([ + 'cloudhsm', + 'config', + 'states', + 'workspaces', + ]); + + // Account for idiosyncratic Service Principals in `us-iso-*` regions + if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) { + switch (service) { + // Services with universal principal + case ('states'): + return universal; + + // Services with a partitional principal + default: + return partitional; + } } - } - // Exceptions for Service Principals in us-isob-* - const US_ISOB_EXCEPTIONS = new Set([ - 'dms', - 'states', - ]); + // Exceptions for Service Principals in us-isob-* + const US_ISOB_EXCEPTIONS = new Set([ + 'dms', + 'states', + ]); + + // Account for idiosyncratic Service Principals in `us-isob-*` regions + if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) { + switch (service) { + // Services with universal principal + case ('states'): + return universal; + + // Services with a partitional principal + default: + return partitional; + } + } - // Account for idiosyncratic Service Principals in `us-isob-*` regions - if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) { switch (service) { - // Services with universal principal - case ('states'): - return `${service}.amazonaws.com`; + // SSM turned from global to regional at some point + case 'ssm': + return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL) + ? universal + : regional; + + // CodeDeploy is regional+partitional in CN, only regional everywhere else + case 'codedeploy': + return region.startsWith('cn-') + ? regionalPartitional + : regional; + + // Services with a regional AND partitional principal + case 'logs': + return regionalPartitional; + + // Services with a regional principal + case 'states': + return regional; // Services with a partitional principal - default: - return `${service}.${urlSuffix}`; - } - } - - // SSM turned from global to regional at some point - if (service === 'ssm') { - return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL) - ? `${service}.amazonaws.com` - : `${service}.${region}.amazonaws.com`; - } - - switch (service) { - // Services with a regional AND partitional principal - case 'codedeploy': - case 'logs': - return `${service}.${region}.${urlSuffix}`; - - // Services with a regional principal - case 'states': - return `${service}.${region}.amazonaws.com`; + case 'ec2': + return partitional; - // Services with a partitional principal - case 'ec2': - return `${service}.${urlSuffix}`; + // Services with a universal principal across all regions/partitions (the default case) + default: + return universal; - // Services with a universal principal across all regions/partitions (the default case) - default: - return `${service}.amazonaws.com`; + } + }; - } + const configuration = determineConfiguration(serviceName); + return configuration(serviceName, region, urlSuffix); } private constructor() { } @@ -108,4 +122,4 @@ export class Default { function extractSimpleName(serviceFqn: string) { const matches = serviceFqn.match(/^([^.]+)(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))?$/); return matches ? matches[1] : undefined; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/region-info/lib/fact.ts b/packages/@aws-cdk/region-info/lib/fact.ts index 0c4d12a53bec4..9c2831f67d2c6 100644 --- a/packages/@aws-cdk/region-info/lib/fact.ts +++ b/packages/@aws-cdk/region-info/lib/fact.ts @@ -9,7 +9,8 @@ export class Fact { * may not be an exhaustive list of all available AWS regions. */ public static get regions(): string[] { - return AWS_REGIONS; + // Return by copy to ensure no modifications can be made to the undelying constant. + return Array.from(AWS_REGIONS); } /** diff --git a/packages/@aws-cdk/region-info/package.json b/packages/@aws-cdk/region-info/package.json index dba2268c2fa22..f3400ed30b3e4 100644 --- a/packages/@aws-cdk/region-info/package.json +++ b/packages/@aws-cdk/region-info/package.json @@ -28,7 +28,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "scripts": { "gen": "bash build-tools/generate.sh", diff --git a/packages/@aws-cdk/region-info/rosetta/default.ts-fixture b/packages/@aws-cdk/region-info/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..3a11f7f47a475 --- /dev/null +++ b/packages/@aws-cdk/region-info/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as regionInfo from '@aws-cdk/region-info'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap b/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap index 7ba9353557b9b..678a65fb4ccc3 100644 --- a/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap +++ b/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap @@ -795,7 +795,7 @@ Object { "servicePrincipals": Object { "application-autoscaling": "application-autoscaling.amazonaws.com", "autoscaling": "autoscaling.amazonaws.com", - "codedeploy": "codedeploy.us-iso-east-1.c2s.ic.gov", + "codedeploy": "codedeploy.us-iso-east-1.amazonaws.com", "ec2": "ec2.c2s.ic.gov", "events": "events.amazonaws.com", "lambda": "lambda.amazonaws.com", @@ -826,7 +826,7 @@ Object { "servicePrincipals": Object { "application-autoscaling": "application-autoscaling.amazonaws.com", "autoscaling": "autoscaling.amazonaws.com", - "codedeploy": "codedeploy.us-iso-west-1.c2s.ic.gov", + "codedeploy": "codedeploy.us-iso-west-1.amazonaws.com", "ec2": "ec2.c2s.ic.gov", "events": "events.amazonaws.com", "lambda": "lambda.amazonaws.com", @@ -857,7 +857,7 @@ Object { "servicePrincipals": Object { "application-autoscaling": "application-autoscaling.amazonaws.com", "autoscaling": "autoscaling.amazonaws.com", - "codedeploy": "codedeploy.us-isob-east-1.sc2s.sgov.gov", + "codedeploy": "codedeploy.us-isob-east-1.amazonaws.com", "ec2": "ec2.sc2s.sgov.gov", "events": "events.amazonaws.com", "lambda": "lambda.amazonaws.com", diff --git a/packages/@aws-cdk/region-info/test/default.test.ts b/packages/@aws-cdk/region-info/test/default.test.ts index d1a320fcaac60..dd0dcd68b0a8d 100644 --- a/packages/@aws-cdk/region-info/test/default.test.ts +++ b/packages/@aws-cdk/region-info/test/default.test.ts @@ -5,12 +5,12 @@ const urlSuffix = '.nowhere.null'; describe('servicePrincipal', () => { for (const suffix of ['', '.amazonaws.com', '.amazonaws.com.cn']) { - for (const service of ['states', 'ssm']) { + for (const service of ['codedeploy', 'states', 'ssm']) { test(`${service}${suffix}`, () => { expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.amazonaws.com`); }); } - for (const service of ['codedeploy', 'logs']) { + for (const service of ['logs']) { test(`${service}${suffix}`, () => { expect(Default.servicePrincipal(`${service}${suffix}`, region, urlSuffix)).toBe(`${service}.${region}.${urlSuffix}`); }); @@ -48,6 +48,12 @@ describe('servicePrincipal', () => { }); } + for (const cnRegion of ['cn-north-1', 'cn-northwest-1']) { + test(`Exceptions: codedeploy in ${cnRegion}`, () => { + expect(Default.servicePrincipal('codedeploy', cnRegion, 'amazonaws.com.cn')).toBe(`codedeploy.${cnRegion}.amazonaws.com.cn`); + }); + } + }); diff --git a/packages/@aws-cdk/yaml-cfn/package.json b/packages/@aws-cdk/yaml-cfn/package.json index b0035c4e80552..c30424077862f 100644 --- a/packages/@aws-cdk/yaml-cfn/package.json +++ b/packages/@aws-cdk/yaml-cfn/package.json @@ -48,7 +48,14 @@ ] } }, - "projectReferences": true + "projectReferences": true, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + } }, "scripts": { "build": "cdk-build", diff --git a/packages/@monocdk-experiment/assert/package.json b/packages/@monocdk-experiment/assert/package.json index f5880d8195f90..a9b70fbf2e5c8 100644 --- a/packages/@monocdk-experiment/assert/package.json +++ b/packages/@monocdk-experiment/assert/package.json @@ -64,8 +64,8 @@ "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, - "stability": "experimental", - "maturity": "developer-preview", + "stability": "deprecated", + "maturity": "deprecated", "publishConfig": { "tag": "latest" } diff --git a/packages/@monocdk-experiment/assert/tsconfig.json b/packages/@monocdk-experiment/assert/tsconfig.json index b426f95fcb96a..e1b9688cb975e 100644 --- a/packages/@monocdk-experiment/assert/tsconfig.json +++ b/packages/@monocdk-experiment/assert/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target":"ES2018", - "lib": ["es2018"], + "target":"ES2019", + "lib": ["es2019"], "module": "CommonJS", "declaration": true, "strict": true, diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index e55df0ea2542c..b4b3fe26b1063 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -238,6 +238,7 @@ "@aws-cdk/aws-iotthingsgraph": "0.0.0", "@aws-cdk/aws-iotwireless": "0.0.0", "@aws-cdk/aws-ivs": "0.0.0", + "@aws-cdk/aws-kafkaconnect": "0.0.0", "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", @@ -351,7 +352,7 @@ "@types/fs-extra": "^8.1.2", "@types/node": "^10.17.60", "constructs": "^3.3.69", - "esbuild": "^0.14.11", + "esbuild": "^0.14.14", "fs-extra": "^9.1.0", "ts-node": "^9.1.1", "typescript": "~3.8.3" @@ -433,7 +434,6 @@ "./aws-codestarconnections": "./aws-codestarconnections/index.js", "./aws-codestarnotifications": "./aws-codestarnotifications/index.js", "./aws-cognito": "./aws-cognito/index.js", - "./aws-cognito-identitypool": "./aws-cognito-identitypool/index.js", "./aws-config": "./aws-config/index.js", "./aws-connect": "./aws-connect/index.js", "./aws-cur": "./aws-cur/index.js", @@ -472,6 +472,7 @@ "./aws-finspace": "./aws-finspace/index.js", "./aws-fis": "./aws-fis/index.js", "./aws-fms": "./aws-fms/index.js", + "./aws-forecast": "./aws-forecast/index.js", "./aws-frauddetector": "./aws-frauddetector/index.js", "./aws-fsx": "./aws-fsx/index.js", "./aws-gamelift": "./aws-gamelift/index.js", @@ -486,6 +487,7 @@ "./aws-iam": "./aws-iam/index.js", "./aws-imagebuilder": "./aws-imagebuilder/index.js", "./aws-inspector": "./aws-inspector/index.js", + "./aws-inspectorv2": "./aws-inspectorv2/index.js", "./aws-iot": "./aws-iot/index.js", "./aws-iot1click": "./aws-iot1click/index.js", "./aws-iotanalytics": "./aws-iotanalytics/index.js", @@ -499,7 +501,9 @@ "./aws-kendra": "./aws-kendra/index.js", "./aws-kinesis": "./aws-kinesis/index.js", "./aws-kinesisanalytics": "./aws-kinesisanalytics/index.js", + "./aws-kinesisanalyticsv2": "./aws-kinesisanalyticsv2/index.js", "./aws-kinesisfirehose": "./aws-kinesisfirehose/index.js", + "./aws-kinesisvideo": "./aws-kinesisvideo/index.js", "./aws-kms": "./aws-kms/index.js", "./aws-lakeformation": "./aws-lakeformation/index.js", "./aws-lambda": "./aws-lambda/index.js", diff --git a/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts b/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts index d2966e756b69d..ac87e25c29655 100644 --- a/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts +++ b/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts @@ -1,5 +1,6 @@ import { Writable } from 'stream'; import * as archiver from 'archiver'; +import * as AWS from 'aws-sdk'; import { flatMap } from '../../util'; import { ISDK } from '../aws-auth'; import { CfnEvaluationException, EvaluateCloudFormationTemplate } from '../evaluate-cloudformation-template'; @@ -232,7 +233,7 @@ class LambdaFunctionHotswapOperation implements HotswapOperation { const operations: Promise[] = []; if (resource.code !== undefined) { - const updateFunctionCodePromise = lambda.updateFunctionCode({ + const updateFunctionCodeResponse = await lambda.updateFunctionCode({ FunctionName: this.lambdaFunctionResource.physicalName, S3Bucket: resource.code.s3Bucket, S3Key: resource.code.s3Key, @@ -240,17 +241,10 @@ class LambdaFunctionHotswapOperation implements HotswapOperation { ZipFile: resource.code.functionCodeZip, }).promise(); + await this.waitForLambdasCodeUpdateToFinish(updateFunctionCodeResponse, lambda); + // only if the code changed is there any point in publishing a new Version if (this.lambdaFunctionResource.publishVersion) { - // we need to wait for the code update to be done before publishing a new Version - await updateFunctionCodePromise; - // if we don't wait for the Function to finish updating, - // we can get a "The operation cannot be performed at this time. An update is in progress for resource:" - // error when publishing a new Version - await lambda.waitFor('functionUpdated', { - FunctionName: this.lambdaFunctionResource.physicalName, - }).promise(); - const publishVersionPromise = lambda.publishVersion({ FunctionName: this.lambdaFunctionResource.physicalName, }).promise(); @@ -269,8 +263,6 @@ class LambdaFunctionHotswapOperation implements HotswapOperation { } else { operations.push(publishVersionPromise); } - } else { - operations.push(updateFunctionCodePromise); } } @@ -304,6 +296,53 @@ class LambdaFunctionHotswapOperation implements HotswapOperation { // run all of our updates in parallel return Promise.all(operations); } + + /** + * After a Lambda Function is updated, it cannot be updated again until the + * `State=Active` and the `LastUpdateStatus=Successful`. + * + * Depending on the configuration of the Lambda Function this could happen relatively quickly + * or very slowly. For example, Zip based functions _not_ in a VPC can take ~1 second whereas VPC + * or Container functions can take ~25 seconds (and 'idle' VPC functions can take minutes). + */ + private async waitForLambdasCodeUpdateToFinish(currentFunctionConfiguration: AWS.Lambda.FunctionConfiguration, lambda: AWS.Lambda): Promise { + const functionIsInVpcOrUsesDockerForCode = currentFunctionConfiguration.VpcConfig?.VpcId || + currentFunctionConfiguration.PackageType === 'Image'; + + // if the function is deployed in a VPC or if it is a container image function + // then the update will take much longer and we can wait longer between checks + // otherwise, the update will be quick, so a 1-second delay is fine + const delaySeconds = functionIsInVpcOrUsesDockerForCode ? 5 : 1; + + // configure a custom waiter to wait for the function update to complete + (lambda as any).api.waiters.updateFunctionCodeToFinish = { + name: 'UpdateFunctionCodeToFinish', + operation: 'getFunction', + // equates to 1 minute for zip function not in a VPC and + // 5 minutes for container functions or function in a VPC + maxAttempts: 60, + delay: delaySeconds, + acceptors: [ + { + matcher: 'path', + argument: "Configuration.LastUpdateStatus == 'Successful' && Configuration.State == 'Active'", + expected: true, + state: 'success', + }, + { + matcher: 'path', + argument: 'Configuration.LastUpdateStatus', + expected: 'Failed', + state: 'failure', + }, + ], + }; + + const updateFunctionCodeWaiter = new (AWS as any).ResourceWaiter(lambda, 'updateFunctionCodeToFinish'); + await updateFunctionCodeWaiter.wait({ + FunctionName: this.lambdaFunctionResource.physicalName, + }).promise(); + } } /** diff --git a/packages/aws-cdk/lib/context-providers/vpcs.ts b/packages/aws-cdk/lib/context-providers/vpcs.ts index 99b2f6e7820a9..66a5e8e6d2cc5 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -80,6 +80,10 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { throw new Error(`Subnet ${subnet.SubnetArn} has invalid subnet type ${type} (must be ${SubnetType.Public}, ${SubnetType.Private} or ${SubnetType.Isolated})`); } + if (args.subnetGroupNameTag && !getTag(args.subnetGroupNameTag, subnet.Tags)) { + throw new Error(`Invalid subnetGroupNameTag: Subnet ${subnet.SubnetArn} does not have an associated tag with Key='${args.subnetGroupNameTag}'`); + } + const name = getTag(args.subnetGroupNameTag || 'aws-cdk:subnet-name', subnet.Tags) || type; const routeTableId = routeTables.routeTableIdForSubnetId(subnet.SubnetId); diff --git a/packages/aws-cdk/lib/init-templates/v1/app/csharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v1/app/csharp/cdk.template.json index 6711bc81bde11..054ecec595e7c 100644 --- a/packages/aws-cdk/lib/init-templates/v1/app/csharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v1/app/csharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.csproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.csproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v1/app/fsharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v1/app/fsharp/cdk.template.json index 040844e83e006..b7b6120fd4bca 100644 --- a/packages/aws-cdk/lib/init-templates/v1/app/fsharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v1/app/fsharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.fsproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.fsproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v1/sample-app/csharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v1/sample-app/csharp/cdk.template.json index 6711bc81bde11..054ecec595e7c 100644 --- a/packages/aws-cdk/lib/init-templates/v1/sample-app/csharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v1/sample-app/csharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.csproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.csproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v1/sample-app/fsharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v1/sample-app/fsharp/cdk.template.json index 040844e83e006..b7b6120fd4bca 100644 --- a/packages/aws-cdk/lib/init-templates/v1/sample-app/fsharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v1/sample-app/fsharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.fsproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.fsproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v2/app/csharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v2/app/csharp/cdk.template.json index 6711bc81bde11..054ecec595e7c 100644 --- a/packages/aws-cdk/lib/init-templates/v2/app/csharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v2/app/csharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.csproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.csproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v2/app/fsharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v2/app/fsharp/cdk.template.json index 040844e83e006..b7b6120fd4bca 100644 --- a/packages/aws-cdk/lib/init-templates/v2/app/fsharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v2/app/fsharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.fsproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.fsproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v2/sample-app/csharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v2/sample-app/csharp/cdk.template.json index 6711bc81bde11..054ecec595e7c 100644 --- a/packages/aws-cdk/lib/init-templates/v2/sample-app/csharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v2/sample-app/csharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.csproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.csproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/lib/init-templates/v2/sample-app/fsharp/cdk.template.json b/packages/aws-cdk/lib/init-templates/v2/sample-app/fsharp/cdk.template.json index 040844e83e006..b7b6120fd4bca 100644 --- a/packages/aws-cdk/lib/init-templates/v2/sample-app/fsharp/cdk.template.json +++ b/packages/aws-cdk/lib/init-templates/v2/sample-app/fsharp/cdk.template.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/%name.PascalCased%/%name.PascalCased%.fsproj", + "app": "dotnet run --project src/%name.PascalCased%/%name.PascalCased%.fsproj", "watch": { "include": ["**"], "exclude": [ diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index e2404ac8515a8..5fd0b9fbcc968 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -53,7 +53,7 @@ "@types/uuid": "^8.3.4", "@types/wrap-ansi": "^3.0.0", "@types/yargs": "^15.0.14", - "aws-sdk-mock": "^5.5.1", + "aws-sdk-mock": "^5.6.0", "@aws-cdk/cdk-build-tools": "0.0.0", "constructs": "^3.3.69", "jest": "^27.4.7", diff --git a/packages/aws-cdk/test/api/hotswap/hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/hotswap-deployments.test.ts index 00a6b2095a569..67b0117a8d7ae 100644 --- a/packages/aws-cdk/test/api/hotswap/hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/hotswap-deployments.test.ts @@ -8,7 +8,7 @@ let mockGetEndpointSuffix: () => string; beforeEach(() => { hotswapMockSdkProvider = setup.setupHotswapTests(); - mockUpdateLambdaCode = jest.fn(); + mockUpdateLambdaCode = jest.fn().mockReturnValue({}); mockUpdateMachineDefinition = jest.fn(); mockGetEndpointSuffix = jest.fn(() => 'amazonaws.com'); hotswapMockSdkProvider.stubLambda({ diff --git a/packages/aws-cdk/test/api/hotswap/hotswap-test-setup.ts b/packages/aws-cdk/test/api/hotswap/hotswap-test-setup.ts index e47b127e68766..7b6aebb9a81ee 100644 --- a/packages/aws-cdk/test/api/hotswap/hotswap-test-setup.ts +++ b/packages/aws-cdk/test/api/hotswap/hotswap-test-setup.ts @@ -83,8 +83,29 @@ export class HotswapMockSdkProvider { }); } - public stubLambda(stubs: SyncHandlerSubsetOf) { - this.mockSdkProvider.stubLambda(stubs); + public stubLambda( + stubs: SyncHandlerSubsetOf, + serviceStubs?: SyncHandlerSubsetOf, + additionalProperties: { [key: string]: any } = {}, + ): void { + this.mockSdkProvider.stubLambda(stubs, { + api: { + waiters: {}, + }, + makeRequest() { + return { + promise: () => Promise.resolve({}), + response: {}, + addListeners: () => {}, + }; + }, + ...serviceStubs, + ...additionalProperties, + }); + } + + public getLambdaApiWaiters(): { [key: string]: any } { + return (this.mockSdkProvider.sdk.lambda() as any).api.waiters; } public setUpdateProjectMock(mockUpdateProject: (input: codebuild.UpdateProjectInput) => codebuild.UpdateProjectOutput) { diff --git a/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts index 9aef8b8778cf0..3ed53e5fd908b 100644 --- a/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts @@ -5,16 +5,26 @@ let mockUpdateLambdaCode: (params: Lambda.Types.UpdateFunctionCodeRequest) => La let mockTagResource: (params: Lambda.Types.TagResourceRequest) => {}; let mockUntagResource: (params: Lambda.Types.UntagResourceRequest) => {}; let hotswapMockSdkProvider: setup.HotswapMockSdkProvider; +let mockMakeRequest: (operation: string, params: any) => AWS.Request; beforeEach(() => { hotswapMockSdkProvider = setup.setupHotswapTests(); - mockUpdateLambdaCode = jest.fn(); + mockUpdateLambdaCode = jest.fn().mockReturnValue({ + PackageType: 'Image', + }); mockTagResource = jest.fn(); mockUntagResource = jest.fn(); + mockMakeRequest = jest.fn().mockReturnValue({ + promise: () => Promise.resolve({}), + response: {}, + addListeners: () => {}, + }); hotswapMockSdkProvider.stubLambda({ updateFunctionCode: mockUpdateLambdaCode, tagResource: mockTagResource, untagResource: mockUntagResource, + }, { + makeRequest: mockMakeRequest, }); }); @@ -65,3 +75,53 @@ test('calls the updateLambdaCode() API when it receives only a code difference i ImageUri: 'new-image', }); }); + +test('calls the getFunction() API with a delay of 5', async () => { + // GIVEN + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ImageUri: 'current-image', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ImageUri: 'new-image', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); + + // WHEN + await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + expect(mockMakeRequest).toHaveBeenCalledWith('getFunction', { FunctionName: 'my-function' }); + expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ + updateFunctionCodeToFinish: expect.objectContaining({ + name: 'UpdateFunctionCodeToFinish', + delay: 5, + }), + })); +}); diff --git a/packages/aws-cdk/test/api/hotswap/lambda-functions-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/lambda-functions-hotswap-deployments.test.ts index 0b43563f233d9..d96b8530bce88 100644 --- a/packages/aws-cdk/test/api/hotswap/lambda-functions-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/lambda-functions-hotswap-deployments.test.ts @@ -4,17 +4,25 @@ import * as setup from './hotswap-test-setup'; let mockUpdateLambdaCode: (params: Lambda.Types.UpdateFunctionCodeRequest) => Lambda.Types.FunctionConfiguration; let mockTagResource: (params: Lambda.Types.TagResourceRequest) => {}; let mockUntagResource: (params: Lambda.Types.UntagResourceRequest) => {}; +let mockMakeRequest: (operation: string, params: any) => AWS.Request; let hotswapMockSdkProvider: setup.HotswapMockSdkProvider; beforeEach(() => { hotswapMockSdkProvider = setup.setupHotswapTests(); - mockUpdateLambdaCode = jest.fn(); + mockUpdateLambdaCode = jest.fn().mockReturnValue({}); mockTagResource = jest.fn(); mockUntagResource = jest.fn(); + mockMakeRequest = jest.fn().mockReturnValue({ + promise: () => Promise.resolve({}), + response: {}, + addListeners: () => {}, + }); hotswapMockSdkProvider.stubLambda({ updateFunctionCode: mockUpdateLambdaCode, tagResource: mockTagResource, untagResource: mockUntagResource, + }, { + makeRequest: mockMakeRequest, }); }); @@ -539,3 +547,177 @@ test('does not call the updateLambdaCode() API when a resource with type that is expect(deployStackResult).toBeUndefined(); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); }); + +test('calls getFunction() after function code is updated with delay 1', async () => { + // GIVEN + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: 'current-bucket', + S3Key: 'current-key', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: 'current-bucket', + S3Key: 'new-key', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); + + // WHEN + await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + expect(mockMakeRequest).toHaveBeenCalledWith('getFunction', { FunctionName: 'my-function' }); + expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ + updateFunctionCodeToFinish: expect.objectContaining({ + name: 'UpdateFunctionCodeToFinish', + delay: 1, + }), + })); +}); + +test('calls getFunction() after function code is updated and VpcId is empty string with delay 1', async () => { + // GIVEN + mockUpdateLambdaCode = jest.fn().mockReturnValue({ + VpcConfig: { + VpcId: '', + }, + }); + hotswapMockSdkProvider.stubLambda({ + updateFunctionCode: mockUpdateLambdaCode, + tagResource: mockTagResource, + untagResource: mockUntagResource, + }); + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: 'current-bucket', + S3Key: 'current-key', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: 'current-bucket', + S3Key: 'new-key', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); + + // WHEN + await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ + updateFunctionCodeToFinish: expect.objectContaining({ + name: 'UpdateFunctionCodeToFinish', + delay: 1, + }), + })); +}); + +test('calls getFunction() after function code is updated on a VPC function with delay 5', async () => { + // GIVEN + mockUpdateLambdaCode = jest.fn().mockReturnValue({ + VpcConfig: { + VpcId: 'abc', + }, + }); + hotswapMockSdkProvider.stubLambda({ + updateFunctionCode: mockUpdateLambdaCode, + tagResource: mockTagResource, + untagResource: mockUntagResource, + }); + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: 'current-bucket', + S3Key: 'current-key', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: 'current-bucket', + S3Key: 'new-key', + }, + FunctionName: 'my-function', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); + + // WHEN + await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ + updateFunctionCodeToFinish: expect.objectContaining({ + name: 'UpdateFunctionCodeToFinish', + delay: 5, + }), + })); +}); diff --git a/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts index 13554cc655dcb..478fd80b538bf 100644 --- a/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts @@ -8,7 +8,7 @@ let hotswapMockSdkProvider: setup.HotswapMockSdkProvider; beforeEach(() => { hotswapMockSdkProvider = setup.setupHotswapTests(); - mockUpdateLambdaCode = jest.fn(); + mockUpdateLambdaCode = jest.fn().mockReturnValue({}); mockTagResource = jest.fn(); mockUntagResource = jest.fn(); hotswapMockSdkProvider.stubLambda({ diff --git a/packages/aws-cdk/test/api/hotswap/lambda-versions-aliases-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/lambda-versions-aliases-hotswap-deployments.test.ts index 4596bb2c96ac0..f937be7c5a5a0 100644 --- a/packages/aws-cdk/test/api/hotswap/lambda-versions-aliases-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/lambda-versions-aliases-hotswap-deployments.test.ts @@ -8,7 +8,7 @@ let hotswapMockSdkProvider: setup.HotswapMockSdkProvider; beforeEach(() => { hotswapMockSdkProvider = setup.setupHotswapTests(); - mockUpdateLambdaCode = jest.fn(); + mockUpdateLambdaCode = jest.fn().mockReturnValue({}); mockPublishVersion = jest.fn(); mockUpdateAlias = jest.fn(); hotswapMockSdkProvider.stubLambda({ diff --git a/packages/aws-cdk/test/api/logs/logs-monitor.test.ts b/packages/aws-cdk/test/api/logs/logs-monitor.test.ts index becb3d97dfbf3..930e6427dbab7 100644 --- a/packages/aws-cdk/test/api/logs/logs-monitor.test.ts +++ b/packages/aws-cdk/test/api/logs/logs-monitor.test.ts @@ -17,20 +17,13 @@ afterAll(() => { monitor.deactivate(); }); -let TIMESTAMP: number; -let HUMAN_TIME: string; - -beforeAll(() => { - TIMESTAMP = new Date().getTime(); - HUMAN_TIME = new Date(TIMESTAMP).toLocaleTimeString(); -}); - test('continue to the next page if it exists', async () => { // GIVEN + const eventDate = new Date(T0 + 102 * 1000); sdk.stubCloudWatchLogs({ filterLogEvents() { return { - events: [event(102, 'message')], + events: [event(102, 'message', eventDate)], nextToken: 'some-token', }; }, @@ -50,22 +43,23 @@ test('continue to the next page if it exists', async () => { await sleep(1000); // THEN + const expectedLocaleTimeString = eventDate.toLocaleTimeString(); expect(stderrMock).toHaveBeenCalledTimes(2); expect(stderrMock.mock.calls[0][0]).toContain( - `[${blue('loggroup')}] ${yellow(HUMAN_TIME)} message`, + `[${blue('loggroup')}] ${yellow(expectedLocaleTimeString)} message`, ); expect(stderrMock.mock.calls[1][0]).toContain( - `[${blue('loggroup')}] ${yellow(new Date(T100).toLocaleTimeString())} >>> \`watch\` shows only the first 100 log messages - the rest have been truncated...`, + `[${blue('loggroup')}] ${yellow(expectedLocaleTimeString)} >>> \`watch\` shows only the first 100 log messages - the rest have been truncated...`, ); }); const T0 = 1597837230504; const T100 = T0 + 100 * 1000; -function event(nr: number, message: string): AWS.CloudWatchLogs.FilteredLogEvent { +function event(nr: number, message: string, timestamp: Date): AWS.CloudWatchLogs.FilteredLogEvent { return { eventId: `${nr}`, message, - timestamp: new Date(T0 * nr * 1000).getTime(), - ingestionTime: new Date(T0 * nr * 1000).getTime(), + timestamp: timestamp.getTime(), + ingestionTime: timestamp.getTime(), }; } diff --git a/packages/aws-cdk/test/context-providers/vpcs.test.ts b/packages/aws-cdk/test/context-providers/vpcs.test.ts index e97090666d710..0b03aaf4cacd9 100644 --- a/packages/aws-cdk/test/context-providers/vpcs.test.ts +++ b/packages/aws-cdk/test/context-providers/vpcs.test.ts @@ -110,6 +110,144 @@ test('throws when no such VPC is found', async () => { })).rejects.toThrow(/Could not find any VPCs matching/); }); +test('throws when subnet with subnetGroupNameTag not found', async () => { + // GIVEN + const filter = { foo: 'bar' }; + const provider = new VpcNetworkContextProviderPlugin(mockSDK); + + mockVpcLookup({ + subnets: [ + { SubnetId: 'sub-123456', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: true }, + { SubnetId: 'sub-789012', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: false }, + ], + routeTables: [ + { + Associations: [{ SubnetId: 'sub-123456' }], + RouteTableId: 'rtb-123456', + Routes: [ + { + DestinationCidrBlock: '1.1.1.1/24', + GatewayId: 'local', + Origin: 'CreateRouteTable', + State: 'active', + }, + { + DestinationCidrBlock: '0.0.0.0/0', + GatewayId: 'igw-xxxxxx', + Origin: 'CreateRoute', + State: 'active', + }, + ], + }, + { + Associations: [{ SubnetId: 'sub-789012' }], + RouteTableId: 'rtb-789012', + Routes: [ + { + DestinationCidrBlock: '1.1.2.1/24', + GatewayId: 'local', + Origin: 'CreateRouteTable', + State: 'active', + }, + { + DestinationCidrBlock: '0.0.0.0/0', + NatGatewayId: 'nat-xxxxxx', + Origin: 'CreateRoute', + State: 'active', + }, + ], + }, + ], + vpnGateways: [{ VpnGatewayId: 'gw-abcdef' }], + }); + + // WHEN + await expect(provider.getValue({ + account: '1234', + region: 'us-east-1', + subnetGroupNameTag: 'DOES_NOT_EXIST', + filter, + })).rejects.toThrow(/Invalid subnetGroupNameTag: Subnet .* does not have an associated tag with Key='DOES_NOT_EXIST'/); +}); + +test('does not throw when subnet with subnetGroupNameTag is found', async () => { + // GIVEN + const filter = { foo: 'bar' }; + const provider = new VpcNetworkContextProviderPlugin(mockSDK); + + mockVpcLookup({ + subnets: [ + { SubnetId: 'sub-123456', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: true, Tags: [{ Key: 'DOES_EXIST', Value: 'SubnetName1' }] }, + { SubnetId: 'sub-789012', AvailabilityZone: 'bermuda-triangle-1337', MapPublicIpOnLaunch: false, Tags: [{ Key: 'DOES_EXIST', Value: 'SubnetName2' }] }, + ], + routeTables: [ + { + Associations: [{ SubnetId: 'sub-123456' }], + RouteTableId: 'rtb-123456', + Routes: [ + { + DestinationCidrBlock: '1.1.1.1/24', + GatewayId: 'local', + Origin: 'CreateRouteTable', + State: 'active', + }, + { + DestinationCidrBlock: '0.0.0.0/0', + GatewayId: 'igw-xxxxxx', + Origin: 'CreateRoute', + State: 'active', + }, + ], + }, + { + Associations: [{ SubnetId: 'sub-789012' }], + RouteTableId: 'rtb-789012', + Routes: [ + { + DestinationCidrBlock: '1.1.2.1/24', + GatewayId: 'local', + Origin: 'CreateRouteTable', + State: 'active', + }, + { + DestinationCidrBlock: '0.0.0.0/0', + NatGatewayId: 'nat-xxxxxx', + Origin: 'CreateRoute', + State: 'active', + }, + ], + }, + ], + vpnGateways: [{ VpnGatewayId: 'gw-abcdef' }], + }); + + // WHEN + const result = await provider.getValue({ + account: '1234', + region: 'us-east-1', + subnetGroupNameTag: 'DOES_EXIST', + filter, + }); + + // THEN + expect(result).toEqual({ + vpcId: 'vpc-1234567', + vpcCidrBlock: '1.1.1.1/16', + availabilityZones: ['bermuda-triangle-1337'], + isolatedSubnetIds: undefined, + isolatedSubnetNames: undefined, + isolatedSubnetRouteTableIds: undefined, + privateSubnetIds: ['sub-789012'], + privateSubnetNames: ['SubnetName2'], + privateSubnetRouteTableIds: ['rtb-789012'], + publicSubnetIds: ['sub-123456'], + publicSubnetNames: ['SubnetName1'], + publicSubnetRouteTableIds: ['rtb-123456'], + vpnGatewayId: 'gw-abcdef', + subnetGroups: undefined, + }); +}); + test('throws when multiple VPCs are found', async () => { // GIVEN const filter = { foo: 'bar' }; diff --git a/packages/aws-cdk/test/util/mock-sdk.ts b/packages/aws-cdk/test/util/mock-sdk.ts index a1ed61a431366..bb8eaae251c47 100644 --- a/packages/aws-cdk/test/util/mock-sdk.ts +++ b/packages/aws-cdk/test/util/mock-sdk.ts @@ -102,8 +102,8 @@ export class MockSdkProvider extends SdkProvider { (this.sdk as any).ssm = jest.fn().mockReturnValue(partialAwsService(stubs)); } - public stubLambda(stubs: SyncHandlerSubsetOf) { - (this.sdk as any).lambda = jest.fn().mockReturnValue(partialAwsService(stubs)); + public stubLambda(stubs: SyncHandlerSubsetOf, additionalProperties: { [key: string]: any } = {}) { + (this.sdk as any).lambda = jest.fn().mockReturnValue(partialAwsService(stubs, additionalProperties)); } public stubStepFunctions(stubs: SyncHandlerSubsetOf) { diff --git a/packages/cdk-dasm/package.json b/packages/cdk-dasm/package.json index 5700d476fc41b..b684e360f6208 100644 --- a/packages/cdk-dasm/package.json +++ b/packages/cdk-dasm/package.json @@ -6,7 +6,8 @@ "types": "lib/index.d.ts", "repository": { "type": "git", - "url": "https://github.com/aws/aws-cdk.git" + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/cdk-dasm" }, "bin": { "cdk-dasm": "bin/cdk-dasm" @@ -42,7 +43,9 @@ "cdk" ], "nozem": { - "ostools": ["chmod"] + "ostools": [ + "chmod" + ] }, "homepage": "https://github.com/aws/aws-cdk", "engines": { diff --git a/packages/cdk/package.json b/packages/cdk/package.json index c1f9a6c167236..ef25d8587edc3 100644 --- a/packages/cdk/package.json +++ b/packages/cdk/package.json @@ -10,7 +10,8 @@ }, "repository": { "type": "git", - "url": "https://github.com/aws/aws-cdk.git" + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/cdk" }, "keywords": [ "aws", @@ -35,7 +36,7 @@ "build+test+package": "npm run build+test && npm run package", "build+test+extract": "npm run build+test", "build+extract": "npm run build" - }, + }, "engines": { "node": ">= 8.10.0" }, diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index d005e103acd8f..402af915f5303 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -235,6 +235,7 @@ "@aws-cdk/aws-iotthingsgraph": "0.0.0", "@aws-cdk/aws-iotwireless": "0.0.0", "@aws-cdk/aws-ivs": "0.0.0", + "@aws-cdk/aws-kafkaconnect": "0.0.0", "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", diff --git a/scripts/align-version.sh b/scripts/align-version.sh index e48e2dd421990..1a0709c9793b5 100755 --- a/scripts/align-version.sh +++ b/scripts/align-version.sh @@ -19,7 +19,6 @@ marker=$(node -p "require('./scripts/resolve-version').marker.replace(/\./g, '\\ # Exclude a couple of specific ones that we don't care about. package_jsons=$(find . -name package.json |\ grep -v node_modules |\ - grep -v packages/decdk/test/fixture/package.json |\ grep -v .github/actions/prlinter/package.json) if grep -l "[^0-9]${marker}" $package_jsons; then diff --git a/scripts/compile-samples b/scripts/compile-samples index a253f3ffaa21b..826031970a73a 100755 --- a/scripts/compile-samples +++ b/scripts/compile-samples @@ -5,7 +5,7 @@ # on them you can use this script for quicker feedback. # # This could maybe have been an 'npm run' script, but it's not self-contained -# (needs the "decdk" package to compile against and runs jsii-rosetta from the +# (needs the "aws-cdk-lib" package to compile against and runs jsii-rosetta from the # repo root) so that didn't feel right. For now this is what we have. set -eu scriptdir=$(cd $(dirname $0) && pwd) @@ -23,9 +23,9 @@ for dir in $dirs; do # Run jsii npm run build - # Run rosetta against decdk dir, failing on compilation errors + # Run rosetta against aws-cdk-lib dir, failing on compilation errors node --experimental-worker $scriptdir/../node_modules/.bin/jsii-rosetta \ - --directory $scriptdir/../packages/decdk \ + --directory $scriptdir/../packages/aws-cdk-lib \ --compile \ --output /dev/null \ --verbose \ diff --git a/scripts/list-deprecated-apis.js b/scripts/list-deprecated-apis.js index a4596ebdf1a69..874a42c4e36ee 100755 --- a/scripts/list-deprecated-apis.js +++ b/scripts/list-deprecated-apis.js @@ -48,8 +48,8 @@ class StripDeprecatedPrinter { async function main(printer) { const typesystem = new jsiiReflect.TypeSystem(); - // Decdk depends on everything, so that's a perfect directory to load as closure - await typesystem.loadNpmDependencies(path.resolve(__dirname, '..', 'packages', 'decdk'), { validate: false }); + // aws-cdk-lib depends on everything, so that's a perfect directory to load as closure + await typesystem.loadNpmDependencies(path.resolve(__dirname, '..', 'packages', 'aws-cdk-lib'), { validate: false }); printer.printHeader(); diff --git a/scripts/run-rosetta.sh b/scripts/run-rosetta.sh index da627fa05e4f8..1a0be40012bf9 100755 --- a/scripts/run-rosetta.sh +++ b/scripts/run-rosetta.sh @@ -62,14 +62,14 @@ fi #---------------------------------------------------------------------- -# Compile examples with respect to "decdk" directory, as all packages will +# Compile examples with respect to "aws-cdk-lib" directory, as all packages will # be symlinked there so they can all be included. echo "💎 Extracting code samples" >&2 time $ROSETTA extract \ --compile \ --verbose \ --cache ${rosetta_cache_file} \ - --directory packages/decdk \ + --directory packages/aws-cdk-lib \ ${extract_opts} \ $(cat $jsii_pkgs_file) @@ -82,7 +82,7 @@ if $infuse; then --compile \ --verbose \ --cache ${rosetta_cache_file} \ - --directory packages/decdk \ + --directory packages/aws-cdk-lib \ $(cat $jsii_pkgs_file) fi diff --git a/tools/@aws-cdk/cdk-integ-tools/bin/cdk-integ-assert.ts b/tools/@aws-cdk/cdk-integ-tools/bin/cdk-integ-assert.ts index cdca064874099..50a50575cf79c 100644 --- a/tools/@aws-cdk/cdk-integ-tools/bin/cdk-integ-assert.ts +++ b/tools/@aws-cdk/cdk-integ-tools/bin/cdk-integ-assert.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node // Verify that all integration tests still match their expected output -import { canonicalizeTemplate } from '@aws-cdk/assert-internal'; import { diffTemplate, formatDifferences } from '@aws-cdk/cloudformation-diff'; +import { canonicalizeTemplate } from '../lib/canonicalize-assets'; import { DEFAULT_SYNTH_OPTIONS, IntegrationTests } from '../lib/integ-helpers'; /* eslint-disable no-console */ diff --git a/tools/@aws-cdk/cdk-integ-tools/lib/canonicalize-assets.ts b/tools/@aws-cdk/cdk-integ-tools/lib/canonicalize-assets.ts new file mode 100644 index 0000000000000..9cee3d4742b3c --- /dev/null +++ b/tools/@aws-cdk/cdk-integ-tools/lib/canonicalize-assets.ts @@ -0,0 +1,71 @@ +/** + * Reduce template to a normal form where asset references have been normalized + * + * This makes it possible to compare templates if all that's different between + * them is the hashes of the asset values. + * + * Currently only handles parameterized assets, but can (and should) + * be adapted to handle convention-mode assets as well when we start using + * more of those. + */ +export function canonicalizeTemplate(template: any): any { + // For the weird case where we have an array of templates... + if (Array.isArray(template)) { + return template.map(canonicalizeTemplate); + } + + // Find assets via parameters + const stringSubstitutions = new Array<[RegExp, string]>(); + const paramRe = /^AssetParameters([a-zA-Z0-9]{64})(S3Bucket|S3VersionKey|ArtifactHash)([a-zA-Z0-9]{8})$/; + + const assetsSeen = new Set(); + for (const paramName of Object.keys(template?.Parameters || {})) { + const m = paramRe.exec(paramName); + if (!m) { continue; } + if (assetsSeen.has(m[1])) { continue; } + + assetsSeen.add(m[1]); + const ix = assetsSeen.size; + + // Full parameter reference + stringSubstitutions.push([ + new RegExp(`AssetParameters${m[1]}(S3Bucket|S3VersionKey|ArtifactHash)([a-zA-Z0-9]{8})`), + `Asset${ix}$1`, + ]); + // Substring asset hash reference + stringSubstitutions.push([ + new RegExp(`${m[1]}`), + `Asset${ix}Hash`, + ]); + } + + // Substitute them out + return substitute(template); + + function substitute(what: any): any { + if (Array.isArray(what)) { + return what.map(substitute); + } + + if (typeof what === 'object' && what !== null) { + const ret: any = {}; + for (const [k, v] of Object.entries(what)) { + ret[stringSub(k)] = substitute(v); + } + return ret; + } + + if (typeof what === 'string') { + return stringSub(what); + } + + return what; + } + + function stringSub(x: string) { + for (const [re, replacement] of stringSubstitutions) { + x = x.replace(re, replacement); + } + return x; + } +} diff --git a/tools/@aws-cdk/cdk-integ-tools/package.json b/tools/@aws-cdk/cdk-integ-tools/package.json index 01e3efa6ec6a1..de5ad2cfed9fd 100644 --- a/tools/@aws-cdk/cdk-integ-tools/package.json +++ b/tools/@aws-cdk/cdk-integ-tools/package.json @@ -39,7 +39,6 @@ "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "@aws-cdk/assert-internal": "0.0.0", "aws-cdk": "0.0.0", "fs-extra": "^9.1.0", "yargs": "^16.2.0" @@ -52,9 +51,6 @@ "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, - "peerDependencies": { - "@aws-cdk/assert-internal": "0.0.0" - }, "ubergen": { "exclude": true } diff --git a/version.v1.json b/version.v1.json index b2dc0b4c0995b..93f1a34486943 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.140.0" + "version": "1.142.0" } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index da1eded3a0af7..d69b35201a8c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,19 +45,19 @@ integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.8.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf" - integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA== + version "7.16.12" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" + integrity sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.16.7" + "@babel/generator" "^7.16.8" "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-module-transforms" "^7.16.7" "@babel/helpers" "^7.16.7" - "@babel/parser" "^7.16.7" + "@babel/parser" "^7.16.12" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/traverse" "^7.16.10" + "@babel/types" "^7.16.8" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -65,7 +65,7 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.16.7", "@babel/generator@^7.16.8", "@babel/generator@^7.7.2": +"@babel/generator@^7.16.8", "@babel/generator@^7.7.2": version "7.16.8" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== @@ -174,18 +174,18 @@ "@babel/types" "^7.16.7" "@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" - integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== + version "7.16.10" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17" - integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7": + version "7.16.12" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" + integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -287,10 +287,10 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.16.7", "@babel/traverse@^7.7.2": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz#bab2f2b09a5fe8a8d9cad22cbfe3ba1d126fef9c" - integrity sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ== +"@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.7.2": + version "7.16.10" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" + integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== dependencies: "@babel/code-frame" "^7.16.7" "@babel/generator" "^7.16.8" @@ -298,7 +298,7 @@ "@babel/helper-function-name" "^7.16.7" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.16.8" + "@babel/parser" "^7.16.10" "@babel/types" "^7.16.8" debug "^4.1.0" globals "^11.1.0" @@ -1442,15 +1442,15 @@ once "^1.4.0" "@octokit/request@^5.2.0", "@octokit/request@^5.6.0": - version "5.6.2" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz#1aa74d5da7b9e04ac60ef232edd9a7438dcf32d8" - integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== + version "5.6.3" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" + integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.1.0" "@octokit/types" "^6.16.1" is-plain-object "^5.0.0" - node-fetch "^2.6.1" + node-fetch "^2.6.7" universal-user-agent "^6.0.0" "@octokit/rest@^16.43.1": @@ -1582,10 +1582,10 @@ dependencies: "@types/glob" "*" -"@types/aws-lambda@^8.10.90": - version "8.10.90" - resolved "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.90.tgz#d9995deb8925afb78b8c6179fc58fa9316925102" - integrity sha512-B08uyjXQuOh/Kyj9NZ0BEJjkfqqj4tsAYpbBgUMRydtYAs1Fs5WL5KiYFtu+OoG5mI64pnsvW8ohFgpJYWdWIg== +"@types/aws-lambda@^8.10.92": + version "8.10.92" + resolved "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.92.tgz#645f769ff88b8eba1acd35542695ac322c7757c4" + integrity sha512-dB14TltT1SNq73z3MaZfKyyBZ37NAgAFl8jze59bisR4fJ6pB6AYGxItHFkooZbN7UcVJX/cFudM4p8wp1W4rA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.18" @@ -1752,9 +1752,9 @@ integrity sha512-uv53RrNdhbkV/3VmVCtfImfYCWC3GTTRn3R11Whni3EJ+gb178tkZBVNj2edLY5CMrB749dQi+SJkg87jsN8UQ== "@types/node@*", "@types/node@>= 8": - version "17.0.10" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab" - integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog== + version "17.0.12" + resolved "https://registry.npmjs.org/@types/node/-/node-17.0.12.tgz#f7aa331b27f08244888c47b7df126184bc2339c5" + integrity sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA== "@types/node@^10.17.60": version "10.17.60" @@ -2291,24 +2291,24 @@ available-typed-arrays@^1.0.5: resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sdk-mock@^5.5.1: - version "5.5.1" - resolved "https://registry.npmjs.org/aws-sdk-mock/-/aws-sdk-mock-5.5.1.tgz#d6bfd71e91e020a8ba07e189f8f1bae5890d1074" - integrity sha512-9wGrPozD7YciPt1qzo0ypviKEZKFJ6Jab8qtYmDRNG41WxZGJ//EEIv1FL6t4bEc8j4bPiwnoam5TP5eTv7UDA== +aws-sdk-mock@^5.6.0: + version "5.6.0" + resolved "https://registry.npmjs.org/aws-sdk-mock/-/aws-sdk-mock-5.6.0.tgz#8cd44e2e8d65f7f38dea8d1ad073fa38f9c719d8" + integrity sha512-gUoEs/FaszVTLG5zBveg2NC2w/j/c9j6Ymgq4whrsdysClW6FWKT9w8eOUERZChliKZe/LwHHJFKjBHGrV9MJg== dependencies: aws-sdk "^2.928.0" sinon "^11.1.1" traverse "^0.6.6" aws-sdk@^2.596.0, aws-sdk@^2.848.0, aws-sdk@^2.928.0, aws-sdk@^2.979.0: - version "2.1059.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1059.0.tgz#8ce3fa0652ef2836665d0c3566bc56a3d5cfc929" - integrity sha512-Q+6T9kpO6aobUNboTOk9MVAmWbs/KK0pxgCNFK0M8YO+7EWUFkNOLHM9tdYOP5vsJK5pLz6D2t2w3lHQjKzGlg== + version "2.1063.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1063.0.tgz#ab5e7f69955358a48be345ee3d76667a68f61dd6" + integrity sha512-UonfKdsDChKEmAkFuDOQ8zeilvR5v7d5dEcWDy+fnKBs+6HGjDThMf7EofhOiKxOXWnFhrAsFKCsKDcfeA6NBg== dependencies: buffer "4.9.2" events "1.1.1" ieee754 "1.1.13" - jmespath "0.15.0" + jmespath "0.16.0" querystring "0.2.0" sax "1.2.1" url "0.10.3" @@ -2598,9 +2598,9 @@ camelcase@^6.2.0, camelcase@^6.2.1, camelcase@^6.3.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001286: - version "1.0.30001300" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz#11ab6c57d3eb6f964cba950401fd00a146786468" - integrity sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA== + version "1.0.30001302" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001302.tgz#da57ce61c51177ef3661eeed7faef392d3790aaa" + integrity sha512-YYTMO+tfwvgUN+1ZnRViE53Ma1S/oETg+J2lISsqi/ZTNThj3ZYBOKP2rHwJc37oCsPqAzJ3w2puZHn0xlLPPw== case@1.6.3, case@^1.6.3: version "1.6.3" @@ -2624,17 +2624,17 @@ cdk-generate-synthetic-examples@^0.1.3: jsii-rosetta "^1.52.1" yargs "^17.3.1" -cdk8s-plus-21@^1.0.0-beta.77: - version "1.0.0-beta.77" - resolved "https://registry.npmjs.org/cdk8s-plus-21/-/cdk8s-plus-21-1.0.0-beta.77.tgz#3ac09d3612cfd7a64e3ff94e9a1016b29eefcfbf" - integrity sha512-W/wqEqFeh7x6hVwaiqFnxilMrjNDbyfpM3Yfh7SytDVFjPdQmzISAVe/kpnP8Om07A8mUHE/synGSvDwRjoVdA== +cdk8s-plus-21@^1.0.0-beta.79: + version "1.0.0-beta.79" + resolved "https://registry.npmjs.org/cdk8s-plus-21/-/cdk8s-plus-21-1.0.0-beta.79.tgz#e22bca4ca6a9fc04a8006342f6476bd5826043a5" + integrity sha512-EASJEqFxYzVTP/DfcDq4m7Adcg2ToINZkMlFZVSC8hABs7qWk858ra2Ff6dRqb271c3K9x+AiJ4V7F+FItVojA== dependencies: minimatch "^3.0.4" -cdk8s@^1.4.6: - version "1.4.6" - resolved "https://registry.npmjs.org/cdk8s/-/cdk8s-1.4.6.tgz#7d38d5a2ac4de11a48e9e1ddf1375b66b826a355" - integrity sha512-LAUIvbIVq8qNVHn5CEnUpFlbpvgsUrXW1fj7BDgBu0FGODdUjab5iUYZjMT/3Cao8I+mQj4xLfvkhzl5uYXIAA== +cdk8s@^1.4.13: + version "1.4.13" + resolved "https://registry.npmjs.org/cdk8s/-/cdk8s-1.4.13.tgz#30e08173f5dd1986ba7a6667aa5504abceeb7e1a" + integrity sha512-JijJ7DwaiCm2kDLZTVQ9dilx8rEwuZk7ZmqO+8nF81UIdBvz14L+wljW9pUj/HuvXO40rLWa1+R94IgIv4JnPA== dependencies: fast-json-patch "^2.2.1" follow-redirects "^1.14.7" @@ -2922,9 +2922,9 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= constructs@^3.3.69: - version "3.3.193" - resolved "https://registry.npmjs.org/constructs/-/constructs-3.3.193.tgz#6fe4ff6495127337cff10f70a81d546e61cab530" - integrity sha512-85lCvrPlrlTnKS/rpl/jiB9y5t4GGWHhubRP3hlQbKiGAeQ2VIqcjODsU5oorrDi5JzDxcdZ83vfPtP1RtELWA== + version "3.3.200" + resolved "https://registry.npmjs.org/constructs/-/constructs-3.3.200.tgz#5d126a7ab99b2017f2109f250ecfcbba4636b917" + integrity sha512-LpHHMB9Dvst9AKDnkR7To7ikFbaWsvZf8FPi40ZBRt7EzAgrJyOf20GQlb7tkF9Ln0Z2nUPlJgrj93sXT/UI/Q== conventional-changelog-angular@^5.0.12: version "5.0.13" @@ -3171,12 +3171,12 @@ cosmiconfig@^7.0.0: yaml "^1.10.0" crc-32@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" - integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + version "1.2.1" + resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.1.tgz#436d2bcaad27bcb6bd073a2587139d3024a16460" + integrity sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w== dependencies: exit-on-epipe "~1.0.1" - printj "~1.1.0" + printj "~1.3.1" crc32-stream@^4.0.2: version "4.0.2" @@ -3267,22 +3267,17 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-format@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" - integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== - -date-format@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" - integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== +date-format@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz#f63de5dc08dc02efd8ef32bf2a6918e486f35873" + integrity sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ== dateformat@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3: version "4.3.3" resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -3569,9 +3564,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.4.17: - version "1.4.48" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz#1948b5227aa0ca1ed690945eae1adbe9e7904575" - integrity sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA== + version "1.4.53" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.53.tgz#5d80a91c399b44952ef485857fb5b9d4387d2e60" + integrity sha512-rFveSKQczlcav+H3zkKqykU6ANseFwXwkl855jOIap5/0gnEcuIhv2ecz6aoTrXavF6I/CEBeRnBnkB51k06ew== emittery@^0.8.1: version "0.8.1" @@ -3726,119 +3721,119 @@ es6-weak-map@^2.0.3: es6-iterator "^2.0.3" es6-symbol "^3.1.1" -esbuild-android-arm64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.11.tgz#b8b34e35a5b43880664ac7a3fbc70243d7ed894f" - integrity sha512-6iHjgvMnC/SzDH8TefL+/3lgCjYWwAd1LixYfmz/TBPbDQlxcuSkX0yiQgcJB9k+ibZ54yjVXziIwGdlc+6WNw== - -esbuild-darwin-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.11.tgz#ba805de98c0412e50fcd0636451797da157b0625" - integrity sha512-olq84ikh6TiBcrs3FnM4eR5VPPlcJcdW8BnUz/lNoEWYifYQ+Po5DuYV1oz1CTFMw4k6bQIZl8T3yxL+ZT2uvQ== - -esbuild-darwin-arm64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.11.tgz#4d3573e448af76ce33e16231f3d9f878542d6fe8" - integrity sha512-Jj0ieWLREPBYr/TZJrb2GFH8PVzDqiQWavo1pOFFShrcmHWDBDrlDxPzEZ67NF/Un3t6sNNmeI1TUS/fe1xARg== - -esbuild-freebsd-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.11.tgz#9294e6ab359ec93590ab097b0f2017de7c78ab4d" - integrity sha512-C5sT3/XIztxxz/zwDjPRHyzj/NJFOnakAanXuyfLDwhwupKPd76/PPHHyJx6Po6NI6PomgVp/zi6GRB8PfrOTA== - -esbuild-freebsd-arm64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.11.tgz#ae3e0b09173350b66cf8321583c9a1c1fcb8bb55" - integrity sha512-y3Llu4wbs0bk4cwjsdAtVOesXb6JkdfZDLKMt+v1U3tOEPBdSu6w8796VTksJgPfqvpX22JmPLClls0h5p+L9w== - -esbuild-linux-32@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.11.tgz#ddadbc7038aa5a6b1675bb1503cf79a0cbf1229a" - integrity sha512-Cg3nVsxArjyLke9EuwictFF3Sva+UlDTwHIuIyx8qpxRYAOUTmxr2LzYrhHyTcGOleLGXUXYsnUVwKqnKAgkcg== - -esbuild-linux-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.11.tgz#d698e3ce3a231ddfeec6b5df8c546ae8883fcd88" - integrity sha512-oeR6dIrrojr8DKVrxtH3xl4eencmjsgI6kPkDCRIIFwv4p+K7ySviM85K66BN01oLjzthpUMvBVfWSJkBLeRbg== - -esbuild-linux-arm64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.11.tgz#85faea9fa99ad355b5e3b283197a4dfd0a110fe7" - integrity sha512-+e6ZCgTFQYZlmg2OqLkg1jHLYtkNDksxWDBWNtI4XG4WxuOCUErLqfEt9qWjvzK3XBcCzHImrajkUjO+rRkbMg== - -esbuild-linux-arm@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.11.tgz#74cbcf0b8a22c8401bcbcd6ebd4cbf2baca8b7b4" - integrity sha512-vcwskfD9g0tojux/ZaTJptJQU3a7YgTYsptK1y6LQ/rJmw7U5QJvboNawqM98Ca3ToYEucfCRGbl66OTNtp6KQ== - -esbuild-linux-mips64le@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.11.tgz#490429211a3233f5cbbd8575b7758b897e42979a" - integrity sha512-Rrs99L+p54vepmXIb87xTG6ukrQv+CzrM8eoeR+r/OFL2Rg8RlyEtCeshXJ2+Q66MXZOgPJaokXJZb9snq28bw== - -esbuild-linux-ppc64le@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.11.tgz#fc79d60710213b5b98345f5b138d48245616827a" - integrity sha512-JyzziGAI0D30Vyzt0HDihp4s1IUtJ3ssV2zx9O/c+U/dhUHVP2TmlYjzCfCr2Q6mwXTeloDcLS4qkyvJtYptdQ== - -esbuild-linux-s390x@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.11.tgz#ca4b93556bbba6cc95b0644f2ee93c982165ba07" - integrity sha512-DoThrkzunZ1nfRGoDN6REwmo8ZZWHd2ztniPVIR5RMw/Il9wiWEYBahb8jnMzQaSOxBsGp0PbyJeVLTUatnlcw== - -esbuild-netbsd-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.11.tgz#edb340bc6653c88804cac2253e21b74258fce165" - integrity sha512-12luoRQz+6eihKYh1zjrw0CBa2aw3twIiHV/FAfjh2NEBDgJQOY4WCEUEN+Rgon7xmLh4XUxCQjnwrvf8zhACw== - -esbuild-openbsd-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.11.tgz#caeff5f946f79a60ce7bcf88871ca4c71d3476e8" - integrity sha512-l18TZDjmvwW6cDeR4fmizNoxndyDHamGOOAenwI4SOJbzlJmwfr0jUgjbaXCUuYVOA964siw+Ix+A+bhALWg8Q== - -esbuild-sunos-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.11.tgz#90ce7e1749c2958a53509b4bae7b8f7d98f276d6" - integrity sha512-bmYzDtwASBB8c+0/HVOAiE9diR7+8zLm/i3kEojUH2z0aIs6x/S4KiTuT5/0VKJ4zk69kXel1cNWlHBMkmavQg== - -esbuild-windows-32@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.11.tgz#d067f4ce15b29efba6336e6a23597120fafe49ec" - integrity sha512-J1Ys5hMid8QgdY00OBvIolXgCQn1ARhYtxPnG6ESWNTty3ashtc4+As5nTrsErnv8ZGUcWZe4WzTP/DmEVX1UQ== - -esbuild-windows-64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.11.tgz#13e86dd37a6cd61a5276fa2d271342d0f74da864" - integrity sha512-h9FmMskMuGeN/9G9+LlHPAoiQk9jlKDUn9yA0MpiGzwLa82E7r1b1u+h2a+InprbSnSLxDq/7p5YGtYVO85Mlg== - -esbuild-windows-arm64@0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.11.tgz#e8edfdf1d712085e6dc3fba18a0c225aaae32b75" - integrity sha512-dZp7Krv13KpwKklt9/1vBFBMqxEQIO6ri7Azf8C+ob4zOegpJmha2XY9VVWP/OyQ0OWk6cEeIzMJwInRZrzBUQ== - -esbuild@^0.14.11: - version "0.14.11" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.11.tgz#ac4acb78907874832afb704c3afe58ad37715c27" - integrity sha512-xZvPtVj6yecnDeFb3KjjCM6i7B5TCAQZT77kkW/CpXTMnd6VLnRPKrUB1XHI1pSq6a4Zcy3BGueQ8VljqjDGCg== +esbuild-android-arm64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.14.tgz#3705f32f209deeb11c275af47c298c8783dd5f0c" + integrity sha512-be/Uw6DdpQiPfula1J4bdmA+wtZ6T3BRCZsDMFB5X+k0Gp8TIh9UvmAcqvKNnbRAafSaXG3jPCeXxDKqnc8hFQ== + +esbuild-darwin-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.14.tgz#c07e4eae6d938300a2d330ea82494c55bcea84e5" + integrity sha512-BEexYmjWafcISK8cT6O98E3TfcLuZL8DKuubry6G54n2+bD4GkoRD6HYUOnCkfl2p7jodA+s4369IjSFSWjtHg== + +esbuild-darwin-arm64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.14.tgz#a8631e13a51a6f784fb0906e2a64c6ab53988755" + integrity sha512-tnBKm41pDOB1GtZ8q/w26gZlLLRzVmP8fdsduYjvM+yFD7E2DLG4KbPAqFMWm4Md9B+DitBglP57FY7AznxbTg== + +esbuild-freebsd-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.14.tgz#c280c2b944746b27ee6c6487c2691865c90bed2e" + integrity sha512-Q9Rx6sgArOHalQtNwAaIzJ6dnQ8A+I7f/RsQsdkS3JrdzmnlFo8JEVofTmwVQLoIop7OKUqIVOGP4PoQcwfVMA== + +esbuild-freebsd-arm64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.14.tgz#aa4e21276efcf20e5ab2487e91ca1d789573189b" + integrity sha512-TJvq0OpLM7BkTczlyPIphcvnwrQwQDG1HqxzoYePWn26SMUAlt6wrLnEvxdbXAvNvDLVzG83kA+JimjK7aRNBA== + +esbuild-linux-32@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.14.tgz#3db4d929239203ce38a9060d5419ac6a6d28846c" + integrity sha512-h/CrK9Baimt5VRbu8gqibWV7e1P9l+mkanQgyOgv0Ng3jHT1NVFC9e6rb1zbDdaJVmuhWX5xVliUA5bDDCcJeg== + +esbuild-linux-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.14.tgz#f880026254c1f565a7a10fdebb7cff9b083a127d" + integrity sha512-IC+wAiIg/egp5OhQp4W44D9PcBOH1b621iRn1OXmlLzij9a/6BGr9NMIL4CRwz4j2kp3WNZu5sT473tYdynOuQ== + +esbuild-linux-arm64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.14.tgz#a34bc3076e50b109c3b8c8bad9c146e35942322b" + integrity sha512-6QVul3RI4M5/VxVIRF/I5F+7BaxzR3DfNGoqEVSCZqUbgzHExPn+LXr5ly1C7af2Kw4AHpo+wDqx8A4ziP9avw== + +esbuild-linux-arm@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.14.tgz#231ffd12fef69ee06365d4c94b69850e4830e927" + integrity sha512-gxpOaHOPwp7zSmcKYsHrtxabScMqaTzfSQioAMUaB047YiMuDBzqVcKBG8OuESrYkGrL9DDljXr/mQNg7pbdaQ== + +esbuild-linux-mips64le@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.14.tgz#bd00570e3a30422224b732c7a5f262146c357403" + integrity sha512-4Jl5/+xoINKbA4cesH3f4R+q0vltAztZ6Jm8YycS8lNhN1pgZJBDxWfI6HUMIAdkKlIpR1PIkA9aXQgZ8sxFAg== + +esbuild-linux-ppc64le@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.14.tgz#430609413fd9e04d9def4e3f06726b031b23d825" + integrity sha512-BitW37GxeebKxqYNl4SVuSdnIJAzH830Lr6Mkq3pBHXtzQay0vK+IeOR/Ele1GtNVJ+/f8wYM53tcThkv5SC5w== + +esbuild-linux-s390x@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.14.tgz#2f0d8cbfe53cf3cb97f6372549a41a8051dbd689" + integrity sha512-vLj6p76HOZG3wfuTr5MyO3qW5iu8YdhUNxuY+tx846rPo7GcKtYSPMusQjeVEfZlJpSYoR+yrNBBxq+qVF9zrw== + +esbuild-netbsd-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.14.tgz#3e44de35e1add7e9582f3c0d2558d86aafbc813b" + integrity sha512-fn8looXPQhpVqUyCBWUuPjesH+yGIyfbIQrLKG05rr1Kgm3rZD/gaYrd3Wpmf5syVZx70pKZPvdHp8OTA+y7cQ== + +esbuild-openbsd-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.14.tgz#04710ef1d01cd9f15d54f50d20b5a3778f8306a2" + integrity sha512-HdAnJ399pPff3SKbd8g+P4o5znseni5u5n5rJ6Z7ouqOdgbOwHe2ofZbMow17WMdNtz1IyOZk2Wo9Ve6/lZ4Rg== + +esbuild-sunos-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.14.tgz#8e583dd92c5c7ac4303ddc37f588e44211e04e19" + integrity sha512-bmDHa99ulsGnYlh/xjBEfxoGuC8CEG5OWvlgD+pF7bKKiVTbtxqVCvOGEZeoDXB+ja6AvHIbPxrEE32J+m5nqQ== + +esbuild-windows-32@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.14.tgz#6d293ddfb71229f21cc13d85d5d2f43e8131693b" + integrity sha512-6tVooQcxJCNenPp5GHZBs/RLu31q4B+BuF4MEoRxswT+Eq2JGF0ZWDRQwNKB8QVIo3t6Svc5wNGez+CwKNQjBg== + +esbuild-windows-64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.14.tgz#08a36844b69542f8ec1cb33a5ddcea02b9d0b2e8" + integrity sha512-kl3BdPXh0/RD/dad41dtzj2itMUR4C6nQbXQCyYHHo4zoUoeIXhpCrSl7BAW1nv5EFL8stT1V+TQVXGZca5A2A== + +esbuild-windows-arm64@0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.14.tgz#ca747ce4066d5b8a79dbe48fe6ecd92d202e5366" + integrity sha512-dCm1wTOm6HIisLanmybvRKvaXZZo4yEVrHh1dY0v582GThXJOzuXGja1HIQgV09RpSHYRL3m4KoUBL00l6SWEg== + +esbuild@^0.14.14: + version "0.14.14" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.14.tgz#3b99f20d628013c3e2ae90e67687e03f1d6eb071" + integrity sha512-aiK4ddv+uui0k52OqSHu4xxu+SzOim7Rlz4i25pMEiC8rlnGU0HJ9r+ZMfdWL5bzifg+nhnn7x4NSWTeehYblg== optionalDependencies: - esbuild-android-arm64 "0.14.11" - esbuild-darwin-64 "0.14.11" - esbuild-darwin-arm64 "0.14.11" - esbuild-freebsd-64 "0.14.11" - esbuild-freebsd-arm64 "0.14.11" - esbuild-linux-32 "0.14.11" - esbuild-linux-64 "0.14.11" - esbuild-linux-arm "0.14.11" - esbuild-linux-arm64 "0.14.11" - esbuild-linux-mips64le "0.14.11" - esbuild-linux-ppc64le "0.14.11" - esbuild-linux-s390x "0.14.11" - esbuild-netbsd-64 "0.14.11" - esbuild-openbsd-64 "0.14.11" - esbuild-sunos-64 "0.14.11" - esbuild-windows-32 "0.14.11" - esbuild-windows-64 "0.14.11" - esbuild-windows-arm64 "0.14.11" + esbuild-android-arm64 "0.14.14" + esbuild-darwin-64 "0.14.14" + esbuild-darwin-arm64 "0.14.14" + esbuild-freebsd-64 "0.14.14" + esbuild-freebsd-arm64 "0.14.14" + esbuild-linux-32 "0.14.14" + esbuild-linux-64 "0.14.14" + esbuild-linux-arm "0.14.14" + esbuild-linux-arm64 "0.14.14" + esbuild-linux-mips64le "0.14.14" + esbuild-linux-ppc64le "0.14.14" + esbuild-linux-s390x "0.14.14" + esbuild-netbsd-64 "0.14.14" + esbuild-openbsd-64 "0.14.14" + esbuild-sunos-64 "0.14.14" + esbuild-windows-32 "0.14.14" + esbuild-windows-64 "0.14.14" + esbuild-windows-arm64 "0.14.14" escalade@^3.1.1: version "3.1.1" @@ -4352,12 +4347,7 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flatted@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -flatted@^3.1.0: +flatted@^3.1.0, flatted@^3.2.4: version "3.2.4" resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== @@ -5066,7 +5056,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.0: +is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== @@ -5811,10 +5801,10 @@ jest@^27.3.1, jest@^27.4.7: import-local "^3.0.2" jest-cli "^27.4.7" -jmespath@0.15.0: - version "0.15.0" - resolved "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" - integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== js-base64@^2.1.9: version "2.6.4" @@ -6355,15 +6345,15 @@ lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log4js@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" - integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== + version "6.4.1" + resolved "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz#9d3a8bf2c31c1e213fe3fc398a6053f7a2bc53e8" + integrity sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg== dependencies: - date-format "^3.0.0" - debug "^4.1.1" - flatted "^2.0.1" - rfdc "^1.1.4" - streamroller "^2.2.4" + date-format "^4.0.3" + debug "^4.3.3" + flatted "^3.2.4" + rfdc "^1.3.0" + streamroller "^3.0.2" lru-cache@^5.1.1: version "5.1.1" @@ -6479,10 +6469,10 @@ map-obj@^4.0.0: resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -markdown-it@12.2.0: - version "12.2.0" - resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-12.2.0.tgz#091f720fd5db206f80de7a8d1f1a7035fd0d38db" - integrity sha512-Wjws+uCrVQRqOoJvze4HCqkKl1AsSh95iFAeQDwnyfxM09divCBSXlDR1uTvyUP3Grzpn4Ru8GeCxYPM8vkCQg== +markdown-it@12.2.0, markdown-it@^12.3.2: + version "12.3.2" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" + integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== dependencies: argparse "^2.0.1" entities "~2.1.0" @@ -6793,9 +6783,9 @@ natural-compare@^1.4.0: integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= negotiator@^0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== neo-async@^2.6.0: version "2.6.2" @@ -6854,7 +6844,7 @@ nock@^13.2.2: lodash.set "^4.3.2" propagate "^2.0.0" -node-fetch@^2.6.1: +node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -7582,9 +7572,9 @@ pify@^5.0.0: integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== pirates@^4.0.4: - version "4.0.4" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz#07df81e61028e402735cdd49db701e4885b4e6e6" - integrity sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw== + version "4.0.5" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" @@ -7622,10 +7612,10 @@ pretty-format@^27.0.0, pretty-format@^27.4.6: ansi-styles "^5.0.0" react-is "^17.0.1" -printj@~1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== +printj@~1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb" + integrity sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg== process-nextick-args@~2.0.0: version "2.0.1" @@ -8055,11 +8045,11 @@ resolve.exports@^1.1.0: integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.20.0: - version "1.21.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" - integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== + version "1.22.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.8.0" + is-core-module "^2.8.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -8081,7 +8071,7 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfdc@^1.1.4: +rfdc@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== @@ -8502,14 +8492,14 @@ standard-version@^9.3.2: resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -streamroller@^2.2.4: - version "2.2.4" - resolved "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" - integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== +streamroller@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz#30418d0eee3d6c93ec897f892ed098e3a81e68b7" + integrity sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA== dependencies: - date-format "^2.1.0" + date-format "^4.0.3" debug "^4.1.1" - fs-extra "^8.1.0" + fs-extra "^10.0.0" strict-uri-encode@^2.0.0: version "2.0.0" @@ -9064,9 +9054,9 @@ typescript@~3.9.10: integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== typescript@~4.5.0: - version "4.5.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" - integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== + version "4.5.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" + integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -9074,9 +9064,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.14.5" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz#cdabb7d4954231d80cb4a927654c4655e51f4859" - integrity sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ== + version "3.15.0" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz#2d6a689d94783cab43975721977a13c2afec28f1" + integrity sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg== uid-number@0.0.6: version "0.0.6"