Infrahub Generator Creator
SkillMediaCreates 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.
No other account needed.
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
| Priority | Category | Prefix | Description |
|---|---|---|---|
| CRITICAL | Architecture | architecture- | Components, groups |
| CRITICAL | Python Class | python- | Generator, generate() |
| HIGH | Tracking | tracking- | Upsert, idempotent |
| HIGH | API Ref | api- | Constructor, props |
| HIGH | Registration | registration- | .infrahub.yml config |
| MEDIUM | Patterns | patterns- | Cleaning, batch, store |
| LOW | Testing | testing- | 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 X | Have 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 parent | Have 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 output | Define 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 group | The target group must be a CoreGeneratorGroup (not CoreStandardGroup) — the dispatcher only recognizes the former | rules/registration-config.md |
| Should be idempotent on re-run | Every save() uses allow_upsert=True; the run's tracking context deletes objects from prior runs that aren't recreated | rules/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 changes | rules/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:
| Signal | Cheaper layer | See rule |
|---|---|---|
| Generator hardcodes object lists, role catalogs, or status sets | YAML 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 primitive | yagni-custom-domain-primitives-instead-of-builtin |
Generator's output shape duplicates objects already in opsmill/schema-library | inherit_from a library generic; the generator computes the instance but not the shape | yagni-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" loop | A built-in resource pool — allocate_next_ip_prefix / allocate_next_ip_address, CoreIPPrefixPool / CoreNumberPool — which tracks utilization and stays idempotent across re-runs | yagni-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 Python | yagni-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:
- Target group -- objects that trigger the generator
- GraphQL query (
.gqlfile) -- fetches the design data - Python class -- inherits from
InfrahubGenerator, implementsgenerate()
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:
- 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.
- Write the GraphQL query — Create a
.gqlfile that fetches the design data. Read ../infrahub-common/graphql-queries.md for query patterns. - Implement the Python class — Inherit from
InfrahubGenerator, implementgenerate(). Read rules/python-generate.md for the class pattern and rules/api-reference.md for available methods. - Make it idempotent — Use
allow_upsert=Trueso re-running creates or updates without duplicates. See rules/tracking-idempotent.md. - Check for
from_graphqladoption opportunity — ifgenerate()iterates response edges and callsself.client.get()to re-fetch typed peers, consider refactoring toInfrahubNode.from_graphql()to collapseO(N + 1)round trips toO(1). Read rules/patterns-hydration.md for the decision tree, detection heuristic, and refactor recipe. - Constrain any graph walk. If
generate()needs the routes between two nodes, use the SDK'straverse_pathsrather 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. - Register in .infrahub.yml — Add under
generator_definitionswith the target group. See rules/registration-config.md. Then declare the Generator's dependencies withwatch.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 existingwatchblock for entries that are missing, stale, or wrong. - Test — Run
infrahubctl generatorto validate. See rules/testing-commands.md.
Supporting References
- reference.md -- Class API,
lifecycle, idempotency contract,
.infrahub.ymlregistration (with thequery:-required shape that differs from check_definitions) - examples.md -- Complete Generator patterns (POP topology, network segment, minimal)
- ../infrahub-common/graphql-queries.md -- GraphQL query writing reference
- ../infrahub-common/infrahub-yml-reference.md -- .infrahub.yml project configuration
- ../infrahub-common/marketplace-reference.md -- reuse a marketplace-published schema for the target data model before hand-rolling one to generate against
- ../infrahub-common/rules/ -- Shared rules (git integration, caching gotchas)
- ../infrahub-common/rules/workflow-information-priority.md
-- Skill content first; how to consult
docs.infrahub.appon a genuine gap (e.g. deleting nodes) - ../infrahub-managing-schemas/SKILL.md -- Schema definitions Generators work with
- rules/ -- Individual rules organized by category prefix
Signals
- GitHub stars
- 26
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
infrahub-managing-generators- Source
- github.com/opsmill/infrahub-skills