

# Developing resource types for CloudFormation templates
<a name="resource-type-develop"></a>

Once you've modeled your resource type, and validated its schema, the next step is to develop the resource. Developing the resource consists of two main steps:
+ Implementing the appropriate event handlers for your resource.
+ Testing the resource locally to ensure it works as expected.

## Implementing resource handlers
<a name="resource-type-develop-implement-handlers"></a>

When you `generate` your resource package, the CloudFormation CLI stubs out empty handler functions, each of which each corresponds to a specific event in the resource lifecycle. You add logic to these handlers to control what happens to your resource type at each stage of its lifecycle.
+ `create`: CloudFormation invokes this handler when the resource is initially created during stack create operations.
+ `read`: CloudFormation invokes this handler as part of a stack update operation when detailed information about the resource's current state is required.
+ `update`: CloudFormation invokes this handler when the resource is updated as part of a stack update operation.
+ `delete`: CloudFormation invokes this handler when the resource is deleted, either when the resource is deleted from the stack as part of a stack update operation, or the stack itself is deleted.
+ `list`: CloudFormation invokes this handler when summary information about multiple resources of this resource type is required.

You can only specify a single handler for each event.

Which handlers you implement for a resource determine what provisioning actions CloudFormation takes with respect to the resource during various stack operations:
+ If the resource type contains both `create` and `update` handlers, CloudFormation invokes the appropriate handler during stack create and update operation.
+ If the resource type doesn't contain an `update` handler, CloudFormation can't update the resource during stack update operations, and will instead replace it. CloudFormation invokes the `create` handler to creates a new resource, then deletes the old resource by invoking the `delete` handler.
+ If the resource type doesn't contain `create`, `read`, and `delete` handlers, CloudFormation can't actually provision the resource.

Use the resource schema to specify which handlers you have implemented. If you choose not to implement a specific handler, remove it from the `handlers` section of the resource schema.

### Accessing AWS APIs from a resource type
<a name="resource-type-develop-executionrole"></a>

If your resource type calls AWS APIs in any of its handlers, you must create an *[IAM execution role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)* that includes the necessary permissions to call those AWS APIs, and provision that execution role in your account. CloudFormation then assumes that execution role to provide your resource type with the appropriate credentials.

When you call `generate`, the CloudFormation CLI automatically generates an execution role template, `resource-role.yaml`, as part of generating the code files for the resource type package. This template is based on the permissions specified for each handler in the `[handlers](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html#schema-properties-handlers)` section of the [Resource type schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html). When you use `submit` to register the resource type, the CloudFormation CLI attempts to create or update an execution role based on the template, and then passes this execution role to CloudFormation as part of the registration.

For more information about the permissions available per AWS service, see [Actions, resources, and condition keys for AWS services](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_actions-resources-contextkeys.html) in the *[AWS Identity and Access Management User Guide](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)*.

## Monitoring runtime logging for resource types
<a name="resource-type-develop-log"></a>

When you register a resource type using `cfn submit`, CloudFormation creates a CloudWatch log group for the resource type in your account. This enables you to access the logs for your resource to help you diagnose any faults. The log group is named according to the following pattern:

`/my-resource-type-stack-ResourceHandler-string`

Where:
+ *my-resource-type* is the three-part resource type name.
+ *string* is a unique string generated by CloudFormation.

Now, when you initiate stack operations for stacks that contain the resource type, CloudFormation delivers log events emitted by the resource type to this log group.

# Testing resource types using contract tests
<a name="resource-type-test"></a>

As you model and develop your resource type, you should have the CloudFormation CLI perform tests to ensure that the resource type is behaving as expected during each event in the resource lifecycle. The CloudFormation CLI performs a suite of tests called contract tests to enforce CloudFormation’s [handler contract](resource-type-test-contract.md). Developing and registering your resource type in CloudFormation signifies an agreement that your resource is compliant and doesn't break any framework expectations. All resources that fail contract tests are blocked from publishing into our registry.

## Testing resource types locally using AWS SAM
<a name="resource-type-develop-test"></a>

Once you've implemented the desired handlers for your resource, you can also test the resource locally using the AWS SAM command line interface (CLI), to make sure your resource behaves as expected, debug what's wrong, and fix any issues.

To start testing, use the AWS SAM CLI to start the Local Lambda service. Run the following command in a terminal separate from your resource type workspace, or as a background process.

```
$ sam local start-lambda
```

If you have functions defined in your AWS SAM template, it will provide an endpoint to invoke these functions locally. This is especially helpful because it allows for remote debugging to step through resource type invocations in real time.

```
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint.
2020-01-15 15:27:19  * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)
```

Alternatively, you can also specify using the public Lambda service and invoke functions deployed in your account. Be aware, however, that using the local service allows for more iteration. To specify a debug port for remote debugging, use the `-d` option:

```
$ sam local start-lambda -d PORT
```

Once you have the Lambda service started, use the `[test](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-test.html)` command to perform contract tests:

```
$ cfn test
```

The CloudFormation CLI selects the appropriate contract tests to execute, based on the handlers specified in your resource type schema. If a test fails, the CloudFormation CLI outputs a detailed trace of the failure, including the related assertion failure and mismatched values.

For more information about testing using AWS SAM CLI, see [Testing and debugging serverless applications ](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-test-and-debug.html) in the *AWS Serverless Application Model Developer Guide*.

## How the CloudFormation CLI constructs and executes contract tests
<a name="resource-type-test-how"></a>

The CloudFormation CLI uses [PyTest](https://docs.pytest.org/en/latest/), an open-source testing framework, to execute the contract tests.

The tests themselves are located in the [suite](https://github.com/aws-cloudformation/cloudformation-cli/tree/master/src/rpdk/core/contract/suite) folder of the CloudFormation CLI repository on GitHub. Each test is adorned with the appropriate `pytest` markers. For example, tests applicable to the `create` handler are adorned with the `@pytest.mark.create` marker. This enables the CloudFormation CLI to execute only those tests appropriate for a resource type, based on the handlers specified in the resource type's schema. For example, suppose a resource type's schema specified `create`, `read`, and `delete` handlers. In this case, the CloudFormation CLI would not perform any test marked with only the `@pytest.mark.update` or `@pytest.mark.list`, since those handlers weren't implemented.

To test `create` and `update` handlers, the CloudFormation CLI uses the resource type's schema and [Hypothesis](https://hypothesis.readthedocs.io/en/latest/), an open-source Python library used to generate testing strategies. The resource type schema is walked to generate a strategy for valid resource models, and the strategy is used to generate models for tests.

Tests create, update, and delete resources to test various aspects of the resource handler contract during handler operations. The CloudFormation CLI uses PyTest `fixtures` to decrease the amount of time the contract tests take to perform. Using fixtures enable the tests within a test module to share resources, rather than have to create a new resource for each test. Currently, the contract tests employ the following fixtures:
+ `created_resource` in the `[handler\$1create](https://github.com/aws-cloudformation/cloudformation-cli/blob/master/src/rpdk/core/contract/suite/resource/handler_create.py)` test module.
+ `updated_resource` in the `[handler\$1update](https://github.com/aws-cloudformation/cloudformation-cli/blob/master/src/rpdk/core/contract/suite/resource/handler_update.py)` test module.
+ `deleted_resource` in `[handler\$1delete](https://github.com/aws-cloudformation/cloudformation-cli/blob/master/src/rpdk/core/contract/suite/resource/handler_delete.py)` test module.

## Specifying input data for use in contract tests
<a name="resource-type-test-input-data"></a>

By default, the CloudFormation CLI performs resource contract tests using input properties generated from the patterns you define in your resource type schema. However, most resources are complex enough that the input properties for creating or updating those resources requires an understanding of the resource being provisioned. To address this, you can specify the input the CloudFormation CLI uses when performing its contract tests.

The CloudFormation CLI offers two ways for you to specify the input data for it to use when performing contract tests:
+ Overrides file

  Using an `overrides` file provides a light-weight way of specifying input data for certain specific properties for the CloudFormation CLI to use during both create and update operations testing.
+ Input files

  You can also use multiple `input` files to specify contract test input data if:
  + You want or need to specify different input data for create and update operations, or invalid data with which to test.
  + You want to specify multiple different input data sets.

### Specifying input data using an override file
<a name="resource-type-test-overrides"></a>

Using an override file enables you to overwrite input values for specific resource properties. Input values specified in the override file are used in contract testing for both create and update operations. You can only specify a single override file, and only specify a single input value for each resource property. For any properties for which you don't specify values, the CloudFormation CLI uses generated input.

Because the input data specified in the `overrides.json` file is used by the CloudFormation CLI during testing of create and update operations, you can't include input values for create-only properties in the file, as this would lead to contract test failures during update operations. For more information, see [createOnlyProperties](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html#schema-properties-createonlyproperties).

To override the input data used for specific properties during contract testing, add an `overrides.json` file to the root directory of your resource type project. The `overrides.json` file should contain only the resource properties to be used in testing. Use the following syntax:

```
{
    "CREATE": {
        "property_name": "property_value" # optional_comment
    }
}
```

For example:

```
{
    "CREATE": {
        "SubnetId": "subnet-0bc6136e" # This should be a real subnet that exists in the account you're testing against.
    }
}
```

You can also use output values from other stacks when specifying input data. For example, suppose you had a stack that contained an export value named `SubnetExport`:

```
Resources:
    VPC:
        Type: "AWS::EC2::VPC"
        Properties:
            CidrBlock: "10.0.0.0/16"
    Subnet:
        Type: "AWS::EC2::Subnet"
        Properties:
           CidrBlock: "10.0.0.0/24"
           VpcId: !Ref VPC
Outputs:
    SubnetId:
        Value: !Ref Subnet
        Export:
            Name: SubnetExport
```

You could then specify that export value as input data using the export value name using the following syntax:

```
{
  "CREATE": {
    "SubnetId": "{{SubnetExport}}"
  }
}
```

For more information, see [Outputs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html).

### Specifying input data using input files
<a name="resource-type-test-inputs"></a>

Use `input` files to specify different kinds of input data for the CloudFormation CLI to use: create input, update input, and invalid input. Each kind of data is specified in a separate file. You can also specify multiple sets of input data for contract tests.

To specify `input` files for the CloudFormation CLI to use in contract testing, add an `inputs` folder to the root directory of your resource type project. Then add your input files.

Specify which kind of input data a file contains by using the following naming conventions, where **n** is an integer:
+ `inputs_n_create.json`: Use files with `_create.json` for specifying inputs for creating the resource. This includes input values for create-only properties. For more information, see [createOnlyProperties](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html#schema-properties-createonlyproperties).
+ `inputs_n_update.json`: Use files with `_update.json` for specifying inputs for updating the resource.
+ `inputs_n_invalid.json`: Use files with `_invalid.json` for specifying invalid inputs to test when creating or updating the resource.

To specify multiple sets of input data for contract tests, increment the integer in the file names to order your input data sets. For example, your first set of input files should be named `inputs_1_create.json`, `inputs_1_update.json`, and `inputs_1_invalid.json`. Your next set would be named `inputs_2_create.json`, `inputs_2_update.json`, and `inputs_2_invalid.json`, and so on.

Each input file is a JSON file containing only the resource properties to be used in testing. Below is an example of an input file data set.

```
{
   "AlarmName": "Name",
   "AlarmDescription": "TestAlarmDimensions Description",
   "Namespace": "CloudWatchNamespace",
   "MetricName": "Fault",
   "Dimensions": [
      {
         "Name": "MethodName",
         "Value": "Value"
      }
   ],
   "Statistic": "Average",
   "Period": 60,
   "EvaluationPeriods": 5,
   "Threshold": 0.01,
   "ComparisonOperator": "GreaterThanOrEqualToThreshold"
}
```

If you specify an `inputs` folder, the CloudFormation CLI uses only the input data included in that folder. Therefore, you must specify create, update, and invalid data files for the CloudFormation CLI to successfully complete the contract tests.

If you specify both input files and an overrides files, the CloudFormation CLI ignores the overrides file and uses the input data specified in the `inputs` folder.

You can also use output values from other stacks when specifying input data. For example, suppose you had a stack that contained an export value named `SubnetExport`:

```
Resources:
    VPC:
        Type: "AWS::EC2::VPC"
        Properties:
            CidrBlock: "10.0.0.0/16"
    Subnet:
        Type: "AWS::EC2::Subnet"
        Properties:
           CidrBlock: "10.0.0.0/24"
           VpcId: !Ref VPC
Outputs:
    SubnetId:
        Value: !Ref Subnet
        Export:
            Name: SubnetExport
```

You could then specify that export value as input data using the export value name using the following syntax:

```
{
   "SubnetId": "{{SubnetExport}}",
   . . . 
}
```

For more information, see [Outputs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html).

## Viewing contract test logs
<a name="debug-contract-test"></a>

It's important to note that contract tests aren't run during private type registration (unless the resource contains one of the following namespaces: `aws`, `amzn`, `alexa`, `amazon`, `awsquickstart`), but failing contract tests does block a publisher's ability to publish their type. This is because public resources are consumed by other external customers and need to maintain a high quality bar. For this reason, it's important to debug your contract test failures early on in the resource development process.

Running contract tests generates two types of logs. Using both helps expedite the debugging process.
+ `Lambda logs` show logs from your handlers and provide more details on the input and output for each handler call.
+ `Test logs` show the result of running the test suite, including which tests have failed or passed, in addition to a traceback if a test has failed.

Because there are multiple ways to invoke contract tests against your resource, there are different places to find logs depending on which operation you are using.
+ If you run contract tests locally, logs are divided into the following two sections:
  + Lambda logs are in the terminal tab in which you ran `sam local start-lambda`.
  + Test logs are in the terminal tab in which you ran `cfn test`.
+ If you run contract tests through the type registration (`cfn submit`), logs are uploaded in two areas. Contract tests are only run during type registration if your type name includes one of the following namespaces: `aws`, `amzn`, `alexa`, `amazon`, `awsquickstart`.
  + Lambda logs are delivered to a CloudWatch log group in your account. The log group adheres to the following naming pattern:

    `<Hyphenated TypeName>-ContractTests-<RegistrationToken>`

    For example, `aws-cloudwatch-alarm-ContractTests-ca7096d7-ccb3-4c7d-ad51-78d0a1a300ca`.
  + To receive test logs in an Amazon S3 bucket, you have to modify the IAM role that's created by CloudFormation, which adheres to the following naming pattern:

    `CloudFormationManagedUplo-LogAndMetricsDeliveryRol-<RandomId>`

    Add the following inline policy:

------
#### [ JSON ]

****  

    ```
    {
        "Version":"2012-10-17",		 	 	 
        "Statement": [
            {
                "Action": [
                    "s3:PutObject"
                ],
                "Resource": [
                    "*"
                ],
                "Effect": "Allow"
            },
            {
                "Action": [
                    "kms:Encrypt",
                    "kms:Decrypt",
                    "kms:ReEncrypt*",
                    "kms:GenerateDataKey*",
                    "kms:DescribeKey"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }
    ```

------

    Also add the following trust policy:

------
#### [ JSON ]

****  

    ```
    {
      "Version":"2012-10-17",		 	 	 
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": [
              "cloudformation.amazonaws.com",
              "resources.cloudformation.amazonaws.com"
            ]
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    ```

------

    Invoking `cfn submit --role-arn <arn for above IAM role>` uploads your test logs to an Amazon S3 bucket named `cloudformationmanageduploadinfrast-artifactbucket-<RandomId>` under the following path:

    `CloudFormation/ContractTestResults/<TypeName>/<ContractTestInvocationToken>.zip`

    Download the zip file to see your test logs.
+ If you run contract tests against your registered type through the `TestType`, both logs are condensed and uploaded to an Amazon S3 bucket in your account. You must specify the [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_TestType.html#API_TestType_RequestParameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_TestType.html#API_TestType_RequestParameters) parameter when invoking `TestType` to receive logs in your account.

## Testing resource types manually
<a name="manual-testing"></a>

Running contract tests with the `cfn-test` command uses the AWS SAM CLI, so it's possible to attach a debugger from your IDE by specifying a port when you start the local Lambda service. However, we don't suggest this approach because the debugger detaches after each individual handler invocation completes.

Instead, you can mimic the scenarios modeled in contract tests by invoking the handlers with the [https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html) command. This allows you to step through each handler invocation without interruption. You first need to define test templates that AWS SAM can run against the resource handlers. Create the test templates in a separate folder in the resource directory and name the folder `sam-tests`.

The test templates must adhere to the following format:

```
{
  "credentials": {
    "accessKeyId": "<Access Key Id>",
    "secretAccessKey": "<Secret Access key>",
    "sessionToken": "<Session Token>"
  },
  "action": "<Action>",
  "request": {
    "clientRequestToken": "<Random UUID>",
    "desiredResourceState": <ResourceModel json>,
    "logicalResourceIdentifier": "<Logical Id>"
  },
  "callbackContext": <CallbackContext json>
}
```
+ For `credentials`, use temporary credentials for an IAM role (such as `PowerUserAccess` or `Developer`) in your personal AWS account. Replace the `accessKeyId`, `secretAccessKey`, and `sessionToken` with their corresponding values. For instructions on how to copy IAM role credentials from the AWS access portal, see [Manual credential refresh](https://docs.aws.amazon.com/singlesignon/latest/userguide/howtogetcredentials.html#how-to-get-temp-credentials-manual) in the *AWS IAM Identity Center User Guide*. The settings for the IAM role you choose determine [how long the temporary credentials are valid](https://docs.aws.amazon.com/singlesignon/latest/userguide/howtosessionduration.html). 

  Using the AWS CLI, you can call an [AWS STS API](https://docs.aws.amazon.com/STS/latest/APIReference/) like `AssumeRole` or `GetFederationToken` and then capture the resulting output. For more information, see [Using temporary credentials with AWS resources](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html) in the *IAM User Guide*. 
**Note**  
In the past, it was common practice to use persistent credentials, such as IAM user credentials or even root credentials, but this is not recommended. For more information, see [Security best practices in IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) in the *IAM User Guide*. 
+ For `action`, specify the handler you want to test. Allowed values: `CREATE`, `READ`, `DELETE`, `UPDATE`, `LIST`.
+ For `clientRequestToken`, specify a random UUID string. To retrieve this, use any UUID generator tool.
+ For `desiredResourceState`, specify the properties of the resource required for the request that follow the resource schema.
+ For `logicalResourceIdentifier`, specify a logical ID to assign to your resource instance. You can use this in subsequent handler invocations for the same resource.
+ For `callbackContext`, the request begins with a null value for callback context. For handlers with stabilization logic, the subsequent requests have callback context from the previous request's response.

Once you've written the input files, do the following to debug your handlers:

1. Ensure that Docker is downloaded and installed on your machine, and that you've added the resource directory to Docker.

1. In one terminal, start the local Lambda service by running `sam local start-lambda`.

1. In your IDE, create a remote configuration and add a port number. Add a breakpoint in the appropriate handler you are invoking.

1. In another terminal, invoke the handler by running `sam local invoke TestEntrypoint --event sam-tests/<input filename> -d <PORT number>`.

1. Step through the code to debug any handler errors.

For more information about testing using the AWS SAM CLI, see [Testing and Debugging Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-test-and-debug.html) in the *AWS Serverless Application Model Developer Guide*.

# Resource type handler contract
<a name="resource-type-test-contract"></a>

The resource type handler contract specifies the expected and required behavior to which a resource must adhere in each given event handler. It defines a set of specific, unambiguous rules with which `create`, `read`, `update`, `delete` and `list` resource handlers must comply. Following the contract will allow customers to interact with all resource types under a uniform set of behaviors and expectations, and prevents creation of unintended or duplicate resources.

A resource implementation MUST pass all resource contract tests to be registered.

Assuming no other concurrent interaction on the resource, the handlers must comply with the following contract.

All terminology in the handler contract requirements adheres to the [RFC 2119 specification](https://www.ietf.org/rfc/rfc2119.txt).

## Create handlers
<a name="resource-type-test-contract-create"></a>

CloudFormation invokes the `create` handler when the resource is created during a stack create operation.

### Input assumptions
<a name="resource-type-test-contract-create-in"></a>

The `create` handler can make the following assumptions about input submitted to it:
+ The input to a `create` handler MUST be valid against the resource schema.

### Output requirements
<a name="resource-type-test-contract-create-out"></a>

The `create` handler must adhere to the following requirements regarding its output:
+ A `create` handler MUST always return a ProgressEvent object within 60 seconds. For more information, see [ProgressEvent Object Schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html).

  In every ProgressEvent object, the `create` handler MUST return a model which conforms to the shape of the resource schema. For more information, see [Returned models must conform to the shape of the schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-shape).

  Every model MUST include the primaryIdentifier. The only exception is if the first progress event is `FAILED`, and the resource hasn't yet been created. In this case, a subsequent `read` call MUST return `NotFound`.
+ A `create` handler MUST NOT return `SUCCESS` until it has applied all properties included in the `create` request. For more information, see [Update, create, and delete handlers must satisfy desired-state stabilization](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-stabilization).
  + A `create` handler MUST return IN\$1PROGRESS if it hasn't yet reached the desired-state.

    A `create` handler SHOULD return a model containing all properties set so far and nothing more during each IN\$1PROGRESS event.
  + A `create` handler MUST return FAILED progress event if it can't reach the desired-state within the timeout specified in the resource schema.

    The progress event MUST return an error message and the most applicable error code. For more information, see [Handler error codes](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract-errors.html).
  + A `create` handler MAY return SUCCESS once it reaches the desired-state.

    Once the desired state has been reached, a `create` handler MAY perform runtime-state stabilization. For more information, see [Update and create handlers should satisfy runtime-state stabilization](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-runtime).

    When the `create` handler returns SUCCESS, it MUST return a ProgressEvent object containing a model that satisfies the following requirements:
    + All properties specified in the `create` request MUST be present in the model returned, and they MUST match exactly, with the exception of properties defined as writeOnlyProperties in the resource schema.
    + The model MUST contain all properties that have values, including any properties that have default values, and any readOnlyProperties as defined in the resource schema.
    + The model MUST NOT return any properties that are null or don't have values.
+ After a `create` operation returns SUCCESS, a subsequent `read` request MUST succeed when passed in the primaryIdentifier or any additionalIdentifiers associated with the provisioned resource instance.
+ After a `create` operation returns SUCCESS, a subsequent `list` operation MUST return the primaryIdentifier associated with the provisioned resource instance.

  If the `list` operation is paginated, the entire `list` operation is defined as all `list` requests until the `nextToken` is `null`.
+ A `create` handler MUST be idempotent. A `create` handler MUST NOT create multiple resources given the same idempotency token.
+ A `create` handler MUST return FAILED with an AlreadyExists error code if the resource already existed before the create request.

## Update handlers
<a name="resource-type-test-contract-update"></a>

CloudFormation invokes the `update` handler when the resource is updated during an update operation.

### Input assumptions
<a name="resource-type-test-contract-update-in"></a>

The `update` handler can make the following assumptions about input submitted to it:
+ The input to an `update` handler MUST be valid against the resource schema.
+ Any `createOnlyProperties` specified in update handler input MUST NOT be different from their previous state.
+ The input to an `update` handler MUST contain either the `primaryIdentifier` or an `additionalIdentifier`.

### Output requirements
<a name="resource-type-test-contract-update-out"></a>

The `update` handler must adhere to the following requirements:
+ An `update` handler MUST always return a ProgressEvent object within 60 seconds. For more information, see [ProgressEvent Object Schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html).

  In every ProgressEvent object, the `update` handler MUST return a model which conforms to the shape of the resource schema. For more information, see [Returned models must conform to the shape of the schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-shape).

  Every model MUST include the primaryIdentifier.

  The primaryIdentifier returned in every progress event must match the primaryIdentifier passed into the request.
+ An `update` handler MUST NOT return `SUCCESS` until it has applied all properties included in the `update` request. For more information, see [Update, create, and delete handlers must satisfy desired-state stabilization](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-stabilization).
  + An `update` handler MUST return IN\$1PROGRESS if it hasn't yet reached the desired-state.

    An `update` handler SHOULD return a model containing all properties set so far and nothing more during each IN\$1PROGRESS event.
  + An `update` handler MUST return FAILED progress event if it can't reach the desired-state within the timeout specified in the resource schema.

    The progress event MUST return an error message and the most applicable error code. For more information, see [Handler error codes](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract-errors.html).
  + An `update` handler MAY return SUCCESS once it reaches the desired-state.

    Once the desired state has been reached, an `update` handler MAY perform runtime-state stabilization. For more information, see [Update and create handlers should satisfy runtime-state stabilization](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-runtime).

    When the `update` handler returns SUCCESS, it MUST return a ProgressEvent object containing a model that satisfies the following requirements:
    + All properties specified in the `update` request MUST be present in the model returned, and they MUST match exactly, with the exception of properties defined as writeOnlyProperties in the resource schema.
    + The model MUST contain all properties that have values, including any properties that have default values, and any readOnlyProperties as defined in the resource schema.
    + The model MUST NOT return any properties that are null or don't have values.

  All list or collection properties MUST be applied in full. The successful outcome MUST be replacement of the previous properties, if any.
+ An `update` handler MUST return FAILED with a `NotFound` error code if the resource didn't exist before the `update` request.
+ An `update` handler MUST NOT create a new physical resource.

## Delete handlers
<a name="resource-type-test-contract-delete"></a>

CloudFormation invokes the `delete` handler when the resource, or entire stack, is deleted during a stack delete operation.

### Input assumptions
<a name="resource-type-test-contract-delete-in"></a>

The `delete` handler can make the following assumptions about input submitted to it:
+ The input to a `delete` handler MUST contain either the `primaryIdentifier` or an `additionalIdentifier`. Any other properties MAY NOT be included in the request.

### Output requirements
<a name="resource-type-test-contract-delete-out"></a>

The `delete` handler must adhere to the following requirements:
+ A `delete` handler MUST always return a ProgressEvent object within 60 seconds. For more information, see [ProgressEvent Object Schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html).
+ A `delete` handler MUST NOT return `SUCCESS` until the resource has reached the desired state for deletion. For more information, see [Update, create, and delete handlers must satisfy desired-state stabilization](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-stabilization).
  + A `delete` handler MUST return IN\$1PROGRESS if it hasn't yet reached the desired state.
  + A `delete` handler MUST return FAILED progress event if it can't reach the desired-state within the timeout specified in the resource schema.

    The progress event MUST return an error message and the most applicable error code. For more information, see [Handler error codes](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract-errors.html).
  + A `delete` handler MUST return SUCCESS once it reaches the desired state. (This is because there is no runtime-state stabilization for delete requests.)

    When the `delete` handler returns SUCCESS, the ProgressEvent object MUST NOT contain a model.
+ A `delete` handler MUST return FAILED with a `NotFound` error code if the resource didn't exist before the delete request.
+ Once a `delete` operation successfully completes, any subsequent `update`, `delete`, or `read` request for the deleted resource instance MUST return `FAILED` with a `NotFound` error code.
+ Once a `delete` operation successfully completes, any subsequent `list` operation MUST NOT return the primaryIdentifier associated with the deleted resource instance.

  If the `list` operation is paginated, the 'list operation' is defined as all `list` calls until the `nextToken` is `null`.
+ Once a `delete` operation successfully completes, a subsequent `create` request with the same primaryIdentifier or additionalIdentifiers MUST NOT return `FAILED` with an `AlreadyExists` error code.
+ Once a `delete` operation successfully completes, the resource SHOULD NOT be billable to the client.

## Read handlers
<a name="resource-type-test-contract-read"></a>

CloudFormation invokes the `read` handler when detailed information about the resource needed during a stack update operation.

### Input assumptions
<a name="resource-type-test-contract-read-in"></a>

The `read` handler can make the following assumptions about input submitted to it:
+ The input to a `read` handler MUST contain either the `primaryIdentifier` or an `additionalIdentifier`. Any other properties MAY NOT be included in the request.

### Output requirements
<a name="resource-type-test-contract-read-out"></a>

The `read` handler must adhere to the following requirements regarding its output:
+ A `read` handler MUST always return a ProgressEvent object within 30 seconds. For more information, see [ProgressEvent Object Schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html).

  A `read` handler MUST always return a status of `SUCCESS` or `FAILED`; it MUST NOT return a status of `IN_PROGRESS`.
+ A `read` handler MUST return a model representation that conforms to the shape of the resource schema.
  + The model MUST contain all properties that have values, including any properties that have default values and any `readOnlyProperties` as defined in the resource schema.
  + The model MUST NOT return any properties that are null or don't have values.
+ A `read` handler MUST return `FAILED` with a `NotFound` error code if the resource doesn't exist.

## List handlers
<a name="resource-type-test-contract-list"></a>

CloudFormation invokes the `list` handler when summary information about multiple resources of this resource type is required.
+ A `list` handler MUST always return a ProgressEvent object within 30 seconds. For more information, see [ProgressEvent Object Schema](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html).

  A `list` handler MUST always return a status of `SUCCESS` or `FAILED`; it MUST NOT return a status of `IN_PROGRESS`.
+ A `list` handler MUST return an array of primary identifiers.

  When passed in a `read` request, each `primaryIdentifier` MUST NOT return `FAILED` with `NotFound` error code.
+ A `list` request MUST support pagination by returning a `NextToken`.

  The `NextToken` returned MUST be able to be used in a subsequent `list` request to retrieve the next set of results from the service.

  The `NextToken` MUST be null when all results have been returned.
+ A `list` request MUST return an empty array if there are no resources found.
+ A `list` handler MAY accept a set of properties conforming to the shape of the resource schema as filter criteria.

  The filter should use `AND(&)` when multiple properties are passed in.

## Additional requirements
<a name="resource-type-test-contract-additional"></a>

The following requirements also apply to resource handlers.

### Returned models must conform to the shape of the schema
<a name="resource-type-test-contract-additional-shape"></a>

A model returned in a [ProgressEvent](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html) object MUST always conform to the shape of the resource schema. This means that each property that's returned MUST adhere to its own individual restrictions: correct data type, regex, length, etc. However, the model returned MAY NOT contain all properties defined as required in the json-schema.

More specifically, contract tests validate models based on json-schema [Validation keywords](https://json-schema.org/draft-07/json-schema-validation.html#rfc.section.6).
+ ALL Validation Keywords for the following MUST be observed:
  + Any Instance Type (Section 6.1)
  + Numeric Instances (Section 6.2)
  + Strings (Section 6.3)
  + Arrays (Section 6.4)
+ All Validation Keywords for Objects (Section 6.5) MUST be observed EXCEPT for:
  + required (Section 6.5.3)
  + dependencies (Section 6.5.7)
  + propertyNames (Section 6.5.8)
+ Contract tests won't validate Validation Keywords for:
  + Applying Subschemas Conditionally (Section 6.6)
  + Applying Subschemas With Boolean Logic (Section 6.7)

### Update, create, and delete handlers must satisfy desired-state stabilization
<a name="resource-type-test-contract-additional-stabilization"></a>

Stabilization is the process of waiting for a resource to be in a particular state. Note that reaching the desired-state is mandatory for all handlers before returning SUCCESS.

#### Create and update handlers
<a name="resource-type-test-contract-additional-stabilization-create"></a>

For Create and Update handlers, desired-state stabilization is satisfied when all properties specified in the request are applied as requested. This is verified by calling the Read handler.

In many cases, the desired-state is reached immediately upon completion of a Create/Update API call. However, in some cases, multiple API calls and or wait periods may be required in order to reach this state.

##### Eventual consistency in desired-state stabilization
<a name="resource-type-test-contract-additional-stabilization-consistency"></a>

Eventual consistency means that the result of an API command you run might not be immediately visible to all subsequent commands you run. Handling API eventual consistency is required as part of desired-state stabilization. This is because a subsequent Read call might fail with a NotFound error code.

Amazon EC2 resources are a great example of this. For more information, see [Eventual Consistency](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/query-api-troubleshooting.html#eventual-consistency) in the *Amazon Elastic Compute Cloud API Reference.*

##### Examples of desired-state stabilization
<a name="resource-type-test-contract-additional-stabilization-examples"></a>

For a simple example of desired-state stabilization, consider the implementation of the `create` handler for the `AWS::Logs::MetricFilter` resource: immediately after the handler code completes the call to the `PutMetricFilter` method, the `AWS::Logs::MetricFilter` has achieved its desired state. You can examine the code for this resource in its open-source repository at [github.com/aws-cloudformation/aws-cloudformation-resource-providers-logs](https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-logs).

A more complex example is the implementation of the `update` handler for the `AWS::Kinesis::Stream` resource. The `update` handler must make multiple API calls during an update, including `AddTagsToStream` or `RemoveTagsFromStream`, `UpdateShardCount`, `IncreaseRetentionPeriod` or `DecreaseRetentionPeriod`, and `StartStreamEncryption` or `StopStreamEncryption`. Meanwhile, each API call will set the `StreamStatus` to `UPDATING`, during which time other API operations can't be performed or the API will throw a `ResourceInUseException`. Therefore, to reach the desired state, the handler will need to wait for the `StreamStatus` to become `ACTIVE` in between each API operation.

#### Delete handlers
<a name="resource-type-test-contract-additional-stabilization-delete"></a>

Usually, the definition of *deleted* is obvious. A `delete` API operation will result in the resource being purged from the database, and the resource is no longer describable to the user.

However, sometimes, a deletion will result in the resource leaving an *audit trail*, in which the resource can still be described by service API operations, but can no longer be interacted with by the user. For example, when you delete a CloudFormation stack, it's assigned a status of `DELETE_COMPLETE`, but it can still be returned from a `[DescribeStacks](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeStacks.html)` API call. For resources like this, the desired-state for deletion is when the resource has reached a *terminal, inoperable, and irrecoverable state*. If the resource can continue to be mutated by the user through another API call, then it isn't *deleted*, it's *updated*.

There is no difference between desired-state stabilization and runtime-state stabilization for a delete handler. By definition, once a resource has reached the desired-state for deletion, a subsequent `read` call MUST return `FAILED` with a `NotFound` error code, and a subsequent `create` call with the same primaryIdentifier or additionalIdentifiers MUST NOT return `FAILED` with an `AlreadyExists` error code. Additional restrictions are defined in the contract above.

So in the case of a CloudFormation stack, a `read` handler MUST return `FAILED` with a `NotFound` error code if the stack is `DELETE_COMPLETE`, even though it's audit trail can still be accessed by the DescribeStacks API.

### Update and create handlers should satisfy runtime-state stabilization
<a name="resource-type-test-contract-additional-runtime"></a>

*Runtime-state stabilization* is a process of waiting for the resource to be "ready" to use. Generally, runtime-state stabilization is done by continually describing the resource until it reaches a particular state, though it can take many forms.

Runtime-state stabilization can mean different things for different resources, but the following are common requirements:
+ *Additional mutating API calls can be made on the resource*

  Some resources can't be modified while they're in a particular state.
+ *Dependent resources can consume the resource*

  There may be other resources which need to consume the resource in some way, but can't until it is in a particular state.
+ *Users can interact with the resource*

  Customers may not be able to use the resource until it is in a particular status. This usually overlaps with the dependent resources requirement, although there could be different qualifications, depending on the resources.

While desired-state stabilization is mandatory, runtime-state stabilization is optional but encouraged. Users have come to expect that once a resource is COMPLETE, they will be able to use it.

#### Examples of run-time stabilization
<a name="resource-type-test-contract-additional-runtime-examples"></a>

For a simple example of run-time stabilization, consider the implementation of the `create` handler for the `AWS::KinesisFirehose::DeliveryStream` resource. The `create` handler invokes only a single API, `CreateDeliveryStream`, in order for the resource to reach its desired state. Immediately after this API call is made, a `read` request will return the correct desired state. However, the resource still has not reached run-time stabilization because it can't be used by the customer or downstream resources until the `DeliveryStreamStatus` is `ACTIVE`.

For a more complex example, consider the implementation of the `update` handler for the `AWS::Kinesis::Stream` resource once again. Once the `update` handler has made its final call, to `StartStreamEncryption` or `StopStreamEncryption` as described in [Examples of desired-state stabilization](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-additional-stabilization-examples), the resource has reached its desired state. However, like the other API calls on the Kinesis resource, the `StreamStatus` will again be set to `UPDATING`. During this period, it has reached its desired state, and customers can even continue using the stream. But it hasn't yet achieved runtime-stabilization, because additional API calls cannot be made on the resource until the `StreamStatus` gets set to `ACTIVE`.

### Handlers must not leak resources
<a name="resource-type-test-contract-additional-leaking"></a>

*Resource leaking* refers to when a handler loses track of the existence of a resource. This happens most often in the following cases:
+ A `create` handler isn't idempotent. Re-invoking the handler with the same idempotencyToken will cause another resource to be created, and the handler is only tracking a single resource.
+ A `create` handler creates the resource, but is unable to communicate an identifier for that resource back to CloudFormation. A subsequent `delete` call doesn't have enough information to delete the resource.
+ A bug in the `delete` handler causes the resource to not actually be deleted, but the `delete` handler reports that the resource was successfully deleted.

# Contract tests for resource types
<a name="contract-tests"></a>

As part of testing your resource, the CloudFormation CLI performs a suite of tests, each written to test a requirement contained in the [resource type handler contract](resource-type-test-contract.md). Each handler invocation is expected to follow the general requirements for that handler listed in the contract. This topic lists tests that explicitly test some more specific requirements.

## `create` handler tests
<a name="contract-tests-create"></a>

The CloudFormation CLI performs the following contract tests for `create` handlers.


| Test | Description | 
| --- | --- | 
|  `contract_create_create`  |  Creates a resource, waits for the resource creation to complete, and then creates the resource again with the expectation that the second create operation will fail with the `AlreadyExists` error code. This test isn't run for resources if the primary identifier or any additional identifiers are read-only.  | 
|  `contract_create_read`  |  Creates a resource, waits for the resource creation to complete, and then reads the created resource to ensure that the input to the `create` handler is equal to the output from the `read` handler. The comparison ignores any read-only/generated properties in the `read` output, as create input can't specify these. It also ignores any write-only properties in the create input, as these are removed from read output to avoid security issues.  | 
|  `contract_create_delete`  |  Creates a resource, waits for the resource creation to complete, and then deletes the created resource. It also checks if the create input is equal to the create output (which is then used for delete input), with the exception of readOnly and writeOnly properties.  | 
|  `contract_create_list`  |  Creates a resource, waits for the resource creation to complete, and then lists out the resources with the expectation that the created resource exists in the returned list.  | 

## `update` handler tests
<a name="contract-tests-update"></a>

The CloudFormation CLI performs the following contract tests for `update` handlers.


| Test | Description | 
| --- | --- | 
|  `contract_update_read`  |  Creates a resource, updates the resource, and then reads the resource to check that the update was made by comparing the read output with the update input. The comparison excludes read-only and write-only properties because they can't be included in the update input and read output, respectively.  | 
|  `contract_update_list`  |  Creates a resource, updates the resource, and then lists the resource to check that the updated resource exists in the returned list.  | 
|  `contract_update_without_create`  |  Updates a resource without creating it first. The test expects the update operation to fail with the `NotFound` error code.  | 

## `delete` handler tests
<a name="contract-tests-delete"></a>

The CloudFormation CLI performs the following contract tests for `delete` handlers.


| Test | Description | 
| --- | --- | 
|  `contract_delete_create`  |  Creates a resource, deletes the resource, and then creates the resource again with the expectation that the deletion was successful and a new resource can be created. The CloudFormation CLI performs this contract test for resources with create-only primary identifiers.  | 
|  `contract_delete_update`  |  Creates a resource, deletes the resource, and then updates the resource with the expectation that the update operation will fail with the `NotFound` error code.  | 
|  `contract_delete_read`  |  Creates a resource, deletes the resource, and then reads the resource with the expectation that the read operation will fail with the `NotFound` error code.  | 
|  `contract_delete_list`  |  Creates a resource, deletes the resource, and then lists the resource with the expectation that the returned list doesn't contain the deleted resource.  | 
|  `contract_delete_delete`  |  Creates a resource, deletes the resource, and then deletes the resource again with the expectation that the second delete operation will fail with the `NotFound` error code.  | 

# Resource type handler error codes
<a name="resource-type-test-contract-errors"></a>

One of the following error codes MUST be returned from the handler whenever there is a [progress event](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-progressevent.html) with an operation status of `FAILED`.
+ `AccessDenied`

  The customer has insufficient permissions to perform this request.

  *Type:* Terminal
+ `AlreadyExists`

  The specified resource already existed before the execution of this handler. This error is applicable to `create` handlers only.

  *Type:* Terminal
+ `GeneralServiceException`

  The downstream service generated an error that doesn't map to any other handler error code.

  *Type:* Terminal
+ `InternalFailure`

  An unexpected error occurred within the handler.

  *Type:* Terminal
+ `InvalidCredentials`

  The credentials provided by the user are invalid.

  *Type:* Terminal
+ `InvalidRequest`

  Invalid input from the user has generated a generic exception.

  *Type:* Terminal
+ `NetworkFailure`

  The request couldn't be completed due to networking issues, such as a failure to receive a response from the server.

  *Type:* Retriable
+ `NotFound`

  The specified resource doesn't exist, or is in a terminal, inoperable, and irrecoverable state.

  *Type:* Terminal
+ `NotStabilized`

  The downstream resource failed to complete all of its ready-state checks.

  *Type:* Terminal
+ `NotUpdatable`

  The user has requested an update to a property defined in the resource type schema as a [create-only property](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html#schema-properties-createonlyproperties). This error is applicable to `update` handlers only.

  *Type:* Terminal
+ `ResourceConflict`

  The resource is temporarily unavailable to be acted upon. For example, if the resource is currently undergoing an operation and can't be acted upon until that operation is finished.

  *Type:* Retriable
+ `ServiceInternalError`

  The downstream service returned an internal error, typically with a `5XX` HTTP Status code.

  *Type:* Retriable
+ `ServiceLimitExceeded`

  A non-transient resource limit was reached on the service side.

  *Type:* Terminal
+ `Throttling`

  The request was throttled by a downstream service.

  *Type:* Retriable

# ProgressEvent object schema
<a name="resource-type-test-progressevent"></a>

A `ProgressEvent` is a JSON object which represents the current operation status of the handler, the current live state of the resource, and any additional resource information the handler wishes to communicate to the CloudFormation CLI. Each handler MUST communicate a progress event to the CloudFormation CLI under certain circumstances, and SHOULD communicate a progress event under others. For more information, see [Handler Communication Contract](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract.html#resource-type-test-contract-communication).

A handler MAY use progress events on a re-invocation to continue work from where it left off. For a detailed discussion of this, see [Progress chaining, stabilization and callback pattern](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-develop-stabilize.html).

## Syntax
<a name="resource-type-test-progressevent-syntax"></a>

Below is the syntax for the ProgressEvent object.

```
{
    "OperationStatus": "string",
    "HandlerErrorCode": "string",
    "Message": "string",
    "CallbackContext": "string",
    "CallbackDelaySeconds": "string",
    "ResourceModel": "string",
    "ResourceModels": [
        "string"
    ],
    "NextToken": "string",
    }
```

## Properties
<a name="resource-type-schema-properties"></a>

`OperationStatus`  <a name="progressevent-properties-OperationStatus"></a>
Indicates whether the handler has reached a terminal state or is still computing and requires more time to complete.  
Values: `PENDING` \$1 `IN_PROGRESS` \$1 `SUCCESS` \$1 `FAILED`  
 *Required*: No

`HandlerErrorCode`  <a name="progressevent-properties-HandlerErrorCode"></a>
A handler error code should be provided when the event operation status is `FAILED` or `IN_PROGRESS`.  
For a list of handler error codes, see [Handler Error Codes](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-test-contract-errors.html).  
 *Required*: Conditional. A handler error codes MUST be returned from the handler whenever there is a progress event with an operation status of `FAILED`.

`Message`  <a name="progressevent-properties-Message"></a>
Information which can be shown to users to indicate the nature of a progress transition or callback delay.  
 *Required*: No

`CallbackContext`  <a name="progressevent-properties-CallbackContext"></a>
Arbitrary information which the handler can return in an event with operation status of `IN_PROGRESS`, to allow the passing through of additional state or metadata between subsequent retries. For example, to pass through a resource identifier which can be used to continue polling for stabilization.  
For more detailed examples, see [Progress chaining, stabilization and callback pattern](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-develop-stabilize.html).  
*Required*: No

`CallbackDelaySeconds`  <a name="progressevent-properties-CallbackDelaySeconds"></a>
A callback will be scheduled with an initial delay of no less than the number of seconds specified.  
Set this value to less than 0 to indicate no callback should be made.  
*Required*: No

`ResourceModel`  <a name="progressevent-properties-ResourceModel"></a>
Resource model returned by a `read` or `list` operation response for synchronous results, or for final response validation/confirmation by `create`, `update`, and `delete` operations.  
*Required*: No

`ResourceModels`  <a name="progressevent-properties-ResourceModels"></a>
List of resource models returned by a `list` operation response for synchronous results.  
 *Required*: Conditional. Required for List handlers.

`NextToken`  <a name="progressevent-properties-NextToken"></a>
Token used to request additional pages of resources from a `list` operation response.  
*Required*: Conditional. Required for List handlers.

# Progress chaining, stabilization, and callback pattern
<a name="resource-type-develop-stabilize"></a>

Often when you develop CloudFormation resource types, when interacting with web service APIs you need to chain them in sequence to apply the desired state. CloudFormation provides a framework to write these chain patterns. The framework does a lot of the heavy lifting needed to handle error conditions, throttle when calling downstream API, and more. The framework provides callbacks that the handler can use to inspect and change the behavior when making these service calls.

Most web service API calls follows a typical pattern:

1. Initiate the call context for the API.

1. Transform the incoming resource model properties to the underlying service API request.

1. Make the service call.

1. (Optional) Handle errors.

1. (Optional) Handle stabilization (if you need resource to be in a specific state before you apply the next state).

1. Finalize progress to the next part of the call chain, or indicate successful completion.

In writing the handler, you don't need to do anything special with replay/continuation semantics. The framework ensures that the call chain is effectively resumed from where it was halted. This is essentially useful when the wait time for resource stabilization runs into minutes or even hours.

## Sample: Amazon Kinesis Data Streams integration
<a name="resource-type-develop-stabilize-example"></a>

The following resource model is an example integration for a Kinesis Data Streams operation.

```
{
    "typeName": "AWS::Kinesis::Stream",
    "description": "Resource Type definition for AWS::Kinesis::Stream",
    "definitions": {
        ...
    },
    "properties": {
        "Arn": {
            "type": "string"
        },
        "Name": {
            "type": "string",
            "pattern": "[a-zA-Z0-9_.-]+"
        },
        "RetentionPeriodHours": {
            "type": "integer",
            "minimum": 24,
            "maximum": 168
        },
        "ShardCount": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000
        },
        "StreamEncryption": {
            "$ref": "#/definitions/AWSKinesisStreamStreamEncryption"
        },
        "Tags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "$ref": "#/definitions/Tag"
            },
            "maximum": 50
        }
    }
    ...
}
```

And a sample CloudFormation template for creating this resource in a stack:

```
---
AWSTemplateFormatVersion: '2010-09-09'
Description: AWS MetricFilter
Resources:
  KinesisStream:
    Type: AWS::Kinesis::Stream
    Properties:
      ShardCount: 100
      RetentionPeriodHours: 36
      Tags:
      - Key: '1'
        Value: one
      - Key: '2'
        Value: two
      StreamEncryption:
        EncryptionType: KMS
        KeyId: alias/KinesisEncryption
```

For Kinesis, the stream must first be created with a name and shard count, then tags can be applied, followed by encryption. After creating a stream, but before any other configuration can be applied, the stream must be in an `ACTIVE` state.

Here is the example of the using the progress-chaining and callback pattern to apply state consistently. Note that much of the error handling is delegated to the framework. The CloudFormation CLI provides some error handling on interpreting errors that can be retried after a delay. The framework provides a fluent API that guides the developer with the right set of calls with strong typing and code completion capabilities in IDEs.

```
public class CreateHandler extends BaseKinesisHandler {
    //
    // The handler is provide with a AmazonWebServicesClientProxy that provides 
    // the framework for making calls that returns a ProgressEvent, 
    // which can then be chained to perform the next task.
    //
    protected ProgressEvent<ResourceModel, CallbackContext> 
        handleRequest(final AmazonWebServicesClientProxy proxy,
                      final ResourceHandlerRequest<ResourceModel> request,
                      final CallbackContext callbackContext,
                      final ProxyClient<KinesisClient> client,
                      final Logger logger) {

        ResourceModel model = request.getDesiredResourceState();
        if (model.getName() == null || model.getName().isEmpty()) {
            model.setName(
               IdentifierUtils.generateResourceIdentifier(
                  "stream-", request.getClientRequestToken(), 128));
        }
        //
        // 1) initiate the call context, we are making createStream API call 
        // 
        return proxy.initiate(
            "kinesis:CreateStream", client, model, callbackContext)
            
            //
            // 2) transform Resource model properties to CreateStreamRequest API
            //
            .request((m) ->
                CreateStreamRequest.builder()
                    .streamName(m.getName()).shardCount(m.getShardCount()).build())
                    
            //
            // 3) Make a service call. Handler does not worry about credentials, they 
            //    are auto injected
            //
            .call((r, c) -> 
                c.injectCredentialsAndInvokeV2(r, c.client()::createStream))

            //
            // provide stabilization callback. The callback is provided with 
            // the following parameters 
            //   a. CreateStreamRequest the we transformed in request()
            //   b. CreateStreamResponse that the service returned with successful call
            //   c. ProxyClient<Kinesis>, we provided in initiate call 
            //   d. ResourceModel we provided in initiate call 
            //   f. CallbackContext callback context.
            // 
            //
            .stabilize((_request, _response, _client, _model, _context) -> 
                 isStreamActive(client1, _model, context))
                 
            //
            // Once ACTIVE return progress
            //     
            .progress()
            
            //
            // we then chain to next state, setting tags on the resource.
            // we receive ProgressEvent object from .progress().
            //
            .then(r -> {
                Set<Tag> tags = model.getTags();
                if (tags != null && !tags.isEmpty()) {
                    return setTags(proxy, client, model, callbackContext, false, logger);
                }
                return r;
            })
            
            //
            // we then setRetention...
            //
            .then(r -> {
                Integer retention = model.getRetentionPeriodHours();
                if (retention != null) {
                    return handleRetention(proxy, client, model, DEFAULT_RETENTION, retention, callbackContext, logger);
                }
                return r;
            })
            
            ... // other steps
            
            //
            // finally we wait for Kinesis stream to be ACTIVE
            //
            .then((r) -> waitForActive(proxy, client, model, callbackContext))
            
            //
            // we then delegate to ReadHandler to read the live state and send
            // back successful response.
            //
            .then((r) -> new ReadHandler()
                .handleRequest(proxy, request, callbackContext, client, logger));
    }
}
```

## How to make other calls
<a name="resource-type-develop-stabilize-other-calls"></a>

The same pattern shown here for `CreateStreamRequest` is followed with others as well. The following is `handleRetention` code:

```
protected ProgressEvent<ResourceModel, CallbackContext> 
    handleRetention(final AmazonWebServicesClientProxy proxy,
                    final ProxyClient<KinesisClient> client,
                    final ResourceModel model,
                    final int previous,
                    final int current,
                    final CallbackContext callbackContext,
                    final Logger logger) {
       
       if (current > previous) {
            //
            // 1) initiate the call context, we are making IncreaseRetentionPeriod API call 
            // 
            return proxy.initiate(
                "kinesis:IncreaseRetentionPeriod:" + getClass().getSimpleName(), 
                client, model, callbackContext)
                //
                // 2) transform Resource model properties to IncreaseStreamRetentionPeriodRequest API
                //
                .request((m) ->
                   IncreaseStreamRetentionPeriodRequest.builder()
                       .retentionPeriodHours(current)
                       .streamName(m.getName()).build())
                //
                // 3) Make a service call. Handler does not worry about credentials, they 
                //    are auto injected
            
                // Add important comments like shown below
                // https://docs.aws.amazon.com/kinesis/latest/APIReference/API_IncreaseStreamRetentionPeriod.html
                // When applying change if stream is not ACTIVE, we get ResourceInUse.
                // We filter this expection back off and then re-try to set this.
               
                //
                // set new retention period
                //
                .call((r, c) -> c.injectCredentialsAndInvokeV2(r, c.client()::increaseStreamRetentionPeriod))
                
                //
                // Filter ResoureceInUse or LimitExceeded. 
                // Currently LimitExceeded is issued even for throttles
                //
                .exceptFilter(this::filterException).progress();
        } else {
            return proxy.initiate("kinesis:DecreaseRetentionPeriod:" + getClass().getSimpleName(), client, model, callbackContext)
                //
                // convert to API model
                //
                .request(m -> DecreaseStreamRetentionPeriodRequest.builder().retentionPeriodHours(current).streamName(m.getName())
                    .build())
                ... // snipped for brevity
                .exceptFilter(this::filterException).progress();
        }
}

protected boolean filterException(AwsRequest request,
                                  Exception e,
                                  ProxyClient<KinesisClient> client,
                                  ResourceModel model,
                                  CallbackContext context) {
    return e instanceof ResourceInUseException || 
           e instanceof LimitExceededException;
}
```