Resource type handler contract
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
Create handlers
CloudFormation invokes the create
handler when the resource is created during a stack create operation.
Input assumptions
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
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.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.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 subsequentread
call MUST returnNotFound
. -
A
create
handler MUST NOT returnSUCCESS
until it has applied all properties included in thecreate
request. For more information, see Update, create, and delete handlers must satisfy desired-state stabilization.-
A
create
handler MUST return IN_PROGRESS 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_PROGRESS 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.
-
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.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 subsequentread
request MUST succeed when passed in the primaryIdentifier or any additionalIdentifiers associated with the provisioned resource instance. -
After a
create
operation returns SUCCESS, a subsequentlist
operation MUST return the primaryIdentifier associated with the provisioned resource instance.If the
list
operation is paginated, the entirelist
operation is defined as alllist
requests until thenextToken
isnull
. -
A
create
handler MUST be idempotent. Acreate
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
CloudFormation invokes the update
handler when the resource is updated during an update operation.
Input assumptions
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 theprimaryIdentifier
or anadditionalIdentifier
.
Output requirements
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.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.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 returnSUCCESS
until it has applied all properties included in theupdate
request. For more information, see Update, create, and delete handlers must satisfy desired-state stabilization.-
An
update
handler MUST return IN_PROGRESS 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_PROGRESS 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.
-
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.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 aNotFound
error code if the resource didn't exist before theupdate
request. -
An
update
handler MUST NOT create a new physical resource.
Delete handlers
CloudFormation invokes the delete
handler when the resource, or entire stack, is deleted during a stack delete operation.
Input assumptions
The delete
handler can make the following assumptions about input
submitted to it:
-
The input to a
delete
handler MUST contain either theprimaryIdentifier
or anadditionalIdentifier
. Any other properties MAY NOT be included in the request.
Output requirements
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. -
A
delete
handler MUST NOT returnSUCCESS
until the resource has reached the desired state for deletion. For more information, see Update, create, and delete handlers must satisfy desired-state stabilization.-
A
delete
handler MUST return IN_PROGRESS 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.
-
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 aNotFound
error code if the resource didn't exist before the delete request. -
Once a
delete
operation successfully completes, any subsequentupdate
,delete
, orread
request for the deleted resource instance MUST returnFAILED
with aNotFound
error code. -
Once a
delete
operation successfully completes, any subsequentlist
operation MUST NOT return the primaryIdentifier associated with the deleted resource instance.If the
list
operation is paginated, the 'list operation' is defined as alllist
calls until thenextToken
isnull
. -
Once a
delete
operation successfully completes, a subsequentcreate
request with the same primaryIdentifier or additionalIdentifiers MUST NOT returnFAILED
with anAlreadyExists
error code. -
Once a
delete
operation successfully completes, the resource SHOULD NOT be billable to the client.
Read handlers
CloudFormation invokes the read
handler when detailed information about the resource needed during a stack update operation.
Input assumptions
The read
handler can make the following assumptions about input
submitted to it:
-
The input to a
read
handler MUST contain either theprimaryIdentifier
or anadditionalIdentifier
. Any other properties MAY NOT be included in the request.
Output requirements
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.A
read
handler MUST always return a status ofSUCCESS
orFAILED
; it MUST NOT return a status ofIN_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 returnFAILED
with aNotFound
error code if the resource doesn't exist.
List handlers
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.A
list
handler MUST always return a status ofSUCCESS
orFAILED
; it MUST NOT return a status ofIN_PROGRESS
. -
A
list
handler MUST return an array of primary identifiers.When passed in a
read
request, eachprimaryIdentifier
MUST NOT returnFAILED
withNotFound
error code. -
A
list
request MUST support pagination by returning aNextToken
.The
NextToken
returned MUST be able to be used in a subsequentlist
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
The following requirements also apply to resource handlers.
Returned models must conform to the shape of the schema
A model returned in a ProgressEvent 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
-
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
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
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
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 in the Amazon Elastic Compute Cloud API Reference.
Examples of desired-state stabilization
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
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
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
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
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
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, 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
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 subsequentdelete
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 thedelete
handler reports that the resource was successfully deleted.