A small WPF/PowerShell GUI that applies the Windows 365 Single Sign-On setting to a hand-picked subset of Cloud PCs — instead of the entire provisioning policy's assigned population.
Community tool — unsupported. Test in a lab tenant before using it against production Cloud PCs.
"Enable single sign-on" lives on the Cloud PC Provisioning Policy, not on the individual Cloud PC. In the Intune portal, the "Apply Region/SSO" action lets you push a Region change to a hand-picked list of Cloud PCs, but for SSO it only offers "apply to all Cloud PCs assigned to this policy" — there's no portal equivalent of "just these three."
The underlying Graph API, however, doesn't enforce that restriction:
POST /deviceManagement/virtualEndpoint/provisioningPolicies/applyConfig
{ "cloudPcIds": [ "...", "..." ], "policySettings": "singleSignOn" }
applyConfig accepts an arbitrary list of Cloud PC IDs — it isn't limited to
"every CPC on the policy." This tool exposes that capability with a safe,
observable, auditable workflow, so you can flip SSO on (or off) for a pilot
group, a single affected user, or a staged rollout, without touching anyone
else on the same policy.
- Connect to Microsoft Graph (delegated auth, one scope — see Permissions below).
- Discover your Enterprise provisioning policies and the Cloud PCs
assigned to each (queried directly by
provisioningPolicyId, not via group membership — see Architecture notes). Enterprise only today — see Known limitations for the Frontline/Flex story. - Pick a policy, then check the individual Cloud PCs you want to change. Each Cloud PC is its own row, so a user with multiple Cloud PCs across different policies can be handled one at a time.
- Queue the selected Cloud PCs with a target SSO value (On/Off).
- Run the queue. For each queued Cloud PC, the tool:
- Temporarily sets the provisioning policy's
enableSingleSignOnto match this job's target (only if it isn't already that value), and confirms the write actually read back before proceeding. - Calls
applyConfigfor that one Cloud PC ID withpolicySettings: "singleSignOn". - Polls the Cloud PC until its status leaves
modifyingSingleSignOn. - Does one final confirmation poll to verify
connectionSetting.enableSingleSignOnactually matches the target. - Once every in-flight job touching that policy has reached a terminal state, restores the policy to whatever value it had before this run first touched it.
- Temporarily sets the provisioning policy's
- Track progress in a live queue grid (5-stage pill per job: Queued →
Applying → Waiting for Modification → Verifying → Done/Failed), a running
log (also written to
Logs\*.lognext to the script), and an Export CSV button for reporting on larger batches.
An optional Schedule feature lets you set a future start time and/or an auto-pause time (e.g., "start at 2 AM, pause after 4 hours") for after-hours batches — see Scheduling below.
Changing the SSO setting isn't a live in-place config change — the VM goes through a shutdown / reconfigure / start-back-up cycle (effectively a cold reboot), whether you're turning SSO On or Off. This matches Microsoft's documented behavior:
"Applying SSO to a large number of Cloud PCs can restart the VMs over a long period of time and won't complete immediately." — Edit a provisioning policy
Lab observations (your mileage may vary — this is not a documented SLA):
- Every Cloud PC tested shuts down while
modifyingSingleSignOn, then starts back up once the change completes — even Cloud PCs only a few days old, not just ones provisioned before April 2023. The April 2023 cutoff in Microsoft's docs describes an unannounced/no-warning reboot risk for older Cloud PCs, not "old Cloud PCs reboot and new ones don't" — assume any Cloud PC will reboot when its SSO setting changes. - Newer Cloud PCs (days old) completed reliably in under ~20 minutes.
- Older Cloud PCs took noticeably longer — potentially a few hours — to
clear
modifyingSingleSignOn, consistent with Microsoft's "won't complete immediately" guidance for larger/older fleets. - Practical implication: warn end users before running this, regardless of Cloud PC age — treat the in-app "Provisioned before Apr 2023" warning icon as a higher-risk flag (longer, less predictable downtime), not as the only condition under which a reboot happens at all.
The core risk in this tool isn't the Graph call itself — it's the fact that the policy and the Cloud PC are momentarily out of sync while this runs. Two things to understand:
applyConfig doesn't accept a per-Cloud-PC value — it just tells the
service "go re-read the policy's current enableSingleSignOn and push it to
these Cloud PC IDs." So to get Cloud PC A to SSO=On while the policy's
"real" steady-state value is Off, the tool must:
briefly set the policy to On → call applyConfig for A → wait for A to
finish → put the policy back to Off.
During that window, any other Cloud PC on the same policy that isn't
part of this run is unaffected — the policy value alone doesn't change
existing Cloud PCs; only applyConfig (or a new provisioning) does. The
exposure is narrow, but not zero. Conditions that could cause an
unintended Cloud PC to pick up the temporary value while the policy is
flipped:
- A new Cloud PC is provisioned under this policy during the window (e.g. a new license assignment triggers provisioning) — it reads the policy's current (temporarily flipped) value at provision time.
- An existing Cloud PC is reprovisioned or reset (via the portal, Graph,
or an automated workflow) during the window — reprovisioning re-reads the
policy just like
applyConfigdoes. - Another admin or automated process independently calls
applyConfig(or the Intune portal's "Apply configuration" action) against this policy during the window — including, notably, applying it to all assigned Cloud PCs rather than a selected few, which would push the temporary value tenant-wide for that policy.
There is currently no automated detection for these cases — the tool does
not diff the full set of Cloud PCs on a policy before/after a run. If you
suspect one of these occurred, check connectionSetting.enableSingleSignOn
on the affected Cloud PCs directly.
Two rounds of overnight experiments (see Logs/history for details) showed
that applyConfig's "204 Accepted" response does not mean the value was
read at that moment — the backend actually reads the policy's current value
sometime during the modifyingSingleSignOn window, which can take
several minutes. Restoring the policy early (e.g., right after the POST)
risks the backend reading the restored (wrong) value mid-flight and
silently applying the opposite of what you intended.
There is no known safe "early restore" timing. The only proven-safe rule is: hold the policy at the target value until the Cloud PC's job reaches a terminal state (Success/Failed), and only then restore it — and only once no other in-flight job still needs that policy at that value. This is exactly what the tool does; it is not configurable, and shouldn't be worked around.
Stop is deliberately "hard" and makes no further Graph calls — a job mid-flight may still be relying on the policy being pinned to its target, and issuing more PATCH calls at that moment would work against the "stop now" intent (and could re-introduce the exact race described above). Instead, Stop shows a dialog listing any policies that may have been left on a temporary value, so you can manually verify/revert them after refreshing the policy list. Pause is safer in this respect — it only blocks new jobs from starting; anything already in flight keeps running to completion (and the policy still gets restored automatically once it does).
| Stage failure | What it means |
|---|---|
| Failed to set policy / failed read-back | The policy PATCH didn't take (or didn't read back correctly) — applyConfig was not called, so nothing was pushed to the Cloud PC. Safe to retry. |
| Timeout in "Waiting for Modification" | The Cloud PC never left modifyingSingleSignOn (or never entered it) within the timeout — the request may not have been processed. Check the Cloud PC directly in the portal before retrying. |
| Confirmed value mismatch in "Verifying" | The status cleared but the final value doesn't match target — rare, but treated as a hard failure rather than silently accepted. |
The Schedule... button arms an optional start time and/or stop (pause) time, useful for running batches after hours. A few behaviors worth knowing:
- Manually clicking Start while a schedule is armed supersedes/clears the pending scheduled start (it's redundant at that point) but leaves a pending scheduled stop alone, since that's still meaningful.
- Stop (manual) cancels the whole schedule outright.
- Pause, whether triggered manually or by a scheduled stop, only blocks new job promotion — in-flight jobs keep running and the policy is still restored automatically once they finish, even while paused.
- While a start is still pending, the tool lightly pings Graph every 10 minutes to keep your token from going stale over a long wait, and does a connection check right before the scheduled start actually fires — aborting gracefully (jobs stay queued) if the connection dropped, rather than failing confusingly mid-run.
The tool requests exactly one delegated Graph scope:
CloudPC.ReadWrite.All
That's sufficient for every Graph call the tool makes: discovering
provisioning policies and their assignments, listing Cloud PCs by policy,
reading/patching a policy's enableSingleSignOn, calling applyConfig, and
polling Cloud PC status. There is no group-membership or directory read —
the "which user is this Cloud PC assigned to" info shown in the UI comes
directly from the Cloud PC object's own userPrincipalName field, not from
a group lookup.
- Single file, no module to install beyond the
Microsoft.GraphPowerShell module (interactive install prompt on first run if missing). - Job state machine: each queued Cloud PC is a
CPCSsoJobStateobject advancing through a 5-stage pipeline (Queued → Applying → Waiting for Modification → Verifying → Success/Failed), driven by a GUIDispatcherTimertick — one small step per job per tick, so the UI never blocks on a long-running Graph poll. - Policy pin/restore bookkeeping (
$script:SsoPolicyOriginalValue) tracks each touched policy's pre-run value and only restores it once no remaining job still needs that policy at its current value — this is what lets multiple jobs on the same policy (even toward different target values, one after another) share it safely without stepping on each other. - Logging: every run auto-logs to
Logs\CloudPC-SSO-Toggle_<timestamp>.lognext to the script (best-effort; falls back to on-screen-only logging if the folder isn't writable).Logs/and exported*.csvfiles are.gitignored.
- Enterprise policies only, by design — the tool intentionally filters
out Frontline/Flex provisioning policies (
cloudPcType == 'frontline'). The underlying mechanism (enableSingleSignOnon the policy,applyConfigwithpolicySettings: "singleSignOn") is documented as the same API regardless of policy type, so the same approach should work for Frontline/Flex too — but this hasn't been tested, and Frontline's shared/multi-user Cloud PC model (a Cloud PC isn't tied to oneuserPrincipalNamethe way Enterprise ones are) means the current one-row-per-user grid wouldn't map cleanly without changes. Treat this as an untested, plausible extension rather than a supported path today. - Windows/WPF only (PowerShell 5.1+,
Microsoft.Graphmodule). - No
Window.Closingguard — closing the window mid-run does not prompt or attempt any cleanup of policies left pinned to a temporary value. - The Stop button's "please verify manually" dialog is the extent of its safety net; there's no automated rollback of a policy left on a temporary value after a hard Stop.
