Skip to content

Workflow extensions

Since 7.0

In 7.0 this mechanism replaced the old workflow system from 6.4. It allows the admin to execute custom code or Ansible when significant platform events occur, not just pre- and post-deploy.

A workflow extension binds tasks to events via the onAssetChange section of the definition:

"onAssetChange": [
{
"stage": "serverRegistered",
"tasks": [
{
"label": "on-server-registered",
"taskType": "ExtensionTaskAnsible",
"options": {
"asset": "my-bundle",
"playbook": "on-register.yaml"
}
}
]
}
]

Refer to the Extensions overview for how to register and publish an extension, and to the extension definition reference for the full schema.

The following values are accepted as events (stage) that trigger the execution of the attached tasks:

  • serverRegistered - Executed after a server is registered
  • serverDecommissioned - Executed after a server is decommissioned or deleted
  • switchRegistered - Executed after a switch is registered
  • switchDecommissioned - Executed after a switch is decommissioned or deleted
  • serverInstanceUpdate - Executed during an instance deployment
  • serverInstanceGroupUpdateDNS - Executed when DNS entries are created, updated or deleted for server instance groups
  • serverInstanceUpdateDNS - Executed when DNS entries are created, updated or deleted for server instances
  • clusterUpdateDNS - Executed when DNS entries are updated for a cluster
  • serverCreateDNS - Executed when DNS entries are created for servers’ BMCs
  • serverDeleteDNS - Executed when DNS entries are deleted for servers’ BMCs
  • switchCreateDNS - Executed when DNS entries are created for a switch’s management interface
  • switchDeleteDNS - Executed when DNS entries are deleted for a switch’s management interface

Definitions written for earlier 7.x releases may reference the stages serverInstanceGroupCreateDNS, serverInstanceGroupDeleteDNS or serverInstanceDeleteDNS. These no longer exist — create/update/delete DNS events are consolidated into the *UpdateDNS stages above.

The following task types can be attached to a stage:

Each stage provides a payload describing the object that triggered the event. Ansible tasks receive it as extra-vars (reference the keys directly, e.g. {{ server.model }}); HTTP and SSH tasks can reference the same keys in their Nunjucks templates (requestTemplate / commandTemplate).

The serverInstanceRecordSet object:

{
"serverInstanceRecordSet": {
"deployStatus": "ongoing",
"deployType": "create",
"deploymentId": 5388,
"instanceIpv4IpRanges": [],
"instanceIpv4Ips": [
{
"cidr": "10.0.0.4/24",
"gateway": "10.0.0.1",
"ip": "10.0.0.4",
"logicalNetworkId": 1214,
"maskBits": 24,
"netmask": "255.255.255.0",
"networkAddress": "10.0.0.0",
"status": "allocated"
}
],
"instanceIpv6IpRanges": [],
"instanceIpv6Ips": [],
"logicalNetworks": [
{
"interfaces": [
{
"macAddress": "8c:84:74:0e:6c:34",
"redundancyIndex": null,
"serverInterfaceId": 688,
"tagged": false
}
],
"ipv4Subnets": [
{
"gateway": "10.0.0.1",
"gatewayPlacement": "default",
"id": 237,
"networkAddress": "10.0.0.0",
"prefixLength": 24,
"scope": {
"kind": "fabric",
"resourceId": 1931
},
"status": "allocated"
}
],
"logicalNetworkId": 1214,
"logicalNetworkLabel": "alex-private-net",
"logicalNetworkName": "alex-private-net",
"vlans": [
{
"id": 314,
"scope": {
"kind": "fabric",
"resourceId": 1931
},
"status": "allocated",
"vlanId": 826
}
]
}
],
"serverId": 204,
"serverInstanceId": 4434,
"serviceStatus": "ordered",
"siteLabel": "dc-eveng-qa02"
}
}

The server object:

{
"server": {
"administrationState": "managed",
"bdkDebug": 0,
"biosInfo": {
"vendor": "Dell Inc.",
"version": "1.17.2"
},
"bmcMacAddress": "08:92:04:a4:d9:98",
"bootLastUpdateTimestamp": "0000-00-00T00:00:00Z",
"bootingCustomIsoInProgress": 0,
"chassisRackId": null,
"customInfo": null,
"datacenterName": "sonic-qts",
"diskCount": 4,
"disks": [],
"extensionInfo": null,
"gpuCount": 0,
"gpuInfo": [],
"instanceCustomInfo": null,
"interfaces": [],
"inventoryId": null,
"ipmiCredentialsNeedUpdate": 0,
"ipmiVersion": "2",
"isBasicCampusEndpoint": 0,
"jobInfo": {
"jobGroupId": null,
"jobId": null
},
"links": [],
"managementAddress": "172.18.33.189",
"mgmtSnmpPasswordEncrypted": "...",
"mgmtSnmpPort": 161,
"model": "PowerEdge R450",
"passwordEncrypted": "...",
"powerStatus": "off",
"powerStatusLastUpdateTimestamp": "2025-09-16T18:48:50Z",
"processorCoreCount": 12,
"processorCoreMhz": 4000,
"processorCount": 1,
"processorCpuMark": null,
"processorName": "Intel(R) Xeon(R) Silver 4310 CPU @ 2.10GHz",
"processorThreads": 24,
"rackName": null,
"rackPositionLowerUnit": null,
"rackPositionUpperUnit": null,
"ramGbytes": 32,
"registeredTimestamp": "2025-09-16T18:48:50Z",
"requiresManualCleaning": 0,
"requiresReRegister": 0,
"resourcePoolId": null,
"revision": 16,
"serialNumber": "6SBT0R3",
"serverAllocationTimestamp": null,
"serverCapacityMbps": 20000,
"serverClass": "bigdata",
"serverCleanupPolicyId": null,
"serverComments": null,
"serverCreatedTimestamp": "2025-09-16T18:13:26Z",
"serverDhcpStatus": "deny_requests",
"serverDiskCount": 4,
"serverDiskWipe": 1,
"serverId": 27,
"serverIsProduction": 0,
"serverMetricsMetadata": {
"fans": [],
"temperatures": [
{
"Label": "temperature.cpu.1",
"Name": "CPU1 Temp",
"Number": 1,
"Oem": null,
"PhysicalContext": "CPU",
"Units": "Celsius",
"UpperThresholdCritical": null,
"UpperThresholdFatal": null
}
]
},
"serverStatus": "deleting",
"serverSupportsOobProvisioning": 1,
"serverSupportsSol": 1,
"serverSupportsVirtualMedia": 1,
"serverTypeId": 9,
"serverUUID": "44454c4c-5300-1042-8054-b6c04f305233",
"siteId": 1,
"storageControllers": [
{
"description": "RAID Controller in SL 3",
"id": 112,
"label": "PERC H745 Front",
"mode": "RAID",
"name": "RAID.SL.3-1",
"options": {
"controllerModesSupported": [
"RAID",
"HBA"
],
"raidTypesSupported": [
"RAID0",
"RAID1",
"RAID5",
"RAID6",
"RAID10",
"RAID50",
"RAID60"
]
},
"serverId": 27
}
],
"submodel": null,
"supportsFcProvisioning": 0,
"tags": null,
"username": "root",
"vendor": "Dell",
"vendorInfo": {
"management": "iDRAC",
"version": "iDRAC9"
},
"vendorSkuId": "PowerEdge R450",
"vncPasswordEncrypted": "...",
"vncPort": 5901
}
}

The networkDevice object:

{
"networkDevice": {
"id": "ND-001",
"revision": 2,
"status": "active",
"siteId": 101,
"identifierString": "switch-01",
"description": "Core switch in datacenter rack 5",
"chassisIdentifier": "CHS-12345",
"country": "USA",
"city": "San Francisco",
"datacenterMeta": "DC-West",
"datacenterRoom": "Room A",
"datacenterRack": "Rack 5",
"rackPositionUpperUnit": 42,
"rackPositionLowerUnit": 37,
"managementAddress": "192.168.1.10",
"managementAddressPrefixLength": 24,
"managementAddressGateway": "192.168.1.1",
"managementPort": 22,
"syslogEnabled": 1,
"username": "admin",
"managementPassword": "password",
"managementMacAddress": "00:1A:2B:3C:4D:5E",
"serialNumber": "SN-987654321",
"driver": {
"name": "sonic_enterprise"
},
"position": {
"role": "leaf"
},
"orderIndex": 1,
"tags": ["production", "core", "leaf"],
"readyForInitialConfiguration": 1,
"bootstrapReadinessCheckInProgress": 0,
"subnetOobId": 2001,
"subnetOobIndex": 1,
"requiresOsInstall": true,
"bootstrapSkipInitialConfiguration": 0,
"bootstrapExpectedPartnerHostname": "switch-02",
"loopbackAddressIpv4": "10.0.0.1",
"loopbackAddressIpv6": "fe80::1",
"asn": 65001,
"vtepAddressIpv4": "10.1.1.1",
"vtepAddressIpv6": "fe80::2",
"mlagSystemMac": "00:1A:2B:3C:4D:5F",
"mlagDomainId": 10,
"quarantineVlan": 999,
"variablesMaterializedForOSAssets": {
"osVersion": "1.2.3"
},
"secretsMaterializedForOSAssets": {
"apiKey": "secret-key"
},
"bootstrapReadinessCheckResult": {
"status": "ready"
},
"isGateway": false,
"extensionInfo": {
"lastRun": "2025-09-17T12:00:00Z"
},
"links": [
{
"rel": "self",
"href": "/networkdevices/ND-001"
}
]
}
}

The serverInstanceGroupDNSRecordSet object: the site DNS zone, the DNS record sets belonging to the server instance group, and the group’s custom variables. Record names carry a terminating dot.

{
"serverInstanceGroupDNSRecordSet": {
"zone": {
"zoneName": "dc1.example.com",
"soaEmail": "admin.dc1.example.com",
"nameServers": [
"ns1.dc1.example.com"
],
"ttl": 3600,
"isDefault": true
},
"records": [
{
"id": 10,
"status": "active",
"siteId": 1,
"infrastructureId": 3870,
"zoneId": 4,
"zoneName": "dc1.example.com",
"name": "lambda.dc1.example.com.",
"type": "A",
"ttl": 3600,
"records": [
"10.20.50.36"
],
"tags": [
"instance-array:3386"
]
}
],
"customVariables": {}
}
}

The serverInstanceDNSRecordSet object — the same shape as serverInstanceGroupDNSRecordSet above (zone, records, customVariables), with the records belonging to the individual server instance.

The clusterDNSRecordSet object — the same shape (zone, records), with the records belonging to the cluster (the extension instance).

The serverDNSRecordSet object:

{
"serverDNSRecordSet": {
"zone": {
"zoneName": "us08.metalsoft.io",
"soaEmail": "admin.us08.metalsoft.io",
"nameServers": ["n1.metalsoft.io"],
"ttl": 3600,
"isDefault": true
},
"serverId": 10,
"serialNumber": "serial-number",
"managementAddress": "192.168.100.100",
"hostname": "server-10",
"fqdn": "server-10.us08.metalsoft.io",
"ip": {
"status": "allocated",
"ip": "192.168.100.100"
},
"operation": "create"
}
}

The switchDNSRecordSet object:

{
"switchDNSRecordSet": {
"zone": {
"zoneName": "us08.metalsoft.io",
"soaEmail": "admin.us08.metalsoft.io",
"nameServers": ["n1.metalsoft.io"],
"ttl": 3600,
"isDefault": true
},
"switchId": 10,
"managementAddress": "192.168.100.100",
"hostname": "switch-10",
"fqdn": "switch-10.us08.metalsoft.io",
"ip": {
"status": "allocated",
"ip": "192.168.100.100"
},
"operation": "create"
}
}

The following examples include everything, including the Ansible bundles: