Custom Policy Checks

Custom Policy Checks are Python scripts that let you specify advanced policies using the Liquibase Policy Checks framework. Using custom policy checks, you can 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 (such as PatternAFollowedByPatternB), those checks may not be robust enough for complex governance requirements. 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.

Business benefits

  • Ensure compliance to your specific issues and tech stack
  • Develop at your own cadence
  • Share easily across your entire organization
  • Easily customize from a base template

Quickstart: Create a custom policy check

For a detailed, step-by-step tutorial of creating and deploying a check, see Create a Custom Policy Check. This tutorial explains:

  • Prerequisites for using custom policy checks
  • Downloading the Liquibase Checks extension
  • Creating your first custom policy check based on a template
  • Configuring your check in the Liquibase checks framework
  • Running your check using Liquibase commands

If you use Docker, see Use Custom Policy Checks with Docker for guidance on configuring the Liquibase image.

Sample custom policy checks

For sample Python scripts that you can run as policy checks, see Sample Custom Policy Check Scripts.

For even more samples, see this GitHub repository: liquibase/custom_policychecks. Liquibase provides a number of real-world samples of custom policy checks in this repository.

If you need help deploying your check, see Create a Custom Policy Check.

Liquibase checks framework

The logic of a custom policy check goes in a Python (.py) script file:

  • You can associate each custom policy check with one Python script. (Your Python script can reference modules and helper scripts.)
  • You can write and modify that script without using Liquibase commands: it's just a Python file.
  • You should store all your Python scripts in the Liquibase working directory or other accessible location.
  • You should keep the logic for different checks in different Python files.

Liquibase commands

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.

You should write your Python script to test one changeset or database object. Like other checks, you can deploy custom policy checks by using the run command. Liquibase then runs your Python script over all the objects in the domain you specify:

  • Once per changeset in your changelog (if you set --checks-scope=changelog)
  • Once per object in a database snapshot (if you set --checks-scope=database)

Custom policy checks are disabled by default. You can enable them by setting the checks run command parameter --checks-scripts-enabled=true using any of the standard methods to set Liquibase parameters.

Automation

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.

When you configure your custom policy check, specify a severity code (exit code) for that check. Your external CI/CD tools can use this exit code to know how to react when a check triggers in your pipeline.

Python runtime dependencies

Liquibase is a Java application. On the back-end, Liquibase uses the GraalPy API to run all custom policy check Python scripts via Java. Liquibase runs custom policy checks in a built-in Python virtual environment that comes with the Liquibase checks extension JAR. GraalVM also provides "patches" for some Python packages, such as replacing some C extensions with Java implementations. This optimizes performance to ensure your Python scripts run efficiently.

Optionally, you can run your Python scripts in a custom virtual environment. To learn how to configure one, see Create a Python Virtual Environment.

Python script pseudocode

Any Python script you use in the custom policy check framework includes:

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

To demonstrate that structure, here's some Python pseudocode:

import modules

define a reusable variable = use Liquibase modules to get changelog object information

for each change type in the changeset:
    if this condition is met, then do the following:
        tell liquibase = the check triggered!
        send this message as output = "NOOOOO"
        exit script with return code 1

otherwise, return false

Note: Your Python script should test a single changeset. Liquibase runs it iteratively across your entire changelog.

Tip: In your changelog, it is a best practice to use only one change per changeset. However, if you have multiple changes within a single changeset, this pseudocode loops through all of them.

import modules

define a reusable variable = use Liquibase modules to get database object information

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

otherwise, return false

Note: Your Python script should test a single database object. Liquibase runs it iteratively across your entire database.

For executable code samples, see Sample Custom Policy Check Scripts.

To configure your Python script in the Liquibase policy checks framework, see Create a Custom Policy Check.

To display more advanced output when checks trigger, see Write Dynamic Status Messages for Custom Policy Checks.

Limitations

  • The loadData Change Type is not supported.
  • When you run checks run --checks-scope=database (or if you tell Liquibase to require a snapshot on a changelog-scoped check), Liquibase generates a database snapshot. If your database contains a very large number of complex objects, the snapshot command may take a long time to execute.
    • If you experience memory limitations while trying to generate the snapshot for database-scoped checks, see Memory Limits of Inspecting Large Schemas.
    • If you want to manually create a snapshot to run database-scoped checks against an offline version of your database, see the instructions on checks-scope.
  • As of Liquibase 4.30.0, you can use the custom policy checks framework only to analyze your changelog and database. You cannot natively use custom policy checks against other Liquibase resources like properties files and flow files.

Related links