Files
BakNRet/tools/lab/payload/provision.ps1
T
Shuery 2937eb6652 chore: 记录改造前基线
改造开始前的完整状态,作为可回退的基点。此提交之后:Pester 175 项、零依赖套件 101 项全绿;PowerShell 5.1 尚不可用(源文件无 BOM)。

包含此前未提交的在制品:安全描述符套件、Hyper-V 实验环境(tools/lab)、agent 约定(AGENTS.md 与 docs/agents)。

.gitignore 增加 *.key / *.pfx:BackupConfig.psd1 的 PasswordFile 此前默认指向仓库内的 baknret.key,一次 git add -A 就会把口令提交进版本库。默认值在后续提交中改为空。
2026-09-26 21:46:55 +08:00

118 lines
6.5 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<#
.SYNOPSIS
BakNRet 隔离测试 VM 的首次登录供给脚本(由 unattend.xml 的 FirstLogonCommands 调用)。
.DESCRIPTION
运行环境是 VM 内全新安装的 Windows 11(Windows PowerShell 5.1、管理员 lab 账户)。
目标:把 VM 变成「可以直接跑 BakNRet 全链路测试」的真机状态:
1. 电源 / 休眠 / 锁屏:测试期间不要因为空闲睡下去;
2. 执行策略 Bypass(仅此实验 VM);
3. 把注入的 7-Zip 与 PowerShell 7 放到机器 PATH 上,与宿主机的工具版本对齐;
4. 安装 Pester 5.9.1(Windows PowerShell 与 pwsh 两侧都能导入);
5. 不让 Windows Update 自动重启,并给实验目录加 Defender 排除
(避免杀软把「文件被占用」类用例变成随机失败,同时显著拖慢压缩);
6. 关掉首次登录后的 SCOOBE「完成设备设置」向导;
7. 写出 state\provisioned.json 与 state\provision.ok 作为「供给完成」的硬凭据。
幂等:可重复执行,第二次跑不会失败。
#>
$ErrorActionPreference = 'Continue'
$ProgressPreference = 'SilentlyContinue'
$lab = 'C:\BakNRet-Lab'
$logDir = Join-Path $lab 'logs'
$stateDir = Join-Path $lab 'state'
New-Item -ItemType Directory -Force -Path $logDir, $stateDir | Out-Null
Start-Transcript -Path (Join-Path $logDir 'provision.log') -Force | Out-Null
function Step($m) { Write-Host "==> $m" }
try {
Step '1/7 电源与显示:不休眠、不锁屏、关休眠'
powercfg /change standby-timeout-ac 0 | Out-Null
powercfg /change monitor-timeout-ac 0 | Out-Null
powercfg /change hibernate-timeout-ac 0 | Out-Null
powercfg /hibernate off | Out-Null
Step '2/7 执行策略:LocalMachine = Bypass(仅此实验 VM)'
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy Bypass -Force
Step '3/7 工具上机器 PATH:7-Zip 与 PowerShell 7'
$zipSrc = Join-Path $lab 'payload\7zip'
$zipDst = 'C:\Program Files\7-Zip'
$pwshSrc = Join-Path $lab 'payload\pwsh'
$pwshDst = 'C:\Program Files\PowerShell\7'
if (-not (Test-Path $zipDst)) { robocopy $zipSrc $zipDst /MIR /NFL /NDL /NJH /NJS /NP | Out-Null }
if (-not (Test-Path $pwshDst)) { robocopy $pwshSrc $pwshDst /MIR /NFL /NDL /NJH /NJS /NP | Out-Null }
$machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
foreach ($p in @($zipDst, $pwshDst)) {
if ($machinePath -notlike "*$p*") { $machinePath = $machinePath.TrimEnd(';') + ';' + $p }
if ($env:Path -notlike "*$p*") { $env:Path = $env:Path.TrimEnd(';') + ';' + $p }
}
[Environment]::SetEnvironmentVariable('Path', $machinePath, 'Machine')
Step '4/7 安装 Pester 5.9.1(Windows PowerShell 与 pwsh 各一份)'
$pesterSrc = Join-Path $lab 'payload\Pester\5.9.1'
foreach ($dst in @("$env:ProgramFiles\WindowsPowerShell\Modules\Pester\5.9.1",
"$env:ProgramFiles\PowerShell\Modules\Pester\5.9.1")) {
if (-not (Test-Path $dst)) { robocopy $pesterSrc $dst /MIR /NFL /NDL /NJH /NJS /NP | Out-Null }
}
Step '5/7 Windows Update 不自动重启 + Defender 排除实验目录'
$wu = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU'
New-Item -Path $wu -Force | Out-Null
New-ItemProperty -Path $wu -Name 'NoAutoRebootWithLoggedOnUsers' -Value 1 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $wu -Name 'AUOptions' -Value 2 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -Name 'HiberbootEnabled' -Value 0 -PropertyType DWord -Force | Out-Null
Add-MpPreference -ExclusionPath 'C:\BakNRet', 'C:\BakNRet-Lab' -ErrorAction SilentlyContinue
Step '6/7 关掉 SCOOBE「完成设备设置」'
$scoobe = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\UserProfileEngagement'
New-Item -Path $scoobe -Force | Out-Null
New-ItemProperty -Path $scoobe -Name 'ScoobeSystemSettingEnabled' -Value 0 -PropertyType DWord -Force | Out-Null
Step '7/7 采集真机事实并落盘'
$zipExe = Join-Path $zipDst '7z.exe'
$pwshExe = Join-Path $pwshDst 'pwsh.exe'
$pwshVer = '缺失'
if (Test-Path $pwshExe) { $pwshVer = (& $pwshExe -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' 2>&1) -join ' ' }
$zipVer = '缺失'
if (Test-Path $zipExe) { $zipVer = (& $zipExe 2>&1 | Select-Object -First 2) -join ' / ' }
$facts = [ordered]@{
ProvisionedAt = (Get-Date).ToString('s')
ComputerName = $env:COMPUTERNAME
User = (whoami)
IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
OsCaption = (Get-CimInstance Win32_OperatingSystem).Caption
OsVersion = (Get-CimInstance Win32_OperatingSystem).Version
OsBuild = (Get-CimInstance Win32_OperatingSystem).BuildNumber
OsArch = (Get-CimInstance Win32_OperatingSystem).OSArchitecture
WindowsPS = $PSVersionTable.PSVersion.ToString()
SevenZipVersion = $zipVer
PwshVersion = $pwshVer
PesterVersion = (Get-Module -ListAvailable Pester -ErrorAction SilentlyContinue | Select-Object -First 1).Version.ToString()
PathHasSevenZip = ([Environment]::GetEnvironmentVariable('Path', 'Machine')) -like '*7-Zip*'
PathHasPwsh = ([Environment]::GetEnvironmentVariable('Path', 'Machine')) -like '*PowerShell\7*'
CpuCount = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
RamGB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 1)
Drives = @(Get-Volume | Where-Object DriveLetter | ForEach-Object {
[ordered]@{ Letter = "$($_.DriveLetter):"; Fs = $_.FileSystemType; SizeGB = [math]::Round($_.Size / 1GB, 1) }
})
}
$facts | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $stateDir 'provisioned.json') -Encoding UTF8
$facts.GetEnumerator() | ForEach-Object { Write-Host (" {0,-16} {1}" -f $_.Key, $_.Value) }
'provision-ok' | Set-Content -LiteralPath (Join-Path $stateDir 'provision.ok') -Encoding ASCII
Write-Host '==> 供给完成'
}
catch {
Write-Host ("供给失败:" + $_.Exception.Message) -ForegroundColor Red
("provision-FAILED: " + $_.Exception.Message) | Set-Content -LiteralPath (Join-Path $stateDir 'provision.FAILED') -Encoding UTF8
}
finally {
Stop-Transcript | Out-Null
}