微软官方的 Windows 装机脚本:一条命令重装你的开发机

微软开源了 WindowsDeveloperConfig——官方维护的 Windows 开发环境自动化配置仓库。一行 PowerShell 命令装上开发工具、改好系统设置、装好 WSL + Ubuntu,中途自动重启一次并自己恢复,而且幂等可重复运行。本文讲清它的三条路线分别是什么、选哪个、会改你的哪些注册表、能不能撤销,以及贡献者必须知道的「双副本」布局。

本文整理自 GitHub 项目 microsoft/WindowsDeveloperConfig(MIT 许可)的 README、src/windows-dev-config/README.md、wsl-comfort/readme.md、src/docs/development.md 与部分 configuration.winget 源码。命令、注册表路径、配置清单忠于原文;章节组织、选型建议与评述为本文编辑归纳。数据截至 2026-10-02(仓库 2.9k star / 200 fork,创建于 2026-05-21)。

新到手的 Windows 11 开发机,从开机到能跑起来 hello world,中间隔着一段没人愿意承认的时间:装 Git、装 Node、装 Python、装 VS Code,改主题、改资源管理器不显示文件扩展名、改长路径、开 WSL,然后再发现 Oh My Posh 的字体没配好,终端里全是方块。

微软把这个流程做成了官方仓库:microsoft/WindowsDeveloperConfig,标语是三句话 —— Opinionated setups for Windows dev boxes. Idempotent. CI-tested.(有主张的 Windows 开发机配置;幂等;CI 验证过)。

它解决的是一个很具体的痛点:你的开发环境应该帮你交付,而不是变成另一个需要调试的项目。


一、先选路线:三条岔路,别走错

这个仓库不是单一脚本,而是三套互相独立的配置方案。走错路线会浪费不少时间,所以先看这张图:

路线入口命令需要克隆仓库吗重启吗
Windows Dev Configirm https://aka.ms/devconfig/standard/setup.ps1 | iex不需要会,一次
WSL Comfort.\wsl-comfort\install.ps1需要首次装 WSL 时会
单语言 Workloads.\Workloads\python\install.ps1需要不会

一个容易混淆的点:Windows Dev Config 和 WinUI 3 这两条路线不需要 winget configure(用自己的引擎),其余 Workloads 依赖 winget configure,需要先启用。后面第五节会讲启用时的坑。


二、路线一:Windows Dev Config —— 一行命令,一次重启

打开任意一个 PowerShell 窗口,提权或不提权都行:

# 标准版
irm https://aka.ms/devconfig/standard/setup.ps1 | iex

# 完整版
irm https://aka.ms/devconfig/full/setup.ps1 | iex

⚠️ 它会重启你的机器,一次。 启用 WSL 需要装一个 Windows 可选功能,而那个必须重启。脚本会给 10 秒警告,然后注册一个计划任务,你重新登录并接受 UAC 之后自动接着装。跑之前先保存工作。

2.1 Standard 装什么

开发工具:Windows Terminal、PowerShell 7、Git、GitHub CLI、GitHub Copilot CLI、VS Code、.NET SDK 10、Python 3.14 + uv、Node.js LTS + nvm、Coreutils for Windows、Windows App CLI、Oh My Posh、PowerToys。

终端:PowerShell 7 设为默认 profile、Oh My Posh 进提示符、Cascadia Mono NF 作默认字体、下拉菜单里多一个 GitHub Copilot profile。

Windows 设置:深色主题、Win32 长路径、文件资源管理器默认值、开始/搜索设置、勿扰模式。

WSL:WSL 平台 + Ubuntu,含重启与之后的自动续跑。

2.2 Full 多加了什么

在 Standard 全部内容之上,追加:

  • Windows 设置:开发者模式、Sudo(inline 模式)、关闭 Widgets、Edge 策略、更多开始/搜索/系统托盘设置。
  • 远程桌面:启用 + 配好防火墙规则。

2.3 它到底改了哪些注册表

这是整套东西里最需要提前知道的部分。官方 windows-dev-config/README.md 给了完整清单,节选最有辨识度的几项:

分类配置项注册表路径值Standard
系统SudoHKLM\…\Windows\CurrentVersion\Sudo\Enabled3跳过
系统开发者模式HKLM\…\AppModelUnlock\AllowDevelopmentWithoutDevLicense1跳过
系统Win32 长路径HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled1✅
系统远程桌面HKLM\…\Control\Terminal Server\fDenyTSConnections0跳过
资源管理器显示文件扩展名HKCU\…\Explorer\Advanced\HideFileExt0✅
资源管理器显示隐藏文件HKCU\…\Explorer\Advanced\Hidden1✅
资源管理器标题栏完整路径HKCU\…\Explorer\CabinetState\FullPath1✅
资源管理器打开"此电脑"HKCU\…\Explorer\Advanced\LaunchTo1✅
任务栏右键"结束任务"HKCU\…\Explorer\Advanced\TaskbarDeveloperSettings\TaskbarEndTask1✅
开始菜单无推荐项HKCU\…\Explorer\Advanced\Start_IrisRecommendations0✅
通知全局勿扰HKCU\…\Notifications\Settings\NOC_GLOBAL_SETTING_TOASTS_ENABLED0跳过
Edge新标签页空白HKLM\SOFTWARE\Policies\Microsoft\Edge\NewTabPageLocationabout:blank跳过
主题应用/系统深色HKCU\…\Themes\Personalize\AppsUseLightTheme / SystemUsesLightTheme0✅

WinForms / WinUI 3 的提醒:这两条会拉几个 GB 的 Visual Studio 组件。真机上没问题,小虚拟机上会很痛苦。

2.4 重启之后到底发生了什么

这是我觉得整个设计里最值得说的一段。

dev-config.ps1 把安装拆成 11 个阶段,Full 全跑,Standard 跳过 Edge 阶段:

#阶段说明
1Getting ready确认 PowerShell 7,必要时把 winget 更新到锁定稳定版
2Packages装全部包 + PowerToys 通知设置
3System settingsSudo、开发者模式、长路径、远程桌面
4File Explorer tweaks资源管理器注册表项
5Taskbar, search & start任务栏/搜索/开始菜单/通知
6Microsoft Edge tweaksEdge 策略(Standard 跳过)
7FontsCascadia Code NF / Mono NF,SHA-256 校验
8Windows Terminal默认 profile、字体、RunOnce 字体更新
9PowerShell profileOh My Posh 配置
10GitHub Copilot终端 profile、WinUI 模板、Copilot CLI 插件(best-effort)
11WSL + Ubuntu故意放最后

第 11 阶段放在最后是有道理的:唯一需要的那次重启被推迟到其它所有事都干完之后。

要点:只重启一次。若 WSL 仍不可用,会停下说明原因,不会陷入循环重启。

2.5 幂等是怎么做出来的

官方文档原话:

It is idempotent — every change is checked before it’s made, so re-running it only fixes what has drifted. It is also resumable — if it fails, or you close the window, running it again picks up where it left off.

机制是每个 step 走 Check → Apply → Verify 三步:

  1. Check:先看是不是已经在目标状态。是 → 打印 already OK,什么都不做。
  2. Apply:不在,才改。
  3. Verify:再跑一遍同一个 check。仍失败就是 error,不是"静默成功"。

标记为 best-effort 的步骤失败时记为 flagged,流程继续,最后在总结里点名。进度记在 devconfig-tally.json 里,所以重启之后(或直接重跑)已成功的步骤几秒内就跳过,只重试 flagged 的。

包只有在 winget 报告 installed 且 current 时才算完成 —— 所以重跑顺带会把可用更新拉下来。


三、路线二:WSL Comfort —— 把 Shell 弄舒服

也叫 Comfort Shell。它是两半结构:

脚本跑在哪职责
install.ps1Windows(PS 5.1 或 7)编排:确保 WSL、挑/装发行版、在发行版里调 bootstrap、装字体、投放 WT profile
comfort-shell-bootstrap.shUbuntu(WSL 或裸机)配置 shell、提示符、工具、shims、Homebrew、git 默认值、dotfiles

关键一句:bootstrap 是独立的。你可以把它 scp 到任何 Ubuntu 主机上单独跑,install.ps1 只是薄薄的编排层。灵感来自 Scott Hanselman 的 MacLikeWSLComfortShell。

3.1 五种用法

.\wsl-comfort\install.ps1                                    # 交互式,逐步确认
.\wsl-comfort\install.ps1 -NonInteractive                    # 无人值守,默认 Ubuntu
.\wsl-comfort\install.ps1 -Distro Ubuntu-24.04               # 指定已有发行版
.\wsl-comfort\install.ps1 -BootstrapArgs '--shell=bash','--no-brew'
./comfort-shell-bootstrap.sh                    # 交互
./comfort-shell-bootstrap.sh --non-interactive  # 默认,不提问
./comfort-shell-bootstrap.sh --dry-run          # 只看不改

3.2 bootstrap 的开关

Flag作用
--shell=zsh|bash默认 shell,默认 zsh
--no-brew跳过 Homebrew
--no-shims跳过 pbcopy/pbpaste/open/xdg-open
--no-prompt跳过 starship
--no-tools跳过 apt CLI 工具
--minimal等于 --no-brew --no-shims --no-tools
--force覆盖已有配置,而不是保留
--dry-run预演

3.3 三个我觉得很见功力的细节

① 托管块(managed blocks)写 dotfiles。 它在 ~/.zshrc 里写的是 # >>> comfort-shell >>> / # <<< comfort-shell <<< 包起来的一段,重跑时整块替换,不踩用户自己的编辑。

② 每发行版一个确定性 GUID。 Windows Terminal fragment 的 GUID 取 MD5("comfort-shell:<distro>")。效果:重跑原地更新;Ubuntu 和 Ubuntu-24.04 能作为两个 profile 共存。Fragment 落在

%LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments\ComfortShell\comfort-shell-<slug>.fragment.json

然后碰一下 settings.json 的 mtime,WT 就会热重载扫描,不用重启终端。

③ 调完 wsl.exe 必须 Reset-TerminalInputMode。 wsl.exe 会给父控制台打开 Win32 Input Mode 和焦点报告,而且不总是还原。不复位,后面 Read-Host 会吐出 ^[[I 这类转义序列。这种坑不自己踩过写不进文档。

还有个 skel mode:以 root 跑时(预焙镜像),HOME 重定向到 /etc/skel,dotfiles 落进模板,Homebrew 延后到 ~/.comfort-shell-first-run 标记触发的新用户首次交互 shell。这正是"Windows 侧能在还没开过封的发行版上跑 bootstrap"的原因。


四、路线三:单语言 Workloads —— 只要一门工具链

12 个 workload,每个是一个 configuration.winget + 一个同名 install.ps1 shim:

Workload装什么
TypeScriptNode.js LTS + 全局 typescript
PHPPHP 8.5
.NET.NET SDK 10
GoGo(rolling)
JavaMicrosoft Build of OpenJDK 25 LTS
RustRust stable via rustup
PythonPython 3.14 + uv
SQLSQL Server + sqlcmd + VS Code 扩展
PowerShellPowerShell 7 + VS Code PowerShell 扩展 + PSScriptAnalyzer 设置
WinForms.NET SDK 10 + Windows Forms desktop workload
WinAppCLI开发者模式 + .NET SDK 10 + Windows App Development CLI
WinUI 3.NET SDK 10 + VS Community + Windows App SDK / WinUI 3 + WinAppCLI

跑的方式:

winget configure -f .\Workloads\python\configuration.winget --accept-configuration-agreements --disable-interactivity

# 想要当前 shell 的 PATH 立刻刷新,改用 shim:
.\Workloads\python\install.ps1

WinUI 3 例外,它跟 Dev Config 同一套引擎,一行就行:

irm https://aka.ms/devconfig/winui/setup.ps1 | iex

配置长什么样?看 python/configuration.winget 全文就明白了 —— 一共 20 行:

$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
  winget:
    processor: dscv3
resources:
  - name: Python
    type: Microsoft.WinGet/Package
    properties:
      id: Python.Python.3.14
      source: winget
      acceptAgreements: true
    metadata:
      winget:
        securityContext: elevated

  - name: Uv
    type: Microsoft.WinGet/Package
    properties:
      id: astral-sh.uv
      source: winget
      acceptAgreements: true
    metadata:
      winget:
        securityContext: elevated

就是一份 DSC v3 文档。理解了这一点,你自己加一门语言就是往上再加一个 resource。

改之前先验证,winget configure 有个 test 动词,跑 TestScript 判断系统当前是否已在目标状态,不装任何东西:

winget configure test --file .\Workloads\python\configuration.winget --accept-configuration-agreements --disable-interactivity

五、winget configure 的三个前置条件(踩过才懂)

除 Dev Config 外,其余 flow 全靠 winget configure。它有三个前置条件,缺任一则 install.ps1 会先报错退出:

  1. App Installer(winget)必须够新 —— 从 Microsoft Store 更新,或从 winget-cli releases 拉最新 MSIX。
  2. Configuration 特性必须打开 —— 新版 winget 上已 GA 且默认开;旧版可能要 winget settings 里设 experimentalFeatures.configuration = true。
  3. 组策略 / MDM 必须允许 —— 如果注册表 HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppInstaller\EnableWindowsPackageManagerConfiguration 是 0,整台机器被禁用,得先改策略。

冒烟测试:

winget configure --help | Select-Object -First 3

那个著名的内部错误

非提权环境下调 winget configure,必须先装 Visual C++ Redistributable,否则报 internal error / -2146233079:

winget install Microsoft.VCRedist.2015+.x64     # x64
winget install Microsoft.VCRedist.2015+.arm64   # ARM64

每个 install.ps1 shim 会先跑 Workloads/_common/assert-winget-configure.ps1,它会明确告诉你是上面三条里的哪一条没满足。仓库的 enable-winget-configure.ps1 也会自动把 VC Redist 装上。


六、能不能撤销?答案有点狠

能,但要先知道代价。

# 提权 PowerShell
& "$env:ProgramData\CalmOS\dev-config.ps1" -Action Uninstall

# 从源码目录(未签名开发模式)
.\src\windows-dev-config\dev-config.ps1 -AllowUnsigned -Action Uninstall

⚠️ Cleanup 是破坏性的、无确认提示。 它会永久删除 Ubuntu 发行版及其文件(等价 wsl --unregister Ubuntu),并卸载全部工具。即便你之前只跑过 Standard,清理也会按 Full 的配置来重置。

-Action Uninstall 会:禁用 Sudo / 开发者模式 / 远程桌面,重置资源管理器、开始菜单、搜索、通知、Widgets、Edge 策略、长路径;主题切回 light;移除 Terminal 里的 defaultProfile、各 profile 项与 Copilot fragment;移除托管的 Oh My Posh profile block、WinUI 模板;卸载 17 个工具包(uv、NVM、Node.js、Python 3.14、Git、GitHub CLI、Oh My Posh、.NET SDK 10、PowerToys、VS Code、Windows App CLI、PowerShell 7 等)。

不会恢复的:字体、Windows Terminal 本体、VC++ runtime、WinGet 及 Windows 可选功能、setup 文件与日志。

想手动清理:

winget uninstall --id <id>                  # 按 winget id 卸包
wsl --unregister Ubuntu                     # 不可逆
Remove-Item "$env:LOCALAPPDATA\Microsoft\Windows Terminal\Fragments\DevConfig" -Recurse -Force
Remove-Item "$env:ProgramData\CalmOS" -Recurse -Force        # 需管理员

结论:别在你唯一的生产机上试。 用一次性 Windows Sandbox / Hyper-V / Dev Box 镜像。


七、安全视角:irm | iex 到底在执行什么

看到 irm <url> | iex 就皱眉是对的 —— 这是在内存里执行远端下载的脚本。这条链路的实际防护是:

  1. bootstrap.ps1 验证 Microsoft Corporation 签名,逐个校验仓库 windows-dev-config/ 下每一个 .ps1。
  2. 落到 %ProgramData%\CalmOS,只有 Administrators / SYSTEM 可写,普通用户只读可执行(防篡改:下载者 = 执行者 ≠ 写入者)。
  3. 用进程级 RemoteSigned,不往 trusted publishers 里加东西。组织强制的 AllSigned 仍可能弹窗。

这比"官网下载 exe 双击"其实更严格一些,但本质承诺是:你信任 Microsoft 的这个签名。第三方 fork 的 aka.ms 链接不是一回事。


八、贡献者必读:仓库有两棵树

这条如果不提前知道,PR 一定会被打回。

规则:

  • 改 src/,别改顶层那三个目录。 CI 跑的是未签名的 src/ 副本 —— 这是刻意的:CI 测贡献者改的东西,签名是发布时才关心的事。
  • 两份副本不会字节相同。 签名副本带 # SIG # Begin signature block 块;块以上的正文应与 src/ 一致,但在"src 改动进 main"到"下一次签名周期"之间会短暂不一致。
  • 删除文件是唯一必须动两棵树的情况。 签名流水线只增改、不删除 —— 从 src/ 删掉的文件会永远留在顶层。所以删或重命名时,同一个 PR 里必须 git rm 两处。
  • 有个 Signed copy guard PR 检查(.github/workflows/signed-copy-guard.yml),只在 PR 触及那三个顶层目录时运行,用 src/tools/check-signed-drift.ps1 做比较器判定漂移,漂移即 fail。

提交前可以跑这几个静态检查(不动机器状态):

# PowerShell 语法检查(不执行)
Get-ChildItem -Recurse -Filter *.ps1 | ForEach-Object {
    $errs = $null
    [void][System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$errs)
    if ($errs) { Write-Error "$($_.FullName): $errs" } else { "OK: $($_.Name)" }
}

Invoke-ScriptAnalyzer -Recurse -Path ./Workloads, ./tests/_harness
python3 -c "import yaml; yaml.safe_load(open('manifest.yml'))"

跑单个 flow 的端到端测试就是 CI 做的事:

./tests/_harness/run-flow.ps1 -Id typescript `
    -Build 'tsc tests/typescript/hello.ts' `
    -Run   'node tests/typescript/hello.js' `
    -Expected tests/typescript/expected.txt
# 期望输出尾部:"INSTALL_OK: typescript"

九、排障速查表

现象原因与处理
Unrecognized command: configure跑 winget configure --enable;仍不行用 Workloads/_common/assert-winget-configure.ps1 判断是 App Installer 太旧、策略禁用还是别的原因
internal error / -2146233079非提权环境缺 VC++ Redist,装对应架构的 Microsoft.VCRedist.2015+.x64 / .arm64
装成功了但 python / node 不在 PATH新开终端,或跑同名 install.ps1 shim 在当前会话刷新 PATH
Dev Config 重启后像卡住WindowsDevConfigResume 任务在登录后约 30 秒自动继续。等两分钟没动静就再跑一次原命令 —— 幂等,会跳过已完成的
某几步显示 flaggedbest-effort 项未完成/未确认,多因依赖包刚装 PATH 还没注册(WinUI 模板需要 .NET SDK 在 PATH,Copilot 插件需要 Copilot CLI)。重跑即可,成功的秒过
wsl --install ... failed with exit code -1硬件虚拟化没开。物理机进 BIOS/UEFI 开 VT-x / AMD-V;VM 在 Hyper-V 宿主上(客户机关机):Set-VMProcessor -VMName <VM_NAME> -ExposeVirtualizationExtensions $true
WSL Comfort 因缺 WSL 失败在 Windows 侧跑 .\wsl-comfort\install.ps1,它会先装 WSL
“Setup is already running in another window”切到另一个窗口;确认没在跑说明上次进程没干净退出,注销再登录后重试
“Windows Terminal settings 无法解析为 JSON”settings.json 有语法错误,按提示修或重命名后重跑(脚本会保留原文件并跳过该步)
下载失败/超时多为代理。winget 和 WinHTTP 都要配代理:netsh winhttp show proxy
都不适用看日志 devconfig-log.txt(通常在 %ProgramData%\CalmOS),带着 winver、命令、相关片段去 Issues

十、它适合你吗

适合

  • 每次重装系统都要重来一遍装机的 Windows 开发者。
  • 想要现成的现代化终端体验(Oh My Posh + Nerd Font + Starship),但懒得自己配。
  • 需要 Repeatable 环境的场景:Dev Box、实验室镜像、CI runner 镜像、src/ 里加一门语言就能批量铺开。
  • 教学/布道:一行命令让学员拿到和讲师一致的環境。

不适合

  • 想精细控制每一个选项的人 —— 它明确写着 opinionated。Full 会开 Sudo、远程桌面、改 Edge 策略,不是所有人都想要。
  • 日常主力机直接跑 —— 卸载会把自己装的 Python/Node/Git 一并卸掉,还会删 Ubuntu。先在 Sandbox 或虚拟机里跑一遍。
  • 离不开 zsh 全套自定义的人 —— WSL Comfort 会整块替换托管块内的 dotfiles 内容,块外的编辑没事,块内的会丢。
  • 非 Ubuntu 用户 —— Comfort Shell 两半都只认 Ubuntu,其它发行版在 preflight 就被拒。

我的建议上手路径

  1. 建一个 Windows Sandbox 或 Hyper-V 一次性虚拟机(若是 Hyper-V 客户机,先开嵌套虚拟化)。
  2. 先跑 winget configure test --file .\Workloads\python\configuration.winget 之类的 dry-check,只看不改,感受一下 DSC 资源的粒度。
  3. 再跑 .\Workloads\python\install.ps1,确认 shim 的 PATH 刷新行为。
  4. 最后再考虑 irm https://aka.ms/devconfig/standard/setup.ps1 | iex —— 记住会重启一次,会顺手帮你把 WSL + Ubuntu 装好。
  5. 觉得哪里不爽,去 src/ 里改 —— 这才是仓库希望你做的。

最后说句实在的:这类"官方装机脚本"最大的价值从来不是"省下那两小时",而是把一套经过验证的环境定义变成可版本控制、可 review、可 CI 测试的文本。今天它帮你装的是 Python 3.14 和 .NET 10,明天你往 manifest.yml 里加一条,团队二十台机器就同步了。这才是 IaC(Infrastructure as Code)在个人开发机上的正确用法。