Mass Update Host Tags and Groups in Zabbix

API Made Visual

Reorganizing monitoring ownership usually means the same three pain points:

  1. Tags out of sync (env, team, sla, owner…)
  2. Host groups that no longer match reality
  3. Doing both for hundreds or thousands of hosts without breaking production

The Zabbix frontend mass-update helps a little. One-off scripts help until the next campaign needs a different filter. ZbxWizz sits in the middle: a spreadsheet of hosts, JavaScript column transforms, then a Push template that calls host.update only on the rows you selected.

ZbxWizz workbench with hosts, groups, and tags

This is the workflow behind the cleanup story in Why I Built ZbxWizz — written as a repeatable how-to.


Mental model

Import hosts (+ tags / groups)
        ↓
Filter to the subset that needs change
        ↓
Transform tags / groupid columns
        ↓
Select visible rows → Push host.update

Pull and Push only touch rows that are both selected and visible. Filters are your safety rail.


1. Connect and import hosts

Configure the API connection (logo top-right). Prefer Sequential while testing updates.

Then Data → Import → From Zabbix · Resource: host:

{
  "limit": 500,
  "output": ["hostid", "host", "name", "status"],
  "selectTags": ["tag", "value"],
  "selectHostGroups": ["groupid", "name"]
}

Always set an explicit limit. If you omit it, ZbxWizz injects limit: 5 as a safety default.

Import data from Zabbix — host request with tags and groups

Nested fields land as JSON strings in cells (and as data.csv on the row for transforms).

Narrow the set with column filters — e.g. tag contains prod, or group name contains Web — before you touch anything.

Selected hosts ready for a bulk Push


2. Mass-add a tag

Goal: append {"tag":"reviewed","value":"2026-08-25"} to every visible host without wiping existing tags.

  1. Open the column menu on tagsTransform
  2. Expression:
json(obj(data.csv.tags).concat({"tag":"reviewed","value":"2026-08-25"}))

Or, if you are transforming the tags cell itself:

json(obj(flds.tags).concat({"tag":"reviewed","value":"2026-08-25"}))
  1. Check the preview on the first row → Apply

Transform column — expression with live preview

obj(...) parses the JSON string; concat keeps prior tags; json(...) writes the array back as a cell value Push can inject.

Replace a tag value instead

Example: force env=prod while keeping other tags:

json(
  obj(data.csv.tags)
    .filter(t => t.tag !== "env")
    .concat({"tag":"env","value":"prod"})
)

3. Push the tag update

  1. Select the rows (header checkbox = all visible)
  2. Zabbix → Push · Resource: host · Operation: update
  3. Template (set the tags column index to match your sheet — here column 7):
{
  "hostid": "${flds.hostid}",
  "tags": ${cols[7]}
}

Push host.update with tags from column 7

Important: ${cols[7]} has no quotes around it — the cell already holds JSON. "${flds.hostid}" stays quoted as a string.

Preview one row → Confirm → Execute. Spot-check a host in the Zabbix UI.


4. Mass-assign host groups

host.update with groups replaces the host’s group membership with the list you send. Plan the full set of groupids you want, not a single “add this group” delta (unless your process intentionally resets membership).

Option A — single group from a column

If the sheet has groupid:

{
  "hostid": "${flds.hostid}",
  "groups": [{"groupid": "${flds.groupid}"}]
}

Push host.update replacing groups from groupid

Transform groupid first if you are remapping (e.g. all filtered hosts → group 15):

"15"

Option B — multiple groups as JSON

Transform a groups column into:

json([
  {"groupid": "12"},
  {"groupid": "40"}
])

Push:

{
  "hostid": "${flds.hostid}",
  "groups": ${cols[YOUR_GROUPS_COL]}
}

Resolve group names → IDs

  1. Import hostgroup into a second sheet
  2. On the hosts sheet, use a cross-sheet lookup (see examples) or paste IDs from a known map

Never guess numeric IDs on production.


5. Tags and groups in one Push

You can combine fields in a single host.update:

{
  "hostid": "${flds.hostid}",
  "tags": ${cols[7]},
  "groups": [{"groupid": "${flds.groupid}"}]
}

Still: one test row first. Group replacement is easy to get wrong at scale.


Practical campaign pattern

Step Action
1 File → Save before-tag-migration.json
2 Import with a tight filter (tags, groupids, or name regex)
3 Transform offline; export CSV for stakeholder review
4 Push 1 row → verify
5 Push the rest in Sequential mode
6 Re-import a sample to confirm tags/groups stuck

Failed rows keep error details on the row (inspect via the row number control) so you can fix and retry without redoing the whole set.


Frontend mass update vs ZbxWizz

Frontend mass update One-off script ZbxWizz
Visual review Limited None Full spreadsheet
Arbitrary filters UI filters Code Column filters + transforms
Reusable request No If you keep the script Saved Push templates
Token / data location Server session Wherever the script runs Browser only

Use the frontend for quick identical edits on a short list. Use ZbxWizz when the change set is data-driven (CSV, lookups, different values per row).


Get started

Next up on this track: Bulk Update Zabbix Hosts from CSV Without Scripts.

— Sergiu, Creator of ZbxWizz

← Back to blog