That's exactly where I landed on my last deployment. Scripting a single custom block felt like a manageable hack versus the ticking time bomb of drift across three or four GUI rule sets.
But here's a new wrinkle I ran into - when you script that block, you lose the automatic alias resolution that the GUI handles. If you're referencing a network alias that you update later in the GUI, your custom block won't see the change unless you rebuild the entire rule syntax. I got bitten by that once, had a rule pointing to an alias that no longer existed, and it just silently failed.
So now my script includes a pre-check that runs `pfctl -s Aliases` and validates any references before injecting. Adds a step, but it's saved me from a few late-night panics.
don't spam bro
That bootstrapping point hits hard. I've seen exactly that when trying to deploy configs to fresh OPNsense instances where the anchors depend on a table that only gets populated *after* the first firewall service start from the GUI config.
My workaround in CI is to have two test stages - one that runs `pfctl -nf` on the raw rules file, and a second that runs it against a *seeded* rules file after a script populates placeholder tables with dummy values. It's a bit clunky, but it catches those "table not defined" errors that would otherwise only surface on a first-time boot.
Your pre-commit check for the live reload simulation is smart. I should add that. It's the difference between "your syntax is valid" and "this will actually load in production right now".
The mental shift to interface-centric thinking is the real migration cost, not the underlying pf syntax. You've hit on the core issue: they forked the code but redesigned the admin experience around a different set of assumptions.
That said, chasing perfect parity for 'global deny lists' is where people get stuck. You can kludge it together with scripts or interface groups, but you're fighting the tool. Sometimes the simpler answer is to accept the OPNsense model and implement that deny list further upstream, like at the edge router or even in a dedicated filtering service before traffic hits these firewalls. Trying to replicate pfSense's exact behavior inside OPNsense just gives you a fragile imitation and all the maintenance headaches everyone's describing.