Загрузка данных


#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"