A hook is a python function, class, or class method that is executed before or after an action is taken for the entire config.

Only the following actions allow pre/post hooks:


using fields pre_deploy and post_deploy


using fields pre_destroy and post_destroy

class cfngin.hook

When defining a hook in one of the supported fields, the follow fields can be used.

Lookup Support

The following fields support lookups:

args: Optional[Dict[str, Any]] = {}

A dictionary of arguments to pass to the hook.

This field supports the use of lookups.


Lookups that change the order of execution, like output, can only be used in a post hook but hooks like rxref are able to be used with either pre or post hooks.


  - args:
      key: ${val}
data_key: Optional[str] = None

If set, and the hook returns data (a dictionary or pydantic.BaseModel), the results will be stored in CfnginContext.hook_data with the data_key as its key.


  - data_key: example-key
enabled: Optional[bool] = True

Whether to execute the hook every CFNgin run. This field provides the ability to execute a hook per environment when combined with a variable.


  - enabled: ${enable_example_hook}
path: str

Python importable path to the hook.


  - path: runway.cfngin.hooks.command.run_command
required: Optional[bool] = True

Whether to stop execution if the hook fails.

Built-in Hooks

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.

When executed, the hook will have various keyword arguments passed to it. The keyword arguments that will always be passed to the hook are context (CfnginContext) and provider (Provider). Anything defined in the args field will also be passed to hook as a keyword argument. For this reason, it is recommended to use an unpack operator (**kwargs) in addition to the keyword arguments the hook requires to ensure future compatibility and account for misconfigurations.

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 a Dict, MutableMap, or pydantic.BaseModel 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 a Dict, MutableMap, or pydantic.BaseModel, it will not be added to the context object.


When using a pydantic.root_validator() or pydantic.validator() allow_reuse=True must be passed to the decorator. This is because of how hooks are loaded/re-loaded for each usage. Failure to do so will result in an error if the hook is used more than once.

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 __future__ import annotations

from typing import TYPE_CHECKING, Any

    from runway.context import CfnginContext

def do_something(context: CfnginContext, **_kwargs: Any) -> None:
    """Do something."""
    s3_client = context.get_session().client("s3")

Example Hook Function

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

def do_something(
    *, is_failure: bool = True, name: str = "Kevin", **_kwargs: str
) -> Optional[Dict[str, str]]:
    """Do something."""
    if is_failure:
        return None
    return {"result": f"You are not a failure {name}."}
namespace: example
sys_path: ./

  - path: hooks.my_hook.do_something
      is_failure: false

Example Hook Class

Hook classes must implement the interface detailed by the CfnginHookProtocol Protocol. This can be done implicitly or explicitly (by creating a subclass of CfnginHookProtocol).

As shown in this example, HookArgsBaseModel or it’s parent class BaseModel can be used to create self validating and sanitizing data models. These can then be used to parse the values provided in the args field to ensure they match what is expected.

"""My hook."""
import logging
from typing import TYPE_CHECKING, Any, Dict, Optional

from runway.utils import BaseModel
from runway.cfngin.hooks.protocols import CfnginHookProtocol

    from ...context import CfnginContext

LOGGER = logging.getLogger(__name__)

class MyClassArgs(BaseModel):
    """Arguments for MyClass hook.

        is_failure: Force the hook to fail if true.
        name: Name used in the response.


    is_failure: bool = False
    name: str

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

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

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

    .. code-block:: yaml

          - path: hooks.my_hook.MyClass
            is_failure: False
            name: Karen


    args: MyClassArgs

    def __init__(self, context: CfnginContext, **kwargs: Any) -> None:
        """Instantiate class.

            context: Context instance. (passed in by CFNgin)
            provider: Provider instance. (passed in by CFNgin)

        kwargs.setdefault("tags", {})

        self.args = self.ARGS_PARSER.parse_obj(kwargs)
        self.context = context

    def post_deploy(self) -> Optional[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")
namespace: example
sys_path: ./

  - path: hooks.my_hook.MyClass
      is_failure: False
      name: Karen