Modeling resource types to use with AWS CloudFormation - Extension Development for CloudFormation

Modeling resource types to use with AWS CloudFormation

The first step in creating a resource type is modeling that resource, which involves crafting a schema that defines the resource, its properties, and their attributes. When you initially create your resource type project using the CloudFormation CLI init command, one of the files created is an example resource schema. Use this schema file as a starting point for defining the shape and semantics of your resource type.

Note

When naming your extension, we recommend that you don't use the following namespaces: aws, amzn, alexa, amazon, awsquickstart. CloudFormation doesn't block private registration using cfn submit for types whose names include these namespaces, but you won't be able to publish these types.

In order to be considered valid, your resource type's schema must adhere to the Resource type definition schema. This meta-schema provides a means of validating your resource specification during resource development.

The Resource Type Definition Schema is a meta-schema that extends draft-07 of the JSON Schema. To simplify authoring resource specifications, the Resource Type Definition Schema constrains the scope of the full JSON Schema standard in terms of how certain validations can be expressed, and encourages consistent modeling for all resource schemas. (For full details on how the Resource Type Definition Schema differs from the full JSON schema, see Divergence From JSON Schema.)

Once you have defined your resource schema, you can use the CloudFormation CLI validate command to verify that the resource schema is valid.

In terms of testing, the resource schema also determines:

  • What unit test stubs are generated in your resource package, and what contract tests are appropriate to run for the resource. When you run the CloudFormation CLI generate command, the CloudFormation CLI generates empty unit tests based on the properties of the resource and their attributes.

  • Which contract tests are appropriate for CloudFormation CLI to run for your resources. When you run the test command, the CloudFormation CLI runs the appropriate contract tests, based on which handlers are included in your resource schema.

Note

Make sure your Lambda runtimes are up-to-date to avoid using a deprecated version. For more information, see Updating Lambda runtimes for resource types and hooks.

Defining property attributes

Certain properties of a resource may have special meaning when used in different contexts. For example, a given resource property may be read-only when read back for state changes, but can be specified when used as the target of a $ref from a related resource. Because of this semantic difference in how this property metadata should be interpreted, certain property attributes are defined at the resource level, rather than at a property level.

These attributes include:

  • primaryIdentifier

  • additionalIdentifiers

  • createOnlyProperties

  • readOnlyProperties

  • writeOnlyProperties

For reference information on resource schema elements, see Resource type schema.

How to define a minimal resource

The example below displays a minimal resource type definition. In this case, the resource consists of a single optional property, Name, which is also specified as its primary (and only) identifier.

Note that this resource schema would require a handlers section with the create, read, and update handlers specified in order for the resource to actually be provisioned within a CloudFormation account.

{ "typeName": "myORG::myService::myResource", "properties": { "Name": { "description": "The name of the resource.", "type": "string", "pattern": "^[a-zA-Z0-9_-]{0,64}$", "maxLength": 64 } }, "createOnlyProperties": [ "/properties/Name" ], "identifiers": [ [ "/properties/Name" ] ], "additionalProperties": false }

Defining the account-level configuration of an extension

There might be cases where your extension includes properties that the user must specify for all instances of the extension in a given account and Region. In such cases, you can define those properties in a configuration definition that the user then sets at the Region level. For example, if your extension needs to access a third-party web service, you can include a configuration for the user to specify their credentials for that service.

When the user sets the configuration, CloudFormation validates it against the configuration definition, and then saves this information at the Region level. From then on, CloudFormation can access that configuration during operations involving any instances of that extension in the Region. Configurations are available to CloudFormation during all resource operations, including read and list events that don't explicitly involve a stack template.

Note

Configuration definitions aren't compatible with module extensions.

Your configuration definition must validate against the provider configuration definition meta-schema.

The CloudFormation property name is reserved, and can't be used to define any properties in your configuration definition.

Use the typeConfiguration element of the provider definition meta-schema to include the configuration definition as part of your extension's schema.

Important

It's strongly recommended that you use dynamic references to restrict sensitive configuration definitions, such as third-party credentials, as in the example below. For more details on dynamic references, see Using dynamic references to specify template values in the AWS CloudFormation User Guide.

Example: Defining a configuration definition to specify third-party credentials

The following example illustrates how you might model third-party credentials in an extension. The schema below for the MyOrg::MyService::Resource resource type includes a typeConfiguration section. The configuration definition includes a required property, ServiceCredentials, of type Credentials. As defined in the definitions section, the Credentials type includes two properties for the user to specify their credentials for a third-party service: ApiKey and ApplicationKey.

In this example, both properties must be dynamic references, as represented by the regex pattern for each property. By using dynamic references here, CloudFormation never stores the actual credential values, but instead retrieves them from AWS Secrets Manager or Systems Manager Parameter Store only when necessary. For more information about dynamic references, including how CloudFormation distinguishes which service to retrieve values from, see Using dynamic references to specify template values in the AWS CloudFormation User Guide.

To see how users set configuration data for their extensions, see Configuring extensions at the account level in the AWS CloudFormation User Guide.

{ "typeName": "MyOrg::MyService::Resource", "description": "Example resource type that requires third-party credentials", "additionalProperties": false, "typeConfiguration": { "properties": { "ServiceCredentials": { "$ref": "#/definitions/Credentials" } }, "additionalProperties": false, "required": [ "ServiceCredentials" ] }, "definitions": { "Credentials": { "type": "object", "properties": { "ApiKey": { "description": "Third-party API key", "type": "string", "pattern": "{{resolve:.*:[a-zA-Z0-9_.-/]+}}" }, "ApplicationKey": { "description": "Third-party application key", "type": "string", "pattern": "{{resolve:.*:[a-zA-Z0-9_.-/]+}}" } }, "additionalProperties": false } }, "properties": { "Id": { "type": "string" }, "Name": { "type": "string" } }, "primaryIdentifier": [ "/properties/Id" ], "additionalIdentifiers": [ ["/properties/Name"] ], "handlers": { } }