From 960189ca14382520aee2c566d466689e7f433e32 Mon Sep 17 00:00:00 2001 From: Shiv Lakshminarayan Date: Tue, 9 Mar 2021 02:52:14 -0800 Subject: [PATCH 01/17] docs(stepfunctions-tasks): fix up all code snippets so they compile (#12820) closes #12606 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-stepfunctions-tasks/README.md | 239 +++++++----------- .../rosetta/default.ts-fixture | 37 +++ .../rosetta/with-batch-job.ts-fixture | 38 +++ 3 files changed, 162 insertions(+), 152 deletions(-) create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/default.ts-fixture create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/with-batch-job.ts-fixture diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md index d7c2d1498394d..69d97936aabcb 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md @@ -102,8 +102,8 @@ The following example provides the field named `input` as the input to the `Task state that runs a Lambda function. ```ts -const submitJob = new tasks.LambdaInvoke(stack, 'Invoke Handler', { - lambdaFunction: submitJobLambda, +const submitJob = new tasks.LambdaInvoke(this, 'Invoke Handler', { + lambdaFunction: fn, inputPath: '$.input' }); ``` @@ -122,8 +122,8 @@ as well as other metadata. The following example assigns the output from the Task to a field named `result` ```ts -const submitJob = new tasks.LambdaInvoke(stack, 'Invoke Handler', { - lambdaFunction: submitJobLambda, +const submitJob = new tasks.LambdaInvoke(this, 'Invoke Handler', { + lambdaFunction: fn, outputPath: '$.Payload.result' }); ``` @@ -140,9 +140,11 @@ The following example adds the item from calling DynamoDB's `getItem` API to the input and passes it to the next state. ```ts -new tasks.DynamoGetItem(this, 'PutItem', { - item: { MessageId: { s: '12345'} }, - tableName: 'my-table', +new tasks.DynamoPutItem(this, 'PutItem', { + item: { + MessageId: tasks.DynamoAttributeValue.fromString('message-id') + }, + table: myTable, resultPath: `$.Item`, }); ``` @@ -163,10 +165,10 @@ The following example provides the field named `input` as the input to the Lambd and invokes it asynchronously. ```ts -const submitJob = new tasks.LambdaInvoke(stack, 'Invoke Handler', { - lambdaFunction: submitJobLambda, - payload: sfn.JsonPath.StringAt('$.input'), - invocationType: tasks.InvocationType.EVENT, +const submitJob = new tasks.LambdaInvoke(this, 'Invoke Handler', { + lambdaFunction: fn, + payload: sfn.TaskInput.fromDataAt('$.input'), + invocationType: tasks.LambdaInvocationType.EVENT, }); ``` @@ -194,7 +196,7 @@ const createMessage = new tasks.EvaluateExpression(this, 'Create message', { }); const publishMessage = new tasks.SnsPublish(this, 'Publish message', { - topic, + topic: new sns.Topic(this, 'cool-topic'), message: sfn.TaskInput.fromDataAt('$.message'), resultPath: '$.sns', }); @@ -224,19 +226,19 @@ Step Functions supports [Athena](https://docs.aws.amazon.com/step-functions/late The [StartQueryExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_StartQueryExecution.html) API runs the SQL query statement. ```ts -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from `@aws-cdk/aws-stepfunctions-tasks`; - -const startQueryExecutionJob = new tasks.AthenaStartQueryExecution(stack, 'Start Athena Query', { +const startQueryExecutionJob = new tasks.AthenaStartQueryExecution(this, 'Start Athena Query', { queryString: sfn.JsonPath.stringAt('$.queryString'), queryExecutionContext: { - database: 'mydatabase', + databaseName: 'mydatabase', }, resultConfiguration: { encryptionConfiguration: { encryptionOption: tasks.EncryptionOption.S3_MANAGED, }, - outputLocation: sfn.JsonPath.stringAt('$.outputLocation'), + outputLocation: { + bucketName: 'query-results-bucket', + objectKey: 'folder', + }, }, }); ``` @@ -246,10 +248,7 @@ const startQueryExecutionJob = new tasks.AthenaStartQueryExecution(stack, 'Start The [GetQueryExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_GetQueryExecution.html) API gets information about a single execution of a query. ```ts -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from `@aws-cdk/aws-stepfunctions-tasks`; - -const getQueryExecutionJob = new tasks.AthenaGetQueryExecution(stack, 'Get Query Execution', { +const getQueryExecutionJob = new tasks.AthenaGetQueryExecution(this, 'Get Query Execution', { queryExecutionId: sfn.JsonPath.stringAt('$.QueryExecutionId'), }); ``` @@ -259,10 +258,7 @@ const getQueryExecutionJob = new tasks.AthenaGetQueryExecution(stack, 'Get Query The [GetQueryResults](https://docs.aws.amazon.com/athena/latest/APIReference/API_GetQueryResults.html) API that streams the results of a single query execution specified by QueryExecutionId from S3. ```ts -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from `@aws-cdk/aws-stepfunctions-tasks`; - -const getQueryResultsJob = new tasks.AthenaGetQueryResults(stack, 'Get Query Results', { +const getQueryResultsJob = new tasks.AthenaGetQueryResults(this, 'Get Query Results', { queryExecutionId: sfn.JsonPath.stringAt('$.QueryExecutionId'), }); ``` @@ -272,10 +268,7 @@ const getQueryResultsJob = new tasks.AthenaGetQueryResults(stack, 'Get Query Res The [StopQueryExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_StopQueryExecution.html) API that stops a query execution. ```ts -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from `@aws-cdk/aws-stepfunctions-tasks`; - -const stopQueryExecutionJob = new tasks.AthenaStopQueryExecution(stack, 'Stop Query Execution', { +const stopQueryExecutionJob = new tasks.AthenaStopQueryExecution(this, 'Stop Query Execution', { queryExecutionId: sfn.JsonPath.stringAt('$.QueryExecutionId'), }); ``` @@ -288,27 +281,7 @@ Step Functions supports [Batch](https://docs.aws.amazon.com/step-functions/lates The [SubmitJob](https://docs.aws.amazon.com/batch/latest/APIReference/API_SubmitJob.html) API submits an AWS Batch job from a job definition. -```ts -import * as batch from '@aws-cdk/aws-batch'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; - -const batchQueue = new batch.JobQueue(this, 'JobQueue', { - computeEnvironments: [ - { - order: 1, - computeEnvironment: new batch.ComputeEnvironment(this, 'ComputeEnv', { - computeResources: { vpc }, - }), - }, - ], -}); - -const batchJobDefinition = new batch.JobDefinition(this, 'JobDefinition', { - container: { - image: ecs.ContainerImage.fromAsset(path.resolve(__dirname, 'batchjob-image')), - }, -}); - +```ts fixture=with-batch-job const task = new tasks.BatchSubmitJob(this, 'Submit Job', { jobDefinition: batchJobDefinition, jobName: 'MyJob', @@ -326,10 +299,8 @@ Step Functions supports [CodeBuild](https://docs.aws.amazon.com/step-functions/l ```ts import * as codebuild from '@aws-cdk/aws-codebuild'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; -import * as sfn from '@aws-cdk/aws-stepfunctions'; -const codebuildProject = new codebuild.Project(stack, 'Project', { +const codebuildProject = new codebuild.Project(this, 'Project', { projectName: 'MyTestProject', buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', @@ -343,7 +314,7 @@ const codebuildProject = new codebuild.Project(stack, 'Project', { }), }); -const task = new tasks.CodeBuildStartBuild(stack, 'Task', { +const task = new tasks.CodeBuildStartBuild(this, 'Task', { project: codebuildProject, integrationPattern: sfn.IntegrationPattern.RUN_JOB, environmentVariablesOverride: { @@ -367,7 +338,7 @@ The [GetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API ```ts new tasks.DynamoGetItem(this, 'Get Item', { key: { messageId: tasks.DynamoAttributeValue.fromString('message-007') }, - table, + table: myTable, }); ``` @@ -382,7 +353,7 @@ new tasks.DynamoPutItem(this, 'PutItem', { Text: tasks.DynamoAttributeValue.fromString(sfn.JsonPath.stringAt('$.bar')), TotalCount: tasks.DynamoAttributeValue.fromNumber(10), }, - table, + table: myTable, }); ``` @@ -391,12 +362,9 @@ new tasks.DynamoPutItem(this, 'PutItem', { The [DeleteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html) operation deletes a single item in a table by primary key. ```ts -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; - new tasks.DynamoDeleteItem(this, 'DeleteItem', { key: { MessageId: tasks.DynamoAttributeValue.fromString('message-007') }, - table, + table: myTable, resultPath: sfn.JsonPath.DISCARD, }); ``` @@ -408,8 +376,10 @@ to the table if it does not already exist. ```ts new tasks.DynamoUpdateItem(this, 'UpdateItem', { - key: { MessageId: tasks.DynamoAttributeValue.fromString('message-007') }, - table, + key: { + MessageId: tasks.DynamoAttributeValue.fromString('message-007') + }, + table: myTable, expressionAttributeValues: { ':val': tasks.DynamoAttributeValue.numberFromString(sfn.JsonPath.stringAt('$.Item.TotalCount.N')), ':rand': tasks.DynamoAttributeValue.fromNumber(20), @@ -443,20 +413,18 @@ The following example runs a job from a task definition on EC2 ```ts import * as ecs from '@aws-cdk/aws-ecs'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; -import * as sfn from '@aws-cdk/aws-stepfunctions'; -const vpc = ec2.Vpc.fromLookup(stack, 'Vpc', { +const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true, }); -const cluster = new ecs.Cluster(stack, 'Ec2Cluster', { vpc }); +const cluster = new ecs.Cluster(this, 'Ec2Cluster', { vpc }); cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro'), vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }, }); -const taskDefinition = new ecs.TaskDefinition(stack, 'TD', { +const taskDefinition = new ecs.TaskDefinition(this, 'TD', { compatibility: ecs.Compatibility.EC2, }); @@ -465,7 +433,7 @@ taskDefinition.addContainer('TheContainer', { memoryLimitMiB: 256, }); -const runTask = new tasks.EcsRunTask(stack, 'Run', { +const runTask = new tasks.EcsRunTask(this, 'Run', { integrationPattern: sfn.IntegrationPattern.RUN_JOB, cluster, taskDefinition, @@ -500,16 +468,14 @@ The following example runs a job from a task definition on Fargate ```ts import * as ecs from '@aws-cdk/aws-ecs'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; -import * as sfn from '@aws-cdk/aws-stepfunctions'; -const vpc = ec2.Vpc.fromLookup(stack, 'Vpc', { +const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true, }); -const cluster = new ecs.Cluster(stack, 'FargateCluster', { vpc }); +const cluster = new ecs.Cluster(this, 'FargateCluster', { vpc }); -const taskDefinition = new ecs.TaskDefinition(stack, 'TD', { +const taskDefinition = new ecs.TaskDefinition(this, 'TD', { memoryMiB: '512', cpu: '256', compatibility: ecs.Compatibility.FARGATE, @@ -520,7 +486,7 @@ const containerDefinition = taskDefinition.addContainer('TheContainer', { memoryLimitMiB: 256, }); -const runTask = new tasks.EcsRunTask(stack, 'RunFargate', { +const runTask = new tasks.EcsRunTask(this, 'RunFargate', { integrationPattern: sfn.IntegrationPattern.RUN_JOB, cluster, taskDefinition, @@ -548,15 +514,15 @@ Corresponds to the [`runJobFlow`](https://docs.aws.amazon.com/emr/latest/APIRefe ```ts -const clusterRole = new iam.Role(stack, 'ClusterRole', { +const clusterRole = new iam.Role(this, 'ClusterRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), }); -const serviceRole = new iam.Role(stack, 'ServiceRole', { +const serviceRole = new iam.Role(this, 'ServiceRole', { assumedBy: new iam.ServicePrincipal('elasticmapreduce.amazonaws.com'), }); -const autoScalingRole = new iam.Role(stack, 'AutoScalingRole', { +const autoScalingRole = new iam.Role(this, 'AutoScalingRole', { assumedBy: new iam.ServicePrincipal('elasticmapreduce.amazonaws.com'), }); @@ -569,16 +535,15 @@ autoScalingRole.assumeRolePolicy?.addStatements( actions: [ 'sts:AssumeRole', ], - }); + })); ) -new tasks.EmrCreateCluster(stack, 'Create Cluster', { +new tasks.EmrCreateCluster(this, 'Create Cluster', { instances: {}, clusterRole, name: sfn.TaskInput.fromDataAt('$.ClusterName').value, serviceRole, autoScalingRole, - integrationPattern: sfn.ServiceIntegrationPattern.FIRE_AND_FORGET, }); ``` @@ -590,7 +555,7 @@ terminated by user intervention, an API call, or a job-flow error. Corresponds to the [`setTerminationProtection`](https://docs.aws.amazon.com/step-functions/latest/dg/connect-emr.html) API in EMR. ```ts -new tasks.EmrSetClusterTerminationProtection(stack, 'Task', { +new tasks.EmrSetClusterTerminationProtection(this, 'Task', { clusterId: 'ClusterId', terminationProtected: false, }); @@ -602,7 +567,7 @@ Shuts down a cluster (job flow). Corresponds to the [`terminateJobFlows`](https://docs.aws.amazon.com/emr/latest/APIReference/API_TerminateJobFlows.html) API in EMR. ```ts -new tasks.EmrTerminateCluster(stack, 'Task', { +new tasks.EmrTerminateCluster(this, 'Task', { clusterId: 'ClusterId' }); ``` @@ -613,7 +578,7 @@ Adds a new step to a running cluster. Corresponds to the [`addJobFlowSteps`](https://docs.aws.amazon.com/emr/latest/APIReference/API_AddJobFlowSteps.html) API in EMR. ```ts -new tasks.EmrAddStep(stack, 'Task', { +new tasks.EmrAddStep(this, 'Task', { clusterId: 'ClusterId', name: 'StepName', jar: 'Jar', @@ -627,7 +592,7 @@ Cancels a pending step in a running cluster. Corresponds to the [`cancelSteps`](https://docs.aws.amazon.com/emr/latest/APIReference/API_CancelSteps.html) API in EMR. ```ts -new tasks.EmrCancelStep(stack, 'Task', { +new tasks.EmrCancelStep(this, 'Task', { clusterId: 'ClusterId', stepId: 'StepId', }); @@ -641,7 +606,7 @@ fleet with the specified InstanceFleetName. Corresponds to the [`modifyInstanceFleet`](https://docs.aws.amazon.com/emr/latest/APIReference/API_ModifyInstanceFleet.html) API in EMR. ```ts -new sfn.EmrModifyInstanceFleetByName(stack, 'Task', { +new tasks.EmrModifyInstanceFleetByName(this, 'Task', { clusterId: 'ClusterId', instanceFleetName: 'InstanceFleetName', targetOnDemandCapacity: 2, @@ -656,7 +621,7 @@ Modifies the number of nodes and configuration settings of an instance group. Corresponds to the [`modifyInstanceGroups`](https://docs.aws.amazon.com/emr/latest/APIReference/API_ModifyInstanceGroups.html) API in EMR. ```ts -new tasks.EmrModifyInstanceGroupByName(stack, 'Task', { +new tasks.EmrModifyInstanceGroupByName(this, 'Task', { clusterId: 'ClusterId', instanceGroupName: sfn.JsonPath.stringAt('$.InstanceGroupName'), instanceGroup: { @@ -703,11 +668,11 @@ Step Functions supports [AWS Glue](https://docs.aws.amazon.com/step-functions/la You can call the [`StartJobRun`](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-jobs-runs.html#aws-glue-api-jobs-runs-StartJobRun) API from a `Task` state. ```ts -new GlueStartJobRun(stack, 'Task', { +new tasks.GlueStartJobRun(this, 'Task', { glueJobName: 'my-glue-job', - arguments: { - key: 'value', - }, + arguments: sfn.TaskInput.fromObject({ + key: 'value', + }), timeout: cdk.Duration.minutes(30), notifyDelayAfter: cdk.Duration.minutes(5), }); @@ -720,8 +685,8 @@ Step Functions supports [AWS Glue DataBrew](https://docs.aws.amazon.com/step-fun You can call the [`StartJobRun`](https://docs.aws.amazon.com/databrew/latest/dg/API_StartJobRun.html) API from a `Task` state. ```ts -new GlueDataBrewStartJobRun(stack, 'Task', { - Name: 'databrew-job', +new tasks.GlueDataBrewStartJobRun(this, 'Task', { + name: 'databrew-job', }); ``` @@ -737,23 +702,8 @@ The following snippet invokes a Lambda Function with the state input as the payl by referencing the `$` path. ```ts -import * as lambda from '@aws-cdk/aws-lambda'; -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; - -const myLambda = new lambda.Function(this, 'my sample lambda', { - code: Code.fromInline(`exports.handler = async () => { - return { - statusCode: '200', - body: 'hello, world!' - }; - };`), - runtime: Runtime.NODEJS_12_X, - handler: 'index.handler', -}); - new tasks.LambdaInvoke(this, 'Invoke with state input', { - lambdaFunction: myLambda, + lambdaFunction: fn, }); ``` @@ -768,13 +718,13 @@ to reference the output of a Lambda executed before it. ```ts new tasks.LambdaInvoke(this, 'Invoke with empty object as payload', { - lambdaFunction: myLambda, + lambdaFunction: fn, payload: sfn.TaskInput.fromObject({}), }); -// use the output of myLambda as input +// use the output of fn as input new tasks.LambdaInvoke(this, 'Invoke with payload field in the state input', { - lambdaFunction: myOtherLambda, + lambdaFunction: fn, payload: sfn.TaskInput.fromDataAt('$.Payload'), }); ``` @@ -784,8 +734,7 @@ the Lambda function response. ```ts new tasks.LambdaInvoke(this, 'Invoke and set function response as task output', { - lambdaFunction: myLambda, - payload: sfn.TaskInput.fromDataAt('$'), + lambdaFunction: fn, outputPath: '$.Payload', }); ``` @@ -797,9 +746,9 @@ integrationPattern, invocationType, clientContext, and qualifier properties. ```ts new tasks.LambdaInvoke(this, 'Invoke and combine function response with task input', { - lambdaFunction: myLambda, + lambdaFunction: fn, payloadResponseOnly: true, - resultPath: '$.myLambda', + resultPath: '$.fn', }); ``` @@ -814,8 +763,8 @@ The following snippet invokes a Lambda with the task token as part of the input to the Lambda. ```ts -new tasks.LambdaInvoke(stack, 'Invoke with callback', { - lambdaFunction: myLambda, +new tasks.LambdaInvoke(this, 'Invoke with callback', { + lambdaFunction: fn, integrationPattern: sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, payload: sfn.TaskInput.fromObject({ token: sfn.JsonPath.taskToken, @@ -830,7 +779,7 @@ Token](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource. AWS Lambda can occasionally experience transient service errors. In this case, invoking Lambda results in a 500 error, such as `ServiceException`, `AWSLambdaException`, or `SdkClientException`. -As a best practive, the `LambdaInvoke` task will retry on those errors with an interval of 2 seconds, +As a best practice, the `LambdaInvoke` task will retry on those errors with an interval of 2 seconds, a back-off rate of 2 and 6 maximum attempts. Set the `retryOnServiceExceptions` prop to `false` to disable this behavior. @@ -843,9 +792,8 @@ Step Functions supports [AWS SageMaker](https://docs.aws.amazon.com/step-functio You can call the [`CreateTrainingJob`](https://docs.aws.amazon.com/sagemaker/latest/dg/API_CreateTrainingJob.html) API from a `Task` state. ```ts -new sfn.SageMakerCreateTrainingJob(this, 'TrainSagemaker', { +new tasks.SageMakerCreateTrainingJob(this, 'TrainSagemaker', { trainingJobName: sfn.JsonPath.stringAt('$.JobName'), - role, algorithmSpecification: { algorithmName: 'BlazingText', trainingInputMode: tasks.InputMode.FILE, @@ -860,16 +808,16 @@ new sfn.SageMakerCreateTrainingJob(this, 'TrainSagemaker', { }, }], outputDataConfig: { - s3OutputLocation: tasks.S3Location.fromBucket(s3.Bucket.fromBucketName(stack, 'Bucket', 'mybucket'), 'myoutputpath'), + s3OutputLocation: tasks.S3Location.fromBucket(s3.Bucket.fromBucketName(this, 'Bucket', 'mybucket'), 'myoutputpath'), }, resourceConfig: { instanceCount: 1, instanceType: ec2.InstanceType.of(ec2.InstanceClass.P3, ec2.InstanceSize.XLARGE2), volumeSize: cdk.Size.gibibytes(50), - }, + }, // optional: default is 1 instance of EC2 `M4.XLarge` with `10GB` volume stoppingCondition: { - maxRuntime: cdk.Duration.hours(1), - }, + maxRuntime: cdk.Duration.hours(2), + }, // optional: default is 1 hour }); ``` @@ -878,19 +826,18 @@ new sfn.SageMakerCreateTrainingJob(this, 'TrainSagemaker', { You can call the [`CreateTransformJob`](https://docs.aws.amazon.com/sagemaker/latest/dg/API_CreateTransformJob.html) API from a `Task` state. ```ts -new sfn.SageMakerCreateTransformJob(this, 'Batch Inference', { +new tasks.SageMakerCreateTransformJob(this, 'Batch Inference', { transformJobName: 'MyTransformJob', modelName: 'MyModelName', modelClientOptions: { - invocationMaxRetries: 3, // default is 0 - invocationTimeout: cdk.Duration.minutes(5), // default is 60 seconds - } - role, + invocationsMaxRetries: 3, // default is 0 + invocationsTimeout: cdk.Duration.minutes(5), // default is 60 seconds + }, transformInput: { transformDataSource: { s3DataSource: { s3Uri: 's3://inputbucket/train', - s3DataType: S3DataType.S3Prefix, + s3DataType: tasks.S3DataType.S3_PREFIX, } } }, @@ -899,7 +846,7 @@ new sfn.SageMakerCreateTransformJob(this, 'Batch Inference', { }, transformResources: { instanceCount: 1, - instanceType: ec2.InstanceType.of(ec2.InstanceClass.M4, ec2.InstanceSize.XLarge), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.M4, ec2.InstanceSize.XLARGE), } }); @@ -910,7 +857,7 @@ new sfn.SageMakerCreateTransformJob(this, 'Batch Inference', { You can call the [`CreateEndpoint`](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateEndpoint.html) API from a `Task` state. ```ts -new sfn.SageMakerCreateEndpoint(this, 'SagemakerEndpoint', { +new tasks.SageMakerCreateEndpoint(this, 'SagemakerEndpoint', { endpointName: sfn.JsonPath.stringAt('$.EndpointName'), endpointConfigName: sfn.JsonPath.stringAt('$.EndpointConfigName'), }); @@ -921,7 +868,7 @@ new sfn.SageMakerCreateEndpoint(this, 'SagemakerEndpoint', { You can call the [`CreateEndpointConfig`](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateEndpointConfig.html) API from a `Task` state. ```ts -new sfn.SageMakerCreateEndpointConfig(this, 'SagemakerEndpointConfig', { +new tasks.SageMakerCreateEndpointConfig(this, 'SagemakerEndpointConfig', { endpointConfigName: 'MyEndpointConfig', productionVariants: [{ initialInstanceCount: 2, @@ -937,7 +884,7 @@ new sfn.SageMakerCreateEndpointConfig(this, 'SagemakerEndpointConfig', { You can call the [`CreateModel`](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateModel.html) API from a `Task` state. ```ts -new sfn.SageMakerCreateModel(this, 'Sagemaker', { +new tasks.SageMakerCreateModel(this, 'Sagemaker', { modelName: 'MyModel', primaryContainer: new tasks.ContainerDefinition({ image: tasks.DockerImage.fromJsonExpression(sfn.JsonPath.stringAt('$.Model.imageName')), @@ -952,7 +899,7 @@ new sfn.SageMakerCreateModel(this, 'Sagemaker', { You can call the [`UpdateEndpoint`](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_UpdateEndpoint.html) API from a `Task` state. ```ts -new sfn.SageMakerUpdateEndpoint(this, 'SagemakerEndpoint', { +new tasks.SageMakerUpdateEndpoint(this, 'SagemakerEndpoint', { endpointName: sfn.JsonPath.stringAt('$.Endpoint.Name'), endpointConfigName: sfn.JsonPath.stringAt('$.Endpoint.EndpointConfig'), }); @@ -965,12 +912,6 @@ Step Functions supports [Amazon SNS](https://docs.aws.amazon.com/step-functions/ You can call the [`Publish`](https://docs.aws.amazon.com/sns/latest/api/API_Publish.html) API from a `Task` state to publish to an SNS topic. ```ts -import * as sns from '@aws-cdk/aws-sns'; -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; - -// ... - const topic = new sns.Topic(this, 'Topic'); // Use a field from the execution data as message. @@ -1001,12 +942,12 @@ AWS Step Functions supports it's own [`StartExecution`](https://docs.aws.amazon. ```ts // Define a state machine with one Pass state -const child = new sfn.StateMachine(stack, 'ChildStateMachine', { - definition: sfn.Chain.start(new sfn.Pass(stack, 'PassState')), +const child = new sfn.StateMachine(this, 'ChildStateMachine', { + definition: sfn.Chain.start(new sfn.Pass(this, 'PassState')), }); // Include the state machine in a Task state with callback pattern -const task = new StepFunctionsStartExecution(stack, 'ChildTask', { +const task = new tasks.StepFunctionsStartExecution(this, 'ChildTask', { stateMachine: child, integrationPattern: sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, input: sfn.TaskInput.fromObject({ @@ -1017,7 +958,7 @@ const task = new StepFunctionsStartExecution(stack, 'ChildTask', { }); // Define a second state machine with the Task state above -new sfn.StateMachine(stack, 'ParentStateMachine', { +new sfn.StateMachine(this, 'ParentStateMachine', { definition: task }); ``` @@ -1057,12 +998,6 @@ You can call the [`SendMessage`](https://docs.aws.amazon.com/AWSSimpleQueueServi to send a message to an SQS queue. ```ts -import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; -import * as sqs from '@aws-cdk/aws-sqs'; - -// ... - const queue = new sqs.Queue(this, 'Queue'); // Use a field from the execution data as message. diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..11558a599e5f4 --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/default.ts-fixture @@ -0,0 +1,37 @@ +// Fixture with packages imported, but nothing else +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as ddb from '@aws-cdk/aws-dynamodb'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import * as lambda from '@aws-cdk/aws-lambda'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as sfn from '@aws-cdk/aws-stepfunctions'; +import * as sns from '@aws-cdk/aws-sns'; +import * as sqs from '@aws-cdk/aws-sqs'; +import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; + +class Fixture extends cdk.Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const fn = new lambda.Function(this, 'lambdaFunction', { + code: lambda.Code.fromInline(`exports.handler = async () => { + return { "hello world"}; + `), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + }); + + const myTable = new ddb.Table(this, 'Messages', { + tableName: 'my-table', + partitionKey: { + name: 'MessageId', + type: ddb.AttributeType.STRING, + }, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/with-batch-job.ts-fixture b/packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/with-batch-job.ts-fixture new file mode 100644 index 0000000000000..47672ba140841 --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/rosetta/with-batch-job.ts-fixture @@ -0,0 +1,38 @@ +// Fixture with packages imported, but nothing else +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as sfn from '@aws-cdk/aws-stepfunctions'; +import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; +import * as batch from '@aws-cdk/aws-batch'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as ecs from '@aws-cdk/aws-ecs'; +import * as path from 'path'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { + isDefault: true, + }); + + const batchQueue = new batch.JobQueue(this, 'JobQueue', { + computeEnvironments: [ + { + order: 1, + computeEnvironment: new batch.ComputeEnvironment(this, 'ComputeEnv', { + computeResources: { vpc }, + }), + }, + ], + }); + + const batchJobDefinition = new batch.JobDefinition(this, 'JobDefinition', { + container: { + image: ecs.ContainerImage.fromAsset(path.resolve(__dirname, 'batchjob-image')), + }, + }); + + /// here + } +} From e08213fd2b5c27ce8b5dd2d22ebbff1dc1b48a1c Mon Sep 17 00:00:00 2001 From: Robert Djurasaj Date: Tue, 9 Mar 2021 04:27:08 -0700 Subject: [PATCH 02/17] chore(cloudfront): check size of the Cache Policy headers (#13425) Similar to #13410 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-cloudfront/lib/cache-policy.ts | 3 +++ .../@aws-cdk/aws-cloudfront/test/cache-policy.test.ts | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/packages/@aws-cdk/aws-cloudfront/lib/cache-policy.ts b/packages/@aws-cdk/aws-cloudfront/lib/cache-policy.ts index 18924ade79748..e9dbf46901eda 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/cache-policy.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/cache-policy.ts @@ -231,6 +231,9 @@ export class CacheHeaderBehavior { if (headers.length === 0) { throw new Error('At least one header to allow must be provided'); } + if (headers.length > 10) { + throw new Error(`Maximum allowed headers in Cache Policy is 10; got ${headers.length}.`); + } return new CacheHeaderBehavior('whitelist', headers); } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cache-policy.test.ts b/packages/@aws-cdk/aws-cloudfront/test/cache-policy.test.ts index 66b5fa80d5749..4f6d618c51621 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cache-policy.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/cache-policy.test.ts @@ -96,6 +96,17 @@ describe('CachePolicy', () => { expect(() => new CachePolicy(stack, 'CachePolicy6', { cachePolicyName: 'My_Policy' })).not.toThrow(); }); + test('throws if more than 10 CacheHeaderBehavior headers are being passed', () => { + const errorMessage = /Maximum allowed headers in Cache Policy is 10; got (.*?)/; + expect(() => new CachePolicy(stack, 'CachePolicy1', { + headerBehavior: CacheHeaderBehavior.allowList('Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do', 'eiusmod'), + })).toThrow(errorMessage); + + expect(() => new CachePolicy(stack, 'CachePolicy2', { + headerBehavior: CacheHeaderBehavior.allowList('Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do'), + })).not.toThrow(); + }); + test('does not throw if cachePolicyName is a token', () => { expect(() => new CachePolicy(stack, 'CachePolicy', { cachePolicyName: Aws.STACK_NAME, From abfc0ea63c10d8033a529b7497cf093e318fdf12 Mon Sep 17 00:00:00 2001 From: Alban Esc Date: Tue, 9 Mar 2021 04:01:33 -0800 Subject: [PATCH 03/17] feat(events): dead-letter queue support for CodeBuild (#13448) Resolves #13447 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-events-targets/README.md | 33 ++++++++ .../aws-events-targets/lib/codebuild.ts | 21 ++++- .../test/codebuild/codebuild.test.ts | 81 +++++++++++++++++++ .../integ.project-events.expected.json | 52 ++++++++++++ .../test/codebuild/integ.project-events.ts | 5 +- 5 files changed, 190 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-events-targets/README.md b/packages/@aws-cdk/aws-events-targets/README.md index 4b795ce5f19b5..d49621b0199cd 100644 --- a/packages/@aws-cdk/aws-events-targets/README.md +++ b/packages/@aws-cdk/aws-events-targets/README.md @@ -90,6 +90,39 @@ const rule = new events.Rule(this, 'rule', { rule.addTarget(new targets.CloudWatchLogGroup(logGroup)); ``` +## Trigger a CodeBuild project + +Use the `CodeBuildProject` target to trigger a CodeBuild project. + +The code snippet below creates a CodeCommit repository that triggers a CodeBuild project +on commit to the master branch. You can optionally attach a +[dead letter queue](https://docs.aws.amazon.com/eventbridge/latest/userguide/rule-dlq.html). + +```ts +import * as codebuild from '@aws-sdk/aws-codebuild'; +import * as codecommit from '@aws-sdk/aws-codecommit'; +import * as sqs from '@aws-sdk/aws-sqs'; +import * as targets from "@aws-cdk/aws-events-targets"; + +const repo = new codecommit.Repository(this, 'MyRepo', { + repositoryName: 'aws-cdk-codebuild-events', +}); + +const project = new codebuild.Project(this, 'MyProject', { + source: codebuild.Source.codeCommit({ repository: repo }), +}); + +const deadLetterQueue = new sqs.Queue(this, 'DeadLetterQueue'); + +// trigger a build when a commit is pushed to the repo +const onCommitRule = repo.onCommit('OnCommit', { + target: new targets.CodeBuildProject(project, { + deadLetterQueue: deadLetterQueue, + }), + branches: ['master'], +}); +``` + ## Trigger a State Machine Use the `SfnStateMachine` target to trigger a State Machine. diff --git a/packages/@aws-cdk/aws-events-targets/lib/codebuild.ts b/packages/@aws-cdk/aws-events-targets/lib/codebuild.ts index 55eac4cadd32f..d5b5e753b3867 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/codebuild.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/codebuild.ts @@ -1,7 +1,8 @@ import * as codebuild from '@aws-cdk/aws-codebuild'; import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; -import { singletonEventRole } from './util'; +import * as sqs from '@aws-cdk/aws-sqs'; +import { addToDeadLetterQueueResourcePolicy, singletonEventRole } from './util'; /** * Customize the CodeBuild Event Target @@ -24,6 +25,18 @@ export interface CodeBuildProjectProps { * @default - the entire EventBridge event */ readonly event?: events.RuleTargetInput; + + /** + * The SQS queue to be used as deadLetterQueue. + * Check out the [considerations for using a dead-letter queue](https://docs.aws.amazon.com/eventbridge/latest/userguide/rule-dlq.html#dlq-considerations). + * + * The events not successfully delivered are automatically retried for a specified period of time, + * depending on the retry policy of the target. + * If an event is not delivered before all retry attempts are exhausted, it will be sent to the dead letter queue. + * + * @default - no dead-letter queue + */ + readonly deadLetterQueue?: sqs.IQueue; } /** @@ -39,9 +52,15 @@ export class CodeBuildProject implements events.IRuleTarget { * Allows using build projects as event rule targets. */ public bind(_rule: events.IRule, _id?: string): events.RuleTargetConfig { + + if (this.props.deadLetterQueue) { + addToDeadLetterQueueResourcePolicy(_rule, this.props.deadLetterQueue); + } + return { id: '', arn: this.project.projectArn, + deadLetterConfig: this.props.deadLetterQueue ? { arn: this.props.deadLetterQueue?.queueArn } : undefined, role: this.props.eventRole || singletonEventRole(this.project, [ new iam.PolicyStatement({ actions: ['codebuild:StartBuild'], diff --git a/packages/@aws-cdk/aws-events-targets/test/codebuild/codebuild.test.ts b/packages/@aws-cdk/aws-events-targets/test/codebuild/codebuild.test.ts index c7bb3d59b68e8..1eba641d7c8ce 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codebuild/codebuild.test.ts +++ b/packages/@aws-cdk/aws-events-targets/test/codebuild/codebuild.test.ts @@ -2,6 +2,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import * as codebuild from '@aws-cdk/aws-codebuild'; import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; +import * as sqs from '@aws-cdk/aws-sqs'; import { CfnElement, Stack } from '@aws-cdk/core'; import * as targets from '../../lib'; @@ -120,4 +121,84 @@ describe('CodeBuild event target', () => { ], })); }); + + test('use a Dead Letter Queue for the rule target', () => { + // GIVEN + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.expression('rate(1 hour)'), + }); + + const queue = new sqs.Queue(stack, 'Queue'); + + // WHEN + const eventInput = { + buildspecOverride: 'buildspecs/hourly.yml', + }; + + rule.addTarget( + new targets.CodeBuildProject(project, { + event: events.RuleTargetInput.fromObject(eventInput), + deadLetterQueue: queue, + }), + ); + + // THEN + expect(stack).to(haveResource('AWS::Events::Rule', { + Targets: [ + { + Arn: projectArn, + Id: 'Target0', + DeadLetterConfig: { + Arn: { + 'Fn::GetAtt': [ + 'Queue4A7E3555', + 'Arn', + ], + }, + }, + Input: JSON.stringify(eventInput), + RoleArn: { + 'Fn::GetAtt': ['MyProjectEventsRole5B7D93F5', 'Arn'], + }, + }, + ], + })); + + expect(stack).to(haveResource('AWS::SQS::QueuePolicy', { + PolicyDocument: { + Statement: [ + { + Action: 'sqs:SendMessage', + Condition: { + ArnEquals: { + 'aws:SourceArn': { + 'Fn::GetAtt': [ + 'Rule4C995B7F', + 'Arn', + ], + }, + }, + }, + Effect: 'Allow', + Principal: { + Service: 'events.amazonaws.com', + }, + Resource: { + 'Fn::GetAtt': [ + 'Queue4A7E3555', + 'Arn', + ], + }, + Sid: 'AllowEventRuleRule', + }, + ], + Version: '2012-10-17', + }, + Queues: [ + { + Ref: 'Queue4A7E3555', + }, + ], + })); + }); }); diff --git a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json index 007503de08383..aec41898a4622 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json @@ -43,6 +43,14 @@ "Arn" ] }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } + }, "Id": "Target0", "RoleArn": { "Fn::GetAtt": [ @@ -394,6 +402,50 @@ } } }, + "DeadLetterQueue9F481546": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DeadLetterQueuePolicyB1FB890C": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "MyRepoOnCommit0E80B304", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + }, + "Sid": "AllowEventRuleawscdkcodebuildeventsMyRepoOnCommit0ED1137A" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "DeadLetterQueue9F481546" + } + ] + } + }, "MyTopic86869434": { "Type": "AWS::SNS::Topic" }, diff --git a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts index 6d17e0e01c688..7974a4188f969 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts +++ b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts @@ -20,6 +20,7 @@ const project = new codebuild.Project(stack, 'MyProject', { }); const queue = new sqs.Queue(stack, 'MyQueue'); +const deadLetterQueue = new sqs.Queue(stack, 'DeadLetterQueue'); const topic = new sns.Topic(stack, 'MyTopic'); topic.addSubscription(new subs.SqsSubscription(queue)); @@ -39,7 +40,9 @@ project.onPhaseChange('PhaseChange', { // trigger a build when a commit is pushed to the repo const onCommitRule = repo.onCommit('OnCommit', { - target: new targets.CodeBuildProject(project), + target: new targets.CodeBuildProject(project, { + deadLetterQueue: deadLetterQueue, + }), branches: ['master'], }); From f5a6647bbe1885ba86029d10550a3ffaf80b6561 Mon Sep 17 00:00:00 2001 From: Darren Date: Tue, 9 Mar 2021 06:37:05 -0600 Subject: [PATCH 04/17] feat(ec2): ESP and AH IPsec protocols for Security Groups (#13471) First contribution. I've gone through the checklist and think i've managed to hit all the requirements. I'd like to contribute more and I'm taking it slow so let me know how I can improve my PRs Closes #13403 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ec2/lib/port.ts | 26 +++++++++++++++++++ packages/@aws-cdk/aws-ec2/package.json | 2 ++ .../aws-ec2/test/integ.vpc.expected.json | 16 +++++++++++- packages/@aws-cdk/aws-ec2/test/integ.vpc.ts | 2 ++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/port.ts b/packages/@aws-cdk/aws-ec2/lib/port.ts index df8d671271b85..314c8d615b0dd 100644 --- a/packages/@aws-cdk/aws-ec2/lib/port.ts +++ b/packages/@aws-cdk/aws-ec2/lib/port.ts @@ -9,6 +9,8 @@ export enum Protocol { UDP = 'udp', ICMP = 'icmp', ICMPV6 = '58', + ESP = 'esp', + AH = 'ah', } /** @@ -171,6 +173,30 @@ export class Port { }); } + /** + * A single ESP port + */ + public static esp(): Port { + return new Port({ + protocol: Protocol.ESP, + fromPort: 50, + toPort: 50, + stringRepresentation: 'ESP 50', + }); + } + + /** + * A single AH port + */ + public static ah(): Port { + return new Port({ + protocol: Protocol.AH, + fromPort: 51, + toPort: 51, + stringRepresentation: 'AH 51', + }); + } + /** * Whether the rule containing this port range can be inlined into a securitygroup or not. */ diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 801040d4c7385..fb14ba55bd21b 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -315,6 +315,8 @@ "docs-public-apis:@aws-cdk/aws-ec2.Protocol.UDP", "docs-public-apis:@aws-cdk/aws-ec2.Protocol.ICMP", "docs-public-apis:@aws-cdk/aws-ec2.Protocol.ICMPV6", + "docs-public-apis:@aws-cdk/aws-ec2.Protocol.ESP", + "docs-public-apis:@aws-cdk/aws-ec2.Protocol.AH", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_SP2_ENGLISH_64BIT_SQL_2008_SP4_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_CHINESE_SIMPLIFIED_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_CHINESE_TRADITIONAL_64BIT_BASE", diff --git a/packages/@aws-cdk/aws-ec2/test/integ.vpc.expected.json b/packages/@aws-cdk/aws-ec2/test/integ.vpc.expected.json index 1586cc8bff5e7..641b97b4ddbd5 100644 --- a/packages/@aws-cdk/aws-ec2/test/integ.vpc.expected.json +++ b/packages/@aws-cdk/aws-ec2/test/integ.vpc.expected.json @@ -567,6 +567,20 @@ "FromPort": 800, "IpProtocol": "udp", "ToPort": 801 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "from 0.0.0.0/0:ESP 50", + "FromPort": 50, + "IpProtocol": "esp", + "ToPort": 50 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "from 0.0.0.0/0:AH 51", + "FromPort": 51, + "IpProtocol": "ah", + "ToPort": 51 } ], "VpcId": { @@ -575,4 +589,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ec2/test/integ.vpc.ts b/packages/@aws-cdk/aws-ec2/test/integ.vpc.ts index 2ffd5653e33f4..88e4dacf9839a 100644 --- a/packages/@aws-cdk/aws-ec2/test/integ.vpc.ts +++ b/packages/@aws-cdk/aws-ec2/test/integ.vpc.ts @@ -16,6 +16,8 @@ const rules = [ ec2.Port.allUdp(), ec2.Port.udp(123), ec2.Port.udpRange(800, 801), + ec2.Port.esp(), + ec2.Port.ah(), ]; for (const rule of rules) { From 122a232343699304d8f206d3024fcddfb2a94bc8 Mon Sep 17 00:00:00 2001 From: Alban Esc Date: Tue, 9 Mar 2021 05:11:58 -0800 Subject: [PATCH 05/17] feat(events): `EventBus.grantPutEventsTo` method for granular grants (#13429) Right now EventBus has a static method `grantPutEvents()` which grants PutEvents to all EventBridge buses in the account. Adding a `grantPutEventsTo()` method to the IEventBus interface that grants PutEvents to the specific event bus. We are also deprecating `grantPutEvents()` in favor to `grantAllPutEvents()` which has the same behavior. Closes #11228. *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-events/README.md | 14 ++++ packages/@aws-cdk/aws-events/lib/event-bus.ts | 31 ++++++++ .../aws-events/test/test.event-bus.ts | 70 +++++++++++++++++ .../lib/event-bridge.ts | 24 ++++-- .../test/destinations.test.ts | 75 +++++++------------ .../test/integ.destinations.expected.json | 21 +++++- .../test/integ.lambda-chain.expected.json | 42 ++++++++++- 7 files changed, 217 insertions(+), 60 deletions(-) diff --git a/packages/@aws-cdk/aws-events/README.md b/packages/@aws-cdk/aws-events/README.md index 565b0537d091d..83aececc56740 100644 --- a/packages/@aws-cdk/aws-events/README.md +++ b/packages/@aws-cdk/aws-events/README.md @@ -186,3 +186,17 @@ bus.archive('MyArchive', { retention: cdk.Duration.days(365), }); ``` + +## Granting PutEvents to an existing EventBus + +To import an existing EventBus into your CDK application, use `EventBus.fromEventBusArn` or `EventBus.fromEventBusAttributes` +factory method. + +Then, you can use the `grantPutEventsTo` method to grant `event:PutEvents` to the eventBus. + +```ts +const eventBus = EventBus.fromEventBusArn(this, 'ImportedEventBus', 'arn:aws:events:us-east-1:111111111:event-bus/my-event-bus'); + +// now you can just call methods on the eventbus +eventBus.grantPutEventsTo(lambdaFunction); +``` diff --git a/packages/@aws-cdk/aws-events/lib/event-bus.ts b/packages/@aws-cdk/aws-events/lib/event-bus.ts index ebbddfe2bc397..2ea9e2300163c 100644 --- a/packages/@aws-cdk/aws-events/lib/event-bus.ts +++ b/packages/@aws-cdk/aws-events/lib/event-bus.ts @@ -47,6 +47,14 @@ export interface IEventBus extends IResource { * @param props Properties of the archive */ archive(id: string, props: BaseArchiveProps): Archive; + + /** + * Grants an IAM Principal to send custom events to the eventBus + * so that they can be matched to rules. + * + * @param grantee The principal (no-op if undefined) + */ + grantPutEventsTo(grantee: iam.IGrantable): iam.Grant; } /** @@ -137,6 +145,14 @@ abstract class EventBusBase extends Resource implements IEventBus { archiveName: props.archiveName, }); } + + public grantPutEventsTo(grantee: iam.IGrantable): iam.Grant { + return iam.Grant.addToPrincipal({ + grantee, + actions: ['events:PutEvents'], + resourceArns: [this.eventBusArn], + }); + } } /** @@ -177,6 +193,7 @@ export class EventBus extends EventBusBase { * so that they can be matched to rules. * * @param grantee The principal (no-op if undefined) + * @deprecated use grantAllPutEvents instead */ public static grantPutEvents(grantee: iam.IGrantable): iam.Grant { // It's currently not possible to restrict PutEvents to specific resources. @@ -188,6 +205,20 @@ export class EventBus extends EventBusBase { }); } + /** + * Permits an IAM Principal to send custom events to EventBridge + * so that they can be matched to rules. + * + * @param grantee The principal (no-op if undefined) + */ + public static grantAllPutEvents(grantee: iam.IGrantable): iam.Grant { + return iam.Grant.addToPrincipal({ + grantee, + actions: ['events:PutEvents'], + resourceArns: ['*'], + }); + } + private static eventBusProps(defaultEventBusName: string, props?: EventBusProps) { if (props) { const { eventBusName, eventSourceName } = props; diff --git a/packages/@aws-cdk/aws-events/test/test.event-bus.ts b/packages/@aws-cdk/aws-events/test/test.event-bus.ts index 2156b39e06e8d..2babb3f9b659e 100644 --- a/packages/@aws-cdk/aws-events/test/test.event-bus.ts +++ b/packages/@aws-cdk/aws-events/test/test.event-bus.ts @@ -306,6 +306,76 @@ export = { test.done(); }, + + 'can grant PutEvents using grantAllPutEvents'(test: Test) { + // GIVEN + const stack = new Stack(); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + }); + + // WHEN + EventBus.grantAllPutEvents(role); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'events:PutEvents', + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + Roles: [ + { + Ref: 'Role1ABCC5F0', + }, + ], + })); + + test.done(); + }, + 'can grant PutEvents to a specific event bus'(test: Test) { + // GIVEN + const stack = new Stack(); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + }); + + const eventBus = new EventBus(stack, 'EventBus'); + + // WHEN + eventBus.grantPutEventsTo(role); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'events:PutEvents', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'EventBus7B8748AA', + 'Arn', + ], + }, + }, + ], + Version: '2012-10-17', + }, + Roles: [ + { + Ref: 'Role1ABCC5F0', + }, + ], + })); + + test.done(); + }, 'can archive events'(test: Test) { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts index f61d8409da7bd..9cdcc5c86a83b 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts @@ -22,15 +22,25 @@ export class EventBridgeDestination implements lambda.IDestination { * Returns a destination configuration */ public bind(_scope: Construct, fn: lambda.IFunction, _options?: lambda.DestinationOptions): lambda.DestinationConfig { - // deduplicated automatically - events.EventBus.grantPutEvents(fn); // Cannot restrict to a specific resource + if (this.eventBus) { + this.eventBus.grantPutEventsTo(fn); + + return { + destination: this.eventBus.eventBusArn, + }; + } + + const existingDefaultEventBus = _scope.node.tryFindChild('DefaultEventBus'); + let eventBus = (existingDefaultEventBus as events.EventBus) || events.EventBus.fromEventBusArn(_scope, 'DefaultEventBus', Stack.of(fn).formatArn({ + service: 'events', + resource: 'event-bus', + resourceName: 'default', + })); + + eventBus.grantPutEventsTo(fn); return { - destination: this.eventBus && this.eventBus.eventBusArn || Stack.of(fn).formatArn({ - service: 'events', - resource: 'event-bus', - resourceName: 'default', - }), + destination: eventBus.eventBusArn, }; } } diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts index 0f8f7d4c85254..70dda9ef43893 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.test.ts @@ -47,56 +47,12 @@ test('event bus as destination', () => { { Action: 'events:PutEvents', Effect: 'Allow', - Resource: '*', - }, - ], - Version: '2012-10-17', - }, - }); -}); - -test('event bus as destination defaults to default event bus', () => { - // WHEN - new lambda.Function(stack, 'Function', { - ...lambdaProps, - onSuccess: new destinations.EventBridgeDestination(), - }); - - // THEN - expect(stack).toHaveResource('AWS::Lambda::EventInvokeConfig', { - DestinationConfig: { - OnSuccess: { - Destination: { - 'Fn::Join': [ - '', - [ - 'arn:', - { - Ref: 'AWS::Partition', - }, - ':events:', - { - Ref: 'AWS::Region', - }, - ':', - { - Ref: 'AWS::AccountId', - }, - ':event-bus/default', + Resource: { + 'Fn::GetAtt': [ + 'EventBus7B8748AA', + 'Arn', ], - ], - }, - }, - }, - }); - - expect(stack).toHaveResource('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: 'events:PutEvents', - Effect: 'Allow', - Resource: '*', + }, }, ], Version: '2012-10-17', @@ -215,7 +171,26 @@ test('lambda payload as destination', () => { { Action: 'events:PutEvents', Effect: 'Allow', - Resource: '*', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':events:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':event-bus/default', + ], + ], + }, }, ], Version: '2012-10-17', diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json index 510fe8cef21a8..009327c46da7e 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.expected.json @@ -219,7 +219,26 @@ { "Action": "events:PutEvents", "Effect": "Allow", - "Resource": "*" + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } }, { "Action": "lambda:InvokeFunction", diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.lambda-chain.expected.json b/packages/@aws-cdk/aws-lambda-destinations/test/integ.lambda-chain.expected.json index 5fc64df8417f3..5fc2d65b80387 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/integ.lambda-chain.expected.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.lambda-chain.expected.json @@ -39,7 +39,26 @@ { "Action": "events:PutEvents", "Effect": "Allow", - "Resource": "*" + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } } ], "Version": "2012-10-17" @@ -289,7 +308,26 @@ { "Action": "events:PutEvents", "Effect": "Allow", - "Resource": "*" + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":events:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":event-bus/default" + ] + ] + } } ], "Version": "2012-10-17" From 781aa97d53fdb7511c34ddde884fdcd84c3f68a6 Mon Sep 17 00:00:00 2001 From: Robert Djurasaj Date: Tue, 9 Mar 2021 06:45:21 -0700 Subject: [PATCH 06/17] fix(ec2): fix typo's in WindowsImage constants (#13446) Admittedly, they are typo fixes, but they're breaking API so they had to be deprecated and replaced... hence the "fix(ec2)" prefix. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-ec2/lib/windows-versions.ts | 26 +++++++++++++++++++ packages/@aws-cdk/aws-ec2/package.json | 13 ++++++++++ 2 files changed, 39 insertions(+) diff --git a/packages/@aws-cdk/aws-ec2/lib/windows-versions.ts b/packages/@aws-cdk/aws-ec2/lib/windows-versions.ts index b14134eec2363..20e959420168f 100644 --- a/packages/@aws-cdk/aws-ec2/lib/windows-versions.ts +++ b/packages/@aws-cdk/aws-ec2/lib/windows-versions.ts @@ -11,11 +11,15 @@ export enum WindowsVersion { WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-Base', WINDOWS_SERVER_2016_ENGLISH_CORE_CONTAINERS = 'Windows_Server-2016-English-Core-Containers', WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP1_WEB = 'Windows_Server-2016-English-Core-SQL_2016_SP1_Web', + /** @deprecated - use WINDOWS_SERVER_2016_GERMAN_FULL_BASE */ WINDOWS_SERVER_2016_GERMAL_FULL_BASE = 'Windows_Server-2016-German-Full-Base', + WINDOWS_SERVER_2016_GERMAN_FULL_BASE = 'Windows_Server-2016-German-Full-Base', WINDOWS_SERVER_2003_R2_SP2_LANGUAGE_PACKS_32BIT_BASE = 'Windows_Server-2003-R2_SP2-Language_Packs-32Bit-Base', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2008_R2_SP3_WEB = 'Windows_Server-2008-R2_SP1-English-64Bit-SQL_2008_R2_SP3_Web', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2012_SP4_EXPRESS = 'Windows_Server-2008-R2_SP1-English-64Bit-SQL_2012_SP4_Express', + /** @deprecated - use WINDOWS_SERVER_2012_R2_SP1_PORTUGUESE_BRAZIL_64BIT_CORE*/ WINDOWS_SERVER_2012_R2_SP1_PORTUGESE_BRAZIL_64BIT_CORE = 'Windows_Server-2008-R2_SP1-Portuguese_Brazil-64Bit-Core', + WINDOWS_SERVER_2012_R2_SP1_PORTUGUESE_BRAZIL_64BIT_CORE = 'Windows_Server-2008-R2_SP1-Portuguese_Brazil-64Bit-Core', WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2016_SP2_STANDARD = 'Windows_Server-2012-R2_RTM-English-64Bit-SQL_2016_SP2_Standard', WINDOWS_SERVER_2012_RTM_ENGLISH_64BIT_SQL_2014_SP2_EXPRESS = 'Windows_Server-2012-RTM-English-64Bit-SQL_2014_SP2_Express', WINDOWS_SERVER_2012_RTM_ITALIAN_64BIT_BASE = 'Windows_Server-2012-RTM-Italian-64Bit-Base', @@ -28,7 +32,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2016_JAPANESE_FULL_FQL_2016_SP2_WEB = 'Windows_Server-2016-Japanese-Full-SQL_2016_SP2_Web', WINDOWS_SERVER_2016_KOREAN_FULL_BASE = 'Windows_Server-2016-Korean-Full-Base', WINDOWS_SERVER_2016_KOREAN_FULL_SQL_2016_SP2_STANDARD = 'Windows_Server-2016-Korean-Full-SQL_2016_SP2_Standard', + /** @deprecated - use WINDOWS_SERVER_2016_PORTUGUESE_PORTUGAL_FULL_BASE */ WINDOWS_SERVER_2016_PORTUGESE_PORTUGAL_FULL_BASE = 'Windows_Server-2016-Portuguese_Portugal-Full-Base', + WINDOWS_SERVER_2016_PORTUGUESE_PORTUGAL_FULL_BASE = 'Windows_Server-2016-Portuguese_Portugal-Full-Base', WINDOWS_SERVER_2019_ENGLISH_FULL_SQL_2017_WEB = 'Windows_Server-2019-English-Full-SQL_2017_Web', WINDOWS_SERVER_2019_FRENCH_FULL_BASE = 'Windows_Server-2019-French-Full-Base', WINDOWS_SERVER_2019_KOREAN_FULL_BASE = 'Windows_Server-2019-Korean-Full-Base', @@ -90,8 +96,12 @@ export enum WindowsVersion { WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2014_SP2_EXPRESS = 'Windows_Server-2012-R2_RTM-English-64Bit-SQL_2014_SP2_Express', WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2014_SP3_WEB = 'Windows_Server-2012-R2_RTM-English-64Bit-SQL_2014_SP3_Web', WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2016_SP1_WEB = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-SQL_2016_SP1_Web', + /** @deprecated - use WINDOWS_SERVER_2012_R2_RTM_PORTUGUESE_BRAZIL_64BIT_BASE */ WINDOWS_SERVER_2012_R2_RTM_PORTUGESE_BRAZIL_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Portuguese_Brazil-64Bit-Base', + WINDOWS_SERVER_2012_R2_RTM_PORTUGUESE_BRAZIL_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Portuguese_Brazil-64Bit-Base', + /** @deprecated - use WINDOWS_SERVER_2012_R2_RTM_PORTUGUESE_PORTUGAL_64BIT_BASE*/ WINDOWS_SERVER_2012_R2_RTM_PORTUGESE_PORTUGAL_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Portuguese_Portugal-64Bit-Base', + WINDOWS_SERVER_2012_R2_RTM_PORTUGUESE_PORTUGAL_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Portuguese_Portugal-64Bit-Base', WINDOWS_SERVER_2012_R2_RTM_SWEDISH_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Swedish-64Bit-Base', WINDOWS_SERVER_2016_ENGLISH_FULL_SQL_2016_SP1_EXPRESS = 'Windows_Server-2016-English-Full-SQL_2016_SP1_Express', WINDOWS_SERVER_2016_ITALIAN_FULL_BASE = 'Windows_Server-2016-Italian-Full-Base', @@ -103,7 +113,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2012_RTM_ENGLISH_64BIT_SQL_2007_R2_SP3_WEB = 'Windows_Server-2012-RTM-English-64Bit-SQL_2008_R2_SP3_Web', WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2014_SP2_WEB = 'Windows_Server-2012-RTM-Japanese-64Bit-SQL_2014_SP2_Web', WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_ENTERPRISE = 'Windows_Server-2016-English-Core-SQL_2016_SP2_Enterprise', + /** @deprecated - use WINDOWS_SERVER_2016_PORTUGUESE_BRAZIL_FULL_BASE */ WINDOWS_SERVER_2016_PORTUGESE_BRAZIL_FULL_BASE = 'Windows_Server-2016-Portuguese_Brazil-Full-Base', + WINDOWS_SERVER_2016_PORTUGUESE_BRAZIL_FULL_BASE = 'Windows_Server-2016-Portuguese_Brazil-Full-Base', WINDOWS_SERVER_2019_ENGLISH_FULL_BASE = 'Windows_Server-2019-English-Full-Base', WINDOWS_SERVER_2003_R2_SP2_ENGLISH_32BIT_BASE = 'Windows_Server-2003-R2_SP2-English-32Bit-Base', WINDOWS_SERVER_2012_R2_RTM_CZECH_64BIT_BASE = 'Windows_Server-2012-R2_RTM-Czech-64Bit-Base', @@ -128,7 +140,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2003_R2_SP2_ENGLISH_64BIT_BASE = 'Windows_Server-2003-R2_SP2-English-64Bit-Base', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_BASE = 'Windows_Server-2008-R2_SP1-English-64Bit-Base', WINDOWS_SERVER_2008_R2_SP1_LANGUAGE_PACKS_64BIT_SQL_2008_R2_SP3_EXPRESS = 'Windows_Server-2008-R2_SP1-Language_Packs-64Bit-SQL_2008_R2_SP3_Express', + /** @deprecated - use WINDOWS_SERVER_2012_SP2_PORTUGUESE_BRAZIL_64BIT_BASE */ WINDOWS_SERVER_2012_SP2_PORTUGESE_BRAZIL_64BIT_BASE = 'Windows_Server-2008-SP2-Portuguese_Brazil-64Bit-Base', + WINDOWS_SERVER_2012_SP2_PORTUGUESE_BRAZIL_64BIT_BASE = 'Windows_Server-2008-SP2-Portuguese_Brazil-64Bit-Base', WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2016_SP1_WEB = 'Windows_Server-2012-R2_RTM-English-64Bit-SQL_2016_SP1_Web', WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2014_SP3_EXPRESS = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-SQL_2014_SP3_Express', WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2016_SP2_ENTERPRISE = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-SQL_2016_SP2_Enterprise', @@ -141,7 +155,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2008_R2_SP1_JAPANESE_64BIT_BASE = 'Windows_Server-2008-R2_SP1-Japanese-64Bit-Base', WINDOWS_SERVER_2008_SP2_ENGLISH_64BIT_SQL_2008_SP4_STANDARD = 'Windows_Server-2008-SP2-English-64Bit-SQL_2008_SP4_Standard', WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_BASE = 'Windows_Server-2012-R2_RTM-English-64Bit-Base', + /** @deprecated - use WINDOWS_SERVER_2012_RTM_PORTUGUESE_BRAZIL_64BIT_BASE */ WINDOWS_SERVER_2012_RTM_PORTUGESE_BRAZIL_64BIT_BASE = 'Windows_Server-2012-RTM-Portuguese_Brazil-64Bit-Base', + WINDOWS_SERVER_2012_RTM_PORTUGUESE_BRAZIL_64BIT_BASE = 'Windows_Server-2012-RTM-Portuguese_Brazil-64Bit-Base', WINDOWS_SERVER_2016_ENGLISH_FULL_SQL_2016_SP1_WEB = 'Windows_Server-2016-English-Full-SQL_2016_SP1_Web', WINDOWS_SERVER_2016_ENGLISH_P3 = 'Windows_Server-2016-English-P3', WINDOWS_SERVER_2016_JAPANESE_FULL_SQL_2016_SP1_ENTERPRISE = 'Windows_Server-2016-Japanese-Full-SQL_2016_SP1_Enterprise', @@ -165,7 +181,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2016_CHINESE_SIMPLIFIED_FULL_BASE = 'Windows_Server-2016-Chinese_Simplified-Full-Base', WINDOWS_SERVER_2019_POLISH_FULL_BASE = 'Windows_Server-2019-Polish-Full-Base', WINDOWS_SERVER_2008_R2_SP1_JAPANESE_64BIT_SQL_2008_R2_SP3_WEB = 'Windows_Server-2008-R2_SP1-Japanese-64Bit-SQL_2008_R2_SP3_Web', + /** @deprecated - use WINDOWS_SERVER_2008_R2_SP1_PORTUGUESE_BRAZIL_64BIT_BASE */ WINDOWS_SERVER_2008_R2_SP1_PORTUGESE_BRAZIL_64BIT_BASE = 'Windows_Server-2008-R2_SP1-Portuguese_Brazil-64Bit-Base', + WINDOWS_SERVER_2008_R2_SP1_PORTUGUESE_BRAZIL_64BIT_BASE = 'Windows_Server-2008-R2_SP1-Portuguese_Brazil-64Bit-Base', WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2016_SP1_ENTERPRISE = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-SQL_2016_SP1_Enterprise', WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2016_SP2_EXPRESS = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-SQL_2016_SP2_Express', WINDOWS_SERVER_2012_RTM_ENGLISH_64BIT_SQL_2014_SP3_EXPRESS = 'Windows_Server-2012-RTM-English-64Bit-SQL_2014_SP3_Express', @@ -196,10 +214,14 @@ export enum WindowsVersion { WINDOWS_SERVER_1709_ENGLISH_CORE_BASE = 'Windows_Server-1709-English-Core-Base', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_61BIT_SQL_2012_RTM_SP2_ENTERPRISE = 'Windows_Server-2008-R2_SP1-English-64Bit-SQL_2012_RTM_SP2_Enterprise', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2012_SP4_STANDARD = 'Windows_Server-2008-R2_SP1-English-64Bit-SQL_2012_SP4_Standard', + /** @deprecated - use WINDOWS_SERVER_2008_SP2_PORTUGUESE_BRAZIL_32BIT_BASE */ WINDOWS_SERVER_2008_SP2_PORTUGESE_BRAZIL_32BIT_BASE = 'Windows_Server-2008-SP2-Portuguese_Brazil-32Bit-Base', + WINDOWS_SERVER_2008_SP2_PORTUGUESE_BRAZIL_32BIT_BASE = 'Windows_Server-2008-SP2-Portuguese_Brazil-32Bit-Base', WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2014_SP2_STANDARD = 'Windows_Server-2012-R2_RTM-Japanese-64Bit-SQL_2014_SP2_Standard', WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2012_SP4_EXPRESS = 'Windows_Server-2012-RTM-Japanese-64Bit-SQL_2012_SP4_Express', + /** @deprecated - use WINDOWS_SERVER_2012_RTM_PORTUGUESE_PORTUGAL_64BIT_BASE */ WINDOWS_SERVER_2012_RTM_PORTUGESE_PORTUGAL_64BIT_BASE = 'Windows_Server-2012-RTM-Portuguese_Portugal-64Bit-Base', + WINDOWS_SERVER_2012_RTM_PORTUGUESE_PORTUGAL_64BIT_BASE = 'Windows_Server-2012-RTM-Portuguese_Portugal-64Bit-Base', WINDOWS_SERVER_2016_CZECH_FULL_BASE = 'Windows_Server-2016-Czech-Full-Base', WINDOWS_SERVER_2016_JAPANESE_FULL_SQL_2016_SP1_STANDARD = 'Windows_Server-2016-Japanese-Full-SQL_2016_SP1_Standard', WINDOWS_SERVER_2019_DUTCH_FULL_BASE = 'Windows_Server-2019-Dutch-Full-Base', @@ -212,7 +234,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_EXPRESS = 'Windows_Server-2016-English-Core-SQL_2016_SP2_Express', WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_WEB = 'Windows_Server-2016-English-Core-SQL_2016_SP2_Web', WINDOWS_SERVER_2016_ENGLISH_FULL_SQL_2017_STANDARD = 'Windows_Server-2016-English-Full-SQL_2017_Standard', + /** @deprecated - use WINDOWS_SERVER_2019_PORTUGUESE_BRAZIL_FULL_BASE */ WINDOWS_SERVER_2019_PORTUGESE_BRAZIL_FULL_BASE = 'Windows_Server-2019-Portuguese_Brazil-Full-Base', + WINDOWS_SERVER_2019_PORTUGUESE_BRAZIL_FULL_BASE = 'Windows_Server-2019-Portuguese_Brazil-Full-Base', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2008_R2_SP3_STANDARD = 'Windows_Server-2008-R2_SP1-English-64Bit-SQL_2008_R2_SP3_Standard', WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SHAREPOINT_2010_SP2_FOUNDATION = 'Windows_Server-2008-R2_SP1-English-64Bit-SharePoint_2010_SP2_Foundation', WINDOWS_SERVER_2012_R2_RTM_ENGLISH_P3 = 'Windows_Server-2012-R2_RTM-English-P3', @@ -221,7 +245,9 @@ export enum WindowsVersion { WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2014_SP3_EXPRESS = 'Windows_Server-2012-RTM-Japanese-64Bit-SQL_2014_SP3_Express', WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_STANDARD = 'Windows_Server-2016-English-Core-SQL_2016_SP2_Standard', WINDOWS_SERVER_2016_JAPANESE_FULL_SQL_2016_SP2_STANDARD = 'Windows_Server-2016-Japanese-Full-SQL_2016_SP2_Standard', + /** @deprecated - use WINDOWS_SERVER_2019_PORTUGUESE_PORTUGAL_FULL_BASE */ WINDOWS_SERVER_2019_PORTUGESE_PORTUGAL_FULL_BASE = 'Windows_Server-2019-Portuguese_Portugal-Full-Base', + WINDOWS_SERVER_2019_PORTUGUESE_PORTUGAL_FULL_BASE = 'Windows_Server-2019-Portuguese_Portugal-Full-Base', WINDOWS_SERVER_2019_SWEDISH_FULL_BASE = 'Windows_Server-2019-Swedish-Full-Base', WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_HYPERV = 'Windows_Server-2012-R2_RTM-English-64Bit-HyperV', WINDOWS_SERVER_2012_RTM_KOREAN_64BIT_BASE = 'Windows_Server-2012-RTM-Korean-64Bit-Base', diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index fb14ba55bd21b..c5d5d9be6a64e 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -140,6 +140,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_HYPERV", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_SWEDISH_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_PORTUGESE_PORTUGAL_FULL_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_PORTUGUESE_PORTUGAL_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_JAPANESE_FULL_SQL_2016_SP2_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2014_SP3_EXPRESS", @@ -149,6 +150,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SHAREPOINT_2010_SP2_FOUNDATION", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2008_R2_SP3_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_PORTUGESE_BRAZIL_FULL_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_PORTUGUESE_BRAZIL_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_FULL_SQL_2017_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_EXPRESS", @@ -162,9 +164,11 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_JAPANESE_FULL_SQL_2016_SP1_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_CZECH_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_PORTUGESE_PORTUGAL_64BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_PORTUGUESE_PORTUGAL_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2012_SP4_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2014_SP2_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_SP2_PORTUGESE_BRAZIL_32BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_SP2_PORTUGUESE_BRAZIL_32BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2012_SP4_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_61BIT_SQL_2012_RTM_SP2_ENTERPRISE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_1709_ENGLISH_CORE_BASE", @@ -327,10 +331,12 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_CORE_CONTAINERS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP1_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_GERMAL_FULL_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_GERMAN_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2003_R2_SP2_LANGUAGE_PACKS_32BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2008_R2_SP3_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_SQL_2012_SP4_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_SP1_PORTUGESE_BRAZIL_64BIT_CORE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_SP1_PORTUGUESE_BRAZIL_64BIT_CORE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2016_SP2_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_ENGLISH_64BIT_SQL_2014_SP2_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_ITALIAN_64BIT_BASE", @@ -344,6 +350,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_KOREAN_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_KOREAN_FULL_SQL_2016_SP2_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_PORTUGESE_PORTUGAL_FULL_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_PORTUGUESE_PORTUGAL_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_SQL_2017_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_FRENCH_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_KOREAN_FULL_BASE", @@ -406,7 +413,9 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2014_SP3_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2016_SP1_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_PORTUGESE_BRAZIL_64BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_PORTUGUESE_BRAZIL_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_PORTUGESE_PORTUGAL_64BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_PORTUGUESE_PORTUGAL_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_SWEDISH_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_FULL_SQL_2016_SP1_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ITALIAN_FULL_BASE", @@ -419,6 +428,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_JAPANESE_64BIT_SQL_2014_SP2_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_CORE_SQL_2016_SP2_ENTERPRISE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_PORTUGESE_BRAZIL_FULL_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_PORTUGUESE_BRAZIL_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2003_R2_SP2_ENGLISH_32BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_CZECH_64BIT_BASE", @@ -443,6 +453,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_ENGLISH_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_LANGUAGE_PACKS_64BIT_SQL_2008_R2_SP3_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_SP2_PORTUGESE_BRAZIL_64BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_SP2_PORTUGUESE_BRAZIL_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_SQL_2016_SP1_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2014_SP3_EXPRESS", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_JAPANESE_64BIT_SQL_2016_SP2_ENTERPRISE", @@ -456,6 +467,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_SP2_ENGLISH_64BIT_SQL_2008_SP4_STANDARD", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_R2_RTM_ENGLISH_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_PORTUGESE_BRAZIL_64BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2012_RTM_PORTUGUESE_BRAZIL_64BIT_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_FULL_SQL_2016_SP1_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_ENGLISH_P3", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2016_JAPANESE_FULL_SQL_2016_SP1_ENTERPRISE", @@ -480,6 +492,7 @@ "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2019_POLISH_FULL_BASE", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_JAPANESE_64BIT_SQL_2008_R2_SP3_WEB", "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_PORTUGESE_BRAZIL_64BIT_BASE", + "docs-public-apis:@aws-cdk/aws-ec2.WindowsVersion.WINDOWS_SERVER_2008_R2_SP1_PORTUGUESE_BRAZIL_64BIT_BASE", "props-default-doc:@aws-cdk/aws-ec2.InterfaceVpcEndpointAttributes.securityGroupId", "props-default-doc:@aws-cdk/aws-ec2.InterfaceVpcEndpointAttributes.securityGroups", "props-physical-name:@aws-cdk/aws-ec2.VpnGatewayProps" From b1449a178b0f9a8a951c2546428f8d75c6431f0f Mon Sep 17 00:00:00 2001 From: Sneha Rathod Solanki Date: Tue, 9 Mar 2021 15:20:00 +0100 Subject: [PATCH 07/17] feat(events,applicationautoscaling): schedule can be a token (#13064) Fix for Issue: https://github.com/aws/aws-cdk/issues/9413 Testing: Unit tests added for Schedule.rate used in ApplicationAutoScaling and Events. * [X] Testing - Unit test added (prefer not to modify an existing test, otherwise, it's probably a breaking change) - __CLI change?:__ coordinate update of integration tests with team - __cdk-init template change?:__ coordinated update of integration tests with team * [X] Docs - __jsdocs__: All public APIs documented - __README__: README and/or documentation topic updated - __Design__: For significant features, design document added to `design` folder * [X] Title and Description - __Change type__: title prefixed with **fix**, **feat** and module name in parents, which will appear in changelog - __Title__: use lower-case and doesn't end with a period - __Breaking?__: last paragraph: "BREAKING CHANGE: " - __Issues__: Indicate issues fixed via: "**Fixes #xxx**" or "**Closes #xxx**" * [ ] Sensitive Modules (requires 2 PR approvers) - IAM Policy Document (in @aws-cdk/aws-iam) - EC2 Security Groups and ACLs (in @aws-cdk/aws-ec2) - Grant APIs (only if not based on official documentation with a reference) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/schedule.ts | 7 ++++ .../test/test.cron.ts | 27 ++++++++++-- packages/@aws-cdk/aws-events/README.md | 1 + packages/@aws-cdk/aws-events/lib/schedule.ts | 7 ++++ .../@aws-cdk/aws-events/test/test.rule.ts | 33 +++++++++++++++ .../@aws-cdk/aws-events/test/test.schedule.ts | 42 +++++++++++++++++-- packages/@aws-cdk/core/lib/duration.ts | 24 ++++++++++- packages/@aws-cdk/core/test/duration.test.ts | 26 ++++++++++++ 8 files changed, 160 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/schedule.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/schedule.ts index c576e17453557..cdeb00be9a426 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/schedule.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/schedule.ts @@ -17,6 +17,13 @@ export abstract class Schedule { * Construct a schedule from an interval and a time unit */ public static rate(duration: Duration): Schedule { + const validDurationUnit = ['minute', 'minutes', 'hour', 'hours', 'day', 'days']; + if (!validDurationUnit.includes(duration.unitLabel())) { + throw new Error("Allowed unit for scheduling is: 'minute', 'minutes', 'hour', 'hours', 'day' or 'days'"); + } + if (duration.isUnresolved()) { + return new LiteralSchedule(`rate(${duration.formatTokenToNumber()})`); + } if (duration.toSeconds() === 0) { throw new Error('Duration cannot be 0'); } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/test/test.cron.ts b/packages/@aws-cdk/aws-applicationautoscaling/test/test.cron.ts index 6af9c525c9bf9..ca0fb69812b10 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/test/test.cron.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/test/test.cron.ts @@ -1,4 +1,4 @@ -import { Duration } from '@aws-cdk/core'; +import { Duration, Stack, Lazy } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as appscaling from '../lib'; @@ -15,8 +15,15 @@ export = { 'rate must be whole number of minutes'(test: Test) { test.throws(() => { - appscaling.Schedule.rate(Duration.seconds(12345)); - }, /'12345 seconds' cannot be converted into a whole number of minutes/); + appscaling.Schedule.rate(Duration.minutes(0.13456)); + }, /'0.13456 minutes' cannot be converted into a whole number of seconds/); + test.done(); + }, + + 'rate must not be in seconds'(test: Test) { + test.throws(() => { + appscaling.Schedule.rate(Duration.seconds(1)); + }, /Allowed unit for scheduling is: 'minute', 'minutes', 'hour', 'hours', 'day' or 'days'/); test.done(); }, @@ -26,4 +33,18 @@ export = { }, /Duration cannot be 0/); test.done(); }, + + 'rate can be token'(test: Test) { + const stack = new Stack(); + const lazyDuration = Duration.minutes(Lazy.number({ produce: () => 5 })); + const rate = appscaling.Schedule.rate(lazyDuration); + test.equal('rate(5 minutes)', stack.resolve(rate).expressionString); + test.done(); + }, + + 'rate can be in allowed type hours'(test: Test) { + test.equal('rate(1 hour)', appscaling.Schedule.rate(Duration.hours(1)) + .expressionString); + test.done(); + }, }; diff --git a/packages/@aws-cdk/aws-events/README.md b/packages/@aws-cdk/aws-events/README.md index 83aececc56740..bb0e434837131 100644 --- a/packages/@aws-cdk/aws-events/README.md +++ b/packages/@aws-cdk/aws-events/README.md @@ -83,6 +83,7 @@ onCommitRule.addTarget(new targets.SnsTopic(topic, { ## Scheduling You can configure a Rule to run on a schedule (cron or rate). +Rate must be specified in minutes, hours or days. The following example runs a task every day at 4am: diff --git a/packages/@aws-cdk/aws-events/lib/schedule.ts b/packages/@aws-cdk/aws-events/lib/schedule.ts index 8c2c04481c0d2..c12afde30aa01 100644 --- a/packages/@aws-cdk/aws-events/lib/schedule.ts +++ b/packages/@aws-cdk/aws-events/lib/schedule.ts @@ -17,6 +17,13 @@ export abstract class Schedule { * Construct a schedule from an interval and a time unit */ public static rate(duration: Duration): Schedule { + const validDurationUnit = ['minute', 'minutes', 'hour', 'hours', 'day', 'days']; + if (validDurationUnit.indexOf(duration.unitLabel()) === -1) { + throw new Error('Allowed unit for scheduling is: \'minute\', \'minutes\', \'hour\', \'hours\', \'day\', \'days\''); + } + if (duration.isUnresolved()) { + return new LiteralSchedule(`rate(${duration.formatTokenToNumber()})`); + } if (duration.toSeconds() === 0) { throw new Error('Duration cannot be 0'); } diff --git a/packages/@aws-cdk/aws-events/test/test.rule.ts b/packages/@aws-cdk/aws-events/test/test.rule.ts index 80cf91948f5fa..72fe340242924 100644 --- a/packages/@aws-cdk/aws-events/test/test.rule.ts +++ b/packages/@aws-cdk/aws-events/test/test.rule.ts @@ -49,6 +49,39 @@ export = { test.done(); }, + 'get rate as token'(test: Test) { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'MyScheduledStack'); + const lazyDuration = cdk.Duration.minutes(cdk.Lazy.number({ produce: () => 5 })); + + new Rule(stack, 'MyScheduledRule', { + ruleName: 'rateInMinutes', + schedule: Schedule.rate(lazyDuration), + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::Events::Rule', { + 'Name': 'rateInMinutes', + 'ScheduleExpression': 'rate(5 minutes)', + })); + + test.done(); + }, + + 'Seconds is not an allowed value for Schedule rate'(test: Test) { + const lazyDuration = cdk.Duration.seconds(cdk.Lazy.number({ produce: () => 5 })); + test.throws(() => Schedule.rate(lazyDuration), /Allowed unit for scheduling is: 'minute', 'minutes', 'hour', 'hours', 'day', 'days'/); + test.done(); + }, + + 'Millis is not an allowed value for Schedule rate'(test: Test) { + const lazyDuration = cdk.Duration.millis(cdk.Lazy.number({ produce: () => 5 })); + + // THEN + test.throws(() => Schedule.rate(lazyDuration), /Allowed unit for scheduling is: 'minute', 'minutes', 'hour', 'hours', 'day', 'days'/); + test.done(); + }, + 'rule with physical name'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-events/test/test.schedule.ts b/packages/@aws-cdk/aws-events/test/test.schedule.ts index d2e045199360f..e9c24a51c92b2 100644 --- a/packages/@aws-cdk/aws-events/test/test.schedule.ts +++ b/packages/@aws-cdk/aws-events/test/test.schedule.ts @@ -1,4 +1,4 @@ -import { Duration } from '@aws-cdk/core'; +import { Duration, Stack, Lazy } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as events from '../lib'; @@ -33,8 +33,15 @@ export = { 'rate must be whole number of minutes'(test: Test) { test.throws(() => { - events.Schedule.rate(Duration.seconds(12345)); - }, /'12345 seconds' cannot be converted into a whole number of minutes/); + events.Schedule.rate(Duration.minutes(0.13456)); + }, /'0.13456 minutes' cannot be converted into a whole number of seconds/); + test.done(); + }, + + 'rate must be whole number'(test: Test) { + test.throws(() => { + events.Schedule.rate(Duration.minutes(1/8)); + }, /'0.125 minutes' cannot be converted into a whole number of seconds/); test.done(); }, @@ -44,4 +51,33 @@ export = { }, /Duration cannot be 0/); test.done(); }, + + 'rate can be from a token'(test: Test) { + const stack = new Stack(); + const lazyDuration = Duration.minutes(Lazy.number({ produce: () => 5 })); + const rate = events.Schedule.rate(lazyDuration); + test.equal('rate(5 minutes)', stack.resolve(rate).expressionString); + test.done(); + }, + + 'rate can be in minutes'(test: Test) { + test.equal('rate(10 minutes)', + events.Schedule.rate(Duration.minutes(10)) + .expressionString); + test.done(); + }, + + 'rate can be in days'(test: Test) { + test.equal('rate(10 days)', + events.Schedule.rate(Duration.days(10)) + .expressionString); + test.done(); + }, + + 'rate can be in hours'(test: Test) { + test.equal('rate(10 hours)', + events.Schedule.rate(Duration.hours(10)) + .expressionString); + test.done(); + }, }; diff --git a/packages/@aws-cdk/core/lib/duration.ts b/packages/@aws-cdk/core/lib/duration.ts index 098744af2d0b8..b1c675d3ce722 100644 --- a/packages/@aws-cdk/core/lib/duration.ts +++ b/packages/@aws-cdk/core/lib/duration.ts @@ -1,4 +1,4 @@ -import { Token } from './token'; +import { Token, Tokenization } from './token'; /** * Represents a length of time. @@ -252,6 +252,28 @@ export class Duration { } return ret; } + + /** + * Checks if duration is a token or a resolvable object + */ + public isUnresolved() { + return Token.isUnresolved(this.amount); + } + + /** + * Returns unit of the duration + */ + public unitLabel() { + return this.unit.label; + } + + /** + * Returns stringified number of duration + */ + public formatTokenToNumber(): string { + const number = Tokenization.stringifyNumber(this.amount); + return `${number} ${this.unit.label}`; + } } /** diff --git a/packages/@aws-cdk/core/test/duration.test.ts b/packages/@aws-cdk/core/test/duration.test.ts index 70b7cb786e344..75b92c76924c2 100644 --- a/packages/@aws-cdk/core/test/duration.test.ts +++ b/packages/@aws-cdk/core/test/duration.test.ts @@ -168,6 +168,32 @@ nodeunitShim({ test.done(); }, + + 'get unit label from duration'(test: Test) { + test.equal(Duration.minutes(Lazy.number({ produce: () => 10 })).unitLabel(), 'minutes'); + test.equal(Duration.minutes(62).unitLabel(), 'minutes'); + test.equal(Duration.seconds(10).unitLabel(), 'seconds'); + test.equal(Duration.millis(1).unitLabel(), 'millis'); + test.equal(Duration.hours(1000).unitLabel(), 'hours'); + test.equal(Duration.days(2).unitLabel(), 'days'); + test.done(); + }, + + 'format number token to number'(test: Test) { + const stack = new Stack(); + const lazyDuration = Duration.minutes(Lazy.number({ produce: () => 10 })); + test.equal(stack.resolve(lazyDuration.formatTokenToNumber()), '10 minutes'); + test.equal(Duration.hours(10).formatTokenToNumber(), '10 hours'); + test.equal(Duration.days(5).formatTokenToNumber(), '5 days'); + test.done(); + }, + + 'duration is unresolved'(test: Test) { + const lazyDuration = Duration.minutes(Lazy.number({ produce: () => 10 })); + test.equal(lazyDuration.isUnresolved(), true); + test.equal(Duration.hours(10).isUnresolved(), false); + test.done(); + }, }); function floatEqual(test: Test, actual: number, expected: number) { From 01b94f8aa6c88b5e676c784aec4c879acddc042f Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Tue, 9 Mar 2021 15:50:55 +0000 Subject: [PATCH 08/17] fix(elasticloadbalancingv2): upgrade to v1.92.0 drops certificates on ALB if more than 2 certificates exist (#13490) Support for multiple certificates attached to a single ALB listener was originally implemented by putting all certificates in an array on a single `ListenerCertificate` resource. The docs state that only one certificate may be specified, although multiple certificates do appear to work initially. Initial resource creation of a `ListenerCertificate` with multiple certificates appears to succeed, but subsequent updates to this resource (to either add or remove certificates) yields undefined and undesireable behavior. The fix in #13332 attempted to fix this by creating a new `ListenerCertificate` per certificate, and -- at my direction -- maintained partial backwards compatibility by keeping the original ID for the first `ListenerCertificate` resource. However, this has the effect of triggering an update to the existing resource, which does not appear to work correctly. By forcing a logical ID change for all `ListenerCertificate` resources, we can force all existing resources to be deleted, and new resources created. This avoids doing any updates on any `ListenerCertificate` resources with an array of certificates, which appears to side-step the undefined behavior. fixes #13437 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/alb/application-listener.ts | 4 +--- .../aws-elasticloadbalancingv2/test/alb/listener.test.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts index b72151f81f2f8..3a13b8a3490a1 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts @@ -266,9 +266,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis // Only one certificate can be specified per resource, even though // `certificates` is of type Array for (let i = 0; i < additionalCerts.length; i++) { - // ids should look like: `id`, `id2`, `id3` (for backwards-compatibility) - const certId = (i > 0) ? `${id}${i + 1}` : id; - new ApplicationListenerCertificate(this, certId, { + new ApplicationListenerCertificate(this, `${id}${i + 1}`, { listener: this, certificates: [additionalCerts[i]], }); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts index b6e379a17e463..e29e5a7837895 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/listener.test.ts @@ -162,7 +162,7 @@ describe('tests', () => { ], }); - expect(listener.node.tryFindChild('DefaultCertificates')).toBeDefined(); + expect(listener.node.tryFindChild('DefaultCertificates1')).toBeDefined(); expect(listener.node.tryFindChild('DefaultCertificates2')).toBeDefined(); expect(listener.node.tryFindChild('DefaultCertificates3')).not.toBeDefined(); From 6c3d4071746179dde30f615602592c2523daa56e Mon Sep 17 00:00:00 2001 From: Jonne Kaunisto Date: Tue, 9 Mar 2021 09:53:10 -0800 Subject: [PATCH 09/17] fix(stepfunctions): no validation on state machine name (#13387) closes #13289 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-stepfunctions/lib/state-machine.ts | 15 +++++++++- .../test/state-machine.test.ts | 30 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index cced1d4519660..fa97eccad71f4 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -46,7 +46,7 @@ export enum LogLevel { /** * Log all errors */ - ERROR= 'ERROR', + ERROR = 'ERROR', /** * Log fatal errors */ @@ -379,6 +379,10 @@ export class StateMachine extends StateMachineBase { physicalName: props.stateMachineName, }); + if (props.stateMachineName != undefined) { + this.validateStateMachineName(props.stateMachineName); + } + this.role = props.role || new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('states.amazonaws.com'), }); @@ -426,6 +430,15 @@ export class StateMachine extends StateMachineBase { this.role.addToPrincipalPolicy(statement); } + private validateStateMachineName(stateMachineName: string) { + if (stateMachineName.length < 1 || stateMachineName.length > 80) { + throw new Error(`State Machine name must be between 1 and 80 characters. Received: ${stateMachineName}`); + } + if (!stateMachineName.match('^[0-9a-zA-Z+!@._-]+$')) { + throw new Error(`State Machine name must match "^[0-9a-zA-Z+!@._-]+$". Received: ${stateMachineName}`); + } + } + private buildLoggingConfiguration(logOptions: LogOptions): CfnStateMachine.LoggingConfigurationProperty { // https://docs.aws.amazon.com/step-functions/latest/dg/cw-logs.html#cloudwatch-iam-policy this.addToRolePolicy(new iam.PolicyStatement({ diff --git a/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts index e721b460a5358..1c26947659a54 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts @@ -62,6 +62,36 @@ describe('State Machine', () => { }), + test('State Machine with invalid name', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const createStateMachine = (name: string) => { + new stepfunctions.StateMachine(stack, name + 'StateMachine', { + stateMachineName: name, + definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, name + 'Pass')), + stateMachineType: stepfunctions.StateMachineType.EXPRESS, + }); + }; + const tooShortName = ''; + const tooLongName = 'M'.repeat(81); + const invalidCharactersName = '*'; + + // THEN + expect(() => { + createStateMachine(tooShortName); + }).toThrow(`State Machine name must be between 1 and 80 characters. Received: ${tooShortName}`); + + expect(() => { + createStateMachine(tooLongName); + }).toThrow(`State Machine name must be between 1 and 80 characters. Received: ${tooLongName}`); + + expect(() => { + createStateMachine(invalidCharactersName); + }).toThrow(`State Machine name must match "^[0-9a-zA-Z+!@._-]+$". Received: ${invalidCharactersName}`); + }); + test('log configuration', () => { // GIVEN const stack = new cdk.Stack(); From c4dc3bce02790903593d80b070fca81fe7b7f08c Mon Sep 17 00:00:00 2001 From: Apoorv Munshi Date: Tue, 9 Mar 2021 10:58:52 -0800 Subject: [PATCH 10/17] feat(ecr): add imageTagMutability prop (#10557) This property allows setting tag mutability on ECR repositoes. Tag mutability is useful to ensure image integrity and can prevent supply chain attacks. Closes #4640 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ecr/README.md | 8 +++++++ packages/@aws-cdk/aws-ecr/lib/repository.ts | 24 +++++++++++++++++++ packages/@aws-cdk/aws-ecr/package.json | 1 + .../@aws-cdk/aws-ecr/test/test.repository.ts | 14 +++++++++++ 4 files changed, 47 insertions(+) diff --git a/packages/@aws-cdk/aws-ecr/README.md b/packages/@aws-cdk/aws-ecr/README.md index 6adb791ba7f76..03172cac6ff82 100644 --- a/packages/@aws-cdk/aws-ecr/README.md +++ b/packages/@aws-cdk/aws-ecr/README.md @@ -74,6 +74,14 @@ ecr.PublicGalleryAuthorizationToken.grantRead(user); This user can then proceed to login to the registry using one of the [authentication methods](https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html#public-registry-auth). +### Image tag immutability + +You can set tag immutability on images in our repository using the `imageTagMutability` construct prop. + +```ts +new ecr.Repository(stack, 'Repo', { imageTagMutability: ecr.TagMutability.IMMUTABLE }); +``` + ## 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 20f110c206428..3734db8176d36 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -354,6 +354,13 @@ export interface RepositoryProps { * @default false */ readonly imageScanOnPush?: boolean; + + /** + * The tag mutability setting for the repository. If this parameter is omitted, the default setting of MUTABLE will be used which will allow image tags to be overwritten. + * + * @default TagMutability.MUTABLE + */ + readonly imageTagMutability?: TagMutability; } export interface RepositoryAttributes { @@ -452,6 +459,7 @@ export class Repository extends RepositoryBase { imageScanningConfiguration: !props.imageScanOnPush ? undefined : { ScanOnPush: true, }, + imageTagMutability: props.imageTagMutability || undefined, }); resource.applyRemovalPolicy(props.removalPolicy); @@ -610,3 +618,19 @@ const enum CountType { */ SINCE_IMAGE_PUSHED = 'sinceImagePushed', } + +/** + * The tag mutability setting for your repository. + */ +export enum TagMutability { + /** + * allow image tags to be overwritten. + */ + MUTABLE = 'MUTABLE', + + /** + * all image tags within the repository will be immutable which will prevent them from being overwritten. + */ + IMMUTABLE = 'IMMUTABLE', + +} diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index 95a78ae04c522..bc3671a7c5585 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -103,6 +103,7 @@ "import:@aws-cdk/aws-ecr.Repository", "construct-base-is-private:@aws-cdk/aws-ecr.RepositoryBase", "docs-public-apis:@aws-cdk/aws-ecr.Repository.fromRepositoryArn", + "docs-public-apis:@aws-cdk/aws-ecr.Repository.imageTagMutability", "docs-public-apis:@aws-cdk/aws-ecr.Repository.fromRepositoryName", "props-default-doc:@aws-cdk/aws-ecr.LifecycleRule.maxImageAge", "props-default-doc:@aws-cdk/aws-ecr.LifecycleRule.maxImageCount", diff --git a/packages/@aws-cdk/aws-ecr/test/test.repository.ts b/packages/@aws-cdk/aws-ecr/test/test.repository.ts index 8c8094be287e9..fe020462f7716 100644 --- a/packages/@aws-cdk/aws-ecr/test/test.repository.ts +++ b/packages/@aws-cdk/aws-ecr/test/test.repository.ts @@ -63,6 +63,20 @@ export = { test.done(); }, + + 'image tag mutability can be set'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + new ecr.Repository(stack, 'Repo', { imageTagMutability: ecr.TagMutability.IMMUTABLE }); + + // THEN + expect(stack).to(haveResource('AWS::ECR::Repository', { + ImageTagMutability: 'IMMUTABLE', + })); + + test.done(); + }, + 'add day-based lifecycle policy'(test: Test) { // GIVEN const stack = new cdk.Stack(); From a7d314c73b9473208d94bac29ad9bd8018e00204 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Tue, 9 Mar 2021 12:32:21 -0700 Subject: [PATCH 11/17] feat(ecs): allow users to provide a CloudMap service to associate with an ECS service (#13192) This PR introduces `BaseService.associateCloudMapService()` which allows the user to associate the ECS service with a CloudMap service that they provide. **API sample** ```ts const cloudMapService = new cloudmap.Service(...); const ecsService = new ecs.FargateService(...); ecsService.associateCloudMapService({ service: cloudMapService, }); ``` Closes #10057 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ecs/README.md | 14 +++ .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 47 +++++++++ .../test/fargate/fargate-service.test.ts | 95 +++++++++++++++++++ 3 files changed, 156 insertions(+) diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 8ac684943e244..cc4bd432e7dba 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -728,6 +728,20 @@ new ecs.Ec2Service(stack, 'Service', { }); ``` +### Associate With a Specific CloudMap Service + +You may associate an ECS service with a specific CloudMap service. To do +this, use the service's `associateCloudMapService` method: + +```ts +const cloudMapService = new cloudmap.Service(...); +const ecsService = new ecs.FargateService(...); + +ecsService.associateCloudMapService({ + service: cloudMapService, +}); +``` + ## Capacity Providers Currently, only `FARGATE` and `FARGATE_SPOT` capacity providers are supported. 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 1e1fdde585f1e..3ff3f20fd8acd 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -601,6 +601,27 @@ export abstract class BaseService extends Resource return cloudmapService; } + /** + * Associates this service with a CloudMap service + */ + public associateCloudMapService(options: AssociateCloudMapServiceOptions): void { + const service = options.service; + + const { containerName, containerPort } = determineContainerNameAndPort({ + taskDefinition: this.taskDefinition, + dnsRecordType: service.dnsRecordType, + container: options.container, + containerPort: options.containerPort, + }); + + // add Cloudmap service to the ECS Service's serviceRegistry + this.addServiceRegistry({ + arn: service.serviceArn, + containerName, + containerPort, + }); + } + /** * This method returns the specified CloudWatch metric name for this service. */ @@ -748,6 +769,10 @@ export abstract class BaseService extends Resource * Associate Service Discovery (Cloud Map) service */ private addServiceRegistry(registry: ServiceRegistry) { + if (this.serviceRegistries.length >= 1) { + throw new Error('Cannot associate with the given service discovery registry. ECS supports at most one service registry per service.'); + } + const sr = this.renderServiceRegistry(registry); this.serviceRegistries.push(sr); } @@ -816,6 +841,28 @@ export interface CloudMapOptions { readonly containerPort?: number; } +/** + * The options for using a cloudmap service. + */ +export interface AssociateCloudMapServiceOptions { + /** + * The cloudmap service to register with. + */ + readonly service: cloudmap.IService; + + /** + * The container to point to for a SRV record. + * @default - the task definition's default container + */ + readonly container?: ContainerDefinition; + + /** + * The port to point to for a SRV record. + * @default - the default port of the task definition's default container + */ + readonly containerPort?: number; +} + /** * Service Registry for ECS service */ diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts index c982920225214..5531b53413f46 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts @@ -262,6 +262,101 @@ nodeunitShim({ test.done(); }, + 'with user-provided cloudmap service'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + container.addPortMappings({ containerPort: 8000 }); + + const cloudMapNamespace = new cloudmap.PrivateDnsNamespace(stack, 'TestCloudMapNamespace', { + name: 'scorekeep.com', + vpc, + }); + + const cloudMapService = new cloudmap.Service(stack, 'Service', { + name: 'service-name', + namespace: cloudMapNamespace, + dnsRecordType: cloudmap.DnsRecordType.SRV, + }); + + const ecsService = new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + }); + + // WHEN + ecsService.associateCloudMapService({ + service: cloudMapService, + container: container, + containerPort: 8000, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Service', { + ServiceRegistries: [ + { + ContainerName: 'web', + ContainerPort: 8000, + RegistryArn: { 'Fn::GetAtt': ['ServiceDBC79909', 'Arn'] }, + }, + ], + })); + + test.done(); + }, + + 'errors when more than one service registry used'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + container.addPortMappings({ containerPort: 8000 }); + + const cloudMapNamespace = new cloudmap.PrivateDnsNamespace(stack, 'TestCloudMapNamespace', { + name: 'scorekeep.com', + vpc, + }); + + const ecsService = new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + }); + + ecsService.enableCloudMap({ + cloudMapNamespace, + }); + + const cloudMapService = new cloudmap.Service(stack, 'Service', { + name: 'service-name', + namespace: cloudMapNamespace, + dnsRecordType: cloudmap.DnsRecordType.SRV, + }); + + // WHEN / THEN + test.throws(() => { + ecsService.associateCloudMapService({ + service: cloudMapService, + container: container, + containerPort: 8000, + }); + }, /at most one service registry/i); + + test.done(); + }, + 'with all properties set'(test: Test) { // GIVEN const stack = new cdk.Stack(); From 40295d2178333cd6f95df661f5f1ce4ea038612d Mon Sep 17 00:00:00 2001 From: Sainath Mallidi Date: Tue, 9 Mar 2021 12:06:04 -0800 Subject: [PATCH 12/17] chore(neptune): update readme to work with default fixture (#13477) fixes #13476 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-neptune/README.md | 6 +++--- packages/@aws-cdk/aws-neptune/rosetta/default.ts-fixture | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-neptune/README.md b/packages/@aws-cdk/aws-neptune/README.md index 1be41b88a1022..57ecabab058be 100644 --- a/packages/@aws-cdk/aws-neptune/README.md +++ b/packages/@aws-cdk/aws-neptune/README.md @@ -67,13 +67,13 @@ versions and limitations. The following example shows enabling IAM authentication for a database cluster and granting connection access to an IAM role. ```ts -const cluster = new rds.DatabaseCluster(stack, 'Cluster', { +const cluster = new neptune.DatabaseCluster(this, 'Cluster', { vpc, instanceType: neptune.InstanceType.R5_LARGE, iamAuthentication: true, // Optional - will be automatically set if you call grantConnect(). }); -const role = new Role(stack, 'DBRole', { assumedBy: new AccountPrincipal(stack.account) }); -instance.grantConnect(role); // Grant the role connection access to the DB. +const role = new iam.Role(this, 'DBRole', { assumedBy: new iam.AccountPrincipal(this.account) }); +cluster.grantConnect(role); // Grant the role connection access to the DB. ``` ## Customizing parameters diff --git a/packages/@aws-cdk/aws-neptune/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-neptune/rosetta/default.ts-fixture index 2e687290371fa..63e17311b5dc5 100644 --- a/packages/@aws-cdk/aws-neptune/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-neptune/rosetta/default.ts-fixture @@ -1,5 +1,6 @@ import { Duration, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import * as iam from '@aws-cdk/aws-iam'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as neptune from '@aws-cdk/aws-neptune'; From 9910f77b3644e350254f797a0a992c490ea41289 Mon Sep 17 00:00:00 2001 From: Nicolai Ferraris Date: Tue, 9 Mar 2021 15:39:10 -0500 Subject: [PATCH 13/17] docs(certificatemanager): Fix typo 'ExampelNet' -> 'ExampleNet' (#13319) Fixing a small typo in the aws-certificatemanager README. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-certificatemanager/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-certificatemanager/README.md b/packages/@aws-cdk/aws-certificatemanager/README.md index 320f22c91663d..24202429ac5df 100644 --- a/packages/@aws-cdk/aws-certificatemanager/README.md +++ b/packages/@aws-cdk/aws-certificatemanager/README.md @@ -68,7 +68,7 @@ When working with multiple domains, use the `CertificateValidation.fromDnsMultiZ const exampleCom = new route53.HostedZone(this, 'ExampleCom', { zoneName: 'example.com', }); -const exampleNet = new route53.HostedZone(this, 'ExampelNet', { +const exampleNet = new route53.HostedZone(this, 'ExampleNet', { zoneName: 'example.net', }); From 3f1c02dac7a50ce7caebce1e7f8953f6e4937e6b Mon Sep 17 00:00:00 2001 From: Jerry Kindall <52084730+Jerry-AWS@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:56:14 -0800 Subject: [PATCH 14/17] fix(init): Python init template's stack ID doesn't match other languages (#13480) In other languages, if you create a directory `hello-cdk`, do `cdk init`, and then `cdk ls`, you get `HelloCdkStack`. In Python you get `hello-cdk`. Change the Python init template to match the other languages. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk/lib/init-templates/v1/app/python/app.template.py | 4 ++-- .../aws-cdk/lib/init-templates/v2/app/python/app.template.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk/lib/init-templates/v1/app/python/app.template.py b/packages/aws-cdk/lib/init-templates/v1/app/python/app.template.py index bc43099fd2026..bf6da5f40868f 100644 --- a/packages/aws-cdk/lib/init-templates/v1/app/python/app.template.py +++ b/packages/aws-cdk/lib/init-templates/v1/app/python/app.template.py @@ -11,7 +11,7 @@ from %name.PythonModule%.%name.PythonModule%_stack import %name.PascalCased%Stack -app = cdk.App() -%name.PascalCased%Stack(app, "%name.StackName%") +app = core.App() +%name.PascalCased%Stack(app, "%name.PascalCased%Stack") app.synth() diff --git a/packages/aws-cdk/lib/init-templates/v2/app/python/app.template.py b/packages/aws-cdk/lib/init-templates/v2/app/python/app.template.py index a1dd331433422..5d1fa551c1c70 100644 --- a/packages/aws-cdk/lib/init-templates/v2/app/python/app.template.py +++ b/packages/aws-cdk/lib/init-templates/v2/app/python/app.template.py @@ -6,6 +6,6 @@ app = core.App() -%name.PascalCased%Stack(app, "%name.StackName%") +%name.PascalCased%Stack(app, "%name.PascalCased%Stack") app.synth() From e5dab7cbc67c234d191c38a8b8b84b634070b15b Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 9 Mar 2021 23:35:17 -0800 Subject: [PATCH 15/17] fix(cfn-include): allow boolean values for string-typed properties (#13508) CloudFormation is pretty lax when it comes to the types it accepts, and allows passing a boolean where a string is expected. Allow that possibility in cloudformation-include. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../test/test-templates/boolean-for-string.json | 10 ++++++++++ .../test/valid-templates.test.ts | 8 ++++++++ packages/@aws-cdk/core/lib/cfn-parse.ts | 6 ++++++ 3 files changed, 24 insertions(+) create mode 100644 packages/@aws-cdk/cloudformation-include/test/test-templates/boolean-for-string.json diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/boolean-for-string.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/boolean-for-string.json new file mode 100644 index 0000000000000..1ef8d59e40b89 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/boolean-for-string.json @@ -0,0 +1,10 @@ +{ + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": true + } + } + } +} diff --git a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts index 34b403a2e7a99..3d8b51098d293 100644 --- a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts @@ -116,6 +116,14 @@ describe('CDK Include', () => { ); }); + test('accepts booleans for properties with type string', () => { + includeTestTemplate(stack, 'boolean-for-string.json'); + + expect(stack).toMatchTemplate( + loadTestFileToJsObject('boolean-for-string.json'), + ); + }); + test('correctly changes the logical IDs, including references, if imported with preserveLogicalIds=false', () => { const cfnTemplate = includeTestTemplate(stack, 'bucket-with-encryption-key.json', { preserveLogicalIds: false, diff --git a/packages/@aws-cdk/core/lib/cfn-parse.ts b/packages/@aws-cdk/core/lib/cfn-parse.ts index 93376ae19d365..8f5d820584d98 100644 --- a/packages/@aws-cdk/core/lib/cfn-parse.ts +++ b/packages/@aws-cdk/core/lib/cfn-parse.ts @@ -132,6 +132,12 @@ export class FromCloudFormation { return new FromCloudFormationResult(value.toString()); } + // CloudFormation treats booleans and strings interchangeably; + // so, if we get a boolean here, convert it to a string + if (typeof value === 'boolean') { + return new FromCloudFormationResult(value.toString()); + } + // in all other cases, just return the input, // and let a validator handle it if it's not a string return new FromCloudFormationResult(value); From 7711981ea30bfdffd21dd840d676be4a2b45c9ba Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 10 Mar 2021 10:38:11 +0100 Subject: [PATCH 16/17] feat(cfnspec): cloudformation spec v30.1.0 (#13519) Co-authored-by: AWS CDK Team --- packages/@aws-cdk/aws-s3outposts/.eslintrc.js | 3 + packages/@aws-cdk/aws-s3outposts/.gitignore | 19 + packages/@aws-cdk/aws-s3outposts/.npmignore | 28 + packages/@aws-cdk/aws-s3outposts/LICENSE | 201 +++ packages/@aws-cdk/aws-s3outposts/NOTICE | 2 + packages/@aws-cdk/aws-s3outposts/README.md | 20 + .../@aws-cdk/aws-s3outposts/jest.config.js | 2 + packages/@aws-cdk/aws-s3outposts/lib/index.ts | 2 + packages/@aws-cdk/aws-s3outposts/package.json | 100 ++ .../aws-s3outposts/test/s3outposts.test.ts | 6 + packages/@aws-cdk/cfnspec/CHANGELOG.md | 59 + packages/@aws-cdk/cfnspec/cfn.version | 2 +- ...0_CloudFormationResourceSpecification.json | 1112 ++++++++++++++++- .../cloudformation-include/package.json | 2 + packages/aws-cdk-lib/package.json | 1 + packages/decdk/package.json | 3 +- packages/monocdk/package.json | 1 + 17 files changed, 1547 insertions(+), 16 deletions(-) create mode 100644 packages/@aws-cdk/aws-s3outposts/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-s3outposts/.gitignore create mode 100644 packages/@aws-cdk/aws-s3outposts/.npmignore create mode 100644 packages/@aws-cdk/aws-s3outposts/LICENSE create mode 100644 packages/@aws-cdk/aws-s3outposts/NOTICE create mode 100644 packages/@aws-cdk/aws-s3outposts/README.md create mode 100644 packages/@aws-cdk/aws-s3outposts/jest.config.js create mode 100644 packages/@aws-cdk/aws-s3outposts/lib/index.ts create mode 100644 packages/@aws-cdk/aws-s3outposts/package.json create mode 100644 packages/@aws-cdk/aws-s3outposts/test/s3outposts.test.ts diff --git a/packages/@aws-cdk/aws-s3outposts/.eslintrc.js b/packages/@aws-cdk/aws-s3outposts/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-s3outposts/.gitignore b/packages/@aws-cdk/aws-s3outposts/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/.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-s3outposts/.npmignore b/packages/@aws-cdk/aws-s3outposts/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-s3outposts/LICENSE b/packages/@aws-cdk/aws-s3outposts/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-s3outposts/NOTICE b/packages/@aws-cdk/aws-s3outposts/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-s3outposts/README.md b/packages/@aws-cdk/aws-s3outposts/README.md new file mode 100644 index 0000000000000..08fc4b75a732a --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/README.md @@ -0,0 +1,20 @@ +# AWS::S3Outposts Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import s3outposts = require('@aws-cdk/aws-s3outposts'); +``` diff --git a/packages/@aws-cdk/aws-s3outposts/jest.config.js b/packages/@aws-cdk/aws-s3outposts/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-s3outposts/lib/index.ts b/packages/@aws-cdk/aws-s3outposts/lib/index.ts new file mode 100644 index 0000000000000..06c96e7c920bc --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::S3Outposts CloudFormation Resources: +export * from './s3outposts.generated'; diff --git a/packages/@aws-cdk/aws-s3outposts/package.json b/packages/@aws-cdk/aws-s3outposts/package.json new file mode 100644 index 0000000000000..7965518ea1d24 --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/package.json @@ -0,0 +1,100 @@ +{ + "name": "@aws-cdk/aws-s3outposts", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::S3Outposts", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.S3Outposts", + "packageId": "Amazon.CDK.AWS.S3Outposts", + "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.s3outposts", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "s3outposts" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-s3outposts", + "module": "aws_cdk.aws_s3outposts" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-s3outposts" + }, + "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" + }, + "cdk-build": { + "cloudformation": "AWS::S3Outposts", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::S3Outposts", + "aws-s3outposts" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-s3outposts/test/s3outposts.test.ts b/packages/@aws-cdk/aws-s3outposts/test/s3outposts.test.ts new file mode 100644 index 0000000000000..e394ef336bfb4 --- /dev/null +++ b/packages/@aws-cdk/aws-s3outposts/test/s3outposts.test.ts @@ -0,0 +1,6 @@ +import '@aws-cdk/assert/jest'; +import {} from '../lib'; + +test('No tests are specified for this package', () => { + expect(true).toBe(true); +}); diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 9485ec700294e..1affea5b1d444 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,62 @@ +# CloudFormation Resource Specification v30.1.0 + +## New Resource Types + +* AWS::Events::ApiDestination +* AWS::Events::Connection +* AWS::IoT::AccountAuditConfiguration +* AWS::IoT::CustomMetric +* AWS::IoT::Dimension +* AWS::IoT::MitigationAction +* AWS::IoT::ScheduledAudit +* AWS::IoT::SecurityProfile +* AWS::S3Outposts::AccessPoint +* AWS::S3Outposts::Bucket +* AWS::S3Outposts::BucketPolicy +* AWS::S3Outposts::Endpoint + +## Attribute Changes + +* AWS::Athena::WorkGroup EffectiveEngineVersion (__added__) + +## Property Changes + +* AWS::Backup::BackupVault BackupVaultTags.PrimitiveType (__deleted__) +* AWS::Backup::BackupVault BackupVaultTags.PrimitiveItemType (__added__) +* AWS::Backup::BackupVault BackupVaultTags.Type (__added__) +* AWS::Cloud9::EnvironmentEC2 ImageId (__added__) +* AWS::CloudFormation::ModuleVersion ModuleName.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::CloudFormation::ModuleVersion ModulePackage.Required (__changed__) + * Old: false + * New: true +* AWS::DataBrew::Dataset Format (__added__) +* AWS::Detective::MemberInvitation DisableEmailNotification (__added__) +* AWS::IVS::Channel RecordingConfigurationArn (__deleted__) +* AWS::SecretsManager::Secret ReplicaRegions (__added__) +* AWS::ServiceDiscovery::HttpNamespace Tags.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::ServiceDiscovery::PrivateDnsNamespace Tags.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::ServiceDiscovery::PublicDnsNamespace Tags.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::ServiceDiscovery::Service Tags.UpdateType (__changed__) + * Old: Immutable + * New: Mutable + +## Property Type Changes + +* AWS::Athena::WorkGroup.EngineVersion (__added__) +* AWS::SecretsManager::Secret.ReplicaRegion (__added__) +* AWS::Athena::WorkGroup.WorkGroupConfiguration EngineVersion (__added__) +* AWS::Athena::WorkGroup.WorkGroupConfigurationUpdates EngineVersion (__added__) +* AWS::Backup::BackupVault.NotificationObjectType BackupVaultEvents.DuplicatesAllowed (__added__) + + # CloudFormation Resource Specification v30.0.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 8dd5c17a1b5cd..a75ef34cbaa5c 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -30.0.0 +30.1.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 75da81b0c270b..05d46dca703b8 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -7164,6 +7164,23 @@ } } }, + "AWS::Athena::WorkGroup.EngineVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-engineversion.html", + "Properties": { + "EffectiveEngineVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-engineversion.html#cfn-athena-workgroup-engineversion-effectiveengineversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SelectedEngineVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-engineversion.html#cfn-athena-workgroup-engineversion-selectedengineversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::Athena::WorkGroup.ResultConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-resultconfiguration.html", "Properties": { @@ -7225,6 +7242,12 @@ "Required": false, "UpdateType": "Mutable" }, + "EngineVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-workgroupconfiguration.html#cfn-athena-workgroup-workgroupconfiguration-engineversion", + "Required": false, + "Type": "EngineVersion", + "UpdateType": "Mutable" + }, "PublishCloudWatchMetricsEnabled": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-workgroupconfiguration.html#cfn-athena-workgroup-workgroupconfiguration-publishcloudwatchmetricsenabled", "PrimitiveType": "Boolean", @@ -7260,6 +7283,12 @@ "Required": false, "UpdateType": "Mutable" }, + "EngineVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-workgroupconfigurationupdates.html#cfn-athena-workgroup-workgroupconfigurationupdates-engineversion", + "Required": false, + "Type": "EngineVersion", + "UpdateType": "Mutable" + }, "PublishCloudWatchMetricsEnabled": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-athena-workgroup-workgroupconfigurationupdates.html#cfn-athena-workgroup-workgroupconfigurationupdates-publishcloudwatchmetricsenabled", "PrimitiveType": "Boolean", @@ -8376,6 +8405,7 @@ "Properties": { "BackupVaultEvents": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-backup-backupvault-notificationobjecttype.html#cfn-backup-backupvault-notificationobjecttype-backupvaultevents", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": true, "Type": "List", @@ -27787,6 +27817,140 @@ } } }, + "AWS::IoT::AccountAuditConfiguration.AuditCheckConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfiguration.html", + "Properties": { + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfiguration.html#cfn-iot-accountauditconfiguration-auditcheckconfiguration-enabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::AccountAuditConfiguration.AuditCheckConfigurations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html", + "Properties": { + "AuthenticatedCognitoRoleOverlyPermissiveCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-authenticatedcognitoroleoverlypermissivecheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "CaCertificateExpiringCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-cacertificateexpiringcheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "CaCertificateKeyQualityCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-cacertificatekeyqualitycheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "ConflictingClientIdsCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-conflictingclientidscheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "DeviceCertificateExpiringCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-devicecertificateexpiringcheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "DeviceCertificateKeyQualityCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-devicecertificatekeyqualitycheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "DeviceCertificateSharedCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-devicecertificatesharedcheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "IotPolicyOverlyPermissiveCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-iotpolicyoverlypermissivecheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "IotRoleAliasAllowsAccessToUnusedServicesCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-iotrolealiasallowsaccesstounusedservicescheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "IotRoleAliasOverlyPermissiveCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-iotrolealiasoverlypermissivecheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "LoggingDisabledCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-loggingdisabledcheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "RevokedCaCertificateStillActiveCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-revokedcacertificatestillactivecheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "RevokedDeviceCertificateStillActiveCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-revokeddevicecertificatestillactivecheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + }, + "UnauthenticatedCognitoRoleOverlyPermissiveCheck": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditcheckconfigurations.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations-unauthenticatedcognitoroleoverlypermissivecheck", + "Required": false, + "Type": "AuditCheckConfiguration", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::AccountAuditConfiguration.AuditNotificationTarget": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditnotificationtarget.html", + "Properties": { + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditnotificationtarget.html#cfn-iot-accountauditconfiguration-auditnotificationtarget-enabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditnotificationtarget.html#cfn-iot-accountauditconfiguration-auditnotificationtarget-rolearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "TargetArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditnotificationtarget.html#cfn-iot-accountauditconfiguration-auditnotificationtarget-targetarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::AccountAuditConfiguration.AuditNotificationTargetConfigurations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditnotificationtargetconfigurations.html", + "Properties": { + "Sns": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-accountauditconfiguration-auditnotificationtargetconfigurations.html#cfn-iot-accountauditconfiguration-auditnotificationtargetconfigurations-sns", + "Required": false, + "Type": "AuditNotificationTarget", + "UpdateType": "Mutable" + } + } + }, "AWS::IoT::DomainConfiguration.AuthorizerConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-domainconfiguration-authorizerconfig.html", "Properties": { @@ -27827,6 +27991,127 @@ } } }, + "AWS::IoT::MitigationAction.ActionParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html", + "Properties": { + "AddThingsToThingGroupParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html#cfn-iot-mitigationaction-actionparams-addthingstothinggroupparams", + "Required": false, + "Type": "AddThingsToThingGroupParams", + "UpdateType": "Mutable" + }, + "EnableIoTLoggingParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html#cfn-iot-mitigationaction-actionparams-enableiotloggingparams", + "Required": false, + "Type": "EnableIoTLoggingParams", + "UpdateType": "Mutable" + }, + "PublishFindingToSnsParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html#cfn-iot-mitigationaction-actionparams-publishfindingtosnsparams", + "Required": false, + "Type": "PublishFindingToSnsParams", + "UpdateType": "Mutable" + }, + "ReplaceDefaultPolicyVersionParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html#cfn-iot-mitigationaction-actionparams-replacedefaultpolicyversionparams", + "Required": false, + "Type": "ReplaceDefaultPolicyVersionParams", + "UpdateType": "Mutable" + }, + "UpdateCACertificateParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html#cfn-iot-mitigationaction-actionparams-updatecacertificateparams", + "Required": false, + "Type": "UpdateCACertificateParams", + "UpdateType": "Mutable" + }, + "UpdateDeviceCertificateParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-actionparams.html#cfn-iot-mitigationaction-actionparams-updatedevicecertificateparams", + "Required": false, + "Type": "UpdateDeviceCertificateParams", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::MitigationAction.AddThingsToThingGroupParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-addthingstothinggroupparams.html", + "Properties": { + "OverrideDynamicGroups": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-addthingstothinggroupparams.html#cfn-iot-mitigationaction-addthingstothinggroupparams-overridedynamicgroups", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "ThingGroupNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-addthingstothinggroupparams.html#cfn-iot-mitigationaction-addthingstothinggroupparams-thinggroupnames", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::MitigationAction.EnableIoTLoggingParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-enableiotloggingparams.html", + "Properties": { + "LogLevel": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-enableiotloggingparams.html#cfn-iot-mitigationaction-enableiotloggingparams-loglevel", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "RoleArnForLogging": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-enableiotloggingparams.html#cfn-iot-mitigationaction-enableiotloggingparams-rolearnforlogging", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::MitigationAction.PublishFindingToSnsParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-publishfindingtosnsparams.html", + "Properties": { + "TopicArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-publishfindingtosnsparams.html#cfn-iot-mitigationaction-publishfindingtosnsparams-topicarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::MitigationAction.ReplaceDefaultPolicyVersionParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-replacedefaultpolicyversionparams.html", + "Properties": { + "TemplateName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-replacedefaultpolicyversionparams.html#cfn-iot-mitigationaction-replacedefaultpolicyversionparams-templatename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::MitigationAction.UpdateCACertificateParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-updatecacertificateparams.html", + "Properties": { + "Action": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-updatecacertificateparams.html#cfn-iot-mitigationaction-updatecacertificateparams-action", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::MitigationAction.UpdateDeviceCertificateParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-updatedevicecertificateparams.html", + "Properties": { + "Action": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-mitigationaction-updatedevicecertificateparams.html#cfn-iot-mitigationaction-updatedevicecertificateparams-action", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::IoT::ProvisioningTemplate.ProvisioningHook": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-provisioningtemplate-provisioninghook.html", "Properties": { @@ -27844,6 +28129,210 @@ } } }, + "AWS::IoT::SecurityProfile.AlertTarget": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-alerttarget.html", + "Properties": { + "AlertTargetArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-alerttarget.html#cfn-iot-securityprofile-alerttarget-alerttargetarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-alerttarget.html#cfn-iot-securityprofile-alerttarget-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.Behavior": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behavior.html", + "Properties": { + "Criteria": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behavior.html#cfn-iot-securityprofile-behavior-criteria", + "Required": false, + "Type": "BehaviorCriteria", + "UpdateType": "Mutable" + }, + "Metric": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behavior.html#cfn-iot-securityprofile-behavior-metric", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "MetricDimension": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behavior.html#cfn-iot-securityprofile-behavior-metricdimension", + "Required": false, + "Type": "MetricDimension", + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behavior.html#cfn-iot-securityprofile-behavior-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "SuppressAlerts": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behavior.html#cfn-iot-securityprofile-behavior-suppressalerts", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.BehaviorCriteria": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html", + "Properties": { + "ComparisonOperator": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-comparisonoperator", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ConsecutiveDatapointsToAlarm": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-consecutivedatapointstoalarm", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ConsecutiveDatapointsToClear": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-consecutivedatapointstoclear", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "DurationSeconds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-durationseconds", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "MlDetectionConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-mldetectionconfig", + "Required": false, + "Type": "MachineLearningDetectionConfig", + "UpdateType": "Mutable" + }, + "StatisticalThreshold": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-statisticalthreshold", + "Required": false, + "Type": "StatisticalThreshold", + "UpdateType": "Mutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-behaviorcriteria.html#cfn-iot-securityprofile-behaviorcriteria-value", + "Required": false, + "Type": "MetricValue", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.MachineLearningDetectionConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-machinelearningdetectionconfig.html", + "Properties": { + "ConfidenceLevel": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-machinelearningdetectionconfig.html#cfn-iot-securityprofile-machinelearningdetectionconfig-confidencelevel", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.MetricDimension": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricdimension.html", + "Properties": { + "DimensionName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricdimension.html#cfn-iot-securityprofile-metricdimension-dimensionname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Operator": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricdimension.html#cfn-iot-securityprofile-metricdimension-operator", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.MetricToRetain": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metrictoretain.html", + "Properties": { + "Metric": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metrictoretain.html#cfn-iot-securityprofile-metrictoretain-metric", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "MetricDimension": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metrictoretain.html#cfn-iot-securityprofile-metrictoretain-metricdimension", + "Required": false, + "Type": "MetricDimension", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.MetricValue": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html", + "Properties": { + "Cidrs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html#cfn-iot-securityprofile-metricvalue-cidrs", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Count": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html#cfn-iot-securityprofile-metricvalue-count", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Number": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html#cfn-iot-securityprofile-metricvalue-number", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "Numbers": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html#cfn-iot-securityprofile-metricvalue-numbers", + "DuplicatesAllowed": false, + "PrimitiveItemType": "Double", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Ports": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html#cfn-iot-securityprofile-metricvalue-ports", + "DuplicatesAllowed": false, + "PrimitiveItemType": "Integer", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Strings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-metricvalue.html#cfn-iot-securityprofile-metricvalue-strings", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile.StatisticalThreshold": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-statisticalthreshold.html", + "Properties": { + "Statistic": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-securityprofile-statisticalthreshold.html#cfn-iot-securityprofile-statisticalthreshold-statistic", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::IoT::Thing.AttributePayload": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iot-thing-attributepayload.html", "Properties": { @@ -46954,6 +47443,93 @@ } } }, + "AWS::S3Outposts::AccessPoint.VpcConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-accesspoint-vpcconfiguration.html", + "Properties": { + "VpcId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-accesspoint-vpcconfiguration.html#cfn-s3outposts-accesspoint-vpcconfiguration-vpcid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::S3Outposts::Bucket.AbortIncompleteMultipartUpload": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-abortincompletemultipartupload.html", + "Properties": { + "DaysAfterInitiation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-abortincompletemultipartupload.html#cfn-s3outposts-bucket-abortincompletemultipartupload-daysafterinitiation", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::S3Outposts::Bucket.LifecycleConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-lifecycleconfiguration.html", + "Properties": { + "Rules": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-lifecycleconfiguration.html#cfn-s3outposts-bucket-lifecycleconfiguration-rules", + "DuplicatesAllowed": false, + "ItemType": "Rule", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::S3Outposts::Bucket.Rule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html", + "Properties": { + "AbortIncompleteMultipartUpload": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html#cfn-s3outposts-bucket-rule-abortincompletemultipartupload", + "Required": false, + "Type": "AbortIncompleteMultipartUpload", + "UpdateType": "Mutable" + }, + "ExpirationDate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html#cfn-s3outposts-bucket-rule-expirationdate", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ExpirationInDays": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html#cfn-s3outposts-bucket-rule-expirationindays", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Filter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html#cfn-s3outposts-bucket-rule-filter", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, + "Id": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html#cfn-s3outposts-bucket-rule-id", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Status": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-bucket-rule.html#cfn-s3outposts-bucket-rule-status", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::S3Outposts::Endpoint.NetworkInterface": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-endpoint-networkinterface.html", + "Properties": { + "NetworkInterfaceId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3outposts-endpoint-networkinterface.html#cfn-s3outposts-endpoint-networkinterface-networkinterfaceid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::SES::ConfigurationSetEventDestination.CloudWatchDestination": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ses-configurationseteventdestination-cloudwatchdestination.html", "Properties": { @@ -50395,6 +50971,23 @@ } } }, + "AWS::SecretsManager::Secret.ReplicaRegion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-replicaregion.html", + "Properties": { + "KmsKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-replicaregion.html#cfn-secretsmanager-secret-replicaregion-kmskeyid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Region": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-secretsmanager-secret-replicaregion.html#cfn-secretsmanager-secret-replicaregion-region", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::ServiceCatalog::CloudFormationProduct.ProvisioningArtifactProperties": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-servicecatalog-cloudformationproduct-provisioningartifactproperties.html", "Properties": { @@ -53056,7 +53649,7 @@ } } }, - "ResourceSpecificationVersion": "30.0.0", + "ResourceSpecificationVersion": "30.1.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -57096,6 +57689,9 @@ "Attributes": { "CreationTime": { "PrimitiveType": "String" + }, + "EffectiveEngineVersion": { + "PrimitiveType": "String" } }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-workgroup.html", @@ -57810,8 +58406,9 @@ }, "BackupVaultTags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-backup-backupvault.html#cfn-backup-backupvault-backupvaulttags", - "PrimitiveType": "Json", + "PrimitiveItemType": "String", "Required": false, + "Type": "Map", "UpdateType": "Mutable" }, "EncryptionKeyArn": { @@ -58229,6 +58826,12 @@ "Required": false, "UpdateType": "Mutable" }, + "ImageId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-imageid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "InstanceType": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloud9-environmentec2.html#cfn-cloud9-environmentec2-instancetype", "PrimitiveType": "String", @@ -58372,12 +58975,12 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-moduleversion.html#cfn-cloudformation-moduleversion-modulename", "PrimitiveType": "String", "Required": true, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "ModulePackage": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-moduleversion.html#cfn-cloudformation-moduleversion-modulepackage", "PrimitiveType": "String", - "Required": false, + "Required": true, "UpdateType": "Immutable" } } @@ -61879,6 +62482,12 @@ "AWS::DataBrew::Dataset": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-dataset.html", "Properties": { + "Format": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-dataset.html#cfn-databrew-dataset-format", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "FormatOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-dataset.html#cfn-databrew-dataset-formatoptions", "PrimitiveType": "Json", @@ -62638,6 +63247,12 @@ "AWS::Detective::MemberInvitation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-detective-memberinvitation.html", "Properties": { + "DisableEmailNotification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-detective-memberinvitation.html#cfn-detective-memberinvitation-disableemailnotification", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "GraphArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-detective-memberinvitation.html#cfn-detective-memberinvitation-grapharn", "PrimitiveType": "String", @@ -69094,6 +69709,52 @@ } } }, + "AWS::Events::ApiDestination": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html", + "Properties": { + "ConnectionArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html#cfn-events-apidestination-connectionarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html#cfn-events-apidestination-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "HttpMethod": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html#cfn-events-apidestination-httpmethod", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "InvocationEndpoint": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html#cfn-events-apidestination-invocationendpoint", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "InvocationRateLimitPerSecond": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html#cfn-events-apidestination-invocationratelimitpersecond", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-apidestination.html#cfn-events-apidestination-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, "AWS::Events::Archive": { "Attributes": { "ArchiveName": { @@ -69137,6 +69798,43 @@ } } }, + "AWS::Events::Connection": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "SecretArn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-connection.html", + "Properties": { + "AuthParameters": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-connection.html#cfn-events-connection-authparameters", + "PrimitiveType": "Json", + "Required": true, + "UpdateType": "Mutable" + }, + "AuthorizationType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-connection.html#cfn-events-connection-authorizationtype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-connection.html#cfn-events-connection-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-connection.html#cfn-events-connection-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, "AWS::Events::EventBus": { "Attributes": { "Arn": { @@ -72094,12 +72792,6 @@ "Required": false, "UpdateType": "Mutable" }, - "RecordingConfigurationArn": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ivs-channel.html#cfn-ivs-channel-recordingconfigurationarn", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ivs-channel.html#cfn-ivs-channel-tags", "DuplicatesAllowed": false, @@ -72847,6 +73539,35 @@ } } }, + "AWS::IoT::AccountAuditConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-accountauditconfiguration.html", + "Properties": { + "AccountId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-accountauditconfiguration.html#cfn-iot-accountauditconfiguration-accountid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "AuditCheckConfigurations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-accountauditconfiguration.html#cfn-iot-accountauditconfiguration-auditcheckconfigurations", + "Required": true, + "Type": "AuditCheckConfigurations", + "UpdateType": "Mutable" + }, + "AuditNotificationTargetConfigurations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-accountauditconfiguration.html#cfn-iot-accountauditconfiguration-auditnotificationtargetconfigurations", + "Required": false, + "Type": "AuditNotificationTargetConfigurations", + "UpdateType": "Mutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-accountauditconfiguration.html#cfn-iot-accountauditconfiguration-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::IoT::Authorizer": { "Attributes": { "Arn": { @@ -72944,6 +73665,80 @@ } } }, + "AWS::IoT::CustomMetric": { + "Attributes": { + "MetricArn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-custommetric.html", + "Properties": { + "DisplayName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-custommetric.html#cfn-iot-custommetric-displayname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "MetricName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-custommetric.html#cfn-iot-custommetric-metricname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "MetricType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-custommetric.html#cfn-iot-custommetric-metrictype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-custommetric.html#cfn-iot-custommetric-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::Dimension": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-dimension.html", + "Properties": { + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-dimension.html#cfn-iot-dimension-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "StringValues": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-dimension.html#cfn-iot-dimension-stringvalues", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-dimension.html#cfn-iot-dimension-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Type": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-dimension.html#cfn-iot-dimension-type", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::IoT::DomainConfiguration": { "Attributes": { "Arn": { @@ -73011,6 +73806,45 @@ } } }, + "AWS::IoT::MitigationAction": { + "Attributes": { + "MitigationActionArn": { + "PrimitiveType": "String" + }, + "MitigationActionId": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-mitigationaction.html", + "Properties": { + "ActionName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-mitigationaction.html#cfn-iot-mitigationaction-actionname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "ActionParams": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-mitigationaction.html#cfn-iot-mitigationaction-actionparams", + "Required": true, + "Type": "ActionParams", + "UpdateType": "Mutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-mitigationaction.html#cfn-iot-mitigationaction-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-mitigationaction.html#cfn-iot-mitigationaction-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::IoT::Policy": { "Attributes": { "Arn": { @@ -73103,6 +73937,117 @@ } } }, + "AWS::IoT::ScheduledAudit": { + "Attributes": { + "ScheduledAuditArn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html", + "Properties": { + "DayOfMonth": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html#cfn-iot-scheduledaudit-dayofmonth", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DayOfWeek": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html#cfn-iot-scheduledaudit-dayofweek", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Frequency": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html#cfn-iot-scheduledaudit-frequency", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ScheduledAuditName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html#cfn-iot-scheduledaudit-scheduledauditname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html#cfn-iot-scheduledaudit-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "TargetCheckNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-scheduledaudit.html#cfn-iot-scheduledaudit-targetchecknames", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::IoT::SecurityProfile": { + "Attributes": { + "SecurityProfileArn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html", + "Properties": { + "AdditionalMetricsToRetainV2": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-additionalmetricstoretainv2", + "DuplicatesAllowed": false, + "ItemType": "MetricToRetain", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "AlertTargets": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-alerttargets", + "ItemType": "AlertTarget", + "Required": false, + "Type": "Map", + "UpdateType": "Mutable" + }, + "Behaviors": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-behaviors", + "DuplicatesAllowed": false, + "ItemType": "Behavior", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "SecurityProfileDescription": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-securityprofiledescription", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SecurityProfileName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-securityprofilename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "TargetArns": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-securityprofile.html#cfn-iot-securityprofile-targetarns", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::IoT::Thing": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iot-thing.html", "Properties": { @@ -81983,6 +82928,138 @@ } } }, + "AWS::S3Outposts::AccessPoint": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-accesspoint.html", + "Properties": { + "Bucket": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-accesspoint.html#cfn-s3outposts-accesspoint-bucket", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-accesspoint.html#cfn-s3outposts-accesspoint-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Policy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-accesspoint.html#cfn-s3outposts-accesspoint-policy", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, + "VpcConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-accesspoint.html#cfn-s3outposts-accesspoint-vpcconfiguration", + "Required": true, + "Type": "VpcConfiguration", + "UpdateType": "Immutable" + } + } + }, + "AWS::S3Outposts::Bucket": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucket.html", + "Properties": { + "BucketName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucket.html#cfn-s3outposts-bucket-bucketname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "LifecycleConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucket.html#cfn-s3outposts-bucket-lifecycleconfiguration", + "Required": false, + "Type": "LifecycleConfiguration", + "UpdateType": "Mutable" + }, + "OutpostId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucket.html#cfn-s3outposts-bucket-outpostid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucket.html#cfn-s3outposts-bucket-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::S3Outposts::BucketPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucketpolicy.html", + "Properties": { + "Bucket": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucketpolicy.html#cfn-s3outposts-bucketpolicy-bucket", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "PolicyDocument": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-bucketpolicy.html#cfn-s3outposts-bucketpolicy-policydocument", + "PrimitiveType": "Json", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::S3Outposts::Endpoint": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CidrBlock": { + "PrimitiveType": "String" + }, + "CreationTime": { + "PrimitiveType": "String" + }, + "Id": { + "PrimitiveType": "String" + }, + "NetworkInterfaces": { + "DuplicatesAllowed": false, + "ItemType": "NetworkInterface", + "Type": "List" + }, + "Status": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-endpoint.html", + "Properties": { + "OutpostId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-endpoint.html#cfn-s3outposts-endpoint-outpostid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "SecurityGroupId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-endpoint.html#cfn-s3outposts-endpoint-securitygroupid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "SubnetId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3outposts-endpoint.html#cfn-s3outposts-endpoint-subnetid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::SDB::Domain": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-simpledb.html", "Properties": { @@ -84282,6 +85359,13 @@ "Required": false, "UpdateType": "Immutable" }, + "ReplicaRegions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-secret.html#cfn-secretsmanager-secret-replicaregions", + "ItemType": "ReplicaRegion", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "SecretString": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-secret.html#cfn-secretsmanager-secret-secretstring", "PrimitiveType": "String", @@ -85124,7 +86208,7 @@ "ItemType": "Tag", "Required": false, "Type": "List", - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, @@ -85179,7 +86263,7 @@ "ItemType": "Tag", "Required": false, "Type": "List", - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "Vpc": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-servicediscovery-privatednsnamespace.html#cfn-servicediscovery-privatednsnamespace-vpc", @@ -85217,7 +86301,7 @@ "ItemType": "Tag", "Required": false, "Type": "List", - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, @@ -85276,7 +86360,7 @@ "ItemType": "Tag", "Required": false, "Type": "List", - "UpdateType": "Immutable" + "UpdateType": "Mutable" } } }, diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index f9b2e79888365..a177759248e07 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -186,6 +186,7 @@ "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/aws-route53resolver": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-s3outposts": "0.0.0", "@aws-cdk/aws-sagemaker": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/aws-sdb": "0.0.0", @@ -334,6 +335,7 @@ "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/aws-route53resolver": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-s3outposts": "0.0.0", "@aws-cdk/aws-sagemaker": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/aws-sdb": "0.0.0", diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 89dbc64979387..bd2ce09408acc 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -247,6 +247,7 @@ "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/aws-s3-deployment": "0.0.0", "@aws-cdk/aws-s3-notifications": "0.0.0", + "@aws-cdk/aws-s3outposts": "0.0.0", "@aws-cdk/aws-sagemaker": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/aws-sdb": "0.0.0", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 8b8b17d52f621..4b2ec6dc2fdc0 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -35,8 +35,8 @@ "@aws-cdk/aws-amplify": "0.0.0", "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", - "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", + "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0", "@aws-cdk/aws-applicationautoscaling": "0.0.0", @@ -173,6 +173,7 @@ "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/aws-s3-deployment": "0.0.0", "@aws-cdk/aws-s3-notifications": "0.0.0", + "@aws-cdk/aws-s3outposts": "0.0.0", "@aws-cdk/aws-sagemaker": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/aws-sdb": "0.0.0", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index c71d755b3c2bb..382f47c2be9d0 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -252,6 +252,7 @@ "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/aws-s3-deployment": "0.0.0", "@aws-cdk/aws-s3-notifications": "0.0.0", + "@aws-cdk/aws-s3outposts": "0.0.0", "@aws-cdk/aws-sagemaker": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/aws-sdb": "0.0.0", From 8dca5079e1893122057f9e2c54c0da0ba644926e Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Wed, 10 Mar 2021 11:51:11 +0000 Subject: [PATCH 17/17] chore(core): update CDK Metadata to report construct-level details (#13423) See CDK RFC 253 (aws/aws-cdk-rfcs#254) for background and details. Currently -- if a user has not opted out -- an AWS::CDK::Metadata resource is added to each generated stack template with details about each loaded module and version that matches an Amazon-specific allow list. This modules list is used to: - Track what library versions customers are using so they can be contacted in the event of a severe (security) issue with a library. - Get business metrics on the adoption of CDK and its libraries. This modules list is sometimes inaccurate (a module may be loaded into memory without actually being used) and too braod to support CDK v2. This feature (mostly) implements the specification proposed in RFC 253 to include metadata about what constructs are present in each stack, rather than modules loaded into memory. The allow-list is still used to ensure only CDK/AWS constructs are reported on. Implementation notes: - The format of the Analytics property has changed slightly since the RFC. See the service-side code for justification and latest spec. - How to handle the jsii runtime information was left un-spec'd. I've chosen to create a psuedo-Construct to add to the list as the simplest solution. - `runtime-info.test.ts` leaps through some serious hoops to work equally well for both v1 and v2, and to fail somewhat gracefully locally if `tsc` was used to compile the module instead of `jsii`. Critques of this approach welcome! - I removed an annoyance from `resolve-version-lib.js` that produced error messages when running unit tests. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../core/lib/private/metadata-resource.ts | 121 ++++++--- .../@aws-cdk/core/lib/private/runtime-info.ts | 143 +++++----- .../core/lib/private/tree-metadata.ts | 30 +-- packages/@aws-cdk/core/test/app.test.ts | 124 --------- .../core/test/metadata-resource.test.ts | 151 +++++++++++ .../@aws-cdk/core/test/runtime-info.test.ts | 252 +++++++++++++----- scripts/resolve-version-lib.js | 1 - 7 files changed, 480 insertions(+), 342 deletions(-) create mode 100644 packages/@aws-cdk/core/test/metadata-resource.test.ts diff --git a/packages/@aws-cdk/core/lib/private/metadata-resource.ts b/packages/@aws-cdk/core/lib/private/metadata-resource.ts index ff84b931f819b..1cccc4f24ff3d 100644 --- a/packages/@aws-cdk/core/lib/private/metadata-resource.ts +++ b/packages/@aws-cdk/core/lib/private/metadata-resource.ts @@ -1,4 +1,4 @@ -import * as cxapi from '@aws-cdk/cx-api'; +import * as zlib from 'zlib'; import { RegionInfo } from '@aws-cdk/region-info'; import { CfnCondition } from '../cfn-condition'; import { Fn } from '../cfn-fn'; @@ -8,41 +8,12 @@ import { Construct } from '../construct-compat'; import { Lazy } from '../lazy'; import { Stack } from '../stack'; import { Token } from '../token'; -import { collectRuntimeInformation } from './runtime-info'; +import { ConstructInfo, constructInfoFromStack } from './runtime-info'; /** * Construct that will render the metadata resource */ export class MetadataResource extends Construct { - /** - * Clear the modules cache - * - * The next time the MetadataResource is rendered, it will do a lookup of the - * modules from the NodeJS module cache again. - * - * Used only for unit tests. - */ - public static clearModulesCache() { - this._modulesPropertyCache = undefined; - } - - /** - * Cached version of the _modulesProperty() accessor - * - * No point in calculating this fairly expensive list more than once. - */ - private static _modulesPropertyCache?: string; - - /** - * Calculate the modules property - */ - private static modulesProperty(): string { - if (this._modulesPropertyCache === undefined) { - this._modulesPropertyCache = formatModules(collectRuntimeInformation()); - } - return this._modulesPropertyCache; - } - constructor(scope: Stack, id: string) { super(scope, id); @@ -51,7 +22,7 @@ export class MetadataResource extends Construct { const resource = new CfnResource(this, 'Default', { type: 'AWS::CDK::Metadata', properties: { - Modules: Lazy.string({ produce: () => MetadataResource.modulesProperty() }), + Analytics: Lazy.string({ produce: () => formatAnalytics(constructInfoFromStack(scope)) }), }, }); @@ -76,17 +47,81 @@ function makeCdkMetadataAvailableCondition() { .map(ri => Fn.conditionEquals(Aws.REGION, ri.name))); } -function formatModules(runtime: cxapi.RuntimeInfo): string { - const modules = new Array(); +/** Convenience type for arbitrarily-nested map */ +class Trie extends Map { } - // inject toolkit version to list of modules - const cliVersion = process.env[cxapi.CLI_VERSION_ENV]; - if (cliVersion) { - modules.push(`aws-cdk=${cliVersion}`); - } +/** + * Formats a list of construct fully-qualified names (FQNs) and versions into a (possibly compressed) prefix-encoded string. + * + * The list of ConstructInfos is logically formatted into: + * ${version}!${fqn} (e.g., "1.90.0!aws-cdk-lib.Stack") + * and then all of the construct-versions are grouped with common prefixes together, grouping common parts in '{}' and separating items with ','. + * + * Example: + * [1.90.0!aws-cdk-lib.Stack, 1.90.0!aws-cdk-lib.Construct, 1.90.0!aws-cdk-lib.service.Resource, 0.42.1!aws-cdk-lib-experiments.NewStuff] + * Becomes: + * 1.90.0!aws-cdk-lib.{Stack,Construct,service.Resource},0.42.1!aws-cdk-lib-experiments.NewStuff + * + * The whole thing is then either included directly as plaintext as: + * v2:plaintext:{prefixEncodedList} + * Or is compressed and base64-encoded, and then formatted as: + * v2:deflate64:{prefixEncodedListCompressedAndEncoded} + * + * Exported/visible for ease of testing. + */ +export function formatAnalytics(infos: ConstructInfo[]) { + const trie = new Trie(); + infos.forEach(info => insertFqnInTrie(`${info.version}!${info.fqn}`, trie)); - for (const key of Object.keys(runtime.libraries).sort()) { - modules.push(`${key}=${runtime.libraries[key]}`); + const plaintextEncodedConstructs = prefixEncodeTrie(trie); + const compressedConstructs = zlib.gzipSync(Buffer.from(plaintextEncodedConstructs)).toString('base64'); + + return `v2:deflate64:${compressedConstructs}`; +} + +/** + * Splits after non-alphanumeric characters (e.g., '.', '/') in the FQN + * and insert each piece of the FQN in nested map (i.e., simple trie). + */ +function insertFqnInTrie(fqn: string, trie: Trie) { + for (const fqnPart of fqn.replace(/[^a-z0-9]/gi, '$& ').split(' ')) { + const nextLevelTreeRef = trie.get(fqnPart) ?? new Trie(); + trie.set(fqnPart, nextLevelTreeRef); + trie = nextLevelTreeRef; } - return modules.join(','); -} \ No newline at end of file + return trie; +} + +/** + * Prefix-encodes a "trie-ish" structure, using '{}' to group and ',' to separate siblings. + * + * Example input: + * ABC,ABD,AEF + * + * Example trie: + * A --> B --> C + * | \--> D + * \--> E --> F + * + * Becomes: + * A{B{C,D},EF} + */ +function prefixEncodeTrie(trie: Trie) { + let prefixEncoded = ''; + let isFirstEntryAtLevel = true; + [...trie.entries()].forEach(([key, value]) => { + if (!isFirstEntryAtLevel) { + prefixEncoded += ','; + } + isFirstEntryAtLevel = false; + prefixEncoded += key; + if (value.size > 1) { + prefixEncoded += '{'; + prefixEncoded += prefixEncodeTrie(value); + prefixEncoded += '}'; + } else { + prefixEncoded += prefixEncodeTrie(value); + } + }); + return prefixEncoded; +} diff --git a/packages/@aws-cdk/core/lib/private/runtime-info.ts b/packages/@aws-cdk/core/lib/private/runtime-info.ts index b0cf266e8e11d..da4fbdcbe99d8 100644 --- a/packages/@aws-cdk/core/lib/private/runtime-info.ts +++ b/packages/@aws-cdk/core/lib/private/runtime-info.ts @@ -1,95 +1,82 @@ -import { basename, dirname } from 'path'; -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { major as nodeMajorVersion } from './node-version'; +import { IConstruct } from '../construct-compat'; +import { Stack } from '../stack'; +import { Stage } from '../stage'; -// list of NPM scopes included in version reporting e.g. @aws-cdk and @aws-solutions-konstruk -const ALLOWED_SCOPES = ['@aws-cdk', '@aws-cdk-containers', '@aws-solutions-konstruk', '@aws-solutions-constructs', '@amzn']; -// list of NPM packages included in version reporting -const ALLOWED_PACKAGES = ['aws-rfdk', 'aws-cdk-lib', 'monocdk']; +const ALLOWED_FQN_PREFIXES = [ + // SCOPES + '@aws-cdk/', '@aws-cdk-containers/', '@aws-solutions-konstruk/', '@aws-solutions-constructs/', '@amzn/', + // PACKAGES + 'aws-rfdk.', 'aws-cdk-lib.', 'monocdk.', +]; /** - * Returns a list of loaded modules and their versions. + * Symbol for accessing jsii runtime information + * + * Introduced in jsii 1.19.0, cdk 1.90.0. */ -export function collectRuntimeInformation(): cxschema.RuntimeInfo { - const libraries: { [name: string]: string } = {}; - - for (const fileName of Object.keys(require.cache)) { - const pkg = findNpmPackage(fileName); - if (pkg && !pkg.private) { - libraries[pkg.name] = pkg.version; - } - } +const JSII_RUNTIME_SYMBOL = Symbol.for('jsii.rtti'); - // include only libraries that are in the allowlistLibraries list - for (const name of Object.keys(libraries)) { - let foundMatch = false; - for (const scope of ALLOWED_SCOPES) { - if (name.startsWith(`${scope}/`)) { - foundMatch = true; - } - } - foundMatch = foundMatch || ALLOWED_PACKAGES.includes(name); +/** + * Source information on a construct (class fqn and version) + */ +export interface ConstructInfo { + readonly fqn: string; + readonly version: string; +} - if (!foundMatch) { - delete libraries[name]; - } +export function constructInfoFromConstruct(construct: IConstruct): ConstructInfo | undefined { + const jsiiRuntimeInfo = Object.getPrototypeOf(construct).constructor[JSII_RUNTIME_SYMBOL]; + if (typeof jsiiRuntimeInfo === 'object' + && jsiiRuntimeInfo !== null + && typeof jsiiRuntimeInfo.fqn === 'string' + && typeof jsiiRuntimeInfo.version === 'string') { + return { fqn: jsiiRuntimeInfo.fqn, version: jsiiRuntimeInfo.version }; + } else if (jsiiRuntimeInfo) { + // There is something defined, but doesn't match our expectations. Fail fast and hard. + throw new Error(`malformed jsii runtime info for construct: '${construct.node.path}'`); } - - // add jsii runtime version - libraries['jsii-runtime'] = getJsiiAgentVersion(); - - return { libraries }; + return undefined; } /** - * Determines which NPM module a given loaded javascript file is from. - * - * The only infromation that is available locally is a list of Javascript files, - * and every source file is associated with a search path to resolve the further - * ``require`` calls made from there, which includes its own directory on disk, - * and parent directories - for example: - * - * [ '...repo/packages/aws-cdk-resources/lib/cfn/node_modules', - * '...repo/packages/aws-cdk-resources/lib/node_modules', - * '...repo/packages/aws-cdk-resources/node_modules', - * '...repo/packages/node_modules', - * // etc... - * ] - * - * We are looking for ``package.json`` that is anywhere in the tree, except it's - * in the parent directory, not in the ``node_modules`` directory. For this - * reason, we strip the ``/node_modules`` suffix off each path and use regular - * module resolution to obtain a reference to ``package.json``. - * - * @param fileName a javascript file name. - * @returns the NPM module infos (aka ``package.json`` contents), or - * ``undefined`` if the lookup was unsuccessful. + * For a given stack, walks the tree and finds the runtime info for all constructs within the tree. + * Returns the unique list of construct info present in the stack, + * as long as the construct fully-qualified names match the defined allow list. */ -function findNpmPackage(fileName: string): { name: string, version: string, private?: boolean } | undefined { - const mod = require.cache[fileName]; +export function constructInfoFromStack(stack: Stack): ConstructInfo[] { + const isDefined = (value: ConstructInfo | undefined): value is ConstructInfo => value !== undefined; - if (!mod?.paths) { - // sometimes this can be undefined. for example when querying for .json modules - // inside a jest runtime environment. - // see https://github.com/aws/aws-cdk/issues/7657 - // potentially we can remove this if it turns out to be a bug in how jest implemented the 'require' module. - return undefined; - } + const allConstructInfos = constructsInStack(stack) + .map(construct => constructInfoFromConstruct(construct)) + .filter(isDefined) + .filter(info => ALLOWED_FQN_PREFIXES.find(prefix => info.fqn.startsWith(prefix))); - // For any path in ``mod.paths`` that is a node_modules folder, use its parent directory instead. - const paths = mod?.paths.map((path: string) => basename(path) === 'node_modules' ? dirname(path) : path); + // Adds the jsii runtime as a psuedo construct for reporting purposes. + allConstructInfos.push({ + fqn: 'jsii-runtime.Runtime', + version: getJsiiAgentVersion(), + }); - try { - const packagePath = require.resolve( - // Resolution behavior changed in node 12.0.0 - https://github.com/nodejs/node/issues/27583 - nodeMajorVersion >= 12 ? './package.json' : 'package.json', - { paths }, - ); - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require(packagePath); - } catch (e) { - return undefined; - } + // Filter out duplicate values + const uniqKeys = new Set(); + return allConstructInfos.filter(construct => { + const constructKey = `${construct.fqn}@${construct.version}`; + const isDuplicate = uniqKeys.has(constructKey); + uniqKeys.add(constructKey); + return !isDuplicate; + }); +} + +/** + * Returns all constructs under the parent construct (including the parent), + * stopping when it reaches a boundary of another stack (e.g., Stack, Stage, NestedStack). + */ +function constructsInStack(construct: IConstruct): IConstruct[] { + const constructs = [construct]; + construct.node.children + .filter(child => !Stage.isStage(child) && !Stack.isStack(child)) + .forEach(child => constructs.push(...constructsInStack(child))); + return constructs; } function getJsiiAgentVersion() { diff --git a/packages/@aws-cdk/core/lib/private/tree-metadata.ts b/packages/@aws-cdk/core/lib/private/tree-metadata.ts index caa5c37a5940d..97fe514bb4d87 100644 --- a/packages/@aws-cdk/core/lib/private/tree-metadata.ts +++ b/packages/@aws-cdk/core/lib/private/tree-metadata.ts @@ -6,16 +6,10 @@ import { Annotations } from '../annotations'; import { Construct, IConstruct, ISynthesisSession } from '../construct-compat'; import { Stack } from '../stack'; import { IInspectable, TreeInspector } from '../tree'; +import { ConstructInfo, constructInfoFromConstruct } from './runtime-info'; const FILE_PATH = 'tree.json'; -/** - * Symbol for accessing jsii runtime information - * - * Introduced in jsii 1.19.0, cdk 1.90.0. - */ -const JSII_RUNTIME_SYMBOL = Symbol.for('jsii.rtti'); - /** * Construct that is automatically attached to the top-level `App`. * This generates, as part of synthesis, a file containing the construct tree and the metadata for each node in the tree. @@ -48,14 +42,12 @@ export class TreeMetadata extends Construct { .filter((child) => child !== undefined) .reduce((map, child) => Object.assign(map, { [child!.id]: child }), {}); - const jsiiRuntimeInfo = Object.getPrototypeOf(construct).constructor[JSII_RUNTIME_SYMBOL]; - const node: Node = { id: construct.node.id || 'App', path: construct.node.path, children: Object.keys(childrenMap).length === 0 ? undefined : childrenMap, attributes: this.synthAttributes(construct), - constructInfo: constructInfoFromRuntimeInfo(jsiiRuntimeInfo), + constructInfo: constructInfoFromConstruct(construct), }; lookup[node.path] = node; @@ -96,16 +88,6 @@ export class TreeMetadata extends Construct { } } -function constructInfoFromRuntimeInfo(jsiiRuntimeInfo: any): ConstructInfo | undefined { - if (typeof jsiiRuntimeInfo === 'object' - && jsiiRuntimeInfo !== null - && typeof jsiiRuntimeInfo.fqn === 'string' - && typeof jsiiRuntimeInfo.version === 'string') { - return { fqn: jsiiRuntimeInfo.fqn, version: jsiiRuntimeInfo.version }; - } - return undefined; -} - interface Node { readonly id: string; readonly path: string; @@ -117,11 +99,3 @@ interface Node { */ readonly constructInfo?: ConstructInfo; } - -/** - * Source information on a construct (class fqn and version) - */ -interface ConstructInfo { - readonly fqn: string; - readonly version: string; -} diff --git a/packages/@aws-cdk/core/test/app.test.ts b/packages/@aws-cdk/core/test/app.test.ts index 69486987f0085..199b36dc87465 100644 --- a/packages/@aws-cdk/core/test/app.test.ts +++ b/packages/@aws-cdk/core/test/app.test.ts @@ -4,7 +4,6 @@ import { nodeunitShim, Test } from 'nodeunit-shim'; import { CfnResource, Construct, Stack, StackProps } from '../lib'; import { Annotations } from '../lib/annotations'; import { App, AppProps } from '../lib/app'; -import { MetadataResource } from '../lib/private/metadata-resource'; function withApp(props: AppProps, block: (app: App) => void): cxapi.CloudAssembly { const app = new App({ @@ -260,90 +259,6 @@ nodeunitShim({ test.done(); }, - 'runtime library versions'(test: Test) { - v1(() => { - MetadataResource.clearModulesCache(); - - const response = withApp({ analyticsReporting: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); - - const stackTemplate = response.getStackByName('stack1').template; - const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = require('../package.json').version; - test.deepEqual(libs['@aws-cdk/core'], version); - test.deepEqual(libs['@aws-cdk/cx-api'], version); - test.deepEqual(libs['jsii-runtime'], `node.js/${process.version}`); - }); - test.done(); - }, - - 'CDK version'(test: Test) { - MetadataResource.clearModulesCache(); - - withCliVersion(() => { - const response = withApp({ analyticsReporting: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); - - const stackTemplate = response.getStackByName('stack1').template; - const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - test.deepEqual(libs['aws-cdk'], '1.2.3'); - }); - - test.done(); - }, - - 'jsii-runtime version loaded from JSII_AGENT'(test: Test) { - process.env.JSII_AGENT = 'Java/1.2.3.4'; - MetadataResource.clearModulesCache(); - - withCliVersion(() => { - const response = withApp({ analyticsReporting: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); - - const stackTemplate = response.getStackByName('stack1').template; - const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); - - test.deepEqual(libs['jsii-runtime'], 'Java/1.2.3.4'); - }); - - delete process.env.JSII_AGENT; - test.done(); - }, - - 'version reporting includes only @aws-cdk, aws-cdk and jsii libraries'(test: Test) { - v1(() => { - MetadataResource.clearModulesCache(); - - const response = withApp({ analyticsReporting: true }, app => { - const stack = new Stack(app, 'stack1'); - new CfnResource(stack, 'MyResource', { type: 'Resource::Type' }); - }); - - const stackTemplate = response.getStackByName('stack1').template; - const libs = parseModules(stackTemplate.Resources?.CDKMetadata?.Properties?.Modules); - const libNames = Object.keys(libs).sort(); - - test.deepEqual(libNames, [ - '@aws-cdk/cloud-assembly-schema', - '@aws-cdk/core', - '@aws-cdk/cx-api', - '@aws-cdk/region-info', - 'jsii-runtime', - ]); - }); - test.done(); - }, - 'deep stack is shown and synthesized properly'(test: Test) { // WHEN const response = withApp({}, (app) => { @@ -420,42 +335,3 @@ class MyConstruct extends Construct { new CfnResource(this, 'r2', { type: 'ResourceType2', properties: { FromContext: this.node.tryGetContext('ctx1') } }); } } - -function parseModules(x?: string): Record { - if (x === undefined) { return {}; } - - const ret: Record = {}; - for (const clause of x.split(',')) { - const [key, value] = clause.split('='); - if (key !== undefined && value !== undefined) { - ret[key] = value; - } - } - return ret; -} - -/** - * Set the CLI_VERSION_ENV environment variable - * - * This is necessary to get the Stack to emit the metadata resource - */ -function withCliVersion(block: () => A): A { - process.env[cxapi.CLI_VERSION_ENV] = '1.2.3'; - try { - return block(); - } finally { - delete process.env[cxapi.CLI_VERSION_ENV]; - } -} - -function v1(block: () => void) { - onVersion(1, block); -} - -function onVersion(version: number, block: () => void) { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const mv: number = require('../../../../release.json').majorVersion; - if (version === mv) { - block(); - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/core/test/metadata-resource.test.ts b/packages/@aws-cdk/core/test/metadata-resource.test.ts new file mode 100644 index 0000000000000..2275bcf7dee9d --- /dev/null +++ b/packages/@aws-cdk/core/test/metadata-resource.test.ts @@ -0,0 +1,151 @@ +import * as zlib from 'zlib'; +import { App, Stack } from '../lib'; +import { formatAnalytics } from '../lib/private/metadata-resource'; + +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '../lib'; +import { ConstructInfo } from '../lib/private/runtime-info'; + +describe('MetadataResource', () => { + let app: App; + let stack: Stack; + + beforeEach(() => { + app = new App({ + analyticsReporting: true, + }); + stack = new Stack(app, 'Stack'); + }); + + test('is not included if the region is known and metadata is not available', () => { + new Stack(app, 'StackUnavailable', { + env: { region: 'definitely-no-metadata-resource-available-here' }, + }); + + const stackTemplate = app.synth().getStackByName('StackUnavailable').template; + + expect(stackTemplate.Resources?.CDKMetadata).toBeUndefined(); + }); + + test('is included if the region is known and metadata is available', () => { + new Stack(app, 'StackPresent', { + env: { region: 'us-east-1' }, + }); + + const stackTemplate = app.synth().getStackByName('StackPresent').template; + + expect(stackTemplate.Resources?.CDKMetadata).toBeDefined(); + }); + + test('is included if the region is unknown with conditions', () => { + new Stack(app, 'StackUnknown'); + + const stackTemplate = app.synth().getStackByName('StackUnknown').template; + + expect(stackTemplate.Resources?.CDKMetadata).toBeDefined(); + expect(stackTemplate.Resources?.CDKMetadata?.Condition).toBeDefined(); + }); + + test('includes the formatted Analytics property', () => { + // A very simple check that the jsii runtime psuedo-construct is present. + // This check works whether we're running locally or on CodeBuild, on v1 or v2. + // Other tests(in app.test.ts) will test version-specific results. + expect(stackAnalytics()).toMatch(/jsii-runtime.Runtime/); + }); + + test('includes the current jsii runtime version', () => { + process.env.JSII_AGENT = 'Java/1.2.3.4'; + + expect(stackAnalytics()).toContain('Java/1.2.3.4!jsii-runtime.Runtime'); + delete process.env.JSII_AGENT; + }); + + test('includes constructs added to the stack', () => { + new TestConstruct(stack, 'Test'); + + expect(stackAnalytics()).toContain('1.2.3!@amzn/core.TestConstruct'); + }); + + test('only includes constructs in the allow list', () => { + new TestThirdPartyConstruct(stack, 'Test'); + + expect(stackAnalytics()).not.toContain('TestConstruct'); + }); + + function stackAnalytics(stackName: string = 'Stack') { + const encodedAnalytics = app.synth().getStackByName(stackName).template.Resources?.CDKMetadata?.Properties?.Analytics as string; + return plaintextConstructsFromAnalytics(encodedAnalytics); + } +}); + +describe('formatAnalytics', () => { + test('analytics are formatted with a prefix of v2:deflate64:', () => { + const constructInfo = [{ fqn: 'aws-cdk-lib.Construct', version: '1.2.3' }]; + + expect(formatAnalytics(constructInfo)).toMatch(/v2:deflate64:.*/); + }); + + test('single construct', () => { + const constructInfo = [{ fqn: 'aws-cdk-lib.Construct', version: '1.2.3' }]; + + expectAnalytics(constructInfo, '1.2.3!aws-cdk-lib.Construct'); + }); + + test('common prefixes with same versions are combined', () => { + const constructInfo = [ + { fqn: 'aws-cdk-lib.Construct', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.CfnResource', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.Stack', version: '1.2.3' }, + ]; + + expectAnalytics(constructInfo, '1.2.3!aws-cdk-lib.{Construct,CfnResource,Stack}'); + }); + + test('nested modules with common prefixes and same versions are combined', () => { + const constructInfo = [ + { fqn: 'aws-cdk-lib.Construct', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.CfnResource', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.Stack', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.aws_servicefoo.CoolResource', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.aws_servicefoo.OtherResource', version: '1.2.3' }, + ]; + + expectAnalytics(constructInfo, '1.2.3!aws-cdk-lib.{Construct,CfnResource,Stack,aws_servicefoo.{CoolResource,OtherResource}}'); + }); + + test('constructs are grouped by version', () => { + const constructInfo = [ + { fqn: 'aws-cdk-lib.Construct', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.CfnResource', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.Stack', version: '1.2.3' }, + { fqn: 'aws-cdk-lib.CoolResource', version: '0.1.2' }, + { fqn: 'aws-cdk-lib.OtherResource', version: '0.1.2' }, + ]; + + expectAnalytics(constructInfo, '1.2.3!aws-cdk-lib.{Construct,CfnResource,Stack},0.1.2!aws-cdk-lib.{CoolResource,OtherResource}'); + }); + + // Compares the output of formatAnalytics with an expected (plaintext) output. + // For ease of testing, the plaintext versions are compared rather than the encoded versions. + function expectAnalytics(constructs: ConstructInfo[], expectedPlaintext: string) { + expect(plaintextConstructsFromAnalytics(formatAnalytics(constructs))).toEqual(expectedPlaintext); + } + +}); + +function plaintextConstructsFromAnalytics(analytics: string) { + return zlib.gunzipSync(Buffer.from(analytics.split(':')[2], 'base64')).toString('utf-8'); +} + +const JSII_RUNTIME_SYMBOL = Symbol.for('jsii.rtti'); + +class TestConstruct extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: '@amzn/core.TestConstruct', version: '1.2.3' } +} + +class TestThirdPartyConstruct extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: 'mycoolthing.TestConstruct', version: '1.2.3' } +} + diff --git a/packages/@aws-cdk/core/test/runtime-info.test.ts b/packages/@aws-cdk/core/test/runtime-info.test.ts index 67f931bb63ec5..7da4f78d74b46 100644 --- a/packages/@aws-cdk/core/test/runtime-info.test.ts +++ b/packages/@aws-cdk/core/test/runtime-info.test.ts @@ -1,73 +1,189 @@ -import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; -import { nodeunitShim, Test } from 'nodeunit-shim'; -import { collectRuntimeInformation } from '../lib/private/runtime-info'; - -nodeunitShim({ - 'version reporting includes @aws-solutions-konstruk libraries'(test: Test) { - const pkgdir = fs.mkdtempSync(path.join(os.tmpdir(), 'runtime-info-konstruk-fixture')); - const mockVersion = '1.2.3'; - - fs.writeFileSync(path.join(pkgdir, 'index.js'), 'module.exports = \'this is foo\';'); - fs.writeFileSync(path.join(pkgdir, 'package.json'), JSON.stringify({ - name: '@aws-solutions-konstruk/foo', - version: mockVersion, - })); - - // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies - require(pkgdir); - - const runtimeInfo = collectRuntimeInformation(); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - test.deepEqual(runtimeInfo.libraries['@aws-solutions-konstruk/foo'], mockVersion); - test.done(); - }, - - 'version reporting finds aws-rfdk package'(test: Test) { - const pkgdir = fs.mkdtempSync(path.join(os.tmpdir(), 'runtime-info-rfdk')); - const mockVersion = '1.2.3'; - - fs.writeFileSync(path.join(pkgdir, 'index.js'), 'module.exports = \'this is foo\';'); - fs.writeFileSync(path.join(pkgdir, 'package.json'), JSON.stringify({ - name: 'aws-rfdk', - version: mockVersion, - })); +import { App, NestedStack, Stack, Stage } from '../lib'; +import { constructInfoFromConstruct, constructInfoFromStack } from '../lib/private/runtime-info'; + +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '../lib'; + +const JSII_RUNTIME_SYMBOL = Symbol.for('jsii.rtti'); + +let app: App; +let stack: Stack; +let _cdkVersion: string | undefined = undefined; +const modulePrefix = cdkMajorVersion() === 1 ? '@aws-cdk/core' : 'aws-cdk-lib'; + +// The runtime metadata this test relies on is only available if the most +// recent compile has happened using 'jsii', as the jsii compiler injects +// this metadata. +// +// If the most recent compile was using 'tsc', the metadata will not have +// been injected, and the test suite will fail. +// +// Tolerate `tsc` builds locally, but not on CodeBuild. +const codeBuild = !!process.env.CODEBUILD_BUILD_ID; +const moduleCompiledWithTsc = constructInfoFromConstruct(new Stack())?.fqn === 'constructs.Construct'; +let describeTscSafe = describe; +if (moduleCompiledWithTsc && !codeBuild) { + // eslint-disable-next-line + console.error('It appears this module was compiled with `tsc` instead of `jsii` in a local build. Skipping this test suite.'); + describeTscSafe = describe.skip; +} + +beforeEach(() => { + app = new App(); + stack = new Stack(app, 'Stack', { + analyticsReporting: true, + }); +}); - // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies - require(pkgdir); +describeTscSafe('constructInfoFromConstruct', () => { + test('returns fqn and version for core constructs', () => { + const constructInfo = constructInfoFromConstruct(stack); + expect(constructInfo).toBeDefined(); + expect(constructInfo?.fqn).toEqual(`${modulePrefix}.Stack`); + expect(constructInfo?.version).toEqual(localCdkVersion()); + }); + + test('returns base construct info if no more specific info is present', () => { + const simpleConstruct = new class extends Construct { }(stack, 'Simple'); + const constructInfo = constructInfoFromConstruct(simpleConstruct); + expect(constructInfo?.fqn).toEqual(`${modulePrefix}.Construct`); + }); + + test('returns more specific subclass info if present', () => { + const construct = new class extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: 'aws-cdk-lib.TestConstruct', version: localCdkVersion() } + }(stack, 'TestConstruct'); + + const constructInfo = constructInfoFromConstruct(construct); + expect(constructInfo?.fqn).toEqual('aws-cdk-lib.TestConstruct'); + }); + + test('throws if the jsii runtime info is not as expected', () => { + const constructRuntimeInfoNotObject = new class extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = 'HelloWorld'; + }(stack, 'RuntimeNotObject'); + const constructWithWrongRuntimeInfoMembers = new class extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { foo: 'bar' }; + }(stack, 'RuntimeWrongMembers'); + const constructWithWrongRuntimeInfoTypes = new class extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: 42, version: { name: '0.0.0' } }; + }(stack, 'RuntimeWrongTypes'); + + const errorMessage = 'malformed jsii runtime info for construct'; + [constructRuntimeInfoNotObject, constructWithWrongRuntimeInfoMembers, constructWithWrongRuntimeInfoTypes].forEach(construct => { + expect(() => constructInfoFromConstruct(construct)).toThrow(errorMessage); + }); + }); +}); - const runtimeInfo = collectRuntimeInformation(); +describeTscSafe('constructInfoForStack', () => { + test('returns stack itself and jsii runtime if stack is empty', () => { + const constructInfos = constructInfoFromStack(stack); + + expect(constructInfos.length).toEqual(2); + + const stackInfo = constructInfos.find(i => /Stack/.test(i.fqn)); + const jsiiInfo = constructInfos.find(i => i.fqn === 'jsii-runtime.Runtime'); + expect(stackInfo?.fqn).toEqual(`${modulePrefix}.Stack`); + expect(stackInfo?.version).toEqual(localCdkVersion()); + expect(jsiiInfo?.version).toMatch(/node.js/); + }); + + test('returns info for constructs added to the stack', () => { + new class extends Construct { }(stack, 'Simple'); + + const constructInfos = constructInfoFromStack(stack); + + expect(constructInfos.length).toEqual(3); + expect(constructInfos.map(info => info.fqn)).toContain(`${modulePrefix}.Construct`); + }); + + test('returns unique info (no duplicates)', () => { + new class extends Construct { }(stack, 'Simple1'); + new class extends Construct { }(stack, 'Simple2'); + + const constructInfos = constructInfoFromStack(stack); + + expect(constructInfos.length).toEqual(3); + expect(constructInfos.map(info => info.fqn)).toContain(`${modulePrefix}.Construct`); + }); + + test('returns info from nested constructs', () => { + new class extends Construct { + constructor(scope: Construct, id: string) { + super(scope, id); + return new class extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: '@aws-cdk/test.TestV1Construct', version: localCdkVersion() } + }(this, 'TestConstruct'); + } + }(stack, 'Nested'); + + const constructInfos = constructInfoFromStack(stack); + + expect(constructInfos.length).toEqual(4); + expect(constructInfos.map(info => info.fqn)).toContain('@aws-cdk/test.TestV1Construct'); + }); + + test('does not return info from nested stacks', () => { + new class extends Construct { + constructor(scope: Construct, id: string) { + super(scope, id); + + new class extends Construct { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: '@aws-cdk/test.TestV1Construct', version: localCdkVersion() } + }(this, 'TestConstruct'); + + new class extends Stack { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: '@aws-cdk/test.TestStackInsideStack', version: localCdkVersion() } + }(this, 'StackInsideStack'); + + new class extends NestedStack { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: '@aws-cdk/test.TestNestedStackInsideStack', version: localCdkVersion() } + }(this, 'NestedStackInsideStack'); + + new class extends Stage { + // @ts-ignore + private static readonly [JSII_RUNTIME_SYMBOL] = { fqn: '@aws-cdk/test.TestStageInsideStack', version: localCdkVersion() } + }(this, 'StageInsideStack'); + } + }(stack, 'ParentConstruct'); + + const constructInfos = constructInfoFromStack(stack); + + const fqns = constructInfos.map(info => info.fqn); + expect(fqns).toContain('@aws-cdk/test.TestV1Construct'); + expect(fqns).not.toContain('@aws-cdk/test.TestStackInsideStack'); + expect(fqns).not.toContain('@aws-cdk/test.TestNestedStackInsideStack'); + expect(fqns).not.toContain('@aws-cdk/test.TestStageInsideStack'); + }); +}); +function cdkMajorVersion(): number { + // eslint-disable-next-line @typescript-eslint/no-require-imports + return require('../../../../release.json').majorVersion; +} + +/** + * The exact values we expect from testing against version numbers in this suite depend on whether we're running + * on a development or release branch. Returns the local package.json version, which will be '0.0.0' unless we're + * on a release branch, in which case it should be the real version numbers (e.g., 1.91.0). + */ +function localCdkVersion(): string { + if (!_cdkVersion) { // eslint-disable-next-line @typescript-eslint/no-require-imports - test.deepEqual(runtimeInfo.libraries['aws-rfdk'], mockVersion); - test.done(); - }, - - 'version reporting finds no version with no associated package.json'(test: Test) { - const pkgdir = fs.mkdtempSync(path.join(os.tmpdir(), 'runtime-info-find-npm-package-fixture')); - const mockVersion = '1.2.3'; - - fs.writeFileSync(path.join(pkgdir, 'index.js'), 'module.exports = \'this is bar\';'); - fs.mkdirSync(path.join(pkgdir, 'bar')); - fs.writeFileSync(path.join(pkgdir, 'bar', 'package.json'), JSON.stringify({ - name: '@aws-solutions-konstruk/bar', - version: mockVersion, - })); - - // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies - require(pkgdir); - - const cwd = process.cwd(); - - // Switch to `bar` where the package.json is, then resolve version. Fails when module.resolve - // is passed an empty string in the paths array. - process.chdir(path.join(pkgdir, 'bar')); - const runtimeInfo = collectRuntimeInformation(); - process.chdir(cwd); - - test.equal(runtimeInfo.libraries['@aws-solutions-konstruk/bar'], undefined); - test.done(); - }, -}); + _cdkVersion = require(path.join('..', 'package.json')).version; + if (!_cdkVersion) { + throw new Error('Unable to determine CDK version'); + } + } + return _cdkVersion; +} diff --git a/scripts/resolve-version-lib.js b/scripts/resolve-version-lib.js index 2a7f0e4eecebc..21a13c0eb4ab2 100755 --- a/scripts/resolve-version-lib.js +++ b/scripts/resolve-version-lib.js @@ -38,7 +38,6 @@ function resolveVersion(rootdir) { // const currentVersion = require(versionFilePath).version; - console.error(`current version: ${currentVersion}`); if (!currentVersion.startsWith(`${majorVersion}.`)) { throw new Error(`current version "${currentVersion}" does not use the expected major version ${majorVersion}`); }