Creating an Object Template
This guide provides a structured approach to defining an object template and creating object instances based on that template.
At a high level, the process consists of three key steps:
- Enable template support within the schema
- Populate the template with data
- Create object instances using the predefined template
To illustrate this process, we will use a common use case: Creating ports for a device.
For more details, refer to the Object Template Topic.
Enable template support within the schema
If you are already familiar with Schema Development in Infrahub, enabling template generation is straightforward.
At the node level, the generate_template property allows users to enable template generation for a given node and its associated components.
generate_template is only available on node definitions. It does not exist on generics.
---
# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json
version: "1.0"
nodes:
- name: Interface
namespace: Infra
label: "Interface"
include_in_menu: true
icon: "mdi:ethernet"
display_label: "{{ name__value }}"
order_by:
- name__value
uniqueness_constraints:
- ["device", "name__value"]
human_friendly_id: ["device__name__value", "name__value"]
attributes:
- name: name
kind: Text
- name: description
kind: Text
optional: true
- name: enable
kind: Boolean
optional: false
default_value: false
relationships:
- name: device
peer: InfraDevice
optional: false
cardinality: one
kind: Parent
- name: DeviceType
namespace: Infra
label: "Device Type"
icon: "mdi:server"
human_friendly_id: ["name__value"]
order_by:
- name__value
display_label: "{{ name__value }}"
attributes:
- name: name
kind: Text
unique: true
- name: number_of_u
kind: Number
default_value: 1
relationships:
- name: devices
peer: InfraDevice
optional: true
cardinality: many
- name: Device
namespace: Infra
generate_template: true # This flag enables template for Device and Interfaces
label: "Device"
icon: "mdi:server"
human_friendly_id: ["name__value"]
order_by:
- name__value
display_label: "{{ name__value }}"
attributes:
- name: name
kind: Text
unique: true
- name: description
kind: Text
optional: true
- name: serial
kind: Text
optional: true
relationships:
- name: device_type
label: device_type
peer: InfraDeviceType
optional: false
cardinality: one
kind: Attribute
- name: interfaces # As this relationship's kind is component, it will automatically be covered by the template
peer: InfraInterface
optional: true
cardinality: many
kind: Component
You can now load this schema into your Infrahub instance. For more details, refer to the Import Schema Guide.
Populate the template with data
Heading back to Infrahub you will notice new entries in the left-hand menu. Before entering template information we will create a device type, this will be useful for later.
Create a device type
- Via the Web Interface
- Via the GraphQL Interface
- Navigate to the left-hand menu and select
Device Type - Click the
+ Add new Device Typebutton - Then fill the form
- Name: SwitchModel123
- Number of U: 2
- Hit the save button
mutation {
InfraDeviceTypeCreate(
data: {name: {value: "SwitchModel123"}, number_of_u: {value: 2}}
) {
ok
}
}
Now that the device type is created, we can proceed to work on the template itself.
In Infrahub's implementation of object template, the template doesn't hold any information about the device model (for example the Number of U). This information is stored in the device type object. Fortunately we can link the template to a device type object, this will be transferred to the object.
Create a device template
All template-related records can be found in the menu under the dedicated section Object Management > Templates.
- Via the Web Interface
- Via the GraphQL Interface
- Go to
Object Management>Templates - Click the
+ Add Object Templatesbutton - In the drop-down list pick
Device - Then fill the form
- Template Name: Template-SwitchModel123
- Device Type: SwitchModel123
- Hit the save button
mutation {
TemplateInfraDeviceCreate(
data: {template_name: {value: "Template-SwitchModel123"}, device_type: {hfid: ["SwitchModel123"]}}
) {
ok
}
}
In the device template, you specify the actual device type object. This means that creating a device using your template will automatically set up a relationship between the new device and the specified device type. Some fields can be left empty because the device template doesn't apply to them. Take the serial number, by definition it will be different from one device to another.
Create an interface template
Ensure you reference the device template created in the previous step. For demonstration purposes, you can create multiple interface templates.
- Via the Web Interface
- Via the GraphQL Interface
- Click the
+ Add Object Templatesbutton - In the drop-down list pick
Object template Interface - Then fill-out the fields
- Template Name: Template-SwitchModel123-Ethernet1; Template-SwitchModel123-Ethernet2; Template-SwitchModel123-Ethernet3
- Device: Template-SwitchModel123
- Name: Ethernet1; Ethernet2; Ethernet3
- Hit the save button
mutation {
CreateTemplateEthernet1: TemplateInfraInterfaceCreate(
data: {name: {value: "Ethernet1"}, template_name: {value: "Template-SwitchModel123-Ethernet1"}, device: {hfid: ["Template-SwitchModel123"]}}
) {
ok
}
CreateTemplateEthernet2: TemplateInfraInterfaceCreate(
data: {name: {value: "Ethernet2"}, template_name: {value: "Template-SwitchModel123-Ethernet2"}, device: {hfid: ["Template-SwitchModel123"]}}
) {
ok
}
CreateTemplateEthernet3: TemplateInfraInterfaceCreate(
data: {name: {value: "Ethernet3"}, template_name: {value: "Template-SwitchModel123-Ethernet3"}, device: {hfid: ["Template-SwitchModel123"]}}
) {
ok
}
}

With the device template and interface templates in place, you're all set to create new instances based on them!
Create object instances using the predefined template
Heading back to the main menu we will create a device object.
- Via the Web Interface
- Via the GraphQL Interface
- Via the Python SDK
- Click
Deviceitem in the left hand side menu - Hit
+ Add Devicebutton - Pick the template option and select the template you created in the previous step

At this stage, you can either create your device from scratch, following the standard process, or start from a predefined template!
- You can fill the missing information (for instance the
serialfield)

Information sourced from the template is indicated by a small chip above the form inputs. You can override this information at any time.
- Hit the save button
mutation {
InfraDeviceCreate(
data: {object_template: {hfid: ["Template-SwitchModel123"]}, serial: {value: "OWI62IUHQ"}, description: {value: "This is a Core Switch"}}
) {
ok
}
}
device = await client.create(
kind="InfraDevice",
object_template="Template-SwitchModel123", # This is the template name
name="my-device",
serial="OWI62IUHQ",
description="This is a Core Switch",
)
await device.save()

When viewing your newly created device object, navigate to the Interfaces tab to see a list of interfaces pre-populated based on the template you defined. Keep in mind that any modifications made to the template will not retroactively apply to objects that have already been created from it.
Using Profiles with templates
When both generate_template and generate_profile are configured on a schema node, you can assign Profiles to templates to enable bulk configuration updates. Objects created from templates automatically inherit the Profiles assigned to those templates, allowing you to update values in bulk by modifying the Profile.
When a Profile is assigned to a template:
- Objects created from the template automatically inherit the template's Profiles
- Template values (when explicitly configured) take precedence over Profile values
- Profiles provide values for attributes not explicitly set on the template
- Multiple Profiles can be assigned to a template with proper priority handling
- This enables consistent configuration management across templated objects
For more information about Profiles and templates, see:
- Creating and assigning Profiles - Guide for working with Profiles
- Understanding Profiles in Infrahub - Deep dive into Profile concepts
Allocating resources from pools via templates
Object templates support automatic resource allocation. This section extends the device and interface schema from above by adding IP address support to InfraInterface, then shows how to wire a CoreIPAddressPool to the interface template.
Update the schema
Add the two IPAM nodes and an ip_address relationship to InfraInterface, if you don't already have them:
---
# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json
version: "1.0"
nodes:
- name: IPAddress
namespace: Ipam
label: "IP Address"
display_label: "{{ address__value }}"
inherit_from:
- BuiltinIPAddress
- name: IPPrefix
namespace: Ipam
label: "IP Prefix"
display_label: "{{ prefix__value }}"
inherit_from:
- BuiltinIPPrefix
- name: Interface
namespace: Infra
label: "Interface"
include_in_menu: true
icon: "mdi:ethernet"
display_label: "{{ name__value }}"
order_by:
- name__value
uniqueness_constraints:
- ["device", "name__value"]
human_friendly_id: ["device__name__value", "name__value"]
attributes:
- name: name
kind: Text
- name: description
kind: Text
optional: true
- name: enable
kind: Boolean
optional: false
default_value: false
relationships:
- name: device
peer: InfraDevice
optional: false
cardinality: one
kind: Parent
- name: ip_address
peer: IpamIPAddress
optional: true
cardinality: one
kind: Attribute
When a schema node has a relationship or attribute that supports resource pool allocation, Infrahub automatically generates a corresponding <attribute_or_relationship_name>_from_resource_pool field on the template node. In this example, because IpamIPAddress can be allocated from a CoreIPAddressPool, an ip_address_from_resource_pool field is generated on TemplateInfraInterface. This is what connects the template to the pool.
Load this updated schema into your Infrahub instance before proceeding.
Create the resource pool
First create a prefix to draw addresses from, then create the pool backed by that prefix.
- Via the Web Interface
- Via the GraphQL Interface
- Via an Object File
- Via the Python SDK
Create the IP prefix:
- Navigate to
IPAM>Prefixes - Click
+ Add IP Prefix - Fill in:
- Prefix:
192.168.0.0/16 - Member Type:
address - Is Pool:
true
- Prefix:
- Save
Create the IP address pool:
- Navigate to
Resource Manager>IP Address Pool - Click
+ Add IP Address Pool - Fill in:
- Name:
Interface IP Pool - Default Address Type:
IpamIPAddress - IP Namespace:
default - Resources: select
192.168.0.0/16
- Name:
- Save
mutation CreatePrefix {
IpamIPPrefixCreate(
data: {prefix: {value: "192.168.0.0/16"}, member_type: {value: "address"}, is_pool: {value: true}}
) {
ok
object { id }
}
}
Then use the returned prefix ID to create the pool:
mutation CreatePool($prefixId: String!) {
CoreIPAddressPoolCreate(
data: {
name: {value: "Interface IP Pool"}
default_address_type: {value: "IpamIPAddress"}
ip_namespace: {hfid: ["default"]}
resources: [{id: $prefixId}]
}
) {
ok
}
}
---
apiVersion: infrahub.app/v1
kind: Object
spec:
kind: CoreIPAddressPool
data:
- name: "Interface IP Pool"
default_address_type: IpamIPAddress
ip_namespace: default
resources:
kind: IpamIPPrefix
data:
- prefix: 192.168.0.0/16
member_type: address
is_pool: true
Load with: infrahubctl object load pools.yml
async def run(client: InfrahubClient, log: logging.Logger, branch: str) -> None:
prefix = await client.create(
kind="IpamIPPrefix",
data={"prefix": "192.168.0.0/16", "member_type": "address", "is_pool": True},
)
await prefix.save(allow_upsert=True)
pool = await client.create(
kind="CoreIPAddressPool",
data={
"name": "Interface IP Pool",
"default_address_type": "IpamIPAddress",
"ip_namespace": {"hfid": ["default"]},
"resources": [{"id": prefix.id}],
},
)
await pool.save(allow_upsert=True)
Assign the pool to the interface template
With the pool created, update the interface template from the previous section to allocate an IP address from it whenever a new interface is created.
- Via the Web Interface
- Via the GraphQL Interface
- Via an Object File
- Via the Python SDK
- Navigate to
Object Management>Templates - Open
Template-SwitchModel123-Ethernet1(or any interface template) - On the
IP Addressfield, click the pool selector button and chooseInterface IP Pool - The field will show an
Allocated by poolbadge - Save
mutation {
TemplateInfraInterfaceUpdate(
data: {
hfid: ["Template-SwitchModel123", "Template-SwitchModel123-Ethernet1"]
ip_address_from_resource_pool: {hfid: ["Interface IP Pool"]}
}
) {
ok
}
}
---
apiVersion: infrahub.app/v1
kind: Object
spec:
kind: TemplateInfraInterface
data:
- template_name: "Template-SwitchModel123-Ethernet1"
device: "Template-SwitchModel123"
ip_address_from_resource_pool: "Interface IP Pool"
async def run(client: InfrahubClient, log: logging.Logger, branch: str) -> None:
pool = await client.get(kind="CoreIPAddressPool", hfid=["Interface IP Pool"])
iface_template = await client.get(
kind="TemplateInfraInterface",
hfid=["Template-SwitchModel123", "Template-SwitchModel123-Ethernet1"],
)
iface_template.ip_address_from_resource_pool = {"id": pool.id}
await iface_template.save(allow_upsert=True)
Create objects — IP addresses are allocated automatically
Creating a device from the template works exactly as described in the Create object instances section above. No additional steps are required — when the device is saved, Infrahub allocates an available IP address from Interface IP Pool and assigns it to each interface's ip_address field.
Resource allocation happens at object creation time, not at template creation time. Each new device gets its own unique address drawn from the pool.
For more information about resource pools, see:
- Managing resource pools - Guide for creating and using pools
- Resource Manager concepts - Deep dive into allocation behaviour