Documentation Index
Fetch the complete documentation index at: https://mintlify.com/OpsMill/infrahub/llms.txt
Use this file to discover all available pages before exploring further.
Profiles apply common attribute values and relationships to multiple objects, reducing duplication and ensuring consistency. Think of them as templates for object configuration.
How Profiles Work
Profiles define attribute values that cascade to objects:
Profile: "Production Device Defaults"
├── status: active
├── monitoring_enabled: true
├── backup_frequency: daily
└── maintenance_window: sunday-2am
Device: router-01
├── name: "router-01" (own value)
├── status: active (from profile)
├── monitoring_enabled: true (from profile)
└── site: atl1 (own value)
Device: router-02
├── name: "router-02" (own value)
├── status: active (from profile)
├── monitoring_enabled: true (from profile)
└── site: dfw1 (own value)
When you update the profile, all associated objects automatically inherit the changes.
Creating Profiles
Profiles are schema-specific. You define them in the schema, then create instances.
Define Profile Schema
In your schema file, define a profile for an object kind:
# Schema definition
profiles:
- name: DeviceProfile
namespace: Infra
label: Device Profile
description: Common settings for devices
apply_to:
- InfraDevice
attributes:
- name: status
kind: Text
optional: true
- name: monitoring_enabled
kind: Boolean
optional: true
default_value: true
- name: backup_frequency
kind: Text
optional: true
choices:
- daily
- weekly
- never
relationships:
- name: maintenance_window
peer: MaintenanceWindow
optional: true
cardinality: one
Load the schema:
infrahubctl schema load schema.yaml
Create Profile Instance
- Navigate to Profiles → Device Profiles
- Click Add Device Profile
- Set profile values:
- Name: Profile identifier
- Label: Display name
- Priority: Lower numbers = higher priority (default: 1000)
- Set attribute values to apply
- Set relationship peers to apply
- Click Save
mutation {
InfraDeviceProfileCreate(
data: {
profile_name: { value: "production-defaults" }
profile_label: { value: "Production Defaults" }
profile_priority: { value: 100 }
status: { value: "active" }
monitoring_enabled: { value: true }
backup_frequency: { value: "daily" }
maintenance_window: { id: "<window-uuid>" }
}
) {
ok
object {
id
display_label
}
}
}
Note: Profile-specific fields use profile_ prefix (profile_name, profile_label, profile_priority).from infrahub_sdk import InfrahubClient
client = InfrahubClient()
# Get maintenance window
window = await client.get(
kind="MaintenanceWindow",
name__value="sunday-2am"
)
# Create profile
profile = await client.create(
kind="InfraDeviceProfile",
profile_name="production-defaults",
profile_label="Production Defaults",
profile_priority=100,
status="active",
monitoring_enabled=True,
backup_frequency="daily",
maintenance_window=window.id
)
await profile.save()
Applying Profiles to Objects
Assign profiles to objects to apply their settings.
Assign During Creation
mutation {
InfraDeviceCreate(
data: {
name: { value: "router-01" }
site: { id: "<site-uuid>" }
device_type: { id: "<type-uuid>" }
profiles: [
{ id: "<profile-uuid>" }
]
}
) {
ok
object {
id
status { value } # Inherited from profile
monitoring_enabled { value } # Inherited from profile
}
}
}
profile = await client.get(
kind="InfraDeviceProfile",
profile_name__value="production-defaults"
)
device = await client.create(
kind="InfraDevice",
name="router-01",
site="<site-uuid>",
device_type="<type-uuid>",
profiles=[profile.id]
)
await device.save()
# Profile values are automatically applied
print(f"Status: {device.status.value}") # "active" from profile
Assign to Existing Object
mutation {
InfraDeviceUpdate(
data: {
id: "<device-uuid>"
profiles: [
{ id: "<profile-uuid>" }
]
}
) {
ok
}
}
device = await client.get(
kind="InfraDevice",
id="<device-uuid>"
)
profile = await client.get(
kind="InfraDeviceProfile",
profile_name__value="production-defaults"
)
device.profiles = [profile.id]
await device.save()
Profile Priority
When multiple profiles apply to the same object, priority determines which profile’s values are used.
Priority Rules
- Lower number = higher priority: Priority 100 overrides priority 1000
- Default priority: 1000 if not specified
- Per-attribute resolution: Each attribute uses the highest-priority profile that sets it
- Explicit values win: Values set directly on the object override all profiles
Example
# Profile 1 (priority 100)
profile1 = await client.create(
kind="InfraDeviceProfile",
profile_name="high-priority",
profile_priority=100,
status="active",
monitoring_enabled=True
)
# Profile 2 (priority 200)
profile2 = await client.create(
kind="InfraDeviceProfile",
profile_name="low-priority",
profile_priority=200,
status="maintenance", # This loses to profile1
backup_frequency="daily" # This wins (profile1 doesn't set it)
)
# Device with both profiles
device = await client.create(
kind="InfraDevice",
name="router-01",
profiles=[profile1.id, profile2.id]
)
# Result:
# - status: "active" (from profile1, priority 100)
# - monitoring_enabled: True (from profile1, only one sets it)
# - backup_frequency: "daily" (from profile2, only one sets it)
Profile Inheritance Tracking
Infrahub tracks which values come from profiles.
Check Value Source
query {
InfraDevice(name__value: "router-01") {
edges {
node {
status {
value
is_from_profile
source {
id
display_label
}
}
monitoring_enabled {
value
is_from_profile
source {
id
}
}
}
}
}
}
Response:
{
"status": {
"value": "active",
"is_from_profile": true,
"source": {
"id": "<profile-uuid>",
"display_label": "Production Defaults"
}
},
"monitoring_enabled": {
"value": true,
"is_from_profile": true,
"source": {
"id": "<profile-uuid>"
}
}
}
Override Profile Values
Set a value directly on the object to override the profile:
mutation {
InfraDeviceUpdate(
data: {
id: "<device-uuid>"
status: { value: "maintenance" } # Overrides profile
}
) {
ok
}
}
Now is_from_profile is false and source is the device itself.
Profile with Relationships
Profiles can set relationship values.
Define Relationship in Profile Schema
profiles:
- name: DeviceProfile
relationships:
- name: tags
peer: BuiltinTag
optional: true
cardinality: many
- name: maintenance_window
peer: MaintenanceWindow
optional: true
cardinality: one
Set Relationships in Profile
mutation {
InfraDeviceProfileCreate(
data: {
profile_name: { value: "production-defaults" }
tags: [
{ id: "<tag-production-uuid>" },
{ id: "<tag-monitored-uuid>" }
]
maintenance_window: { id: "<window-uuid>" }
}
) {
ok
}
}
All devices with this profile inherit the tags and maintenance window.
Updating Profiles
Changes to profiles automatically propagate to associated objects.
Update Profile Values
mutation {
InfraDeviceProfileUpdate(
data: {
id: "<profile-uuid>"
backup_frequency: { value: "weekly" } # Change from daily to weekly
}
) {
ok
}
}
All devices using this profile now have backup_frequency: "weekly" (unless they override it).
Profile Change Propagation
The NodeProfilesApplier handles propagation:
from infrahub.profiles.node_applier import NodeProfilesApplier
# After updating a profile
applier = NodeProfilesApplier(db=db, branch=branch)
# Apply to a specific node
await applier.apply_profiles(node=device)
await device.save()
# Or apply to all nodes using the profile
devices = await client.all(
kind="InfraDevice",
profiles__id="<profile-uuid>"
)
for device in devices:
await applier.apply_profiles(node=device)
await device.save()
Infrahub automatically applies profiles during save operations.
Use Cases
Environment Profiles
Define profiles per environment:
# Production profile
prod_profile = await client.create(
kind="InfraDeviceProfile",
profile_name="production",
monitoring_enabled=True,
backup_frequency="daily",
sla="99.99"
)
# Development profile
dev_profile = await client.create(
kind="InfraDeviceProfile",
profile_name="development",
monitoring_enabled=False,
backup_frequency="never",
sla="none"
)
Location Profiles
Define profiles per datacenter:
# Atlanta DC profile
atl_profile = await client.create(
kind="InfraDeviceProfile",
profile_name="atlanta-dc",
timezone="America/New_York",
dns_servers=["10.0.1.1", "10.0.1.2"],
ntp_servers=["10.0.1.10"]
)
Role Profiles
Define profiles by device role:
# Edge router profile
edge_profile = await client.create(
kind="InfraDeviceProfile",
profile_name="edge-router",
bgp_enabled=True,
firewall_enabled=True,
port_security=True
)
# Access switch profile
access_profile = await client.create(
kind="InfraDeviceProfile",
profile_name="access-switch",
poe_enabled=True,
vlan_mode="trunk",
stp_enabled=True
)
Compliance Profiles
Define profiles for compliance requirements:
# PCI-DSS profile
pci_profile = await client.create(
kind="InfraDeviceProfile",
profile_name="pci-dss",
encryption_required=True,
audit_logging=True,
password_complexity="high",
session_timeout=15
)
Best Practices
- Use descriptive names: Make profile purpose clear
- Set appropriate priorities: Reserve low numbers (100-500) for critical profiles
- Document in label: Use
profile_label for human-readable names
- Avoid too many profiles: 3-5 profiles per object is usually sufficient
- Profile by concern: Separate environment, location, and role profiles
- Test before applying: Validate profile values before applying to production objects
- Review periodically: Audit profile usage and remove unused profiles
Profiles vs. Templates
| Feature | Profiles | Templates |
|---|
| Purpose | Apply common settings | Create pre-configured objects |
| Timing | Applied at any time | Applied at creation |
| Updates | Propagate to objects | Don’t propagate |
| Override | Objects can override | Objects inherit but don’t track |
| Use case | Shared defaults | Object blueprints |
Use profiles for settings that should stay synchronized across objects. Use templates for creating objects with initial configuration that may diverge over time.
Next Steps