Infrahub Check Creator

SkillDev tools

Creates Infrahub check definitions — Python validation logic, GraphQL queries, and YAML-driven tests for proposed change pipelines. TRIGGER when: writing validation checks, creating Python checks, building data quality guards for proposed changes, writing or running tests for a check. DO NOT TRIGGER when: designing schemas, querying live data, building transforms or generators.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Infrahub Check Creator skill

What this skill tells your AI

The instructions your AI receives, as published by opsmill/infrahub-skills in skills/infrahub-managing-checks/SKILL.md and read by ahel’s review.

Overview

Expert guidance for creating Infrahub checks. Checks are user-defined validation logic (Python + GraphQL) that run as part of a proposed change pipeline. If a check logs any errors, the proposed change cannot be merged.

Project Context

Infrahub config: !cat .infrahub.yml 2>/dev/null || echo "No .infrahub.yml found"

Existing checks: !find . -name "*.py" -path "*/checks/*" 2>/dev/null | head -20

Existing queries: !find . -name "*.gql" -path "*/queries/*" 2>/dev/null | head -20

When to Use

  • Writing validation logic for proposed changes
  • Creating data quality guards (e.g., rack collision detection)
  • Building global checks that validate all objects of a type
  • Building targeted checks that validate specific grouped objects
  • Debugging check failures or understanding the check lifecycle

Rule Categories

PriorityCategoryPrefixDescription
CRITICALArchitecturearchitecture-Three components, global vs targeted, execution flow
CRITICALPython Classpython-InfrahubCheck base class, validate(), log_error/log_info
HIGHAPI Referenceapi-Class attributes, instance properties, methods, lifecycle, and which API surfaces a rejection (a GraphQL error is a 200)
HIGHRegistrationregistration-.infrahub.yml config, query name matching, parameters
MEDIUM (HIGH for patterns-shared-module)Patternspatterns-Error collection, shared utilities, scoped validation, relationship-traversal validation, sharing a module across artifact types
HIGHTestingtesting-Resources Testing Framework (YAML-driven tests), infrahubctl check commands

Schema Features This Skill Depends On

A check is only useful if it can fetch and validate the right data. Most check failures at deploy time are actually schema-side gaps:

If the check...The schema (or .infrahub.yml) must...See
Reads an attribute via GraphQLExpose it on the schema node with the same name (name__value-shaped paths)../infrahub-managing-schemas/rules/attribute-defaults-and-types.md
Walks a relationship to validate related objectsHave both sides of the relationship defined with matching identifiers; otherwise the traversal returns nothing../infrahub-managing-schemas/rules/relationship-identifiers.md
Validates a node against a related node's state (child vs parent lifecycle, peer consistency)Fetch the related node's comparison attribute in the query by traversing the relationship; a check runs one query with no lazy fetchrules/patterns-relationship-traversal.md
Is targeted (per-object)Register a CoreStandardGroup as targets: in .infrahub.yml and map parameters: to bind GraphQL variablesrules/registration-config.md
Needs the GraphQL response keyed to typed nodesSelect id and __typename in the query — the SDK relies on both../infrahub-common/graphql-queries.md
Should never block a merge but only annotateUse self.log_info() instead of log_error(); log_warning() does not existrules/python-validate.md

Before writing Python

If a cheaper layer can express the constraint, use it. A schema constraint runs at load time on every write path; a Python check runs only inside the proposed- change pipeline, so bad data created via other paths slips through. Walk this short ladder before reaching for InfrahubCheck:

SignalCheaper layerSee rule
Validating uniqueness, presence, allowed values, or regex on a single attributeSchema constraint (uniqueness_constraints, optional: false, kind: Dropdown choices, regex)yagni-python-validator-vs-schema-constraint
Check whose body is a GraphQL query plus a single if len(...) > 0: raiseOne .gql file plus 5 lines of Pythonyagni-redundant-check-that-graphql-can-answer
Enforcing that a relationship is single-peered or non-optionalSchema cardinality: one, kind: Parent / Component, optional: falseyagni-python-validator-vs-schema-constraint

Only when none of these apply should you write a Python check. The cross-node business rules, out-of-band reconciliations, and stateful assertions in rules/python-validate.md are the legitimate use cases.

When the check reads objects through the SDK (rather than only its GraphQL query), type those calls with generated protocol classes rather than string kinds — client.get(DcimDevice, ...), not kind="DcimDevice". Match the --sync protocol variant to the check's client. See protocols-adopt-typed-kinds.

Check Basics

Every check has three components:

  1. GraphQL query (.gql file) -- fetches the data to validate, and is registered under the top-level queries: section of .infrahub.yml
  2. Python class -- inherits from InfrahubCheck, sets query = "<query_name>", implements validate()
  3. Configuration -- declared in .infrahub.yml under check_definitions (which does not take a query: field — see below)
from infrahub_sdk.checks import InfrahubCheck


class MyCheck(InfrahubCheck):
    query = "my_query"  # Must match queries[].name in .infrahub.yml

    def validate(self, data: dict) -> None:
        # Validation logic here
        if something_is_wrong:
            self.log_error(
                message="Problem description"
            )

Where the query is bound: the Python class (query = "..."), not check_definitions. The repository config model uses extra="forbid", so putting query: under check_definitions: makes the whole repo config fail validation. This is the #1 confusion vs. generator_definitions:, which does take a top-level query:. See rules/registration-config.md.

Workflow

Follow these steps when creating a check:

  1. Understand the validation goal — What data condition should block a proposed change? Determine whether this is a global check (all objects of a type) or targeted (specific group). Read rules/architecture-types.md.
  2. Write the GraphQL query — Create a .gql file that fetches the data to validate. Read ../infrahub-common/graphql-queries.md for query patterns. If the rule compares a node against a related node's state (child vs parent lifecycle, peer consistency), the query must fetch that related attribute by traversing the relationship now — a check runs one query with no later fetch. See rules/patterns-relationship-traversal.md.
  3. Implement the Python class — Inherit from InfrahubCheck, implement validate(). Read rules/python-validate.md for the class pattern and rules/api-reference.md for available methods. If the check calls back into Infrahub, read rules/api-error-surfaces.md too: a rejected GraphQL request still returns HTTP 200, so branching on a status code makes the check pass on the failure it exists to catch. If the logic is shared with a generator or a transform, read rules/patterns-shared-module.md before reaching for a relative import.
  4. Register in .infrahub.yml — Add the check under check_definitions. The query name must match the Python class query attribute. See rules/registration-config.md.
  5. Add tests — Create YAML-driven test definitions (smoke, unit, integration) alongside the check so it is validated automatically in the proposed change pipeline. Read rules/testing-resource-framework.md.
  6. Test locally — Run infrahubctl check to validate against a feature branch. See rules/testing-commands.md.

Supporting References

Signals

GitHub stars
26
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
infrahub-managing-checks
Source
github.com/opsmill/infrahub-skills