Skip to content

Writing your own rules

To add custom rules first extend the Rule class. Then implement the invoke method by adding your logic.

    @abstractmethod
    def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result:
        pass

CFRipper uses pycfmodel to create a Python model of the CloudFormation script. This model is passed to the invoke function as the cfmodel parameter. You can use the model's iterate through the resources and other objects of the model and use the helper functions to perform various checks. Look at the current rules for examples.

Making your rules available to CFRipper

CFRipper uses pluggy in order to load dynamic rules. You have to define an entrypoint for your distribution so that CFRipper finds your plugin module. Entrypoints are a feature provided by setuptools. The CFRipper CLI looks up the cfripper entrypoint to discover its plugins and load them.

In case that more than one rule uses the same ID, the last added will be preserved.

CFRipper just have one hook that has to be implemented to provide custom rules. cfripper_get_rules hook returns a dictionary where key are strings and values are rule inherited classes.

Example

This is a two file example (setup.py and plugin.py) than provides one custom rule to CFRipper.

setup.py

from setuptools import setup


setup(
    name="cfripper-shiny-new-plugin",
    install_requires="cfripper>=0.24.0,<0.25.0",
    # the following makes a plugin available to cfripper
    entry_points={"cfripper": ["shiny_new_plugin = plugin"]},
    py_modules=["plugin"],
)

plugin.py

from typing import Dict, Optional

from cfripper.config.pluggy import hookimpl
from cfripper.model.result import Result
from cfripper.rules.base_rules import Rule
from pycfmodel.model.cf_model import CFModel


class DummyRule(Rule):
    def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result:
        print("I'm Dummy Rule Defined in a plugin!!!")
        return Result()


@hookimpl
def cfripper_get_rules():
    return {DummyRule.__name__: DummyRule}

After installing our module using python setup.py install, we can check that is available in our environment.

pip list
Package                   Version
------------------------- -------
boto3                     1.17.1
botocore                  1.20.1
cfn-flip                  1.2.3
cfripper                  1.0.0
cfripper-shiny-new-plugin 0.0.0
click                     7.1.2
importlib-metadata        3.4.0
jmespath                  0.10.0
pip                       20.3.3
pluggy                    0.13.1
pycfmodel                 0.8.1
pydantic                  1.7.3
pydash                    4.7.6
python-dateutil           2.8.1
PyYAML                    5.4.1
s3transfer                0.3.4
setuptools                51.0.0
six                       1.15.0
typing-extensions         3.7.4.3
urllib3                   1.26.3
wheel                     0.36.2
zipp                      3.4.0

When running CFRipper, it's going to load automically our rules defined at our module. So when we run it, we can see the print defined on our Dummy Rule being executed.

cfripper --resolve test.json
Analysing test.json...
I'm Dummy Rule Defined in a plugin!!!
Valid: True