Skip to main content

A base class for AWS CloudFormation Custom Resource Providers.

Project description

This ResourceProvider base class makes it very simple to implement a Custom CloudFormation Resource.

First, you inherit from the base class and specify a JSON schema which defines the resource properties you require:

from cfn_resource_provider import ResourceProvider

class SecretProvider(ResourceProvider):
    def __init__(self):
            super(SecretProvider, self).__init__()
            self.request_schema =  {
                "type": "object",
                "required": ["Name"],
                "properties": {
                    "Name": {"type": "string",
                             "minLength": 1,
                             "pattern": "[a-zA-Z0-9_/]+",
                             "description": "the name of the value in the parameters store"},
                    "Description": {"type": "string",
                                    "default": "",
                                    "description": "the description of the value in the parameter store"},
                    "Alphabet": {"type": "string",
                                 "default": "abcdfghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_",
                                 "description": "the characters from which to generate the secret"},
                    "ReturnSecret": {"type": "boolean",
                                     "default": False,
                                     "description": "return secret as attribute 'Secret'"},
                    "KeyAlias": {"type": "string",
                                 "default": "alias/aws/ssm",
                                 "description": "KMS key to use to encrypt the value"},
                    "Length": {"type": "integer",
                               "minimum": 1, "maximum": 512,
                               "default": 30,
                               "description": "length of the secret"}
                }
            }

The JSON schema allows you to specify the expected properties, constraints and default values. After that, you only need to implement the methods create, update and delete:

class SecretProvider(ResourceProvider):
    ...
    def create(self):
        try:
            value = "".join(choice(self.get('Alphabet') for x in range(0, self.get('Length')))
            self.ssm.put_parameter(Name=self.get('Name'), KeyId=self.get('KeyAlias'),
                                   Type='SecureString', Overwrite=False, Value=value)
            self.set_attribute('Arn', self.arn)
            if self.get('ReturnSecret'):
                self.set_attribute('Secret', value)

            self.physical_resource_id = self.arn
        except ClientError as e:
            self.physical_resource_id = 'could-not-create'
            self.fail(str(e))

    def create(self):
        ....

    def delete(self):
        ....

In these methods, you can safely access all the properties defined in your JSON schema. The methods are only called after validation of the request against your schema.

  • to return values which can be accessed by Fn::GetAtt, you can call the method set_attribute.

  • to return a resource id for your resource, you can set the property physical_resource_id.

  • to indicate a failed request, you can call the method fail.

  • to indicate a succesful request, you can call the method success.

Finally, at the end of your module implement the AWS Lambda handle function:

provider = SecretProvider()
def handle(request, context):
    provider.handle(request, context)

Processing boolean and integer properties

AWS CloudFormation passes all properties in string format, eg ‘true’, ‘false’, ‘123’. This does not go down well with the json schema validator. Therefore, before the validator is called, it calls the method convert_property_types. Use this method to do the conversion of the non string properties:

def convert_property_types(self):
     try:
         if 'Length' in self.properties and isinstance(self.properties['Length'], (str, unicode,)):
             self.properties['Length'] = int(self.properties['Length'])
         if 'ReturnSecret' in self.properties and isinstance(self.properties['ReturnSecret'], (str, unicode,)):
             self.properties['ReturnSecret'] = (self.properties['ReturnSecret'] == 'true')
     except ValueError as e:
         log.error('failed to convert property types %s', e)

it is ok if you cannot convert the values: the validator will report the error for you :-)

Alternatively, you may use the heuristic_convert_property_types method:

def convert_property_types(self):
     self.heuristic_convert_property_types(self.properties)

it will convert all integer strings to int type, and ‘true’ and ‘false’ strings to a boolean type. Recurses through your dictionary.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cfn-resource-provider-1.0.6.tar.gz (6.5 kB view details)

Uploaded Source

File details

Details for the file cfn-resource-provider-1.0.6.tar.gz.

File metadata

  • Download URL: cfn-resource-provider-1.0.6.tar.gz
  • Upload date:
  • Size: 6.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.7.7

File hashes

Hashes for cfn-resource-provider-1.0.6.tar.gz
Algorithm Hash digest
SHA256 004271c1c477ba04e23417a2c28f00172accb28391c264f81575280d6b312d42
MD5 2b8942c880e1717fc6dbc274e74bc083
BLAKE2b-256 4f02fa0d8d896a3c0c3776d3f19526081098496d7f45092029dfc710a5c925b7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page