Picture a firewall rule that turns ICMP echo on for the server's public address whenever a named VPN profile is connected. It reads like "ping only through the tunnel", and that reading is wrong in a way that matters. The rule does not restrict who may reach the host; it changes what the public interface answers while some other connection happens to be up. Those are different policies, and one of them quietly widens exposure.

The rule you think you wrote

The intended requirement is usually narrow: the monitoring system, or the one administrator on the VPN, should be able to confirm the server is alive. Written as a sentence, it sounds like access control on the tunnel. Written as a firewall object, it turns into a condition on the public profile, because that is where the echo request arrives.

Once the condition is a connection state rather than a source address, the set of allowed senders is no longer the VPN peer set. Anyone who can reach the public address can send an echo request while the gate is open. The tunnel did not become the path; the tunnel became the switch.

Marketers of the idea are usually not lying about the effect they wanted. They needed a check that only answers when the link is healthy, and the link being healthy was easiest to observe through the VPN. The mistake is treating an availability signal as an access decision, and then automating it.

What the gate actually controls

Two independent things get merged by this design, and separating them is most of the fix. The first is scope: which local address, which remote addresses, which protocol and which ICMP type. The second is condition: whether the rule object is enabled at this moment. Scope is what a firewall evaluates per packet; condition is what a script writes into the policy store, and the two are reconciled on entirely different clocks.

If the requirement really is VPN peers only, scope answers it without any connection-state logic: bind the rule to the tunnel interface or to the tunnel address range, restrict the remote addresses to the client pool, and specify the echo type rather than "ICMP". That rule stays correct when no VPN is up and cannot be flipped open by an unrelated profile.

If the requirement is genuinely a public gate, then say so in the policy text, because the acceptance criteria change. A public gate has to name the exact profile, define what connected means, and state that public senders are in scope by design. Most of the confusion disappears at the moment someone is forced to write that sentence down.

Build the object once, then toggle it

Pre-provision a single rule with a stable identity, disabled, and let the controller change only its enabled state. Creating rules by display name on each run is how a policy store ends up with five near-identical objects and no reliable way to remove them.

New-NetFirewallRule -Name "VpnGateEchoV4" -DisplayName "Public echo gated on VPN state" -Direction Inbound -Action Allow -Protocol ICMPv4 -IcmpType 8 -LocalAddress 203.0.113.10 -RemoteAddress Any -Enabled False
Set-NetFirewallRule -Name "VpnGateEchoV4" -Enabled True

Name is the durable identifier; display name is documentation that a human may edit. Reconcile against -Name and the script stops producing duplicates. The same command shape covers the IPv6 case with -Protocol ICMPv6 -IcmpType 128, which is a separate rule because the echo type numbers differ between the two families.

Keep address family and ICMP type explicit for a second reason: -Protocol ICMPv4 without a type allows every ICMPv4 message the profile permits, including the ones you never meant to accept. Naming type 8 narrows the rule to echo request, which is the only thing a reachability check sends.

Where the automation contradicts the intent

Polling a client profile every few minutes cannot produce an immediate boundary. Between the connection dropping and the next poll, the rule still answers on the public address; between the connection coming up and the next poll, it does not. Stating that lag is honest, and claiming the design enforces a strict invariant is not.

There is a scope trap in the state check itself. Get-VpnConnection reports client profiles configured on the machine, for the current user by default and for all users with -AllUserConnection. It does not report the remote users currently dialed into an RRAS server; that is a different observation with a different cmdlet. A gate built on the wrong one is connected to nothing.

Then cover the states nobody scripts: startup with the rule already enabled from a previous run, controller crash while the gate is open, and a profile deleted while its rule stays live. Each needs a defined default, and the safe default for a public allowance is disabled. If the requirement truly cannot tolerate the poll window, a state-gated rule is the wrong instrument and the enforcement belongs at the tunnel, not at the public interface.

Finally, test the public request twice, once with the selected connection up and once with it down, from a host that is not on the VPN. A successful ping from inside the tunnel tests nothing about this policy, and that is the measurement most deployments skip.

Skyldar greinar