Windows provides a powerful and flexible management framework that enables organizations to configure, restrict, or simplify the Settings experience for their users. One of the key mechanisms behind this framework is the ms-settings: URI scheme — a consistent, internal navigation system that defines how each Settings page in Windows is accessed.
If you have ever opened Windows Settings and navigated to Privacy → Camera, you were effectively visiting a page identified by a specific internal URI:
ms-settings:privacy-webcam.
These URIs exist for virtually every part of the modern Settings interface — from Windows Update to Bluetooth, Accounts, and Accessibility. Administrators and automation tools can call these URIs directly to open pages, but they can also use them for more advanced purposes, such as controlling which pages are available to users through Group Policy or MDM.
What Are ms-settings URIs?
Each page within Windows Settings has an internal URI (Uniform Resource Identifier) that begins with the prefix ms-settings: followed by a descriptive identifier. For example:
- ms-settings:windowsupdate – Windows Update
- ms-settings:bluetooth – Bluetooth settings
- ms-settings:privacy-microphone – Microphone permissions
- ms-settings:network-wifi – Wi-Fi configuration
Typing any of these URIs into Win + R or a command prompt immediately opens the corresponding Settings page. These same URIs are also what Group Policy and Intune use when defining visibility rules for Settings.
Over time, new pages and features are added to Windows, and therefore new URIs are introduced. Having a reliable way to extract these URIs directly from Windows helps administrators maintain accurate and consistent configuration policies across versions.
Why ms-settings URIs Matter to Enterprises
In enterprise environments, Windows devices are rarely unmanaged. Organizations typically enforce baseline configurations to meet security, usability, and compliance requirements. Control over the Settings experience can be a crucial part of that strategy.
Common scenarios include:
- Kiosk and shared PCs – where users should not modify system or network settings.
- Education or classroom devices – limiting what students can access or change.
- Corporate desktops – ensuring consistent, compliant configurations across all departments.
- Call center terminals or point-of-sale systems – providing only task-relevant options.
- High-security workstations – preventing changes to privacy, update, or device settings.
By controlling access to specific Settings pages, IT administrators can:
- Reduce accidental misconfiguration.
- Lower support costs.
- Improve compliance posture.
- Simplify user experience for task-focused roles.
- Prevent exposure of system areas that are centrally managed or locked down.
Managing Settings Visibility with Group Policy
Windows includes a built-in Group Policy setting that allows administrators to control which pages in the Settings app are visible to users.
Policy location:
Computer/User Configuration → Administrative Templates → Control Panel → Settings Page Visibility
This policy uses a semicolon-separated list of rules that reference Settings pages by their ms-settings identifiers. The part after the colon is what the policy recognizes. For example:
Hide selected pages
hide:privacy-webcam;bluetooth;display
Show only selected pages
showonly:windowsupdate;about
Administrators can combine these directives to tailor the Settings experience precisely to the needs of their organization.
This approach is particularly useful in locked-down environments where users have a limited set of configuration options, or where privacy and security policies mandate restricted access to certain features.
The Challenge: Keeping Visibility Rules Current
The Windows Settings app evolves continuously. With each new feature update or release, new categories, pages, and URIs may appear. For administrators maintaining long-term device configurations, that means GPO lists need to be reviewed and updated regularly.
While Microsoft provides extensive documentation for the most common pages, enterprise administrators often need a complete and current list of all URIs available on the system they are managing. This ensures that policies remain accurate and compatible, even when upgrading from one Windows build to another.
Introducing Get-MSSettingsURIs.ps1
To simplify this process, we created a small PowerShell script called Get-MSSettingsURIs.ps1.
This script scans the system’s SystemSettings.dll file — the core component behind the modern Settings interface — and extracts every ms-settings: URI it contains. Because it reads directly from the operating system, it always reflects exactly what that Windows build supports.
You can use the script to:
- Discover all current Settings URIs on any Windows device.
- Compare differences between Windows versions (e.g., 23H2 → 25H2).
- Build or update your Group Policy “Settings Page Visibility” configuration.
- Validate that newly introduced Settings pages are correctly managed.
How the Script Works
At a high level, Get-MSSettingsURIs.ps1 reads binary data from C:\Windows\ImmersiveControlPanel\SystemSettings.dll and searches for all strings that match the ms-settings: pattern. It supports both ASCII and Unicode encodings to ensure no identifiers are missed.
It then sorts and outputs the unique list of URIs. You can optionally format the results to make them directly usable in a Group Policy setting.
Because the script runs locally and reads system files, it does not require any administrative privileges beyond read access to the Windows directory.
Example Usage
To run the script with default settings:
.\Get-MSSettingsURIs.ps1
This outputs a complete list of all ms-settings: URIs found in the current Windows installation.
If you want the results formatted for direct use in Group Policy (without the ms-settings: prefix):
.\Get-MSSettingsURIs.ps1 -GpoStyle
To scan a copy of SystemSettings.dll from another Windows build (for testing or preparation):
.\Get-MSSettingsURIs.ps1 -Binary "C:\Temp\SystemSettings.dll"
Integrating the Script Into IT Operations
Once you have the list of Settings URIs, you can automate several key tasks:
- Baseline validation – Ensure the same visibility configuration applies across device groups.
- Upgrade preparation – Before deploying a new Windows release, compare URI lists to identify new or renamed pages.
- Compliance auditing – Verify that restricted settings remain hidden as expected.
- Kiosk image building – Generate a minimal set of allowed pages for kiosk or shared devices.
- Automation – Integrate the PowerShell output into configuration pipelines, Intune scripts, or Group Policy templates.
Because the script runs without external dependencies, it can easily be distributed as part of enterprise configuration management or imaging workflows.
Example Workflow for Administrators
- Run the PowerShell script on a reference Windows build.
- Save the resulting URI list to a text file, such as MSSettings_URIs_23H2.txt.
- Use the -GpoStyle option to generate a GPO-ready list.
- Configure the Settings Page Visibility policy in Group Policy or Intune.
- Optionally rerun the script after major OS updates to check for new entries.
By incorporating this simple step into your management process, you ensure that every system in your environment reflects the intended configuration and that new Settings pages introduced by future Windows versions are quickly identified.
Conclusion
The ms-settings: URI system is one of Windows’ most useful yet under-appreciated administrative capabilities. For enterprises that depend on configuration consistency, compliance, or controlled user experiences, understanding and managing these URIs is key.
The Get-MSSettingsURIs.ps1 script gives administrators an easy way to extract, review, and apply these identifiers directly from any Windows installation. Combined with Group Policy or MDM, it provides a fast and reliable method to shape the Settings experience for users — ensuring that each system remains secure, focused, and predictable.
Script Code
param(
[string]$Path = "C:\Windows\ImmersiveControlPanel\SystemSettings.dll",
[string]$OutFile,
[switch]$GpoKeysOnly
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $Path)) {
throw "Datei nicht gefunden: $Path"
}
# --- High-performance Scanner in C# (compiled on-the-fly) ---
$cs = @"
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
public static class MsSettingsScanner
{
static readonly byte[] AsciiPrefix = Encoding.ASCII.GetBytes("ms-settings:");
static readonly byte[] Utf16Prefix = Encoding.Unicode.GetBytes("ms-settings:");
public static List<string> ExtractAll(string filePath)
{
byte[] data = File.ReadAllBytes(filePath); // 10 MB: ok & schnell
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var s in FindAscii(data))
set.Add(s);
foreach (var s in FindUtf16(data))
set.Add(s);
var list = new List<string>(set);
list.Sort(StringComparer.OrdinalIgnoreCase);
return list;
}
// ---- ASCII ----
static IEnumerable<string> FindAscii(byte[] data)
{
foreach (int idx in FindAll(data, AsciiPrefix))
{
int i = idx + AsciiPrefix.Length;
var sb = new StringBuilder("ms-settings:");
while (i < data.Length)
{
char c = (char)data[i];
if (!IsValidUriChar(c)) break;
sb.Append(c);
i++;
}
yield return sb.ToString();
}
}
// ---- UTF-16LE ----
static IEnumerable<string> FindUtf16(byte[] data)
{
foreach (int idx in FindAll(data, Utf16Prefix))
{
int pos = idx + Utf16Prefix.Length;
var sb = new StringBuilder("ms-settings:");
// Nach dem Präfix: 2-Byte-Schritte (LE). Nur ASCII (hi=0) akzeptieren.
while (pos + 1 < data.Length)
{
byte lo = data[pos];
byte hi = data[pos + 1];
if (hi != 0) break; // nur ASCII-Zeichen
char c = (char)lo;
if (!IsValidUriChar(c)) break;
sb.Append(c);
pos += 2;
}
yield return sb.ToString();
}
}
// Boyer–Moore–Horspool über Bytes (schnell, allokationsarm)
static IEnumerable<int> FindAll(byte[] haystack, byte[] needle)
{
int n = haystack.Length, m = needle.Length;
if (m == 0 || n < m) yield break;
int[] skip = new int[256];
for (int i = 0; i < skip.Length; i++) skip[i] = m;
for (int i = 0; i < m - 1; i++) skip[needle[i]] = m - 1 - i;
int pos = 0;
int last = m - 1;
while (pos <= n - m)
{
int j = last;
while (j >= 0 && haystack[pos + j] == needle[j]) j--;
if (j < 0)
{
yield return pos;
pos += m; // nächster möglicher Start
}
else
{
pos += skip[haystack[pos + last]];
}
}
}
// erlaubte Zeichen nach "ms-settings:"
static bool IsValidUriChar(char c)
{
if ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9'))
return true;
switch (c)
{
case '-': case '_': case '.': case ':':
case '/': case ';': case '?': case '&':
case '=': case '#': case '%': case '+':
case ',': case '@':
return true;
default:
return false;
}
}
}
"@
# Make sure to only compile once per session
$loaded = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetTypes() -as [type] } | Where-Object { $_.GetType("MsSettingsScanner") }
if (-not $loaded) {
Add-Type -TypeDefinition $cs -Language CSharp -IgnoreWarnings
}
$uris = [MsSettingsScanner]::ExtractAll($Path)
if ($GpoKeysOnly) {
$uris = $uris | ForEach-Object {
if ($_ -like 'ms-settings:*') { $_.Substring(12) } else { $_ }
} | Where-Object { $_ }
}
if ($OutFile) {
$uris | Set-Content -LiteralPath $OutFile -Encoding UTF8
Write-Host ("Fertig. {0} Einträge → {1}" -f $uris.Count, $OutFile)
} else {
$uris
Write-Host ("`nTotal: {0}" -f $uris.Count)
}

Leave a Reply
You must be logged in to post a comment.