Hooks

A hook is a python function or class method that is executed before or after the action is taken. To see how to define hooks in a config file see the Pre & Post Hooks documentation.

Built-in Hooks

acm.Certificate

Requirements

  • Route 53 hosted zone

    • authoritative for the domain the certificate is being created for

    • in the same AWS account as the certificate being created

Description

Manage a DNS validated certificate in AWS Certificate Manager.

When used in the pre_build or post_build stage this hook will create a CloudFormation stack containing a DNS validated certificate. It will automatically create a record in Route 53 to validate the certificate and wait for the stack to complete before returning the CertificateArn as hook data. The CloudFormation stack also outputs the ARN of the certificate as CertificateArn so that it can be referenced from other stacks.

When used in the pre_destroy or post_destroy stage this hook will delete the validation record from Route 53 then destroy the stack created during a deploy stage.

If the hook fails during a deploy stage (e.g. stack rolls back or Route 53 can’t be updated) all resources managed by this hook will be destroyed. This is done to avoid orphaning resources/record sets which would cause errors during subsequent runs. Resources effected include the CloudFormation stack it creates, ACM certificate, and Route 53 validation record.

Hook Path

runway.cfngin.hooks.acm.Certificate

Args

alt_names (Optional[List[str]])

Additional FQDNs to be included in the Subject Alternative Name extension of the ACM certificate. For example, you can add www.example.net to a certificate for which the domain field is www.example.com if users can reach your site by using either name.

domain (str)

The fully qualified domain name (FQDN), such as www.example.com, with which you want to secure an ACM certificate. Use an asterisk (*) to create a wildcard certificate that protects several sites in the same domain. For example, *.example.com protects www.example.com, site.example.com, and images.example.com.

hosted_zone_id (str)

The ID of the Route 53 Hosted Zone that contains the resource record sets that you want to change. This must exist in the same account that the certificate will be created in.

stack_name (Optional[str])

Provide a name for the stack used to create the certificate. If not provided, the domain is used (replacing . with -). If the is provided in a deploy stage, its needs to be provided in the matching destroy stage.

ttl (Optional[int])

The resource record cache time to live (TTL), in seconds. (default: 300)

Example

namespace: example
cfngin_bucket: ''

sys_path: ./

pre_build:
  acm-cert:
    path: runway.cfngin.hooks.acm.Certificate
    required: true
    args:
      domain: www.example.com
      hosted_zone_id: ${rxref example-com::HostedZone}

stack:
  sampleapp:
    class_path: blueprints.sampleapp.BlueprintClass
    variables:
      cert_arn: ${rxref www-example-com::CertificateArn}

post_destroy:
  acm-cert:
    path: runway.cfngin.hooks.acm.Certificate
    required: true
    args:
      domain: www.example.com
      hosted_zone_id: ${rxref example-com::HostedZone}

aws_lambda.upload_lambda_functions

Description

Build Lambda payloads from user configuration and upload them to S3.

Constructs ZIP archives containing files matching specified patterns for each function, uploads the result to Amazon S3, then stores objects (of type troposphere.awslambda.Code) in the context’s hook data, ready to be referenced in blueprints.

Configuration consists of some global options, and a dictionary of function specifications. In the specifications, each key indicating the name of the function (used for generating names for artifacts), and the value determines what files to include in the ZIP (see more details below).

If a requirements.txt or Pipfile/Pipfile.lock files are found at the root of the provided path, the hook will use the appropriate method to package dependencies with your source code automatically. If you want to explicitly use pipenv over pip, provide use_pipenv: true for the function.

Docker can be used to collect python dependencies instead of using system python to build appropriate binaries for Lambda. This can be done by including the dockerize_pip configuration option which can have a value of true or non-linux.

Payloads are uploaded to either a custom bucket or the CFNgin default bucket, with the key containing it’s checksum, to allow repeated uploads to be skipped in subsequent runs.

Hook Path

runway.cfngin.hooks.aws_lambda.upload_lambda_functions

Args

bucket (Optional[str])

Custom bucket to upload functions to. Omitting it will cause the default CFNgin bucket to be used.

bucket_region (Optional[str])

The region in which the bucket should exist. If not given, the region will be either be that of the global cfngin_bucket_region setting, or else the region in use by the provider.

prefix (Optional[str])

S3 key prefix to prepend to the uploaded zip name.

follow_symlinks (Optional[bool])

Will determine if symlinks should be followed and included with the zip artifact. (default: False)

payload_acl (Optional[str])

The canned S3 object ACL to be applied to the uploaded payload. (default: private)

functions (Dict[str, Any])

Configurations of desired payloads to build. Keys correspond to function names, used to derive key names for the payload. Each value should itself be a dictionary, with the following data:

docker_file (Optional[str])

Path to a local DockerFile that will be built and used for dockerize_pip. Must provide exactly one of docker_file, docker_image, or runtime.

docker_image (Optional[str])

Custom Docker image to use with dockerize_pip. Must provide exactly one of docker_file, docker_image, or runtime.

dockerize_pip (Optional[Union[str, bool]])

Whether to use Docker when restoring dependencies with pip. Can be set to true/false or the special string non-linux which will only run on non Linux systems. To use this option Docker must be installed.

exclude (Optional[Union[str, List[str]]])

Pattern or list of patterns of files to exclude from the payload. If provided, any files that match will be ignored, regardless of whether they match an inclusion pattern.

Commonly ignored files are already excluded by default, such as .git, .svn, __pycache__, *.pyc, .gitignore, etc.

include (Optional[Union[str, List[str]]])

Pattern or list of patterns of files to include in the payload. If provided, only files that match these patterns will be included in the payload.

Omitting it is equivalent to accepting all files that are not otherwise excluded.

path (str)

Base directory of the Lambda function payload content. If it not an absolute path, it will be considered relative to the directory containing the CFNgin configuration file in use.

Files in this directory will be added to the payload ZIP, according to the include and exclude patterns. If no patterns are provided, all files in this directory (respecting default exclusions) will be used.

Files are stored in the archive with path names relative to this directory. So, for example, all the files contained directly under this directory will be added to the root of the ZIP file.

python_path (Optional[str])

Absolute path to a python interpreter to use for pip/pipenv actions. If not provided, the current python interpreter will be used for pip and pipenv will be used from the current $PATH.

runtime (Optional[str])

Runtime of the AWS Lambda Function being uploaded. Used with dockerize_pip to automatically select the appropriate Docker image to use. Must provide exactly one of docker_file, docker_image, or runtime.

use_pipenv (Optional[bool]):

Will determine if pipenv will be used to generate requirements.txt from an existing Pipfile. To use this option pipenv must be installed.

Example

Hook configuration

pre_build:
  upload_functions:
    path: runway.cfngin.hooks.aws_lambda.upload_lambda_functions
    required: true
    enabled: true
    data_key: lambda
    args:
      bucket: custom-bucket
      follow_symlinks: true
      prefix: cloudformation-custom-resources/
      payload_acl: authenticated-read
      functions:
        MyFunction:
          path: ./lambda_functions
          dockerize_pip: non-linux
          use_pipenv: true
          runtime: python3.8
          include:
            - '*.py'
            - '*.txt'
          exclude:
            - '*.pyc'
            - test/

Blueprint Usage

from troposphere.awslambda import Function
from runway.cfngin.blueprints.base import Blueprint

class LambdaBlueprint(Blueprint):
    def create_template(self):
        code = self.context.hook_data['lambda']['MyFunction']

        self.template.add_resource(
            Function(
                'MyFunction',
                Code=code,
                Handler='my_function.handler',
                Role='...',
                Runtime='python2.7'
            )
        )

build_staticsite.build

Description

Build static site. Used by the staticsite module type.

Hook Path

runway.hooks.staticsite.build_staticsite.build

Args

See staticsite module documentation for details.

cleanup_s3.purge_bucket

Description

Delete objects in bucket. Primarily used as a pre_destroy hook before deleting an S3 bucket.

Hook Path

runway.hooks.cleanup_s3.purge_bucket

Args

bucket_name (str)

Name of the S3 bucket.

bucket_output_lookup (str)

Value to pass to runway.cfngin.lookups.handlers.output.OutputLookup to retrieve an S3 bucket name.

bucket_rxref_lookup (str)

Value to pass to runway.cfngin.lookups.handlers.rxref.RxrefLookup to retrieve an S3 bucket name.

bucket_xref_lookup (str)

Value to pass to runway.cfngin.lookups.handlers.xref.XrefLookup to retrieve an S3 bucket name.

cleanup_ssm.delete_param

Description

Delete SSM parameter. Primarily used when an SSM parameter is created by a hook rather than CloudFormation.

Hook Path

runway.hooks.cleanup_ssm.delete_param

Args

parameter_name (str)

Name of an SSM parameter.

command.run_command

Description

Run a custom command as a hook.

Hook Path

runway.cfngin.hooks.command.run_command

Args

command (Union[str, List[str]])

Command(s) to run.

capture (bool)

If enabled, capture the command’s stdout and stderr, and return them in the hook result. (default: False)

interactive (bool)

If enabled, allow the command to interact with stdin. Otherwise, stdin will be set to the null device. (default: False)

ignore_status (bool)

Don’t fail the hook if the command returns a non-zero status. (default: False)

quiet (bool)

Redirect the command’s stdout and stderr to the null device, silencing all output. Should not be enabled if capture is also enabled. (default: False)

stdin (Optional[str])

String to send to the stdin of the command. Implicitly disables interactive.

env (Optional[Dict[str, str]])

Dictionary of environment variable overrides for the command context. Will be merged with the current environment.

**kwargs (Any)

Any other arguments will be forwarded to the subprocess.Popen function. Interesting ones include: cwd and shell.

Example

pre_build:
  command_copy_environment:
    path: runway.cfngin.hooks.command.run_command
    required: true
    enabled: true
    data_key: copy_env
    args:
      command: ['cp', 'environment.template', 'environment']
  command_git_rev_parse:
    path: runway.cfngin.hooks.command.run_command
    required: true
    enabled: true
    data_key: get_git_commit
    args:
      command: ['git', 'rev-parse', 'HEAD']
      cwd: ./my-git-repo
      capture: true
  command_npm_install:
    path: runway.cfngin.hooks.command.run_command
    args:
      command: '`cd $PROJECT_DIR/project; npm install`'
      env:
        PROJECT_DIR: ./my-project
        shell: true

docker

docker.image.build

Description

Docker image build hook.

Replicates the functionality of the docker image build CLI command.

Hook Path

runway.cfngin.hooks.docker.image.build

Args

docker (Optional[Dict[str, Any]])

Options for docker image build.

buildargs (Optional[Dict[str, str]])

Dict of build-time variables.

custom_context (bool)

Optional if providing a path to a zip file. (default: False)

extra_hosts (Optional[Dict[str, str]])

Extra hosts to add to /etc/hosts in the building containers. Defined as a mapping of hostmane to IP address.

forcerm (bool)

Always remove intermediate containers, even after unsuccessful builds. (default: False)

isolation (Optional[str])

Isolation technology used during build.

network_mode (Optional[str])

Network mode for the run commands during build.

nocache (bool)

Don’t use cache when set to True. (default: False)

platform (Optional[str])

Set platform if server is multi-platform capable. Uses format os[/arch[/variant]].

pull (bool)

Download any updates to the FROM image in the Dockerfile. (default: False)

rm (bool)

Remove intermediate containers. (default: True)

squash (bool)

Squash the resulting image layers into a single layer. (default: False)

tag (Optional[str])

Optional name and tag to apply to the base image when it is built.

target (Optional[str])

Name of the build-stage to build in a multi-stage Dockerfile.

timeout (Optional[int])

HTTP timeout.

use_config_proxy (bool)

If True and if the docker client configuration file (~/.docker/config.json by default) contains a proxy configuration, the corresponding environment variables will be set in the container being built. (default: False)

dockerfile (Optional[str])

Path within the build cont4ext to the Dockerfile. (default: ./Dockerfile)

ecr_repo (Optional[Dict[str, Optional[str]]])

Information describing an ECR repository. This is used to construct the repository URL. If providing a value for this field, do not provide a value for repo.

If using a private registry, only repo_name is required. If using a public registry, repo_name and registry_alias.

account_id (Optional[str])

AWS account ID that owns the registry being logged into. If not provided, it will be acquired automatically if needed.

aws_region (Optional[str])

AWS region where the registry is located. If not provided, it will be acquired automatically if needed.

registry_alias (Optional[str])

If it is a public repository, provide the alias.

repo_name (str)

The name of the repository

path (Optional[str])

Path to the directory continaing the Dockerfile. (defaults to the current working directory)

repo (Optional[str])

URI of a non Docker Hub repository where the image will be stored. If providing one of the other repo values, leave this value empty.

tags (Optional[List[str]])

List of tags to apply to the image. (default: ["latest"])

Returns

The following are values accessible with the hook_data Lookup under the data_key of docker (do not specify a data_key for the hook, this is handled automatically).

image (DockerImage)

A DockerImage object for the image that was just built.

Important

Each execution of this hook overwrites any previous values stored in this attribute. It is advices to consume the resulting image object after it has been built, if it will be consumed by a later hook/stack.

Example

pre_build:
  - path: runway.cfngin.hooks.docker.login
    args:
      ecr: true
      password: ${ecr login-password}
  - path: runway.cfngin.hooks.docker.image.build
    args:
      ecr_repo:
        repo_name: ${cfn ${namespace}-test-ecr.Repository}
      tags:
        - latest
        - python3.9
  - path: runway.cfngin.hooks.docker.image.push
    args:
      image: ${hook_data docker.image}

docker.image.push

Description

Docker image push hook.

Replicates the functionality of the docker image push CLI command.

Hook Path

runway.cfngin.hooks.docker.image.push

Args

ecr_repo (Optional[Dict[str, Optional[str]]])

Information describing an ECR repository. This is used to construct the repository URL. If providing a value for this field, do not provide a value for image or repo.

If using a private registry, only repo_name is required. If using a public registry, repo_name and registry_alias.

account_id (Optional[str])

AWS account ID that owns the registry being logged into. If not provided, it will be acquired automatically if needed.

aws_region (Optional[str])

AWS region where the registry is located. If not provided, it will be acquired automatically if needed.

registry_alias (Optional[str])

If it is a public repository, provide the alias.

repo_name (str)

The name of the repository

image (Optional[DockerImage])

A DockerImage object. This can be retrieved from hook_data for a preceding build using the hook_data Lookup.

If providing a value for this field, do not provide a value for ecr_repo or repo.

repo (Optional[str])

URI of a non Docker Hub repository where the image will be stored. If providing one of the other repo values or image, leave this value empty.

tags (Optional[List[str]])

List of tags push. (default: ["latest"])

Example

pre_build:
  - path: runway.cfngin.hooks.docker.login
    args:
      ecr: true
      password: ${ecr login-password}
  - path: runway.cfngin.hooks.docker.image.build
    args:
      ecr_repo:
        repo_name: ${cfn ${namespace}-test-ecr.Repository}
      tags:
        - latest
        - python3.9
  - path: runway.cfngin.hooks.docker.image.push
    args:
      image: ${hook_data docker.image}

stacks:
  ecr-lambda-function:
    class_path: blueprints.EcrFunction
    variables:
      ImageUri: ${hook_data docker.image.uri.latest}

docker.image.remove

Description

Docker image build hook.

Replicates the functionality of the docker image remove CLI command.

Hook Path

runway.cfngin.hooks.docker.image.remove

Args

ecr_repo (Optional[Dict[str, Optional[str]]])

Information describing an ECR repository. This is used to construct the repository URL. If providing a value for this field, do not provide a value for image or repo.

If using a private registry, only repo_name is required. If using a public registry, repo_name and registry_alias.

account_id (Optional[str])

AWS account ID that owns the registry being logged into. If not provided, it will be acquired automatically if needed.

aws_region (Optional[str])

AWS region where the registry is located. If not provided, it will be acquired automatically if needed.

registry_alias (Optional[str])

If it is a public repository, provide the alias.

repo_name (str)

The name of the repository

force (bool)

Force removal of the image. (default: False)

image (Optional[DockerImage])

A DockerImage object. If providing an Image object from `hook_data, it will be removed from from there as well.

If providing a value for this field, do not provide a value for ecr_repo or repo.

noprune (bool)

Do not delete untagged parents. (default: False)

repo (Optional[str])

URI of a non Docker Hub repository where the image is stored. If providing one of the other repo values or image, leave this value empty.

tags (Optional[List[str]])

List of tags delete. (default: ["latest"])

Example

pre_build:
  - path: runway.cfngin.hooks.docker.login
    args:
      ecr: true
      password: ${ecr login-password}
  - path: runway.cfngin.hooks.docker.image.build
    args:
      ecr_repo:
        repo_name: ${cfn ${namespace}-test-ecr.Repository}
      tags:
        - latest
        - python3.9
  - path: runway.cfngin.hooks.docker.image.push
    args:
      image: ${hook_data docker.image}
      tags:
        - latest
        - python3.9

stacks:
  ...

post_build:
  - path: runway.cfngin.hooks.docker.image.remove
    args:
      image: ${hook_data docker.image}
      tags:
        - latest
        - python3.9

docker.login

Description

Docker login hook.

Replicates the functionality of the docker login CLI command.

This hook does not modify the Docker config file. The credentials/authenticated session is stored in memory and is deleted after processing a given CFNgin config file.

This hook can be executed as a pre or post hook. The authenticated session carries over from pre to post and to each subsequent built-in Docker hook.

Hook Path

runway.cfngin.hooks.docker.login

Args

dockercfg_path (Optional[str])

Use a custom path for the Docker config file ($HOME/.docker/config.json if present, otherwise $HOME/.dockercfg).

ecr (Optional[Union[bool, Dict[str, Optional[str]]]])

Information describing an ECR registry. This is used to construct the registry URL. If providing a value for this field, do not provide a value for registry.

If using a private registry, the value can simply be true. If using a public registry, more information is required.

account_id (Optional[str])

AWS account ID that owns the registry being logged into. If not provided, it will be acquired automatically if needed.

alias (Optional[str])

If it is a public repository, provide the alias.

aws_region (Optional[str])

AWS region where the registry is located. If not provided, it will be acquired automatically if needed.

email (Optional[str])

The email for the registry account.

password (str)

The plaintext password.

registry (Optional[str])

URL to the registry (e.g. https://index.docker.io/v1/).

If providing a value for this field, do not provide a value for ecr.

username (str)

The registry username. Defaults to AWS if supplying ecr.

Example

pre_build:
  - path: runway.cfngin.hooks.docker.login
    args:
      ecr: true
      password: ${ecr login-password}

ecr.purge_repository

Description

Purge all images from an ECR repository.

Hook Path

runway.cfngin.hooks.ecr.purge_repository

Args

repository_name (str)

The name of the ECR repository to purge.

Example

pre_destroy:
  - path: runway.cfngin.hooks.ecr.purge_repository
    args:
      repository_name: example-repo

ecs.create_clusters

Description

Create ECS clusters.

Hook Path

runway.cfngin.hooks.ecs.create_clusters

Args

clusters (List[str])

Names of clusters to create.

iam.create_ecs_service_role

Description

Create ecsServiceRole, which has to be named exactly that currently.

http://docs.aws.amazon.com/AmazonECS/latest/developerguide/IAM_policies.html#service_IAM_role

Hook Path

runway.cfngin.hooks.iam.create_ecs_service_role

Args

role_name (str)

Name of the role to create. (default: ecsServiceRole)

iam.ensure_server_cert_exists

Description

Ensure server cert exists.

Hook Path

runway.cfngin.hooks.iam.ensure_server_cert_exists

Args

cert_name (str)

Name of the certificate that should exist.

prompt (bool)

Whether to prompt to upload a certificate if one does not exist. (default: True)

keypair.ensure_keypair_exists

Description

Ensure a specific keypair exists within AWS. If the key doesn’t exist, upload it.

Hook Path

runway.cfngin.hooks.keypair.ensure_keypair_exists

Args

keypair (str)

Name of the key pair to create

ssm_parameter_name (Optional[str])

Path to an SSM store parameter to receive the generated private key, instead of importing it or storing it locally.

ssm_key_id (Optional[str])

ID of a KMS key to encrypt the SSM parameter with. If omitted, the default key will be used.

public_key_path (Optional[str])

Path to a public key file to be imported instead of generating a new key. Incompatible with the SSM options, as the private key will not be available for storing.

route53.create_domain

Description

Create a domain within route53.

Hook Path

runway.cfngin.hooks.route53.create_domain

Args

domain (str)

Domain name for the Route 53 hosted zone to be created.

upload_staticsite.get_distribution_data

Description

Retrieve information about the CloudFront distribution. Used by the Static Site module type.

Hook Path

runway.hooks.staticsite.upload_staticsite.get_distribution_data

Args

See Static Site module documentation for details.

upload_staticsite.sync

Description

Sync static website to S3 bucket. Used by the Static Site module type.

Hook Path

runway.hooks.staticsite.upload_staticsite.sync

Args

See Static Site module documentation for details.

Writing A Custom Hook

A custom hook must be in an executable, importable python package or standalone file. The hook must be importable using your current sys.path. This takes into account the sys_path defined in the config file as well as any paths of package_sources.

The hook must accept a minimum of two arguments, context and provider. Aside from the required arguments, it can have any number of additional arguments or use **kwargs to accept anything passed to it. The values for these additional arguments come from the args key of the hook definition.

The hook must return True or a truthy object if it was successful. It must return False or a falsy object if it failed. This signifies to CFNgin whether or not to halt execution if the hook is required. If dict is returned, it can be accessed by subsequent hooks, lookups, or Blueprints from the context object. It will be stored as context.hook_data[data_key] where data_key is the value set in the hook definition. If data_key is not provided or the type of the returned data is not dict, it will not be added to the context object.

If using boto3 in a hook, use context.get_session() instead of creating a new session to ensure the correct credentials are used.

"""context.get_session() example."""
from runway.cfngin.context import Context
from runway.cfngin.providers.aws.default import Provider

def do_something(context: Context, provider: Provider, **kwargs: str) -> None:
    """Do something."""
    session = context.get_session()
    s3_client = session.client('s3')

Example Hook Function

local_path/hooks/my_hook.py

"""My hook."""
from typing import Dict

from runway.cfngin.context import Context
from runway.cfngin.providers.aws.default import Provider


def do_something(context: Context,
                 provider: Provider,
                 is_failure: bool = True,
                 **kwargs: str
                 ) -> Dict[str, str]:
    """Do something."""
    if is_failure:
        return None
    return {'result': f"You are not a failure {kwargs.get('name', 'Kevin')}."}

local_path/cfngin.yaml

namespace: example
sys_path: ./

pre_build:
  my_hook_do_something:
    path: hooks.my_hook.do_something
    args:
      is_failure: False

Example Hook Class

local_path/hooks/my_hook.py

"""My hook."""
import logging
from typing import Dict

from runway.cfngin.hooks.base import Hook

LOGGER = logging.getLogger(__name__)

class MyClass(Hook):
    """My class does a thing.

    Keyword Args:
        is_failure (bool): Force the hook to fail if true.
        name (str): Name used in the response.

    Returns:
        Dict[str, str]: Response message is stored in ``result``.

    Example:
    .. code-block:: yaml

      pre_build:
        my_hook_do_something:
          path: hooks.my_hook.MyClass
          args:
            is_failure: False
            name: Karen

    """

    def post_deploy(self) -> Dict[str, str]:
        """Run during the **post_deploy** stage."""
        if self.args.is_failure:
            return None
        return {'result': f"You are not a failure {self.args.name}."}

    def post_destroy(self) -> None:
        """Run during the **post_destroy** stage."""
        LOGGER.error('post_destroy is not supported by this hook')

    def pre_deploy(self) -> None:
        """Run during the **pre_deploy** stage."""
        LOGGER.error('pre_deploy is not supported by this hook')

    def pre_destroy(self) -> None:
        """Run during the **pre_destroy** stage."""
        LOGGER.error('pre_destroy is not supported by this hook')

local_path/cfngin.yaml

namespace: example
sys_path: ./

pre_build:
  my_hook_do_something:
    path: hooks.my_hook.MyClass
    args:
      is_failure: False
      name: Karen