[GSA 3.1] Build Sheet: The Microsoft Profile and the Compliant Network Policy

From a tenant with nothing to an enforced compliant network Conditional Access policy, with the client packaged, the co-requisite device settings deployed, every exclusion argued for, and an end-to-end test that proves the block instead of assuming it.


By the end of this you have the Microsoft traffic profile enabled and scoped, the client packaged and deployed through Intune with the settings it actually depends on, and a Conditional Access policy that blocks access from anywhere except your own secure edge, proven with a test that fails when it should rather than one that passes because nothing was checked.

Before step 1 you need: Microsoft Entra ID P1 or P2 for every user in scope, which is the only licence this build requires; the Global Secure Access Administrator and Conditional Access Administrator roles, both activated, because two steps need them together and one needs Application Administrator as well; Microsoft Intune with devices already enrolled; and a Windows 10 LTSC 2021 or later, or Windows 11, device estate that is Microsoft Entra joined or hybrid joined. That last one is a hard prerequisite and step 1 explains why.

The worked estate throughout is catsnackjack.com, the same one used in the on-premises PKI series, with a first ring of eight named users.


Step 1. Decide who cannot be in scope, before you build anything

This step produces no configuration. It produces a list, and doing it first is the difference between a rollout and an incident. Every population below either cannot run the client or cannot satisfy the check, and each one has to be excluded deliberately or kept out of the pilot.

PopulationCan it satisfy the checkWhy
Windows, Entra joined or hybrid joinedYesCarries all four tunnels including Microsoft traffic
Windows, Entra registeredNoCarries the private tunnel only. No Microsoft traffic means no compliant network signal, so the policy blocks it
macOS, Entra registered with enrollmentYesCarries all four tunnels
iOS and Android, Entra registeredYesCarries all four tunnels. The client lives inside the Defender app, so Defender resources need excluding
Any device not enrolled in MDMNoThe compliant network location condition is supported only for MDM-enrolled devices
AVD multi-session, RDP session hostsNoThe client does not support concurrent sessions on one machine
Any machine hosting virtual machines, or a Hyper-V host with an external virtual switchNoUnsupported configuration for the client
Devices in provisioning, kiosks, shared first-runNo, by constructionThe client runs after sign-in and needs a primary refresh token, so there is nothing to satisfy the check with
Service accounts and service principalsNot applicablePolicies scoped to users do not block service principal calls

The Windows registered row is the one that catches people, and it catches them because two widely read community guides say registered devices are unsupported outright, which stopped being true in February 2026, and because the correction most people then make is too coarse. Registered support on Windows is Private Access only and it ships as preview. On macOS, iOS and Android, registered is enough. So a mixed BYOD estate can be partly in scope and partly not, and the split runs by platform rather than by policy.


Step 2. Fix the naming before you create the first object

Microsoft’s guidance on naming for this build is, in full, to create a meaningful standard. So here is one, and the reason it is worth adopting whole is that four of these objects are created once and referenced forever, and one of them has to survive being read at three in the morning by somebody who did not build it.

ObjectNameWhy this shape
Pilot and ring groupsSG-GSA-Ring1, SG-GSA-Ring2, SG-GSA-Ring3Rings are the unit of every later change: profile assignment, client version, policy scope. One set of groups drives all three
Policy exclusion groupSG-CA-Excl-CompliantNetworkMatches the framework rule of one dedicated exclusion group per policy, never shared. A shared exclusion group is how an exception to one policy silently becomes an exception to four
Conditional Access policyCA007-VCIO-Global-CompliantNetworkFramework convention, foundation tier, all identities. See the note below
Intune Win32 appGSA-Client-Win-x64-2.31.125The version belongs in the name. You will have three of these at once during a ring rollout and the console sorts them for you
Intune configuration profileGSA-Device-Prereqs-WinHolds the settings the client depends on rather than the client itself
Intune remediationGSA-Harden-Client-WinDetection and remediation pair, both 64-bit

On the policy number: this policy is written to the [CA 4] VCIO CA Framework convention so it drops into an estate that already runs the framework, but it is not part of the framework and should not be imported as though it were. The framework’s core assumes a licence and nothing more, and this policy assumes an activated service, an enabled profile and a deployed client. If you are not running the framework, name it however your estate names policies and keep the dedicated exclusion group regardless.


Step 3. Activate the service and enable the Microsoft traffic profile

Create SG-GSA-Ring1 as a security group and put your eight pilot users in it directly. Direct membership, not nested. Nested group membership is not supported for profile assignment, it fails silently, and the symptom is a user whose client never receives a profile while the group looks correct in every blade you check.

In the Global Secure Access area, activate the service if your tenant prompts you to. Microsoft’s own documentation is inconsistent here: the dashboard tells you to onboard the tenant and links to a page that no longer contains an activation step, while the only pages still naming the Get started and Activate nouns are architecture articles using a retired profile name. In practice a modern tenant either shows you the activation control or has already self-activated. There is no supported unattended path, because the onboarding API publishes no permission model at all, so this is a portal action or nothing.

Then enable the Microsoft traffic profile under traffic forwarding, and assign it to SG-GSA-Ring1. Enabling and assigning both require Global Secure Access Administrator and Application Administrator together. Split those roles across two people and the configuration completes without doing anything.

The assignment default is asymmetric and it will surprise you. A profile that was already enabled is assigned to all users. A profile you enable now is assigned to zero. So enabling first and assigning second is safe, and inheriting somebody else’s half-built tenant is not.

Leave the policy groups inside the profile at their defaults, all forwarding. One warning for later: setting a rule to Bypass does not hand that traffic to the Internet Access profile. Bypassed traffic leaves the service entirely and egresses through the client’s normal network path, so a bypass added for performance is a silent removal from inspection.


Step 4. Package the client as an Intune Win32 app

Download the installer from the client download area of the Global Secure Access portal. Keep the filename GlobalSecureAccessClient.exe. Arm64 devices need a different installer entirely and the x64 one must not be used on them, so if you have both, you are building two apps and targeting them with a requirement rule on processor architecture. Wrap with the Microsoft Win32 Content Prep Tool as normal; the pattern is the same one used in [7.1.1] Deploying Win32 Applications.

SettingValueWhy
Install commandGlobalSecureAccessClient.exe /quietSilent install, no user interaction
Uninstall command"%ProgramFiles%\Global Secure Access Client\GlobalSecureAccessClient.exe" /uninstall /quiet /norestartMicrosoft publishes this unrooted, which does not resolve at uninstall time because the packaged payload is long gone. Root it at the installed path
Install behaviourSystemInstallation requires local administrator rights
Device restart behaviourDetermine behavior based on return codesThe IPv4 change in step 5 legitimately returns a soft reboot code
Return code 0Success
Return code 3010Soft rebootExpected, not a failure
Return code 1618RetryAnother install in progress
Detection rule typeFile
Detection pathC:\Program Files\Global Secure Access Client\TrayAppNote the TrayApp subfolder. One Microsoft page gives the parent folder instead; verify on your first pilot device before you assign widely
Detection fileGlobalSecureAccessClient.exe
Detection methodString (version), greater than or equal to, 2.31.125See the caveat below
AssignmentRequired, to SG-GSA-Ring1Rings are groups, so a ring promotion is a group membership change and nothing else

The detection rule caveat, because it is the one thing in this step that has a credible field objection. Microsoft’s documented rule is the file version comparison above. At least one practitioner reports that Intune-driven upgrades fail with it, leave multiple versions registered simultaneously, and are not fixed by supersedence with uninstall-previous, and that product code detection with an Equals comparison plus a pre-install step that removes every prior version is what works. I have not reproduced that, so I am printing the documented rule and telling you the objection exists. If your second version deployment leaves two entries in installed programs, that is the report you have just confirmed, and the remedy is the pre-install uninstall.

There is no tenant-side update ring for this client and no published support lifecycle for its versions. Ringing is therefore an operating duty, not an optimisation, which is why the version is in the app name and the rings are groups.


Step 5. Deploy the settings the client depends on

These are not optional and they are not hardening. The client will appear to install successfully without them and will then acquire traffic incorrectly or not at all, which is the failure that presents as “it works for some sites and not others”.

SettingValueWhy
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters, DisabledComponents, REG_DWORD0x20Prefer IPv4. The client tunnels IPv4 only and passes IPv6 straight to the network, so without this a dual-stack device silently sends traffic around the service. Requires a restart
DNS over HTTPS, Microsoft EdgeDisabled, by settings catalogThe client supports no secure DNS variant. With DoH on, FQDN rules stop matching while address rules keep working
DNS over HTTPS, ChromeDisabled, by settings catalogSame, and also disable the built-in async DNS resolver
Chrome built-in DNS clientHKCU\Software\Policies\Google\Chrome, BuiltInDnsClientEnabled = 0DNS over TCP port 53 is unsupported and browsers ship their own resolvers
Edge built-in DNS clientHKLM\SOFTWARE\Policies\Microsoft\Edge, BuiltInDnsClientEnabled = 0As above
Firefox, where presentpolicies.json setting network.trr.mode to 0Community-sourced rather than documented in the Global Secure Access set, but the underlying requirement is documented
OS-level secure DNSOffRequired, and Microsoft publishes no settings catalog noun for it, so this is a script or a Windows setting

Two corrections to what you will read elsewhere.

First, there is no registry value called IPv4Preferred. Microsoft’s own client install page names one, and it does not exist. The real value is DisabledComponents under Tcpip6\Parameters, which is a standard Windows IPv6 configuration value and behaves exactly as it does anywhere else.

Second, this build does not need QUIC disabled. Almost every deployment guide tells you to purge QUIC across the fleet. QUIC is unsupported for the Internet Access profile and is supported for Microsoft 365 workloads and for Private Access, so a Microsoft-profile-only build needs the DNS work and not the QUIC work. Microsoft’s own material buries this by placing the QUIC guidance under an Internet Access heading on a page you are reading for a different reason. If you go on to deploy Internet Access, this is the command:

New-NetFirewallRule -DisplayName "Block QUIC" -Direction Outbound -Action Block -Protocol UDP -RemotePort 443

Expected result: browsers fall back to HTTPS over TCP, which the service does acquire. Failure mode to watch for: applications that use QUIC on non-standard ports are unaffected by this rule and will keep bypassing, and video conferencing quality complaints usually arrive within a day if you deploy it fleet-wide without a pilot.


Step 6. Harden the client before the policy enforces, not after

Out of the box, any standard user can pause this client from the system tray. Once the policy in step 10 is enforcing, a user who pauses the client has locked themselves out of Microsoft 365 and will report it as an outage. Deploy the hardening as an Intune remediation script pair, both halves running in 64-bit PowerShell, under the name GSA-Harden-Client-Win.

Value under HKLM\SOFTWARE\Microsoft\Global Secure Access ClientSet toWhy
RestrictNonPrivilegedUsers0x1Disabling the client now requires elevation. Defaults to 0x0, meaning unrestricted
HideDisableButton0x1Removes the control entirely. Defaults to shown
HideSignOutButtonLeave alone on joined devicesAlready hidden on Entra joined devices, and visible by default on registered ones. Setting it blindly is a no-op you will carry forever

Microsoft’s published sample remediation script ships with both of the values that matter set to zero, which is a no-op for the two controls anybody deploys this for. Set them deliberately.

There is a sequencing argument against doing this immediately, and Microsoft makes it: run the pilot unrestricted first so you discover the users who legitimately need to disable the client, usually because of a third-party VPN or one stubborn destination. My compromise is to run ring one unrestricted, harden before ring two, and never let an enforcing policy meet an unrestricted client.

Do not set anything else under that key. Microsoft documents five values there and cautions against changing others, and the client’s own forwarding profile values are managed state rather than configuration.


Step 7. Turn on Conditional Access signalling, once

In the Global Secure Access settings, under session management, find Adaptive Access and enable Enable Conditional Access Signaling for Microsoft Entra ID. You need Global Secure Access Administrator and Conditional Access Administrator both active to do it.

This is also source IP restoration. Microsoft’s compliant network tutorial states that you must enable source IP restoration before you can target the compliant network in Conditional Access, and that reads as a second prerequisite feature. It is the same switch, described on two pages under two names. There is no second toggle and looking for one wastes an afternoon.

Then confirm the result before going further. In Conditional Access named locations there should now be a location called All Compliant Network locations with a location type of Network Access. You can optionally mark it trusted. If it is not there, nothing downstream will work and the policy in the next step will be unbuildable.


Step 8. Build the policy in report-only

Create CA007-VCIO-Global-CompliantNetwork with the shape below, and set it to Report-only. Do not enable it. The whole value of this step is the fortnight of log reading in step 9.

SectionValue
Users, includeSG-GSA-Ring1 for the pilot. All users at full rollout
Users, excludeSG-CA-Excl-CompliantNetwork, containing the accounts from the table below
Target resources, includeAll resources (formerly ‘All cloud apps’)
Target resources, excludeMicrosoft Intune, Microsoft Intune Enrollment, and where Explicit Forward Proxy is enabled, GSA-ExplicitForwardProxy
Network, ConfigureYes
Network, includeAny location
Network, excludeAll Compliant Network locations
GrantBlock access
Enable policyReport-only

Every exclusion has to be argued for, so here are the arguments. An exclusion you cannot justify in one sentence is one you should not have.

ExclusionReason
Break-glass accountsThe same reason they are excluded from every blocking policy in your tenant
Microsoft Intune and Microsoft Intune EnrollmentCircular dependency. An unenrolled device cannot be running the client, so requiring a compliant network to reach enrolment resolves in neither direction
Guests and external collaboratorsThey have no client and never will. Microsoft’s compliant network example omits this and its sibling internet-traffic page includes it, so a build that follows the first page literally ships a policy that blocks B2B users
External administratorsSame reasoning, higher consequence
Service accounts used interactivelyAny account signing in without a client fails the check. Non-interactive service principals are unaffected by user-scoped policies anyway
GSA-ExplicitForwardProxyOnly if you run Explicit Forward Proxy. Entra ID traffic is excluded from proxy configuration by design, so the signal is absent at the moment the proxy authenticates, and the policy fails closed against it. This one is not automatic
Windows Entra registered devicesBy exclusion of their users, or by keeping them out of scope. They carry no Microsoft traffic and cannot pass

You do not need to exclude the service’s own resources. Those are excluded automatically once the compliant network condition is in the policy, and they cover the traffic profiles and the internal policy service. They do not cover Private Access applications, which are ordinary enterprise applications and are therefore in scope of an All resources policy while the control is documented as unsupported against them. If you have Private Access published, scope this policy to selected resources instead until you have tested that combination yourself.


Step 9. Read the logs for a fortnight, and treat the failures as the design document

In the sign-in logs, filter to the policy and look at report-only results. What you are hunting is any sign-in that would have been blocked. Each one is either a population you forgot or a device that is not working, and the two look identical in the log, which is why this step takes a fortnight rather than an afternoon.

The string your service desk will report once you enforce is error code 53003, so put it in the ticket template now.

If you have exported traffic logs to Log Analytics, this is the query that shows whether the profile is actually acquiring anything for a user:

NetworkAccessTraffic
| where TimeGenerated > ago(1d)
| where UserPrincipalName == "[email protected]"
| summarize Transactions = count() by TrafficType, DeviceOperatingSystem
| order by Transactions desc

Expected result: rows with a traffic type of microsoft365. Failure mode: no rows at all, which means the client is installed but acquiring nothing, and step 11 tells you why. One trap worth knowing before it wastes an hour: the two log tables disagree on the literal. NetworkAccessTraffic uses microsoft365 and NetworkAccessConnectionEvents uses m365, and nothing reconciles them.


Step 10. Enforce

Set the policy from Report-only to On, with the scope still at SG-GSA-Ring1. Widen by ring, not by flipping to all users.

Before you widen past ring one, do the thing almost nobody does. Check which point of presence your users actually egress from, and check that country against any country-scoped Conditional Access policy you own. In November 2025 a tenant’s sign-in logs began showing service backbone addresses that geolocated Italian users to France, which tripped their own country policy and blocked legitimate users, and the fix was a support case for a geolocation database correction with no self-service option. Your egress country is not your users’ country, and the tool for finding out ships inside the client:

& "C:\Program Files\Global Secure Access Client\GSATracert\GsaTracert.exe" --host outlook.office365.com --count 4

Expected result: a round trip summary naming the point of presence the session egressed from. Failure mode: the executable is absent on older clients, in which case the egress address in your own sign-in logs is the fallback signal.


Step 11. The end-to-end test, in stages that fail separately

Microsoft’s published test is to disable the client and confirm you are blocked. That is a smoke test: it passes for several reasons that are not the one you want, and when it fails it tells you nothing about which layer broke. Run these five in order on one pilot device and stop at the first that fails.

One. Prove the device has an identity to work with.

dsregcmd /status

Expected: AzureAdJoined : YES and AzureAdPrt : YES. Failure mode: a device that is registered rather than joined shows joined as NO, and on Windows that device can never pass this policy, which is step 1 arriving late.

Two. Prove the client can reach the service.

Test-NetConnection -ComputerName "<your tenant id>.m365.client.globalsecureaccess.microsoft.com" -Port 443

Expected: TcpTestSucceeded : True. Failure mode: a TLS-inspecting egress firewall or an outbound proxy without exclusions. A proxy is supported; an unexcluded proxy is not, and the suffix people forget is .edgediagnostic.globalsecureaccess.microsoft.com, which is what makes the tray icon report health incorrectly rather than failing honestly.

Three. Prove the client received the profile and is acquiring the destination you think it is. In the client’s Advanced diagnostics, open Forwarding profile and use the Policy tester against a Microsoft 365 hostname, then open Hostname acquisition and confirm the name resolves to a generated address rather than falling through to the real one.

Failure mode: the profile is missing entirely. The supported remedy on current clients is the Get policy button in the same tab. If that fails, delete ForwardingProfile and ForwardingProfileTimestamp under HKLM\SOFTWARE\Microsoft\Global Secure Access Client, restart the Global Secure Access Forwarding Profile Service, and restart the client. Microsoft’s health check page still names that service by its old name, Policy Retriever Service, which was replaced in client 2.22.90.

Four. Prove the block happens, in the log rather than in the browser. Sign the pilot user out completely, disable the client, open a fresh browser session and attempt to reach a Microsoft 365 service. The user sees “You can’t access this right now.” Then find that sign-in in the logs and confirm the policy shows as the reason, with failure code 53003.

Failure mode, and it is the reason this test is written with the sign-out step: an existing session is not interrupted. The condition is re-evaluated when a sign-in is next required, so testing without signing out produces a pass that proves nothing. Revoking the user’s sessions first makes it deterministic.

Five. Prove it recovers. Re-enable the client, sign in again, confirm access returns and that the sign-in now records the compliant network as satisfied. A control that blocks and does not restore is an outage with good intentions.


Step 12. Rollback, rehearsed before you need it

Three levels, fastest last.

One user: remove them from SG-GSA-Ring1, or add them to SG-CA-Excl-CompliantNetwork. One policy: set it back to Report-only. Everything: disable the traffic forwarding profiles, which puts every client into the state that reports being disabled by your organisation and stops it tunnelling anything.

Microsoft publishes a break-glass script that does the last two together, flipping every policy that references the compliant network location to report-only and then disabling every enabled forwarding profile. Two things to know before you rely on it. It is blunter than it sounds, because it disables all forwarding profiles rather than the Microsoft one. And its companion recovery script is defective as published: it patches forwarding profiles back to enabled but its own sign-in line does not request the permission scope needed to do it, so the Conditional Access half of the recovery succeeds and the profile half fails, printing a soft message an operator can miss. Add the missing scope before you file it as your runbook:

Connect-MgGraph -Scopes "Policy.Read.All,Policy.ReadWrite.ConditionalAccess,NetworkAccess.ReadWrite.All"

Run either script from an emergency access account that is excluded from the policy, for the obvious reason. Propagation of a profile disable to clients is documented inconsistently at between five minutes and an hour depending on which page you read, so plan on the hour and be pleased when it is faster.

One ordering rule that is not reversible in the obvious direction: if you ever want to turn the Adaptive Access signalling back off, delete the policies first. Disabling the signal while policies still reference the compliant network location locks out the users those policies target.


The same build, as a script

Two parts of this build have no supported automation and it is better to say so than to fake symmetry. Tenant activation publishes no permission model at all, so there is no unattended path. And user and group assignment to a traffic forwarding profile has no documented API, so scoping the pilot is a portal action. What follows automates the rest.

#requires -Modules Microsoft.Graph.Beta.Identity.SignIns
$ErrorActionPreference = 'Stop'

Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess,Policy.Read.All,NetworkAccess.ReadWrite.All"

# Assert the tenant, so this never runs against the wrong one.
$ctx = Get-MgContext
Write-Host "Tenant: $($ctx.TenantId) as $($ctx.Account)"

# 1. Enable the Microsoft traffic profile.
$profiles = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/networkaccess/forwardingProfiles"
$m365 = $profiles.value | Where-Object { $_.trafficForwardingType -eq 'm365' }
Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/beta/networkaccess/forwardingProfiles/$($m365.id)" -Body @{ state = 'enabled' }

# 2. Find the compliant network named location created by the Adaptive Access toggle.
$loc = Get-MgBetaIdentityConditionalAccessNamedLocation | Where-Object { $_.DisplayName -eq 'All Compliant Network locations' }
if (-not $loc) { throw "Compliant network location not present. Enable Conditional Access signalling first." }

# 3. Create the policy, report-only.
$params = @{
    DisplayName = 'CA007-VCIO-Global-CompliantNetwork'
    State       = 'enabledForReportingButNotEnforced'
    Conditions  = @{
        Users        = @{ IncludeGroups = @($ringOneGroupId); ExcludeGroups = @($exclusionGroupId) }
        Applications = @{ IncludeApplications = @('All'); ExcludeApplications = @('0000000a-0000-0000-c000-000000000000') }
        Locations    = @{ IncludeLocations = @('All'); ExcludeLocations = @($loc.Id) }
    }
    GrantControls = @{ Operator = 'OR'; BuiltInControls = @('block') }
}
New-MgBetaIdentityConditionalAccessPolicy -BodyParameter $params

Expected result: a report-only policy visible in the portal with the compliant network location excluded. Failure modes worth anticipating: the named location lookup returns nothing if step 7 was skipped, which is why it throws rather than continuing; the excluded application identifier above is Intune’s, and you need the enrolment application as well, which you should look up in your own tenant rather than copy from an article; and the whole network access surface is on the beta endpoint, which Microsoft states is not supported for production use, so this belongs in a change record rather than in a pipeline.


Completion checklist

The populations that cannot pass are listed and either excluded or out of scope. The naming convention is applied to all six objects. The Microsoft traffic profile is enabled and assigned to a ring group with direct membership. The client is packaged with a rooted uninstall command and a version-bearing name. The IPv4 preference and DNS settings are deployed and a device has been restarted since. The client is hardened before any ring beyond the first meets an enforcing policy. Adaptive Access signalling is on and the named location exists. The policy ran report-only for a fortnight and every would-have-blocked sign-in was resolved or excluded. The five-stage test passes on a pilot device, including the recovery stage. The egress point of presence has been checked against any country-scoped policy. The rollback script carries the corrected permission scope and has been run once, deliberately, while nobody was depending on it.

That last one is the item people skip. A break-glass procedure that has never been executed is a document, not a control.

The argument for why this control is worth the work, and where it stops, is in [GSA 3] The Microsoft Profile and Conditional Access. The report-only discipline this build sheet leans on is set out properly in [CA 2.1] Testing Conditional Access. Private Access is the next profile, and it is a different project with a different set of edges.


Global Secure Access
‹ Previous: [GSA 3] The Microsoft Profile and Conditional Access: The Cheapest Security Win You Own