| name | infrahub-managing-generators |
| description | 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. |
| allowed-tools | ["Read","Write","Edit","Bash","Grep"] |
| argument-hint | [generator-name] [description...] |
| metadata | {"version":"1.2.7","author":"OpsMill"} |
Infrahub Generator Creator
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.
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 |
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.
Generator Basics
Every generator has three components:
- Target group -- objects that trigger the generator
- GraphQL query (
.gql file) -- fetches the design data
- 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:
- 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
.gql file
that fetches the design data. Read
../infrahub-common/graphql-queries.md
for query patterns.
- 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.
- Make it idempotent — Use
allow_upsert=True so
re-running creates or updates without duplicates.
See rules/tracking-idempotent.md.
- 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.
- Register in .infrahub.yml — Add under
generator_definitions with the target group. See
rules/registration-config.md.
- Test — Run
infrahubctl generator to validate.
See rules/testing-commands.md.
Supporting References