Infrahub Generator Creator

SkillMedia

Creates Infrahub Generators — design-driven automation that builds infrastructure objects from templates and topology definitions. TRIGGER when: building design-to-implementation workflows, auto-creating objects from templates, topology-driven generation. DO NOT TRIGGER when: designing schemas, writing data transforms, querying live data, populating static data files.

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 Generator Creator skill

What this skill tells your AI

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

Overview

Expert guidance for creating Infrahub Generators. Generators query data from Infrahub via GraphQL and create new nodes and relationships based on the result -- enabling design-driven automation where a "design" object automatically creates downstream infrastructure.

Project Context

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

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

When to Use

  • Building design-driven automation (topology -> devices)
  • Creating objects from templates or design definitions
  • Implementing idempotent create-or-update workflows
  • Auto-generating infrastructure from high-level designs
  • Understanding the generator tracking system

Rule Categories

PriorityCategoryPrefixDescription
CRITICALArchitecturearchitecture-Components, groups
CRITICALPython Classpython-Generator, generate()
HIGHTrackingtracking-Upsert, idempotent
HIGHAPI Refapi-Constructor, props
HIGHRegistrationregistration-.infrahub.yml config
MEDIUMPatternspatterns-Cleaning, batch, store
LOWTestingtesting-infrahubctl commands

Schema Features This Skill Depends On

Generators create real objects, so the schema must permit the shape they emit. Catch these gaps before the first run — re-running a buggy generator can delete data via the tracking cleanup.

If the generator...The schema must...See
Creates objects of kind XHave node X defined with the attributes the generator sets — extra attributes silently fail validation, missing required ones abort the create../infrahub-managing-schemas/rules/attribute-defaults-and-types.md
Links the created object to a parentHave a Component/Parent relationship pair with matching identifiers and optional: false on the Parent side../infrahub-managing-schemas/rules/relationship-component-parent.md
Reads a "design" node to drive outputDefine that node's human_friendly_id so the generator's tracking key stays stable across runs../infrahub-managing-schemas/rules/display-human-friendly-id.md
Is triggered by membership in a groupThe target group must be a CoreGeneratorGroup (not CoreStandardGroup) — the dispatcher only recognizes the formerrules/registration-config.md
Should be idempotent on re-runEvery save() uses allow_upsert=True; the run's tracking context deletes objects from prior runs that aren't recreatedrules/tracking-idempotent.md
Imports anything from the repository (a shared package, generated protocols, its own query model)Carry a watch: block in .infrahub.yml naming every one of those paths — imports are never followed, so an undeclared helper means the Generator silently stops re-running when that helper changesrules/registration-watch-dependencies.md

Before writing Python

A generator should compute objects from a design. If what you're about to write is "make these N specific objects from a hardcoded list," that list is data — move it to objects/ and either let the object loader handle it directly or pass it into a smaller generator. Walk this ladder before reaching for InfrahubGenerator:

SignalCheaper layerSee rule
Generator hardcodes object lists, role catalogs, or status setsYAML data files under objects/ (loaded by the object loader)yagni-generator-hardcoding-data
Generator recreates a built-in IPAM/VLAN primitive (custom IP address, prefix, VLAN nodes)inherit_from: [BuiltinIPAddress / BuiltinIPPrefix / IpamVLAN] in the schema, then the generator computes references rather than reimplementing the primitiveyagni-custom-domain-primitives-instead-of-builtin
Generator's output shape duplicates objects already in opsmill/schema-libraryinherit_from a library generic; the generator computes the instance but not the shapeyagni-duplicate-shape-not-extracted-to-generic
Generator allocates a subnet/IP/VLAN/port with ipaddress math, random, or a hand-written "find the first free one" loopA built-in resource pool — allocate_next_ip_prefix / allocate_next_ip_address, CoreIPPrefixPool / CoreNumberPool — which tracks utilization and stays idempotent across re-runsyagni-imperative-allocation-vs-resource-pool
generate() stamps a fixed set of children with constant values and no computation (no branching, derived naming, or allocation)An Object Template (generate_template: true) users clone — the structure lives in data, not Pythonyagni-generator-that-should-be-template

Bootstrap, seed, and demo generators (under bootstrap/, seed/, demo/) are exempt — they exist specifically to hardcode initial state. Use Python when the generator is genuinely computing objects from a design definition; see rules/python-generate.md for the legitimate cases.

Once you are writing Python, type your SDK calls with generated protocol classes rather than string kinds — client.create(NetworkDevice, ...), not kind="NetworkDevice" — so a schema change fails type-check instead of at runtime. See protocols-adopt-typed-kinds.

Generator Basics

Every generator has three components:

  1. Target group -- objects that trigger the generator
  2. GraphQL query (.gql file) -- fetches the design data
  3. Python class -- inherits from InfrahubGenerator, implements generate()
from infrahub_sdk.generator import InfrahubGenerator

class MyGenerator(InfrahubGenerator):
    async def generate(self, data: dict) -> None:
        obj = await self.client.create(
            kind="DcimDevice",
            data={"name": "spine-01"},
        )
        await obj.save(allow_upsert=True)

Workflow

Follow these steps when creating a generator:

  1. Identify the design pattern — What "design" object triggers generation? What objects should be created from it? Read rules/architecture-components.md for the target group and generator components.
  2. Write the GraphQL query — Create a .gql file that fetches the design data. Read ../infrahub-common/graphql-queries.md for query patterns.
  3. Implement the Python class — Inherit from InfrahubGenerator, implement generate(). Read rules/python-generate.md for the class pattern and rules/api-reference.md for available methods.
  4. Make it idempotent — Use allow_upsert=True so re-running creates or updates without duplicates. See rules/tracking-idempotent.md.
  5. Check for from_graphql adoption opportunity — if generate() iterates response edges and calls self.client.get() to re-fetch typed peers, consider refactoring to InfrahubNode.from_graphql() to collapse O(N + 1) round trips to O(1). Read rules/patterns-hydration.md for the decision tree, detection heuristic, and refactor recipe.
  6. Constrain any graph walk. If generate() needs the routes between two nodes, use the SDK's traverse_paths rather than a hand-written per-hop walk, and constrain it by relationship identifier plus a depth bound. Kind filtering restricts which nodes may appear, not which edges are followed, so a shared reference object still bridges unrelated subgraphs and the walk returns structurally valid nonsense. Read rules/patterns-path-traversal.md for the parameter semantics and the truncation signal.
  7. Register in .infrahub.yml — Add under generator_definitions with the target group. See rules/registration-config.md. Then declare the Generator's dependencies with watch.files: read its imports and runtime file reads, and name every first-party path they resolve to — sibling query models included. Always carry the key; files: [] when the Generator genuinely has no dependency beyond its own file. Read rules/registration-watch-dependencies.md, which also covers reviewing an existing watch block for entries that are missing, stale, or wrong.
  8. Test — Run infrahubctl generator to validate. See rules/testing-commands.md.

Supporting References

Signals

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