No VPN, No Internet: Building a Windows Firewall Kill Switch for FortiClient with Intune ...
FortiClient VPN kill switch using Windows Firewall and Intune. In this post you will learn how to prevent Windows 11 machines from accessing the internet when not connected to your corporate VPN -- and all the things that go wrong along the way.
DISCLAIMER: This is a proof-of-concept that has not been thoroughly tested across all environments at the time of writing. Your mileage may vary. But sharing is caring, so here we are. Do your own tests before deploying this to production, Mmmkay?
Ever had one of those "perfectly reasonable" requests that spirals into a full-blown deep dive through Windows Firewall internals, adapter classification quirks, and chicken-and-egg problems you didn't know existed? Yeah, me too. Just recently, in fact.
Of course, like every other person trying to keep up with all the new stuff these days, I used AI to help me work through the problem. Specifically, I paired up with CLAUDE (Anthropic's AI) and together we iterated through solutions in real time. So when I say "we" throughout this post, I mean me and my tireless AI colleague who never once complained about the fifth script rewrite. Good times.
Let me share the journey with you good folks reading this blog, because I think it might save someone out there from going mad.

>> The problem

The environment: Windows 11 devices running the standalone FORTICLIENT VPN (no EMS, no FortiGate integration beyond the tunnel itself). The requirement: IF THE VPN IS DOWN, THE INTERNET IS OFF. Local network traffic? Fine. Public internet? Not without the tunnel.
How hard could it be? (_Narrator: it was hard-ish._)

>> The obvious answer (that wasn't)

The first instinct is the right one: use the WINDOWS FIREWALL. Set the default outbound action to Block, add allow rules for the VPN client, and call it a day. It's built into the OS, it's free, it doesn't require third-party software, and it survives reboots. What's not to love?
Well, as it turns out, quite a lot.

>> Attempt 1: Block everything, allow the VPN adapter

The initial approach was straightforward:
  1. Set DefaultOutboundAction to Block on all firewall profiles
  2. Allow FortiClient executables so the VPN can establish
  3. Allow traffic on the Fortinet VPN adapter by its InterfaceAlias
Step 3 is where the first crack appeared. The New-NetFirewallRule cmdlet with -InterfaceAlias does not play nicely with virtual VPN adapters. The adapter exists in a sort of Schrödinger's state -- it's technically present even when the VPN is disconnected, but the firewall doesn't always agree that it's a real interface worth filtering on.
Q: What error did you get?
A: An unhelpful one. As is tradition with Windows Firewall.

>> Attempt 2: The profile trick

With some digging, we discovered something promising. When FortiClient's SSL VPN connects, its virtual adapter (Fortinet SSL VPN Virtual Ethernet Adapter) registers under the DOMAIN firewall profile -- because the tunnel reaches the corporate network where a Domain Controller lives. Meanwhile, the physical NIC sits on PUBLIC or PRIVATE.
This led to an elegant idea: block outbound on Public and Private, leave Domain as Allow. The VPN tunnel traffic flows through the Domain-profile adapter, so everything works once connected.
Except it didn't.
The VPN adapter starts its life as PUBLIC. Windows NLA (Network Location Awareness) needs to reach a Domain Controller to reclassify the adapter as Domain. But we've blocked outbound on Public. So NLA can't reach the DC. So the adapter stays Public. So traffic stays blocked. So NLA can't reach the DC...
This is what we in the industry call a CHICKEN-AND-EGG PROBLEM, and what I in the moment called something my mother wouldn't approve of.

>> Attempt 3: Allow svchost -- surely THAT fixes it

NLA runs as a service inside svchost.exe. If we allow svchost outbound on all profiles, NLA can reach the DC, detect the domain, flip the adapter to Domain profile, and our elegant solution works.
It did not work.
Even with svchost allowed, the NLA detection through a VPN tunnel proved unreliable. The adapter stubbornly remained on Public. Whether this was a timing issue, a caching issue, or Windows simply having a bad day -- the result was the same: no Domain profile, no internet.
This is where I went full CLAUDE OPUS 4.6 EXTENDED THINKING MODE. We threw everything at it -- restarting the NLA service, adding delays, praying to the firewall gods. Nothing. The profile would not flip.

>> The breakthrough: Stop fighting profiles

After several rounds of increasingly creative (and decreasingly dignified) troubleshooting, the solution came from stepping back and asking: _why are we fighting the profile system at all?_
The VPN runs in FULL-TUNNEL MODE. That means when it's connected, _all_ traffic -- including internet-bound traffic -- routes through the tunnel. And what does the tunnel look like from a routing perspective? It's a gateway on a PRIVATE IP ADDRESS.
That's the key insight right there.
Instead of trying to detect which profile the adapter is on, we can simply:
  1. BLOCK ALL OUTBOUND on all profiles (Domain, Private, Public)
  2. ALLOW OUTBOUND TO ALL PRIVATE IP RANGES (RFC1918 + link-local + multicast + loopback)
  3. ALLOW FORTICLIENT EXECUTABLES (so the VPN tunnel can establish to the public gateway)
  4. ALLOW SVCHOST.EXE (for DNS, DHCP, NCSI, and NLA)
  5. ALLOW INTUNE AND DEFENDER (so the device stays managed and protected -- more on this later)
When the VPN is disconnected, the device can only talk to private IPs -- local network, printers, file shares, all fine. But no public internet. When the VPN connects, the full-tunnel routes all traffic through the tunnel's private gateway IP, which is allowed by our RFC1918 rule. Internet works, routed through the corporate network.
No profile detection needed. No adapter alias games. No NLA timing issues. Beautiful.
The private IP ranges we allow:
┌──────────────────────┬────────────────────┐
│ Range                │ Purpose            │
├──────────────────────┼────────────────────┤
│ `10.0.0.0/8`         │ RFC1918 Class A    │
├──────────────────────┼────────────────────┤
│ `172.16.0.0/12`      │ RFC1918 Class B    │
├──────────────────────┼────────────────────┤
│ `192.168.0.0/16`     │ RFC1918 Class C    │
├──────────────────────┼────────────────────┤
│ `169.254.0.0/16`     │ Link-local / APIPA │
├──────────────────────┼────────────────────┤
│ `224.0.0.0/4`        │ Multicast          │
├──────────────────────┼────────────────────┤
│ `255.255.255.255/32` │ Broadcast          │
├──────────────────────┼────────────────────┤
│ `127.0.0.0/8`        │ Loopback           │
└──────────────────────┴────────────────────┘

>> Keeping the device managed: Intune agents and Defender

Now, blocking all outbound is a bold move. You know what also needs outbound access to keep working? The very system that's enforcing your policy. Don't lock yourself out of the house, folks.
We need to make sure the INTUNE MANAGEMENT AGENTS and WINDOWS DEFENDER can phone home regardless of VPN state.
INTUNE MANAGEMENT EXTENSION:
┌──────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Process          │ Path                                                                                                                │
├──────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ IME Agent        │ `C:\Program Files (x86)\Microsoft Intune Management Extension\Microsoft.Management.Services.IntuneWindowsAgent.exe` │
├──────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ AgentExecutor    │ `C:\Program Files (x86)\Microsoft Intune Management Extension\AgentExecutor.exe`                                    │
├──────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ClientHealthEval │ `C:\Program Files (x86)\Microsoft Intune Management Extension\ClientHealthEval.exe`                                 │
├──────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ SensorService    │ `C:\Program Files (x86)\Microsoft Intune Management Extension\SensorService.exe`                                    │
└──────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
OTHER SYSTEM PROCESSES:
┌─────────────────┬──────────────────────────────────────────┐
│ Process         │ Path                                     │
├─────────────────┼──────────────────────────────────────────┤
│ Device Enroller │ `C:\Windows\System32\deviceenroller.exe` │
└─────────────────┴──────────────────────────────────────────┘
WINDOWS DEFENDER -- and here's a fun one. Defender's executables (MsMpEng.exe, MpCmdRun.exe, NisSrv.exe) live under C:\ProgramData\Microsoft\Windows Defender\Platform\<version>\. That version folder changes with every Defender update.
Q: Can't you just use a wildcard path?
A: No. And I know this because I tried it years ago, and it didn't work. Microsoft's own documentation confirms: WILDCARD PATTERNS LIKE C:\*\EXECUTABLE.EXE ARE NOT SUPPORTED IN WINDOWS FIREWALL APPLICATION RULES. Only full paths. Still. In 2026. Cool.
The workaround is to use the SERVICE NAME filter instead of the file path. Windows Defender runs as the WinDefend service, and Intune firewall rules have a dedicated "Service name" field. Problem solved -- and version-folder-proof.
Q: What about the OMA-DM client for Intune sync?
A: It runs inside svchost.exe via the dmwappushservice, so that's already covered by our svchost allow rule. Same goes for Windows Update and BITS.

>> The rule bypass problem

With the default outbound set to Block, the next issue was that WINDOWS SHIPS WITH DOZENS OF PRE-EXISTING OUTBOUND ALLOW RULES. Any one of these could let traffic escape without VPN. Worse, any application installed with admin privileges can create _new_ outbound allow rules at will.
This is where the deployment method matters - and where we need to talk about doing this properly.

>> The Intune solution (the right way)

The production-grade approach uses two Intune ENDPOINT SECURITY > FIREWALL policies:

>> Policy 1: Windows Firewall Profile

This configures the firewall defaults for all three profiles:
  • DEFAULT OUTBOUND ACTION: Block
  • IGNORE ALL LOCAL FIREWALL RULES: Yes
  • IGNORE AUTHORIZED APPLICATION FIREWALL RULES: Yes
  • IGNORE GLOBAL PORT FIREWALL RULES: Yes
  • IGNORE CONNECTION SECURITY RULES: Yes
Those four "Ignore" settings are the critical piece. They tell the firewall to DISREGARD EVERY RULE IN THE LOCAL STORE -- including Windows defaults and anything installed by software. Only rules delivered via Intune's Endpoint Security Firewall Rules policy are active. This completely eliminates the rule bypass problem. No software can add its own exceptions. No user can create a sneaky allow rule. The MDM policy store is king.

>> Policy 2: Windows Firewall Rules

This delivers the specific allow rules. Here's the full set:
INFRASTRUCTURE RULES:
┌────────────────────────────┬───────────┬────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule                       │ Direction │ Action │ Details                                                                                                                                 │
├────────────────────────────┼───────────┼────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow Private IPs          │ Out       │ Allow  │ Remote addresses: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `224.0.0.0/4`, `255.255.255.255/32`, `127.0.0.0/8` │
├────────────────────────────┼───────────┼────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow svchost              │ Out       │ Allow  │ File path: `C:\Windows\System32\svchost.exe`                                                                                            │
├────────────────────────────┼───────────┼────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow Device Enroller      │ Out       │ Allow  │ File path: `C:\Windows\System32\deviceenroller.exe`                                                                                     │
├────────────────────────────┼───────────┼────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow Defender (WinDefend) │ Out       │ Allow  │ Service name: `WinDefend` (no file path -- avoids the version folder problem)                                                           │
└────────────────────────────┴───────────┴────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
FORTICLIENT RULES (one per executable, all Outbound / Allow):
┌───────────────────────────┬─────────────────────────────────────────────────────────────────┐
│ Rule                      │ File Path                                                       │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiClient         │ `C:\Program Files\Fortinet\FortiClient\FortiClient.exe`         │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiClientConsole  │ `C:\Program Files\Fortinet\FortiClient\FortiClientConsole.exe`  │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiClientSecurity │ `C:\Program Files\Fortinet\FortiClient\FortiClientSecurity.exe` │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiSSLVPNdaemon   │ `C:\Program Files\Fortinet\FortiClient\FortiSSLVPNdaemon.exe`   │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiSSLVPNsys      │ `C:\Program Files\Fortinet\FortiClient\FortiSSLVPNsys.exe`      │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiTray           │ `C:\Program Files\Fortinet\FortiClient\FortiTray.exe`           │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiVPN            │ `C:\Program Files\Fortinet\FortiClient\FortiVPN.exe`            │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiGui            │ `C:\Program Files\Fortinet\FortiClient\FortiGui.exe`            │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FortiAuth           │ `C:\Program Files\Fortinet\FortiClient\FortiAuth.exe`           │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FCAuth              │ `C:\Program Files\Fortinet\FortiClient\FCAuth.exe`              │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FCCOMInt            │ `C:\Program Files\Fortinet\FortiClient\FCCOMInt.exe`            │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FCConfig            │ `C:\Program Files\Fortinet\FortiClient\FCConfig.exe`            │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow ipsec               │ `C:\Program Files\Fortinet\FortiClient\ipsec.exe`               │
├───────────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Allow FSSOMA              │ `C:\Program Files\Fortinet\FortiClient\FSSOMA.exe`              │
└───────────────────────────┴─────────────────────────────────────────────────────────────────┘
INTUNE MANAGEMENT EXTENSION RULES (one per executable, all Outbound / Allow):
┌────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Rule                   │ File Path                                                                                                           │
├────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow IME Agent        │ `C:\Program Files (x86)\Microsoft Intune Management Extension\Microsoft.Management.Services.IntuneWindowsAgent.exe` │
├────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow AgentExecutor    │ `C:\Program Files (x86)\Microsoft Intune Management Extension\AgentExecutor.exe`                                    │
├────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow ClientHealthEval │ `C:\Program Files (x86)\Microsoft Intune Management Extension\ClientHealthEval.exe`                                 │
├────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Allow SensorService    │ `C:\Program Files (x86)\Microsoft Intune Management Extension\SensorService.exe`                                    │
└────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
That's 23 RULES total -- well within Intune's 150-rule-per-policy limit. Plenty of room for your future "oh wait, we also need _this_" moments.

>> Deployment order

Do yourself a solid and deploy POLICY 2 (ALLOW RULES) FIRST. Once it reports success on your pilot group, deploy Policy 1 (block + disable local merge). This avoids a window where outbound is blocked with no allow rules in place. Unless you enjoy locking yourself out of remote devices -- in which case, be my guest.

>> Verification

After both policies apply, run these on a target device:
# Confirm outbound is blocked
Get-NetFirewallProfile | Format-Table Name, DefaultOutboundAction

# Confirm only Intune rules are active
Get-NetFirewallRule -PolicyStore ActiveStore -Direction Outbound -Action Allow -Enabled True |
    Format-Table DisplayName

# Test: VPN off, no internet
Test-NetConnection google.com -Port 443

# Test: VPN on, internet works
Test-NetConnection google.com -Port 443

>> The PoC scripts (for testing only)

During the development of this solution, we wrote PowerShell setup and undo scripts to rapidly iterate without waiting for Intune sync cycles. I've published these to GitHub so you can use them for your own testing:
GITHUB: github.com/mardahl/PSBucket/tree/master/FortinetVPNKillSwitch
│ - Setup-VPNKillSwitch.ps1 -- Applies the kill switch configuration
│ - Undo-VPNKillSwitch.ps1 -- Reverts to the previous firewall state
│ - Detect-VPNKillSwitch.ps1 -- Detection script for Intune Win32 packaging
IMPORTANT: These scripts were built as proof-of-concept tooling to iterate quickly during development. They do NOT include all the Intune Management Extension, Device Enroller, and Windows Defender exclusions that we recommend for production use. If you use the scripts as-is, Intune sync and Defender updates may be blocked when the VPN is disconnected. For a complete production deployment, use the Intune Firewall Policy approach described above.

>> The security caveat: scripts vs. Intune policy

Here's the important distinction: THE POWERSHELL SCRIPTS AND THE INTUNE FIREWALL POLICY ARE NOT EQUIVALENT FROM A SECURITY PERSPECTIVE.
The scripts manipulate the LOCAL FIREWALL STORE. This means:
  • Any local administrator can undo them
  • Any software running with admin privileges can add outbound allow rules that bypass the block
  • The scripts disable pre-existing rules, but new ones can appear at any time
  • There is no continuous enforcement -- if someone reverts the changes, they stay reverted
The Intune Firewall Policy leverages the MDM POLICY STORE and the "Ignore local rules" CSP settings. This means:
  • Local firewall rules are completely inactive -- even if they exist, they have no effect
  • New rules created locally are also ignored
  • The policy is continuously enforced and re-applied on every Intune sync
  • Users cannot override it without unenrolling from Intune
USE THE SCRIPTS FOR TESTING. USE INTUNE FOR PRODUCTION.

>> Final Words

What started as "just block outbound and allow the VPN" turned into a tour through Windows Firewall profile classification, NLA detection quirks, virtual adapter behavior, wildcard paths that Microsoft still doesn't support in 2026, and the nuances of local vs. MDM policy stores.
The final solution is relatively simple: block all outbound, allow private IPs, allow the VPN client, allow svchost, allow Intune agents, and allow Defender by service name. Five concepts, twenty-three rules, two Intune policies, and the confidence that your users aren't browsing the internet from a hotel Wi-Fi without the corporate tunnel.
The journey to get there was anything but simple. But that's why we share, right?
Please bear in mind that this knowledge is shared for learning purposes only. Do your own tests before deploying this into production environments.
As always, I hope you find inspiration in this article. And I welcome any feedback in the LinkedIn comments or preferably a follow on LinkedIn: @michael-mardahl.
FINAL DISCLAIMER: This is a proof-of-concept. It was born from real-world need and tested on a limited set of devices, but it has not been validated across every FortiClient version, Windows 11 build, or network configuration. The Intune policies and PowerShell scripts are provided as-is with no warranty. If you find issues or improvements, contribute back to the GitHub repo. Sharing is caring!
_And if you've found an even more creative way for NLA to refuse to detect a domain network, reach out -- misery loves company._

FortiClient VPN kill switch using Windows Firewall and Intune. In this post you will learn how to prevent Windows 11 machines from accessing the internet when not connected to your corporate VPN — and all the things that go wrong along the way.

Disclaimer: This is a proof-of-concept that has not been thoroughly tested across all environments at the time of writing. Your mileage may vary. But sharing is caring, so here we are. Do your own tests before deploying this to production, Mmmkay?


Ever had one of those "perfectly reasonable" requests that spirals into a full-blown deep dive through Windows Firewall internals, adapter classification quirks, and chicken-and-egg problems you didn't know existed? Yeah, me too. Just recently, in fact.

Of course, like every other person trying to keep up with all the new stuff these days, I used AI to help me work through the problem. Specifically, I paired up with Claude (Anthropic's AI) and together we iterated through solutions in real time. So when I say "we" throughout this post, I mean me and my tireless AI colleague who never once complained about the fifth script rewrite. Good times.

Let me share the journey with you good folks reading this blog, because I think it might save someone out there from going mad.

The problem

The environment: Windows 11 devices running the standalone FortiClient VPN (no EMS, no FortiGate integration beyond the tunnel itself). The requirement: if the VPN is down, the internet is off. Local network traffic? Fine. Public internet? Not without the tunnel.

How hard could it be? (Narrator: it was hard-ish.)

The obvious answer (that wasn't)

The first instinct is the right one: use the Windows Firewall. Set the default outbound action to Block, add allow rules for the VPN client, and call it a day. It's built into the OS, it's free, it doesn't require third-party software, and it survives reboots. What's not to love?

Well, as it turns out, quite a lot.

Attempt 1: Block everything, allow the VPN adapter

The initial approach was straightforward:

  1. Set DefaultOutboundAction to Block on all firewall profiles
  2. Allow FortiClient executables so the VPN can establish
  3. Allow traffic on the Fortinet VPN adapter by its InterfaceAlias

Step 3 is where the first crack appeared. The New-NetFirewallRule cmdlet with -InterfaceAlias does not play nicely with virtual VPN adapters. The adapter exists in a sort of Schrödinger's state — it's technically present even when the VPN is disconnected, but the firewall doesn't always agree that it's a real interface worth filtering on.

Q: What error did you get?

A: An unhelpful one. As is tradition with Windows Firewall.

Attempt 2: The profile trick

With some digging, we discovered something promising. When FortiClient's SSL VPN connects, its virtual adapter (Fortinet SSL VPN Virtual Ethernet Adapter) registers under the Domain firewall profile — because the tunnel reaches the corporate network where a Domain Controller lives. Meanwhile, the physical NIC sits on Public or Private.

This led to an elegant idea: block outbound on Public and Private, leave Domain as Allow. The VPN tunnel traffic flows through the Domain-profile adapter, so everything works once connected.

Except it didn't.

The VPN adapter starts its life as Public. Windows NLA (Network Location Awareness) needs to reach a Domain Controller to reclassify the adapter as Domain. But we've blocked outbound on Public. So NLA can't reach the DC. So the adapter stays Public. So traffic stays blocked. So NLA can't reach the DC...

This is what we in the industry call a chicken-and-egg problem, and what I in the moment called something my mother wouldn't approve of.

Attempt 3: Allow svchost — surely THAT fixes it

NLA runs as a service inside svchost.exe. If we allow svchost outbound on all profiles, NLA can reach the DC, detect the domain, flip the adapter to Domain profile, and our elegant solution works.

It did not work.

Even with svchost allowed, the NLA detection through a VPN tunnel proved unreliable. The adapter stubbornly remained on Public. Whether this was a timing issue, a caching issue, or Windows simply having a bad day — the result was the same: no Domain profile, no internet.

This is where I went full Claude Opus 4.6 extended thinking mode. We threw everything at it — restarting the NLA service, adding delays, praying to the firewall gods. Nothing. The profile would not flip.

The breakthrough: Stop fighting profiles

After several rounds of increasingly creative (and decreasingly dignified) troubleshooting, the solution came from stepping back and asking: why are we fighting the profile system at all?

The VPN runs in full-tunnel mode. That means when it's connected, all traffic — including internet-bound traffic — routes through the tunnel. And what does the tunnel look like from a routing perspective? It's a gateway on a private IP address.

That's the key insight right there.

Instead of trying to detect which profile the adapter is on, we can simply:

  1. Block all outbound on all profiles (Domain, Private, Public)
  2. Allow outbound to all private IP ranges (RFC1918 + link-local + multicast + loopback)
  3. Allow FortiClient executables (so the VPN tunnel can establish to the public gateway)
  4. Allow svchost.exe (for DNS, DHCP, NCSI, and NLA)
  5. Allow Intune and Defender (so the device stays managed and protected — more on this later)

When the VPN is disconnected, the device can only talk to private IPs — local network, printers, file shares, all fine. But no public internet. When the VPN connects, the full-tunnel routes all traffic through the tunnel's private gateway IP, which is allowed by our RFC1918 rule. Internet works, routed through the corporate network.

No profile detection needed. No adapter alias games. No NLA timing issues. Beautiful.

The private IP ranges we allow:

RangePurpose
10.0.0.0/8RFC1918 Class A
172.16.0.0/12RFC1918 Class B
192.168.0.0/16RFC1918 Class C
169.254.0.0/16Link-local / APIPA
224.0.0.0/4Multicast
255.255.255.255/32Broadcast
127.0.0.0/8Loopback

Keeping the device managed: Intune agents and Defender

Now, blocking all outbound is a bold move. You know what also needs outbound access to keep working? The very system that's enforcing your policy. Don't lock yourself out of the house, folks.

We need to make sure the Intune management agents and Windows Defender can phone home regardless of VPN state.

Intune Management Extension:

ProcessPath
IME AgentC:\Program Files (x86)\Microsoft Intune Management Extension\Microsoft.Management.Services.IntuneWindowsAgent.exe
AgentExecutorC:\Program Files (x86)\Microsoft Intune Management Extension\AgentExecutor.exe
ClientHealthEvalC:\Program Files (x86)\Microsoft Intune Management Extension\ClientHealthEval.exe
SensorServiceC:\Program Files (x86)\Microsoft Intune Management Extension\SensorService.exe

Other system processes:

ProcessPath
Device EnrollerC:\Windows\System32\deviceenroller.exe

Windows Defender — and here's a fun one. Defender's executables (MsMpEng.exe, MpCmdRun.exe, NisSrv.exe) live under C:\ProgramData\Microsoft\Windows Defender\Platform\<version>\. That version folder changes with every Defender update.

Q: Can't you just use a wildcard path?

A: No. And I know this because I tried it years ago, and it didn't work. Microsoft's own documentation confirms: wildcard patterns like C:\*\executable.exe are not supported in Windows Firewall application rules. Only full paths. Still. In 2026. Cool.

The workaround is to use the service name filter instead of the file path. Windows Defender runs as the WinDefend service, and Intune firewall rules have a dedicated "Service name" field. Problem solved — and version-folder-proof.

Q: What about the OMA-DM client for Intune sync?

A: It runs inside svchost.exe via the dmwappushservice, so that's already covered by our svchost allow rule. Same goes for Windows Update and BITS.

The rule bypass problem

With the default outbound set to Block, the next issue was that Windows ships with dozens of pre-existing outbound allow rules. Any one of these could let traffic escape without VPN. Worse, any application installed with admin privileges can create new outbound allow rules at will.

This is where the deployment method matters - and where we need to talk about doing this properly.

The Intune solution (the right way)

The production-grade approach uses two Intune Endpoint Security > Firewall policies:

Policy 1: Windows Firewall Profile

This configures the firewall defaults for all three profiles:

  • Default Outbound Action: Block
  • Ignore all local firewall rules: Yes
  • Ignore authorized application firewall rules: Yes
  • Ignore global port firewall rules: Yes
  • Ignore connection security rules: Yes

Those four "Ignore" settings are the critical piece. They tell the firewall to disregard every rule in the local store — including Windows defaults and anything installed by software. Only rules delivered via Intune's Endpoint Security Firewall Rules policy are active. This completely eliminates the rule bypass problem. No software can add its own exceptions. No user can create a sneaky allow rule. The MDM policy store is king.

Policy 2: Windows Firewall Rules

This delivers the specific allow rules. Here's the full set:

Infrastructure rules:

RuleDirectionActionDetails
Allow Private IPsOutAllowRemote addresses: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 224.0.0.0/4, 255.255.255.255/32, 127.0.0.0/8
Allow svchostOutAllowFile path: C:\Windows\System32\svchost.exe
Allow Device EnrollerOutAllowFile path: C:\Windows\System32\deviceenroller.exe
Allow Defender (WinDefend)OutAllowService name: WinDefend (no file path — avoids the version folder problem)

FortiClient rules (one per executable, all Outbound / Allow):

RuleFile Path
Allow FortiClientC:\Program Files\Fortinet\FortiClient\FortiClient.exe
Allow FortiClientConsoleC:\Program Files\Fortinet\FortiClient\FortiClientConsole.exe
Allow FortiClientSecurityC:\Program Files\Fortinet\FortiClient\FortiClientSecurity.exe
Allow FortiSSLVPNdaemonC:\Program Files\Fortinet\FortiClient\FortiSSLVPNdaemon.exe
Allow FortiSSLVPNsysC:\Program Files\Fortinet\FortiClient\FortiSSLVPNsys.exe
Allow FortiTrayC:\Program Files\Fortinet\FortiClient\FortiTray.exe
Allow FortiVPNC:\Program Files\Fortinet\FortiClient\FortiVPN.exe
Allow FortiGuiC:\Program Files\Fortinet\FortiClient\FortiGui.exe
Allow FortiAuthC:\Program Files\Fortinet\FortiClient\FortiAuth.exe
Allow FCAuthC:\Program Files\Fortinet\FortiClient\FCAuth.exe
Allow FCCOMIntC:\Program Files\Fortinet\FortiClient\FCCOMInt.exe
Allow FCConfigC:\Program Files\Fortinet\FortiClient\FCConfig.exe
Allow ipsecC:\Program Files\Fortinet\FortiClient\ipsec.exe
Allow FSSOMAC:\Program Files\Fortinet\FortiClient\FSSOMA.exe

Intune Management Extension rules (one per executable, all Outbound / Allow):

RuleFile Path
Allow IME AgentC:\Program Files (x86)\Microsoft Intune Management Extension\Microsoft.Management.Services.IntuneWindowsAgent.exe
Allow AgentExecutorC:\Program Files (x86)\Microsoft Intune Management Extension\AgentExecutor.exe
Allow ClientHealthEvalC:\Program Files (x86)\Microsoft Intune Management Extension\ClientHealthEval.exe
Allow SensorServiceC:\Program Files (x86)\Microsoft Intune Management Extension\SensorService.exe

That's 23 rules total — well within Intune's 150-rule-per-policy limit. Plenty of room for your future "oh wait, we also need this" moments.

Deployment order

Do yourself a solid and deploy Policy 2 (allow rules) first. Once it reports success on your pilot group, deploy Policy 1 (block + disable local merge). This avoids a window where outbound is blocked with no allow rules in place. Unless you enjoy locking yourself out of remote devices — in which case, be my guest.

Verification

After both policies apply, run these on a target device:

# Confirm outbound is blocked
Get-NetFirewallProfile | Format-Table Name, DefaultOutboundAction

# Confirm only Intune rules are active
Get-NetFirewallRule -PolicyStore ActiveStore -Direction Outbound -Action Allow -Enabled True |
    Format-Table DisplayName

# Test: VPN off, no internet
Test-NetConnection google.com -Port 443

# Test: VPN on, internet works
Test-NetConnection google.com -Port 443

The PoC scripts (for testing only)

During the development of this solution, we wrote PowerShell setup and undo scripts to rapidly iterate without waiting for Intune sync cycles. I've published these to GitHub so you can use them for your own testing:

GitHub: github.com/mardahl/PSBucket/tree/master/FortinetVPNKillSwitch

  • Setup-VPNKillSwitch.ps1 — Applies the kill switch configuration
  • Undo-VPNKillSwitch.ps1 — Reverts to the previous firewall state
  • Detect-VPNKillSwitch.ps1 — Detection script for Intune Win32 packaging

Important: These scripts were built as proof-of-concept tooling to iterate quickly during development. They do not include all the Intune Management Extension, Device Enroller, and Windows Defender exclusions that we recommend for production use. If you use the scripts as-is, Intune sync and Defender updates may be blocked when the VPN is disconnected. For a complete production deployment, use the Intune Firewall Policy approach described above.

The security caveat: scripts vs. Intune policy

Here's the important distinction: the PowerShell scripts and the Intune Firewall Policy are not equivalent from a security perspective.

The scripts manipulate the local firewall store. This means:

  • Any local administrator can undo them
  • Any software running with admin privileges can add outbound allow rules that bypass the block
  • The scripts disable pre-existing rules, but new ones can appear at any time
  • There is no continuous enforcement — if someone reverts the changes, they stay reverted

The Intune Firewall Policy leverages the MDM policy store and the "Ignore local rules" CSP settings. This means:

  • Local firewall rules are completely inactive — even if they exist, they have no effect
  • New rules created locally are also ignored
  • The policy is continuously enforced and re-applied on every Intune sync
  • Users cannot override it without unenrolling from Intune

Use the scripts for testing. Use Intune for production.

Final Words

What started as "just block outbound and allow the VPN" turned into a tour through Windows Firewall profile classification, NLA detection quirks, virtual adapter behavior, wildcard paths that Microsoft still doesn't support in 2026, and the nuances of local vs. MDM policy stores.

The final solution is relatively simple: block all outbound, allow private IPs, allow the VPN client, allow svchost, allow Intune agents, and allow Defender by service name. Five concepts, twenty-three rules, two Intune policies, and the confidence that your users aren't browsing the internet from a hotel Wi-Fi without the corporate tunnel.

The journey to get there was anything but simple. But that's why we share, right?

Please bear in mind that this knowledge is shared for learning purposes only. Do your own tests before deploying this into production environments.

As always, I hope you find inspiration in this article. And I welcome any feedback in the LinkedIn comments or preferably a follow on LinkedIn: @michael-mardahl.


Final disclaimer: This is a proof-of-concept. It was born from real-world need and tested on a limited set of devices, but it has not been validated across every FortiClient version, Windows 11 build, or network configuration. The Intune policies and PowerShell scripts are provided as-is with no warranty. If you find issues or improvements, contribute back to the GitHub repo. Sharing is caring!

And if you've found an even more creative way for NLA to refuse to detect a domain network, reach out — misery loves company.

C:\IPHASE\Posts\Intune\No VPN, No Internet Building a Windows Firewall Kill Switch for FortiClient with Intune.TXT