Duplicates and merging¶
Find duplicates¶
Objects are grouped by value, not name. h-web1, web-primary, and
h-web1-slash (all 10.0.0.10/32) land in one bucket; tcp-443 and
svc-https (both TCP/443) in another. Each row tells you the value and every
object name + location that defines it.
Strict by default¶
Address matching is strict: only byte-identical values are duplicates. A
host accidentally written with a subnet mask — web-server = 10.1.1.50/24 —
is not reported as a duplicate of the network internal = 10.1.1.0/24,
even though both mask down to the same /24. (10.0.0.10 and 10.0.0.10/32
are still the same host, so they group.)
Pass --not-strict for the looser, fringe behaviour that masks host bits and
collapses a host-with-mask onto its network:
--not-strict only widens what's listed — it grants no merge power. A pair it
surfaces (e.g. a host and its network) has different exact values, so
dedup merge still refuses it unless you pass
--allow-value-change. Treat --not-strict as a discovery aid, then decide
case by case whether the masked-equal objects really should be merged.
Merge two objects¶
dedup merge collapses one object (--remove) into another (--keep),
repointing every reference before deleting the loser:
By default this is a dry-run. You'll see the plan:
merge address 'web-primary'@shared -> 'h-web1'@shared
• address-group 'grp-web' @shared static: ['h-web1', 'web-primary'] -> ['h-web1']
• nat-rule 'nat-web' @shared pre source: ['web-primary'] -> ['h-web1']
• delete address 'web-primary' @shared
dry-run — re-run with --apply to execute
What gets rewritten¶
Every reference the reference graph knows about:
- static address-group membership,
- security rule
source/destination, - NAT rule
source/destination(translation fields are flagged for review), - every other rulebase's
source/destination/service/tag(PBF, decryption, authentication, QoS, application-override, DoS, SD-WAN, tunnel-inspect, network-packet-broker). A PBF next-hop object has no flat member list, so a merge that would strand it is blocked for manual review.
References are rewritten before the object is deleted, and duplicate members are collapsed (a group that listed both names ends up with just the survivor).
Merge a whole bucket at once¶
Pairwise --keep/--remove collapses two objects. When a value has three or more
duplicate names, --group <value> collapses the entire bucket toward one
survivor in a single plan — every non-survivor is repointed onto the survivor and
deleted:
psc -c panorama.xml dedup merge --group 10.0.0.10/32 # dry-run
psc -c panorama.xml dedup merge --group 10.0.0.10/32 --keep h-web1 # pick the survivor
psc -c panorama.xml dedup merge --group 10.0.0.10/32 --apply --out fixed.xml
- The value is a bucket from
dedup addresses(run it first to see them). --keep NAMEchooses the survivor; omit it and the most visible member wins — the one highest in the device-group hierarchy (shared, else the device-group nearest the root). Collapsing upward is what makes a duplicate disappear for every device-group at once. A member the other members' rules could never resolve — one in an unrelated device-group branch — is skipped however high it sits, since keeping it would only block the merge.--groupand--removeare mutually exclusive.--not-strictmatches the bucket under host-bit masking (see Strict by default).
The safety gate below applies unchanged — a bucket whose members differ in value
still needs --allow-value-change.
The safety gate¶
psc refuses (exit 6) a merge that would change meaning or that it can't
perform safely:
- Different values. Merging objects with different values changes what rules
match. Blocked unless you pass
--allow-value-change. - Invisible survivor. If the kept object isn't visible where a reference lives (e.g. it's in a sibling device-group), the merge is blocked rather than creating a dangling reference.
$ psc -c panorama.xml -o json dedup merge --keep net-10 --remove local-only --remove-location DG-EDGE
{"error": "plan blocked (unsafe): value mismatch: ...", "type": "conflict", ...}
$ echo $?
6
Visibility is judged against the config as the plan leaves it, not as it
stands now. PAN-OS resolves a name by walking upward only — the referrer's own
device-group, then its parent, and so on up to shared; a sibling device-group
is never consulted.
Collapsing a device-group's local copy¶
The most common cleanup is a device-group holding its own copy of an object that
already exists in shared under the same name. The local copy shadows the
shared one, so every rule in that device-group binds to the local object today.
Merging them deletes the local copy and lets those rules re-resolve upward to the
survivor. No member list changes — the rules keep the same name — so the plan
carries only the delete, and psc warns about what silently moved:
$ psc -c panorama.xml dedup merge --keep web --keep-location shared --remove web --remove-location DG-EDGE
! 2 reference(s) will re-resolve from 'web'@DG-EDGE to 'web'@shared (inheritance collapse)
• delete address 'web' @DG-EDGE
This still blocks when an intermediate device-group sits on the upward walk
with an object of the same name. In shared → DG-EMEA → DG-EDGE, dropping
'web'@DG-EDGE in favour of 'web'@shared makes DG-EDGE stop at
'web'@DG-EMEA — not the survivor. Merge the intermediate copy too (a --group
bucket merge collapses all of them in one plan), or the plan is refused.
Attribute drift¶
The merge gate compares values. A dropped device-group copy can still carry
tags or a description the survivor lacks, and losing those changes what that
device-group sees. Tags are the sharp edge — they decide dynamic address-group
membership, so a dropped tag changes what traffic a DAG matches. psc warns
rather than blocks; the plan is yours to approve:
! dropped 'web'@DG-EDGE has tags not on 'web'@shared: prod
! tag 'prod' is used by dynamic address-group 'dag-prod'@DG-EDGE — its membership will change
Applying¶
fixed.xml is a complete, loadable config. The source export is untouched.
Locations¶
By default both objects are taken from --device-group (or shared). Override
per object with --keep-location / --remove-location.
Promote a cross-DG duplicate into shared¶
dedup merge needs a survivor to collapse onto. The most common Panorama
cleanup finds a duplicate that has no survivor to pick: the same object
defined independently in DG-A and DG-B, and nowhere in shared or a common
ancestor. There is nothing above the device-groups to merge into — dedup
merge structurally cannot fix this one.
dedup promote is the missing operation. It creates the object once at the
destination (shared by default, or --to <ancestor-DG>) and deletes every
device-group copy. Because promotion only ever moves upward, every reference
falls through to the new definition by ordinary PAN-OS shadowing — the plan
never needs to repoint a single reference:
$ psc -c panorama.xml dedup promote address --group 10.0.0.1/32
promote 2 address(s) -> @shared
• create address 'web' @shared
• delete address 'web' @DG-A
• delete address 'web' @DG-B
dry-run — re-run with --apply to execute
$ psc -c panorama.xml -o set dedup promote address --group 10.0.0.1/32
# promote 2 address(s) -> @shared
set shared address web ip-netmask 10.0.0.1/32
delete device-group DG-A address web
delete device-group DG-B address web
Selecting the bucket¶
--group <value>(address/service) selects a duplicate-value bucket, the same valuesdedup addresses/dedup serviceslist.--name <name>(address-group or tag) selects every object with that name — both kinds are name-keyed. For address-groups there's no way to type an effective-leaf-set selector, and their members' effective sets must match or the plan is blocked (the same equivalence checkdedup groupsuses). Tags have no value at all, so a same-named pair is always one bucket (see Duplicate tags).--allsweeps every promotable bucket of the kind in one plan. Buckets it cannot promote are skipped and reported on stderr, never silently dropped:
$ psc -c panorama.xml dedup promote address --all
note skipped 1 bucket(s):
- ip-netmask 10.0.0.10/32: bucket names diverge ('h-web1'@DG-A, 'web-primary'@DG-B); pass --keep NAME to unify them on one name
promote all duplicate address buckets -> @shared
--all and --keep are mutually exclusive — one survivor name cannot span
every bucket in the sweep. Promote a divergent bucket on its own with
--group/--name and --keep instead.
Divergently named copies: --keep¶
When the device-group copies were named differently (h-web1@DG-A vs.
web-primary@DG-B), promote has no way to choose a survivor name on its own
— --keep NAME picks it, and every reference to the other names is
repointed onto it before their copies are deleted:
$ psc -c panorama.xml dedup promote address --group 10.0.0.1/32 --keep h-web1
promote 2 address(s) -> @shared
• create address 'h-web1' @shared
• security-rule 'allow-web-primary' @DG-B pre source: ['web-primary'] -> ['h-web1']
• delete address 'h-web1' @DG-A
• delete address 'web-primary' @DG-B
dry-run — re-run with --apply to execute
Without --keep, a divergently-named bucket is a blocker (exit 6) rather
than a guess at which name should win.
--cascade for address-groups¶
--cascade also promotes a group bucket's transitive dependencies (its
members, their tags) to the same destination, deepest-first, in one plan —
without it, a dependency not already visible at the destination blocks the
promotion and is listed to promote first. This is the same cascade move
offers for a single object, extended to a whole bucket: every device-group's
copy of a shared dependency folds onto one upsert at the destination
(sources still lose their own copy), and a dependency still needed by an
object left behind is promoted but its source copy is retained (with a
warning).
Safety¶
The same gates as move/merge apply, run once per bucket member: a
destination that isn't shared or a common ancestor of every member is
rejected outright, an intermediate device-group already defining the name (a
shadow) blocks, and a dependency invisible at the destination blocks unless
--cascade is passed. Promote additionally requires every member to carry the
same value (or, for groups, the same effective leaf-address set) — there's no
--allow-value-change escape hatch, since promote never re-derives a
canonical value. A sibling device-group that keeps defining the same name after
the promotion is not a blocker — the plan still repoints the members it knows
about — but it warns, since that sibling will keep shadowing the promoted
object for its own subtree.
Applies the same way as merge: dry-run by default, --apply --out FILE
offline (never overwriting the source export), --apply alone on a live
profile (candidate config, never committed), and -o set/-of set for the
PAN-OS script.
Duplicate tags¶
A tag defined under the same name in more than one location (e.g. prod in
shared, DG-A, and DG-B) is a redundant definition of one logical tag.
PAN-OS resolves a tag reference by name up the device-group chain, so those
copies all stand in for the same binding. dedup tags lists them:
Tags are name-keyed, not value-keyed: a tag carries no match-affecting value
(its color/comments are cosmetic), so a same-named pair is always one bucket.
Consolidate a bucket with dedup promote tag:
$ psc -c panorama.xml dedup promote tag --name prod
promote 2 tag(s) -> @shared
• create tag 'prod' @shared
• delete tag 'prod' @DG-A
• delete tag 'prod' @DG-B
dry-run — re-run with --apply to execute
Because the name is stable, the consolidation repoints nothing — deleting the device-group copies simply lets their references (objects, rules, and dynamic-address-group filters) re-resolve upward to the survivor by name. This makes tag consolidation strictly safer than address/service promote, which can require repoints when copies are named differently.
The one thing to watch is cosmetic: if the discarded copies carry a different
color or comments than the survivor, the survivor's win, and psc warns
(it never blocks on this — the operator decides):
$ psc -c panorama.xml dedup promote tag --name prod
...
note 'prod'@DG-B has color color5 the promoted copy will not carry (survivor keeps color1)
--all sweeps every duplicate-tag bucket in one plan. --group, --keep, and
--cascade do not apply to tag: tags aren't value-keyed (no --group),
a bucket has a single name (no --keep), and a tag is a dependency leaf with no
member closure to cascade. Consolidation is available in the workbench dedup
spoke too — select same-named tags and pick a promote destination.
Duplicate address-groups¶
dedup addresses/services finds duplicate objects; dedup groups finds
duplicate address-groups — two groups that resolve to the same effective
set of leaf addresses:
Groups are bucketed by the canonical set of hosts they expand to (nested groups are flattened first), so two groups land in the same bucket even if their names and direct members differ, as long as they ultimately reach the same addresses.
{
"kind": "address-group",
"value": "{ip-netmask:10.0.0.1/32, ip-netmask:10.0.0.2/32}",
"members": [
{"name": "grp-a", "location": "shared"},
{"name": "grp-b", "location": "shared"}
]
}
The audit is not exhaustive: dynamic (filter-based) groups are runtime-only,
and groups with dangling/malformed members can't be resolved — both are excluded
and counted on stderr (note audit is not exhaustive: skipped N dynamic and M
unresolvable group(s)). Scope the comparison with --location (default: the
global -d/--device-group if set, else compare across all locations).
Merge two address-groups¶
dedup merge-group collapses one group into another, reusing the same
repoint-before-delete engine as object merge:
psc -c panorama.xml dedup merge-group --keep grp-a --remove grp-b
psc -c panorama.xml dedup merge-group --keep grp-a --remove grp-b --apply --out fixed.xml
--keep(required): the survivor group.--remove(required): the group collapsed into--keepand deleted.--locationsets both;--keep-location/--remove-locationoverride per group (default: the global-d/--device-group, elseshared).- plus
--apply,--out,-of/--output-format.
Every referrer of --remove is repointed onto --keep before the dropped
group is deleted. Unlike object merge, there is no value-change override —
the merge is refused (exit 6) unless the two groups expand to the same
effective member set, because collapsing groups that mean different things would
silently change rule matching. It also blocks on a nested or cyclic pair
(one group already contains the other) and when the survivor isn't visible
where a reference lives.