│ 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?
>> The problem
>> The obvious answer (that wasn't)
>> Attempt 1: Block everything, allow the VPN adapter
- Set
DefaultOutboundActiontoBlockon all firewall profiles - Allow FortiClient executables so the VPN can establish
- Allow traffic on the Fortinet VPN adapter by its
InterfaceAlias
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.>> Attempt 2: The profile trick
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.>> Attempt 3: Allow svchost -- surely THAT fixes it
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.>> The breakthrough: Stop fighting profiles
- BLOCK ALL OUTBOUND on all profiles (Domain, Private, Public)
- ALLOW OUTBOUND TO ALL PRIVATE IP RANGES (RFC1918 + link-local + multicast + loopback)
- ALLOW FORTICLIENT EXECUTABLES (so the VPN tunnel can establish to the public gateway)
- ALLOW SVCHOST.EXE (for DNS, DHCP, NCSI, and NLA)
- ALLOW INTUNE AND DEFENDER (so the device stays managed and protected -- more on this later)
┌──────────────────────┬────────────────────┐
│ 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
┌──────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 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` │
└──────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘┌─────────────────┬──────────────────────────────────────────┐
│ Process │ Path │
├─────────────────┼──────────────────────────────────────────┤
│ Device Enroller │ `C:\Windows\System32\deviceenroller.exe` │
└─────────────────┴──────────────────────────────────────────┘MsMpEng.exe, MpCmdRun.exe, NisSrv.exe) live under C:\ProgramData\Microsoft\Windows Defender\Platform\<version>\. That version folder changes with every Defender update.C:\*\EXECUTABLE.EXE ARE NOT SUPPORTED IN WINDOWS FIREWALL APPLICATION RULES. Only full paths. Still. In 2026. Cool.WinDefend service, and Intune firewall rules have a dedicated "Service name" field. Problem solved -- and version-folder-proof.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
>> The Intune solution (the right way)
>> Policy 1: Windows Firewall Profile
- 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
>> Policy 2: Windows Firewall 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) │
└────────────────────────────┴───────────┴────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘┌───────────────────────────┬─────────────────────────────────────────────────────────────────┐
│ 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` │
└───────────────────────────┴─────────────────────────────────────────────────────────────────┘┌────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 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` │
└────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘>> Deployment order
>> Verification
# 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)
│ 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>> The security caveat: scripts vs. Intune policy
- 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
- 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
>> Final Words
│ 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!
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:
- Set
DefaultOutboundActiontoBlockon all firewall profiles - Allow FortiClient executables so the VPN can establish
- 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:
- Block all outbound on all profiles (Domain, Private, Public)
- Allow outbound to all private IP ranges (RFC1918 + link-local + multicast + loopback)
- Allow FortiClient executables (so the VPN tunnel can establish to the public gateway)
- Allow svchost.exe (for DNS, DHCP, NCSI, and NLA)
- 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 configurationUndo-VPNKillSwitch.ps1— Reverts to the previous firewall stateDetect-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.