Custom Policy Checks

Custom Policy Checks are Python scripts that allow you run advanced policies using the Liquibase Policy Checks framework. Custom policy checks allow you to enforce compliance for a wide array of security, code standards, data quality, and more.

While it's possible to configure the behavior of many built-in Liquibase policy checks using regular expressions, those checks only work for predefined use-cases. Sometimes, they might not be robust enough for your needs. In contrast, you can use custom policy checks to create unique checks for any situation in your Liquibase workflow.

Note: This is a Liquibase Pro feature, so you need a Liquibase Pro License Key to use it.

Quickstart: Create a custom policy check

For a step-by-step tutorial, see Create a Custom Policy Check.

Liquibase checks framework

The logic of a custom policy check is stored in a Python (.py) script file in your Liquibase working directory. You can write and modify that script without using Liquibase commands: it's just a Python file. You should keep the logic for different checks in different Python files.

You can integrate your Python file into Liquibase by using the same Policy Checks Commands you would use to copy a built-in policy check. Specifically, there is a built-in policy check called CustomChecksTemplate that you can copy and customize to specify your own logic. In the CLI, you specify a short name for your custom check in Liquibase, the Python script containing the check logic to run, and other configurations like the check's severity level.

Since a custom policy check runs in the Liquibase policy checks framework, you can deploy custom policy checks into your CI/CD automation the same way as built-in checks. This allows your code to be robust but keeps your processes simple.

Python script looping

Liquibase always runs every enabled policy check against every object the check targets. This works the same for custom policy checks. For example, Liquibase runs any custom policy check with a scope of:

  • changelog: once per changeset in that changelog. If the changelog uses include or includeAll to reference child changelogs, the script also runs once per changeset in each of those changelogs.
  • database: once per database object.

Warning: If you write a custom policy check that is individually performance-heavy, and you have a very large changelog or database, Liquibase may take a long time to run your custom check iteratively.

Python helper scripts

Liquibase has a public API that you can import in your Python scripts in order to access a library of Liquibase helper scripts. For example, there are helper scripts to find the author of a changeset and to determine whether a database object is a table.

Using helper scripts makes it easier to access information in Liquibase resources and your database. This way, you can keep your Python scripts concise.

Python script pseudocode

Your Python script contains:

  1. Imports of useful Liquibase helper scripts to access the Liquibase API
  2. Clearly named variables that call on functions in the helper scripts (for ease of use)
  3. The logic of your custom check. The code for Liquibase to run against your changelog or database.
  4. A default return code (False)
import helpers

some_variable = helpers.get_useful_object()
particular_changeset = helpers.other_function()

for change_type in particular_changeset:
    if some condition is met, then do the following:
        tell liquibase: the check triggered!
        send this message: "NOOOOO"
        exit script with return code 1

otherwise, return false

Note: Remember that your Python script should test a single changeset. Liquibase then iterates it across your entire changelog.

import helpers

some_variable = helpers.get_useful_object()
database_object = helpers.get_database_object()

if some condition is met, then do the following:
    tell liquibase: the check triggered!
    send this message: "NOOOOO"
    exit script with return code 1

otherwise, return false

Note: Remember that your Python script should test a single database object. Liquibase then iterates it across your entire database.