Junos apply-groups: Configuration Groups Explained - 夜莺博客

Junos apply-groups: Configuration Groups Explained

Configuration groups (also known as apply-groups) are one of Junos's most powerful features, and also one of the least understood. A configuration group lets you define reusable blocks of configuration - interface templates, NTP settings, SNMP parameters, syslog options - and then apply them across the hierarchy with a single apply-groups statement. Instead of pasting the same 20 lines of NTP config into every device, you define it once in a group and reuse it everywhere. This guide explains how groups work, how precedence resolves conflicts, and shows working examples you can adapt immediately.

How Groups Actually Merge Into the Configuration

It helps to be precise about what a group is, because most confusion about apply-groups comes from treating them as textual includes. A group is a container of configuration statements that lives under the top-level groups hierarchy. The statements inside it are validated and merged in the context where the group is applied, not where it is defined. A group containing an interfaces stanza applied at the top level injects that stanza into the top-level interfaces hierarchy; the same group applied under a specific interface would inject the same stanza one level deeper, and Junos would reject the merge as invalid.

Three consequences follow. First, a group that is defined but never applied does nothing at all — it consumes candidate configuration space and nothing else, which makes it safe to stage a template in advance. Second, merging happens at commit time, so the effect of a group is visible in the resulting configuration object, not as a separate runtime layer; when you debug inheritance you are debugging the merged result. Third, because the merge is structural rather than textual, an unapplied group can be committed on a platform that does not support the features inside it, which is exactly what you want when you build a fleet-wide template that only some devices use.

Basic Structure

groups {
    GLOBAL {
        system {
            ntp {
                server 10.0.0.10;
                server 10.0.0.11;
            }
        }
    }
}
apply-groups GLOBAL;

The group named GLOBAL holds NTP configuration, and the top-level apply-groups GLOBAL; injects it into the system hierarchy. You can apply groups at any level: apply-groups under interfaces, under a specific interface, under protocols, etc.

set groups GLOBAL system ntp server 10.0.0.10
set groups GLOBAL system ntp server 10.0.0.11
set apply-groups GLOBAL
show configuration groups
show configuration | display inheritance

Interface Templates with Wildcards

The classic use case: apply the same family config to every interface with a wildcard:

groups {
    IF-MGMT {
        interfaces {
            <*> {
                unit 0 {
                    family inet {
                        mtu 1500;
                    }
                }
            }
        }
    }
}
apply-groups IF-MGMT;

The <*> wildcard matches all interface names. Junos also supports $junos-interface-ifd-name to build dynamic address patterns:

groups {
    LOOPBACK {
        interfaces {
            lo0 {
                unit 0 {
                    family inet {
                        address $junos-loopback-0/32;
                    }
                }
            }
        }
    }
}

Dynamic Variables: The Junos Substitution List

Wildcards alone are not enough once a template needs to reference the interface it is being applied to. Junos solves this with predefined variables that are substituted per interface during the merge, which is what makes a single group able to configure an unlimited number of interfaces without repeat entries.

  • $junos-interface-ifd-name — the physical interface name, such as ge-0/0/1.
  • $junos-interface-unit — the logical unit number the group is being merged into.
  • $junos-interface-name — the full interface name including the unit, such as ge-0/0/1.100.
  • $junos-loopback-0, $junos-loopback-1 — the addresses configured on the corresponding loopback units, which is the standard way to configure a router ID or an unnumbered interface.
groups {
    IF-ACCESS {
        interfaces {
            <ge-0/0/*> {
                unit 0 {
                    description "access port - $junos-interface-ifd-name";
                    family ethernet-switching;
                }
            }
        }
    }
}

Substitution happens at commit time, so the description and any derived address appear in the effective configuration with the real interface name filled in. This is the trick that turns a 200-line per-interface configuration into a 15-line group.

Precedence: Specific Configuration Wins

When the same parameter appears both in a group and directly in the configuration, the direct configuration wins. Groups applied at a lower level override groups applied at a higher level. The resolution order is:

  1. Direct (explicit) configuration in the hierarchy
  2. Groups applied at the most specific level
  3. Groups applied at higher levels
  4. junos-defaults groups

Use apply-groups-except to exclude specific statements:

apply-groups-except [ IF-MGMT GLOBAL ];

apply-groups-except is the correct tool for the one interface in the fleet that must not receive the template: instead of building a second template, apply the group as usual and add a single exception at the interface level. Note that apply-groups-except applies to the whole subtree beneath the point where it is configured, so place it as low in the hierarchy as the exception requires.

Overriding and Replacing Inherited Values

Direct configuration beats the group, but there is a subtlety when the parameter is a list rather than a single value. Prefix lists, community lists, address books and similar containers are merged by default, so a group that supplies ten NTP servers and an interface that supplies one will end up with eleven entries. When you need the local statement to replace the inherited list instead of extending it, use the replace: tag inside the group, which makes the group's list replace any list at the application point.

groups {
    NTP-STANDARD {
        system {
            ntp {
                replace: server 10.0.0.10;
                server 10.0.0.11;
            }
        }
    }
}
apply-groups NTP-STANDARD;

Without the tag, mixed behaviour is the most common source of "the device has more NTP servers than I configured" tickets. Check the merged result with display inheritance before you trust either behaviour.

Nested Groups, junos-defaults and Ordering

Groups can reference other groups: an apply-groups statement inside a group pulls in that group's contents as part of the parent. This lets you build a layered template — a BASE group with syslog, NTP and SNMP, a DC group that applies BASE plus data-centre-specific settings, and a per-site group that applies DC.

groups {
    BASE {
        system {
            host-name ?;
            time-zone UTC;
            services { ssh; netconf; }
        }
    }
    DC {
        apply-groups BASE;
        system {
            syslog { host 10.0.0.20 { any any; } }
        }
    }
}
apply-groups DC;

Junos also maintains a built-in group called junos-defaults, which is applied last and holds factory default settings. You will see it in inheritance output and can override its values with an ordinary group, which is how defaults such as default SNMP communities or protocol timers are adjusted safely across a platform family. Keep nesting shallow — two or three levels is readable, five is not — and remember that group ordering matters when two groups at the same level define the same leaf: Junos documents the order in which groups are merged, and the safest design is a group that does not overlap with its siblings at all.

Practical Example: Syslog + SNMP on Every Device

groups {
    BASE {
        system {
            syslog {
                file messages {
                    any notice;
                    authorization info;
                }
                host 10.0.0.20 {
                    any any;
                }
            }
        }
        snmp {
            community public {
                authorization read-only;
            }
            trap-group NMS {
                targets {
                    10.0.0.21;
                }
            }
        }
    }
}
apply-groups BASE;

Add this block to every new device in your fleet and logging/monitoring is consistent without re-typing.

A Group for a Repeated Protocol Block

Groups are not limited to the system hierarchy. The same technique configures BFD liveness detection, interface ranges for uplinks, or the export policy every iBGP neighbour needs:

groups {
    BFD-FAST {
        protocols {
            bfd {
                minimum-interval 300;
                multiplier 3;
            }
        }
    }
    IBGP-COMMON {
        protocols {
            bgp {
                group my-internal-group {
                    type internal;
                    local-address 10.0.0.1;
                    export NEXT-HOP-SELF;
                }
            }
        }
    }
}
apply-groups [ BFD-FAST IBGP-COMMON ];

Applying several groups in one statement is valid and common. The order of the names in the list determines precedence between the groups themselves, so list the most generic group last and the most specific first, and verify with inheritance output.

Verification and Troubleshooting

show configuration | display inheritance
show configuration interfaces | display inheritance
show configuration groups
show configuration | display set
show configuration | display inheritance | display set | match NTP
show configuration | display inheritance | except "##"

display inheritance is the key command: it shows the effective configuration with group values inlined, so you can see exactly what a group contributed. In the output, inherited lines are prefixed with ## and a comment naming the source group; piping through | except "##" shows only the explicit configuration, which is a fast way to see what is local to the device. Add | display set when you want flat set syntax rather than the curly-brace tree, and | display xml when you are feeding the output into an automation pipeline. If a group does not seem to apply, check:

  • Typo in the group name vs the apply-groups statement.
  • Group applied at the wrong hierarchy level.
  • An explicit configuration overriding the group (direct config wins).
  • A container that merges rather than replaces, so the device has both the group's entries and the local ones.
  • The group is deactivated, or the apply-groups statement itself is deactivated.

To test a change without committing to the whole fleet, deactivate groups STATE-NAME on a lab device and compare inheritance output before and after — the difference is precisely what the group contributes.

Groups, Commit Confirmed and Rescue Configuration

Because a group change can alter hundreds of interfaces at once, groups and the safe-change tooling belong together. Always use commit confirmed when modifying a group that is applied fleet-wide, so an unusable result rolls back by itself, and keep an up-to-date rescue configuration on the box as the last line of defence. If the merge produces something you did not intend, rollback 1 returns to the previous commit including the group definitions, because groups are part of the configuration object rather than an external template store. Our guides to the Junos commit confirmed and rollback workflow and to the Junos rescue configuration cover the mechanics; groups simply make the stakes higher.

Groups as an Automation Building Block

Groups are the natural output of any templating layer. Junos PyEZ can push a group and the apply-groups statement as two candidate-configuration loads, and Jinja2 templates can generate the group body from an inventory variable, which keeps device configuration declarative while still giving you a readable, inheritable result on the box. The same group can be emitted by Ansible, NSO or a commit script, which means the local configuration stays small and the intent lives in one place. Our walkthrough on automating Junos with PyEZ and the multi-vendor Ansible and Jinja2 workflow show both patterns end to end.

Why Use Groups?

  • Consistency: one change in the group updates every interface/device.
  • Less config to store and review - especially valuable on EX/QFX fleets.
  • Combines with NSO/Ansible templating for full automation.
  • Fewer typos: the template is written once, not once per interface.
  • Faster audits: reading one group tells you what every device in the fleet shares.

Common Pitfalls

  • Over-broad wildcards. <*> matches management and loopback interfaces too; scope the pattern to <ge-0/0/*> before you commit.
  • Assuming show configuration tells the truth. It shows explicit configuration only; without display inheritance you will not see the group's contribution.
  • List merging surprises. Inherited and local entries coexist unless you use replace:.
  • Deep nesting. Layered groups are powerful and quickly unreadable; document the order.
  • No rollback plan. A fleet-wide group edit without commit confirmed is an outage waiting for a typo.

Related: Juniper EX switch initial setup and multi-vendor automation with Ansible and Jinja2.

原文链接:https://www.juniper.net/documentation/us/en/software/junos/cli/topics/topic-map/configuring-configuration-groups.html

Related reading