Загрузка данных
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Turns a Windows 10/11 PC into a "programming only" machine for a standard user.
.DESCRIPTION
Accounts and BIOS are NOT touched by this script - do them by hand.
Step Cleanup : removes game launchers, games, Xbox/UWP games, leftovers, autostart entries
and scheduled tasks. Asks before every removal (use -Force to skip questions).
Step Web : Edge allow-list (everything blocked except dev docs), no InPrivate/Guest,
no DNS-over-HTTPS, no extensions, other browsers denied for the user,
Microsoft Store and Game Bar disabled.
.EXAMPLE
# 1) rehearsal - changes nothing, only prints what would be done
.\setup-dev-pc.ps1 -UserName brother -DryRun
# 2) real run
.\setup-dev-pc.ps1 -UserName brother
# only one step
.\setup-dev-pc.ps1 -UserName brother -Steps Web
Run in Windows PowerShell 5.1 as Administrator:
powershell -ExecutionPolicy Bypass -File .\setup-dev-pc.ps1 -UserName brother
#>
[CmdletBinding()]
param(
# Name of the EXISTING local account of the restricted user (used only to deny other browsers)
[Parameter(Mandatory = $true)]
[string]$UserName,
[ValidateSet('Cleanup', 'Web')]
[string[]]$Steps = @('Cleanup', 'Web'),
[switch]$DryRun,
[switch]$Force
)
$ErrorActionPreference = 'Continue'
# =====================================================================
# EDITABLE LISTS
# =====================================================================
# Sites the user may open. A domain also covers its subdomains.
# A path (e.g. google.com/search) allows only that path.
$AllowedSites = @(
# docs / references
'developer.mozilla.org', 'learn.microsoft.com', 'devdocs.io', 'w3schools.com',
'web.dev', 'caniuse.com', 'regex101.com', 'json.org',
# languages / runtimes / package registries
'docs.python.org', 'python.org', 'pypi.org', 'nodejs.org', 'npmjs.com',
'typescriptlang.org', 'go.dev', 'rust-lang.org', 'docs.rs', 'crates.io',
'php.net', 'openjdk.org', 'docs.oracle.com',
# frameworks
'react.dev', 'vuejs.org', 'angular.dev', 'svelte.dev', 'nextjs.org',
'vitejs.dev', 'expressjs.com', 'tailwindcss.com', 'getbootstrap.com',
'fastapi.tiangolo.com', 'flask.palletsprojects.com', 'djangoproject.com',
# tooling / version control / Q&A
'github.com', 'git-scm.com', 'code.visualstudio.com', 'stackoverflow.com',
'stackexchange.com', 'postgresql.org', 'sqlite.org', 'docker.com',
# learning
'freecodecamp.org', 'javascript.info', 'learn.javascript.ru', 'metanit.com',
# search engines (search pages only)
'google.com/search', 'bing.com/search', 'duckduckgo.com'
# 'youtube.com' # uncomment if tutorials are OK (big loophole!)
)
# Home page / new-tab page for the user
$HomePage = 'https://developer.mozilla.org'
# Edge extension IDs the user may install (empty = no extensions at all)
$AllowedExtensionIds = @()
# Other browsers: folders where the user gets "deny execute"
$OtherBrowserDirs = @(
"$env:ProgramFiles\Google\Chrome",
"${env:ProgramFiles(x86)}\Google\Chrome",
"$env:ProgramFiles\Mozilla Firefox",
"${env:ProgramFiles(x86)}\Mozilla Firefox",
"$env:ProgramFiles\BraveSoftware",
"$env:ProgramFiles\Opera", "$env:ProgramFiles\Opera GX",
"$env:ProgramFiles\Vivaldi",
"${env:ProgramFiles(x86)}\Yandex",
"$env:ProgramFiles\Tor Browser"
)
# UWP / Store packages to remove (wildcards allowed)
$AppxPatterns = @(
'Microsoft.XboxApp', 'Microsoft.GamingApp', 'Microsoft.XboxGamingOverlay',
'Microsoft.XboxGameOverlay', 'Microsoft.XboxSpeechToTextOverlay',
'Microsoft.Xbox.TCUI', 'Microsoft.XboxIdentityProvider',
'Microsoft.MicrosoftSolitaireCollection', 'king.com.*',
'Microsoft.MinecraftUWP', 'Microsoft.MinecraftEducationEdition', '*Roblox*'
)
# Installed programs (regex on DisplayName) that count as games / launchers
$GameAppPatterns = @(
'\bSteam', 'Epic Games', 'Battle\.net', 'Blizzard', 'EA app', 'Electronic Arts',
'^Origin$', 'Ubisoft', 'Uplay', 'GOG GALAXY', '\bRiot', 'VALORANT', 'League of Legends',
'Minecraft', 'Roblox', 'Rockstar', 'Wargaming', 'Lesta', 'World of (Tanks|Warships)',
'HoYoverse', 'Genshin', 'Honkai', 'Overwolf', 'VK Play', 'Game Center', 'GameCenter',
'Xbox', 'itch', 'Playnite', 'Discord'
)
# Processes to stop before cleanup
$GameProcesses = @(
'steam', 'steamwebhelper', 'EpicGamesLauncher', 'Battle.net', 'Agent', 'RiotClientServices',
'RiotClientUx', 'Origin', 'EADesktop', 'UbisoftConnect', 'upc', 'GalaxyClient', 'Discord',
'Overwolf', 'Roblox*', 'Minecraft*', 'GameCenter', 'VKPlay*'
)
# Folder names looked for in drive roots, Program Files, ProgramData
$GameFolderNames = @(
'Steam', 'SteamLibrary', 'Epic Games', 'Battle.net', 'Riot Games', 'Origin', 'Origin Games',
'EA Games', 'Electronic Arts', 'Ubisoft', 'GOG Games', 'GOG Galaxy', 'XboxGames',
'Games', 'Wargaming.net', 'Lesta', 'Blizzard Entertainment', 'Rockstar Games', 'VK Play'
)
# Folders looked for inside every user profile
$GameProfileSubdirs = @(
'Documents\My Games', 'Saved Games', 'AppData\Roaming\.minecraft',
'AppData\Roaming\Battle.net', 'AppData\Roaming\discord', 'AppData\Roaming\Wargaming.net',
'AppData\Local\Steam', 'AppData\Local\EpicGamesLauncher', 'AppData\Local\Battle.net',
'AppData\Local\Riot Games', 'AppData\Local\Roblox', 'AppData\Local\Discord',
'AppData\Local\Ubisoft Game Launcher', 'AppData\Local\Origin', 'AppData\Local\Electronic Arts',
'AppData\Local\VK Play', 'AppData\Local\Programs\Discord'
)
# =====================================================================
# HELPERS
# =====================================================================
function Do-Action {
param([string]$Text, [scriptblock]$Action)
if ($DryRun) {
Write-Host "[dry-run] $Text" -ForegroundColor Yellow
}
else {
Write-Host " -> $Text"
try { & $Action } catch { Write-Warning "Failed: $Text :: $($_.Exception.Message)" }
}
}
function Confirm-Item {
param([string]$Message)
if ($Force -or $DryRun) { return $true }
$a = Read-Host "$Message [y/N]"
return ($a -match '^(y|yes)$')
}
function Set-Reg {
param([string]$Path, [string]$Name, $Value, [string]$Type = 'DWord')
if (-not (Test-Path $Path)) { New-Item -Path $Path -Force | Out-Null }
New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $Type -Force | Out-Null
}
function Set-RegList {
# writes a Chromium-style policy list: key\1, key\2, ...
param([string]$Path, [string[]]$Items)
if (Test-Path $Path) { Remove-Item -Path $Path -Recurse -Force }
New-Item -Path $Path -Force | Out-Null
$i = 1
foreach ($item in $Items) {
New-ItemProperty -Path $Path -Name "$i" -Value $item -PropertyType String -Force | Out-Null
$i++
}
}
# =====================================================================
# STEP 2: CLEANUP
# =====================================================================
function Uninstall-Program {
param($App)
$cmd = if ($App.QuietUninstallString) { $App.QuietUninstallString } else { $App.UninstallString }
if (-not $cmd) { Write-Warning "No uninstall command for '$($App.DisplayName)' - remove manually"; return }
if ($cmd -match 'msiexec') {
$cmd = $cmd -replace '/I', '/X'
if ($cmd -notmatch '/q') { $cmd += ' /qn /norestart' }
}
elseif (-not $App.QuietUninstallString) {
$cmd += ' /S' # silent flag for NSIS-style uninstallers; ignored by others
}
Start-Process -FilePath 'cmd.exe' -ArgumentList "/c `"$cmd`"" -Wait -WindowStyle Hidden
}
function Step-Cleanup {
Write-Host "`n== Step 2: remove games ==" -ForegroundColor Cyan
# 2.1 stop running game processes
foreach ($p in $GameProcesses) {
foreach ($proc in (Get-Process -Name $p -ErrorAction SilentlyContinue)) {
Do-Action "Stop process $($proc.Name)" { Stop-Process -Id $proc.Id -Force }
}
}
# 2.2 Store / UWP games
Write-Host "`n-- Store (UWP) games"
foreach ($pattern in $AppxPatterns) {
foreach ($pkg in (Get-AppxPackage -AllUsers -Name $pattern -ErrorAction SilentlyContinue)) {
Do-Action "Remove UWP package $($pkg.Name)" {
Remove-AppxPackage -Package $pkg.PackageFullName -AllUsers -ErrorAction Stop
}
}
foreach ($prov in (Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like $pattern })) {
Do-Action "Remove provisioned package $($prov.DisplayName)" {
Remove-AppxProvisionedPackage -Online -PackageName $prov.PackageName | Out-Null
}
}
}
# 2.3 classic programs
Write-Host "`n-- Installed programs"
$keys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$found = foreach ($a in (Get-ItemProperty $keys -ErrorAction SilentlyContinue)) {
if (-not $a.DisplayName) { continue }
foreach ($pat in $GameAppPatterns) {
if ($a.DisplayName -match $pat) { $a; break }
}
}
if (-not $found) { Write-Host " nothing found" }
foreach ($app in $found) {
if ($DryRun) {
Write-Host "[dry-run] Uninstall program: $($app.DisplayName)" -ForegroundColor Yellow
}
elseif (Confirm-Item "Uninstall '$($app.DisplayName)'?") {
Write-Host " -> Uninstalling $($app.DisplayName)"
try { Uninstall-Program -App $app } catch { Write-Warning $_.Exception.Message }
}
}
# 2.4 leftover folders
Write-Host "`n-- Leftover folders"
$candidates = New-Object System.Collections.Generic.List[string]
$drives = (Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3').DeviceID
foreach ($d in $drives) {
foreach ($n in $GameFolderNames) {
foreach ($base in @("$d\", "$d\Program Files\", "$d\Program Files (x86)\")) {
$c = Join-Path $base $n
if (Test-Path -LiteralPath $c) { $candidates.Add($c) }
}
}
# Steam libraries on any drive (depth-limited search)
Get-ChildItem -Path "$d\" -Directory -Filter 'steamapps' -Recurse -Depth 3 -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notlike "$env:windir*" } |
ForEach-Object { $candidates.Add($_.FullName) }
}
foreach ($n in $GameFolderNames) {
$c = Join-Path $env:ProgramData $n
if (Test-Path -LiteralPath $c) { $candidates.Add($c) }
}
foreach ($profile in (Get-ChildItem 'C:\Users' -Directory -ErrorAction SilentlyContinue)) {
foreach ($sub in $GameProfileSubdirs) {
$c = Join-Path $profile.FullName $sub
if (Test-Path -LiteralPath $c) { $candidates.Add($c) }
}
}
foreach ($folder in ($candidates | Sort-Object -Unique)) {
if ($DryRun) {
Write-Host "[dry-run] Delete folder: $folder" -ForegroundColor Yellow
}
elseif (Confirm-Item "Delete folder '$folder'?") {
Write-Host " -> Deleting $folder"
Remove-Item -LiteralPath $folder -Recurse -Force -ErrorAction SilentlyContinue
if (Test-Path -LiteralPath $folder) { Write-Warning "Could not fully delete $folder (in use? reboot and rerun)" }
}
}
# 2.5 autostart entries
Write-Host "`n-- Autostart entries"
$runKeys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
)
$skipProps = 'PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider'
foreach ($k in $runKeys) {
if (-not (Test-Path $k)) { continue }
$props = (Get-ItemProperty $k).PSObject.Properties | Where-Object { $_.Name -notin $skipProps }
foreach ($pr in $props) {
foreach ($pat in $GameAppPatterns) {
if ("$($pr.Name) $($pr.Value)" -match $pat) {
Do-Action "Remove autostart '$($pr.Name)' from $k" { Remove-ItemProperty -Path $k -Name $pr.Name -Force }
break
}
}
}
}
# 2.6 scheduled tasks
Write-Host "`n-- Scheduled tasks"
foreach ($t in (Get-ScheduledTask -ErrorAction SilentlyContinue)) {
foreach ($pat in $GameAppPatterns) {
if ($t.TaskName -match $pat -and $t.TaskPath -notlike '\Microsoft\Windows*') {
Do-Action "Remove scheduled task $($t.TaskPath)$($t.TaskName)" {
Unregister-ScheduledTask -TaskName $t.TaskName -TaskPath $t.TaskPath -Confirm:$false
}
break
}
}
}
# 2.7 Game Bar / Game DVR off
Do-Action 'Disable Game Bar / Game DVR via policy' {
Set-Reg 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\GameDVR' 'AllowGameDVR' 0
}
}
# =====================================================================
# STEP 3: WEB RESTRICTIONS
# =====================================================================
function Step-Web {
Write-Host "`n== Step 3: web restrictions ==" -ForegroundColor Cyan
$edge = 'HKLM:\SOFTWARE\Policies\Microsoft\Edge'
# 3.1 Edge allow-list (applies machine-wide: use another browser for your own admin work)
Do-Action 'Edge: block everything, allow only listed sites' {
Set-RegList "$edge\URLBlocklist" @('*')
Set-RegList "$edge\URLAllowlist" (@('edge://*', 'about:blank') + $AllowedSites)
}
Do-Action 'Edge: disable InPrivate, Guest mode, DNS-over-HTTPS, built-in DNS client, surf game' {
Set-Reg $edge 'InPrivateModeAvailability' 1
Set-Reg $edge 'BrowserGuestModeEnabled' 0
Set-Reg $edge 'DnsOverHttpsMode' 'off' 'String'
Set-Reg $edge 'BuiltInDnsClientEnabled' 0
Set-Reg $edge 'AllowSurfGame' 0
}
Do-Action 'Edge: home page and new-tab page' {
Set-Reg $edge 'HomepageLocation' $HomePage 'String'
Set-Reg $edge 'HomepageIsNewTabPage' 0
Set-Reg $edge 'NewTabPageLocation' $HomePage 'String'
Set-Reg $edge 'RestoreOnStartup' 4
Set-RegList "$edge\RestoreOnStartupURLs" @($HomePage)
}
Do-Action 'Edge: block extensions (VPN/proxy add-ons)' {
Set-RegList "$edge\ExtensionInstallBlocklist" @('*')
if ($AllowedExtensionIds.Count -gt 0) { Set-RegList "$edge\ExtensionInstallAllowlist" $AllowedExtensionIds }
}
# 3.2 other browsers: deny execute for this user
Write-Host "`n-- Other browsers"
$accountExists = [bool](Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue)
if (-not $accountExists) {
Write-Warning "Local user '$UserName' not found - browser deny rules skipped. Create the account, then rerun: -Steps Web"
}
foreach ($dir in $OtherBrowserDirs) {
if ($accountExists -and (Test-Path -LiteralPath $dir)) {
Do-Action "Deny '$UserName' access to $dir" {
& icacls $dir /deny "${UserName}:(OI)(CI)(RX)" | Out-Null
}
}
}
# 3.3 Microsoft Store (policy works on Pro/Enterprise/Education only)
Do-Action 'Disable Microsoft Store (policy; Pro+ only)' {
Set-Reg 'HKLM:\SOFTWARE\Policies\Microsoft\WindowsStore' 'RemoveWindowsStore' 1
}
Write-Host "`n Check in Edge: edge://policy (Reload policies)" -ForegroundColor Green
Write-Host " Edge policies are machine-wide: for your own admin browsing use Chrome/Firefox," -ForegroundColor Green
Write-Host " they are blocked only for '$UserName' via file permissions." -ForegroundColor Green
}
# =====================================================================
# MAIN
# =====================================================================
if ($DryRun) { Write-Host "DRY RUN: nothing will be changed." -ForegroundColor Yellow }
if ($Steps -contains 'Cleanup') { Step-Cleanup }
if ($Steps -contains 'Web') { Step-Web }
Write-Host "`nDone. Manual leftovers:" -ForegroundColor Cyan
Write-Host " - Log in once as '$UserName' and test: docs open, everything else is blocked"
Write-Host " - Install dev tools (VS Code, Git, Node, Python) as admin; standard user cannot install system-wide"
Write-Host " - Reboot the PC"