Windows 自动安装应答文件 Unattend.xml 生成工具
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1273 lines
49 KiB

param(
$Language = 'zh-CN',
$OsVersion = 11,
$WindowsProductName = 'Enterprise',
$Architecture = 'x64',
$DiskId = -1,
$PartitionID = -1,
$FullName = 'MyPC',
$VentoyDriverLetter = '',
$ISOPath = '',
[switch]$Interactive,
[switch]$NotFormat,
[switch]$Version
)
function GetDefaultFolderPath {
return [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Desktop)
}
function GetPartitionTypeName {
param($GptType, $MbrType, $FileSystem)
if ($GptType) {
if ($GptType -ieq '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}') {
return '微软恢复分区'
}
elseif ($GptType -ieq '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}') {
return 'EFI 系统分区'
}
elseif ($GptType -ieq '{e3c9e316-0b5c-4db8-817d-f92df00215ae}') {
return '微软保留分区'
}
elseif ($GptType -ieq '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}') {
return '基本数据分区'
}
elseif ($GptType -ieq '{af9b60a0-1431-4f62-bc68-3311714a69ad}') {
return '逻辑磁盘管理器元数据分区'
}
elseif ($GptType -ieq '{af9b60a0-1431-4f62-bc68-3311714a69ad}') {
return '逻辑磁盘管理器数据分区'
}
}
if ($MbrType) {
if ($MbrType -ieq '1') {
return 'FAT12 分区'
}
elseif ($MbrType -ieq '4') {
return 'FAT16 分区'
}
elseif ($MbrType -ieq '5') {
return '扩展分区'
}
elseif ($MbrType -ieq '6') {
return '逻辑分区'
}
elseif ($MbrType -ieq '7') {
return "$FileSystem 分区"
}
elseif ($MbrType -ieq '12') {
return 'FAT32 分区'
}
}
return '未知分区'
}
function GetCurrentDisk {
$CurrentDisks = [ordered]@{}
Get-Disk | ForEach-Object {
$Partitions = [ordered]@{}
$PhydicalDisk = Get-PhysicalDisk -FriendlyName $_.FriendlyName
Get-Partition -DiskNumber $_.DiskNumber | ForEach-Object {
$Volume = Get-Volume -Partition $_
$PartitionTypeName = GetPartitionTypeName -GptType $_.GptType -MbrType $_.MbrType `
-FileSystem $Volume.FileSystem
$Partition = @{
'Guid' = $_.Guid; # 分区 ID
'PartitionNumber' = $_.PartitionNumber; # 分区编号,值从 1 开始
'DiskNumber' = $_.DiskNumber; # 所属硬盘编号,值从 0 开始
'GptType' = $_.GptType; # 分区类型的 ID
'DriveLetter' = $_.DriveLetter; # 驱动器号,如 C
'FileSystem' = $Volume.FileSystem; # 分区文件系统类型
'FileSystemType' = $Volume.FileSystemType; # 分区文件系统类型
'SizeRemaining' = $Volume.SizeRemaining; # 分区可用空间大小,单位为 Byte
'Size' = $_.Size; # 分区容量,单位为 Byte
'Type' = $_.Type; # GPT 分区类型,System 表示 EFI 分区,Basic 表示基本数据分区,Reserved 表示 MSR 保留分区
'TypeName' = $PartitionTypeName;
'MbrType' = $_.MbrType; # MBR 分区类型的 ID
'IsHidden' = $_.IsHidden; # 是否隐藏分区
'IsBoot' = $_.IsBoot; # 是否启动分区
'IsSystem' = $_.IsSystem; # 是否 EFI 系统分区
'IsActive' = $_.IsActive # 是否活动分区,MBR 才有意义,GPT 无意义
}
$Partitions.Add($_.PartitionNumber, $Partition)
}
$Disk = @{
'DiskNumber' = $_.DiskNumber; # 硬盘编号,值从 0 开始
'PartitionStyle' = $_.PartitionStyle; # 分区类型,如 GPT 或 MBR
'MediaType' = $PhydicalDisk.MediaType; # 硬盘类型,如 SSD 或 HDD
'OperationalStatus' = $_.OperationalStatus; # 硬盘状态,如 Online
'HealthStatus' = $_.HealthStatus; # 硬盘健康状态,如 Healthy
'BusType' = $_.BusType; # 硬盘接口类型,如 RAID 或 USB
'BootFromDisk' = $_.BootFromDisk; # 是否从该硬盘启动
'FirmwareVersion' = $_.FirmwareVersion; # 硬盘固件版本
'FriendlyName' = $_.FriendlyName; # 硬盘名称
'IsBoot' = $_.IsBoot; # 是否从该硬盘启动
'IsSystem' = $_.IsSystem; # 是否系统盘
'Manufacturer' = $_.Manufacturer; # 硬盘制造商
'Model' = $_.Model; # 硬盘型号
'NumberOfPartitions' = $_.NumberOfPartitions; # 硬盘分区数量
'Size' = $_.Size; # 硬盘容量,单位为 Byte
'AllocatedSize' = $PhydicalDisk.AllocatedSize; # 已分配容量,单位为 Byte
'Partitions' = $Partitions
}
$CurrentDisks.Add($_.DiskNumber, $Disk)
}
return $CurrentDisks
}
function GetSystemDiskId {
$CurrentDisks = GetCurrentDisk
foreach ($_ in $CurrentDisks.GetEnumerator()) {
if ($_.Value['IsBoot']) {
return $_.Value['DiskNumber']
}
}
return 0
}
function GetSystemPartitionId {
$CurrentDisks = GetCurrentDisk
foreach ($_ in $CurrentDisks.GetEnumerator()) {
foreach ($__ in $_.Value['Partitions'].GetEnumerator()) {
if ($__.Value['IsBoot']) {
return $__.Value['PartitionNumber']
}
}
}
return 1
}
function FormatSize {
param($Size)
$Tb = [Math]::Round($Size * 1.0 / 1024 / 1024 / 1024 / 1024, 2)
if ($Tb -gt 1) {
return "$Tb" + ' TB'
}
$Gb = [Math]::Round($Size * 1.0 / 1024 / 1024 / 1024, 2)
if ($Gb -gt 1) {
return "$Gb" + ' GB'
}
$Mb = [Math]::Round($Size * 1.0 / 1024 / 1024, 2)
if ($Mb -gt 1) {
return "$Mb" + ' MB'
}
$Kb = [Math]::Round($Size * 1.0 / 1024, 2)
if ($Kb -gt 1) {
return "$Kb" + ' KB'
}
if ($Size -le 0) {
return '0 Byte'
}
return "$Size" + ' Byte'
}
function ShowLanguageSelect {
Write-Host -Object '============================'
Write-Host -Object '选择要安装系统的语言,推荐 1'
Write-Host -Object '============================'
Write-Host -Object ''
Write-Host -Object '1: 简体中文 zh-CN'
Write-Host -Object ''
Write-Host -Object '2: 英文 en-US'
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入选择的序号(默认为 1),按回车键确认'
if ($InputOption -ieq '' -or $InputOption -ieq '1') {
Write-Host -Object ''
return 'zh-CN'
}
elseif ($InputOption -ieq '2') {
Write-Host -Object ''
return 'en-US'
}
else {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
}
}
function ShowOsVersionSelect {
Write-Host -Object '============================'
Write-Host -Object '选择要安装系统的版本,推荐 1'
Write-Host -Object '============================'
Write-Host -Object ''
Write-Host -Object '1: Windows 11'
Write-Host -Object ''
Write-Host -Object '2: Windows 10'
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入选择的序号(默认为 1),按回车键确认'
if ($InputOption -ieq '' -or $InputOption -ieq '1') {
Write-Host -Object ''
return 11
}
elseif ($InputOption -ieq '2') {
Write-Host -Object ''
return 10
}
else {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
}
}
function ShowWindowsProductNameSelect {
Write-Host -Object '============================'
Write-Host -Object '选择要安装系统的产品,推荐 1'
Write-Host -Object '============================'
Write-Host -Object ''
Write-Host -Object '1: 企业版 Enterprise'
Write-Host -Object ''
Write-Host -Object '2: 教育版 Education'
Write-Host -Object ''
Write-Host -Object '3: 专业版 Pro'
Write-Host -Object ''
Write-Host -Object '4: 专业教育版 Pro Education'
Write-Host -Object ''
Write-Host -Object '5: 专业工作站版 Pro For Workstations'
Write-Host -Object ''
Write-Host -Object '6: 其他'
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入选择的序号(默认为 1),按回车键确认'
if ($InputOption -ieq '' -or $InputOption -ieq '1') {
Write-Host -Object ''
return 'Enterprise'
}
elseif ($InputOption -ieq '2') {
Write-Host -Object ''
return 'Education'
}
elseif ($InputOption -ieq '3') {
Write-Host -Object ''
return 'Pro'
}
elseif ($InputOption -ieq '4') {
Write-Host -Object ''
return 'Pro Education'
}
elseif ($InputOption -ieq '5') {
Write-Host -Object ''
return 'Pro For Workstations'
}
elseif ($InputOption -ieq '6') {
Write-Host -Object ''
return ''
}
else {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
}
}
function ShowArchitectureSelect {
param($OsVersion)
if ($OsVersion -eq 11) {
return 'x64'
}
Write-Host -Object '============================'
Write-Host -Object '选择要安装系统的架构,推荐 1'
Write-Host -Object '============================'
Write-Host -Object ''
Write-Host -Object '1: 64 位系统 x64'
Write-Host -Object ''
Write-Host -Object '2: 32 位系统 x86'
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入选择的序号(默认为 1),按回车键确认'
if ($InputOption -ieq '' -or $InputOption -ieq '1') {
Write-Host -Object ''
return 'x64'
}
elseif ($InputOption -ieq '2') {
Write-Host -Object ''
return 'x86'
}
else {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
}
}
function ShowDiskIdSelect {
$SystemDiskId = GetSystemDiskId
$CurrentDisks = GetCurrentDisk
Write-Host -Object '================================================'
Write-Host -Object "选择要安装系统的硬盘编号,推荐当前系统所在硬盘 $SystemDiskId"
Write-Host -Object '================================================'
Write-Host -Object ''
Write-Host -Object '当前系统识别到的硬盘如下:红色字体的硬盘为当前系统所在硬盘,红色字体的分区为当前系统所在分区'
$CurrentDisks.GetEnumerator() | ForEach-Object {
$msg = '硬盘编号: ' + $_.Value['DiskNumber'] + ', 硬盘类型: ' + $_.Value['MediaType'] + ', 接口类型: ' `
+ $_.Value['BusType'] + ', 分区类型: ' + $_.Value['PartitionStyle'] + ', 硬盘容量: ' `
+ (FormatSize -Size $_.Value['Size']) + ', 硬盘名称: ' + $_.Value['FriendlyName']
Write-Host -Object ''
if ($_.Value['IsBoot']) {
Write-Host -Object $msg -ForegroundColor Red
}
else {
Write-Host -Object $msg
}
$_.Value['Partitions'].GetEnumerator() | ForEach-Object {
$msg = ' |- 分区编号: ' + $_.Value['PartitionNumber'] + ', 驱动器: ' + $_.Value['DriveLetter'] `
+ ', 是否隐藏: ' + $_.Value['IsHidden'] + ', 文件系统: ' + $_.Value['FileSystem'] + ', 类型: ' `
+ $_.Value['TypeName'] + ', 可用空间: ' + (FormatSize -Size $_.Value['SizeRemaining']) + ', 容量: ' `
+ ( FormatSize -Size $_.Value['Size'])
Write-Host -Object ' |'
if ($_.Value['IsBoot']) {
Write-Host -Object $msg -ForegroundColor Red
}
else {
Write-Host -Object $msg
}
}
}
while ($true) {
Write-Host -Object ''
try {
[System.Int32]$InputOption = Read-Host `
-Prompt "请输入选择的硬盘编号(硬盘编号从 0 开始,默认为 $SystemDiskId),按回车键确认"
if ($InputOption -ge 0) {
Write-Host -Object ''
return $InputOption
}
else {
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
}
}
catch {
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
}
}
}
function ShowWipeDiskSelect {
param($DiskId)
$CurrentDisks = GetCurrentDisk
$SelectDisk = $CurrentDisks[$DiskId]
$DefaultWipe = $false
$DefalultSelect = 2
if ($SelectDisk -and $SelectDisk['PartitionStyle'] -ine 'GPT') {
Write-Host -Object '======================================================================'
Write-Host -Object '选择是否对所选硬盘进行 GPT 分区,所选系统只支持 GPT 分区的硬盘,推荐 2'
Write-Host -Object '======================================================================'
$DefaultWipe = $true
$DefalultSelect = 2
}
else {
Write-Host -Object '======================================================================'
Write-Host -Object '选择是否对所选硬盘进行 GPT 分区,所选系统只支持 GPT 分区的硬盘,推荐 1'
Write-Host -Object '======================================================================'
$DefaultWipe = $false
$DefalultSelect = 1
}
Write-Host -Object ''
Write-Host -Object '1: 否'
Write-Host -Object ''
Write-Host -Object '2: 是,注意:会清除所选硬盘的数据,请及时备份所选硬盘的数据' -ForegroundColor Red
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt "请输入选择的序号(默认为 $DefalultSelect),按回车键确认"
if ($InputOption -ieq '') {
Write-Host -Object ''
return $DefaultWipe
}
elseif ($InputOption -ieq '1') {
Write-Host -Object ''
return $false
}
elseif ($InputOption -ieq '2') {
Write-Host -Object ''
return $true
}
else {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
}
}
function ShowNewPartition {
param($CreatePartitionInfo)
Write-Host -Object '所选硬盘重新分区如下:红色字体的分区为系统安装分区'
Write-Host -Object ''
$CreatePartitionInfo.GetEnumerator() | ForEach-Object {
$VolumeSize = '硬盘剩余所有空间'
if (!$_.Value['Extend']) {
$VolumeSize = FormatSize -Size $($_.Value['Size'] * 1024 * 1024)
}
$msg = '分区编号: ' + $_.Value['Order'] + ', 是否隐藏: ' + $_.Value['IsHidden'] + ', 文件系统: ' `
+ $_.Value['FileSystem'] + ', 类型: ' + $_.Value['TypeName'] + ', 容量: ' + $VolumeSize
if ($_.Value['IsBoot']) {
Write-Host -Object $msg -ForegroundColor Red
}
else {
Write-Host -Object $msg
}
Write-Host -Object ''
}
}
function ShowAddNewPartition {
while ($true) {
$InputOption = Read-Host -Prompt '是否增加新分区(0: 否, 1: 是),按回车键确认'
Write-Host -Object ''
if ($InputOption -ieq '0') {
return $false
}
elseif ($InputOption -ieq '1') {
return $true
}
else {
Write-Warning -Message '输入无效,请重新输入'
Write-Host -Object ''
}
}
}
function ShowIsBoot {
param($CreatePartitionInfo)
Write-Host -Object '================================================'
Write-Host -Object '选择要安装系统的分区编号,类型必须为基本数据分区'
Write-Host -Object '================================================'
Write-Host -Object ''
ShowNewPartition -CreatePartitionInfo $CreatePartitionInfo
while ($true) {
$InputOption = Read-Host -Prompt '请输入选择的分区编号(分区编号从 1 开始,类型必须为基本数据分区),按回车键确认'
$SelectPartition = $CreatePartitionInfo[$InputOption]
if ($SelectPartition) {
if ($SelectPartition['Type'] -ieq 'Primary') {
Write-Host -Object ''
$SelectPartition['IsBoot'] = $true
$Script:PartitionID = $SelectPartition['Order']
return
}
else {
Write-Host -Object ''
Write-Warning -Message '所选分区非基本数据分区,只能选择基本数据分区,请重新输入'
Write-Host -Object ''
}
}
else {
Write-Host -Object ''
Write-Warning -Message '所选分区不存在,请重新输入'
Write-Host -Object ''
}
}
}
function ShowCreatePartition {
$CreatePartitionInfo = [ordered]@{
'1' = @{
'Order' = 1;
'Size' = 300;
'Type' = 'EFI';
'TypeName' = 'EFI 分区';
'FileSystem' = 'FAT32';
'Extend' = $false;
'IsHidden' = $true;
'IsBoot' = $false
};
'2' = @{
'Order' = 2;
'Size' = 200;
'Type' = 'MSR';
'TypeName' = '微软保留分区';
'FileSystem' = '';
'Extend' = $false;
'IsHidden' = $true;
'IsBoot' = $false
}
}
Write-Host -Object '=========='
Write-Host -Object '创建新分区'
Write-Host -Object '=========='
Write-Host -Object ''
$PartitionNumber = 2
ShowNewPartition -CreatePartitionInfo $CreatePartitionInfo
while ($true) {
try {
$PartitionNumber = $PartitionNumber + 1
[System.Int32]$InputOption = Read-Host `
-Prompt "输入第 $PartitionNumber 个分区的大小,0 表示占用硬盘所有剩余空间(默认为 0),单位为 MB,按回车键确认"
if ($InputOption -eq 0) {
Write-Host -Object ''
$CreatePartitionInfo.Add([System.String]$PartitionNumber, @{
'Order' = $PartitionNumber;
'Size' = 0;
'Type' = 'Primary';
'TypeName' = '基本数据分区';
'FileSystem' = 'NTFS';
'Extend' = $true;
'IsHidden' = $false;
'IsBoot' = $false
})
ShowNewPartition -CreatePartitionInfo $CreatePartitionInfo
break
}
elseif ($InputOption -gt 0) {
Write-Host -Object ''
$CreatePartitionInfo.Add([System.String]$PartitionNumber, @{
'Order' = $PartitionNumber;
'Size' = $InputOption;
'Type' = 'Primary';
'TypeName' = '基本数据分区';
'FileSystem' = 'NTFS';
'Extend' = $false;
'IsHidden' = $false;
'IsBoot' = $false
})
ShowNewPartition -CreatePartitionInfo $CreatePartitionInfo
}
else {
$PartitionNumber = $PartitionNumber - 1
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
Write-Host -Object ''
continue
}
$ShowAddNewPartition = ShowAddNewPartition
if (!$ShowAddNewPartition) {
$CreatePartitionInfo["$PartitionNumber"]['Extend'] = $true
break
}
}
catch {
$PartitionNumber = $PartitionNumber - 1
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
Write-Host -Object ''
}
}
ShowIsBoot -CreatePartitionInfo $CreatePartitionInfo
ShowNewPartition -CreatePartitionInfo $CreatePartitionInfo
return $CreatePartitionInfo
}
function ShowPartitionIdSelect {
param($DiskId)
$DefalultSelect = GetSystemPartitionId
$CurrentDisks = GetCurrentDisk
$SelectDisk = $CurrentDisks[$DiskId]
if ($SelectDisk) {
Write-Host -Object '================================================'
Write-Host -Object "选择要安装系统的分区编号,推荐当前系统所在分区 $DefalultSelect"
Write-Host -Object '================================================'
Write-Host -Object ''
Write-Host -Object '所选硬盘的分区如下:红色字体的分区为当前系统所在分区'
$SelectDisk['Partitions'].GetEnumerator() | ForEach-Object {
$msg = '分区编号: ' + $_.Value['PartitionNumber'] + ', 驱动器: ' + $_.Value['DriveLetter'] + ', 是否隐藏: ' `
+ $_.Value['IsHidden'] + ', 文件系统: ' + $_.Value['FileSystem'] + ', 类型: ' + $_.Value['TypeName'] `
+ ', 可用空间: ' + (FormatSize -Size $_.Value['SizeRemaining']) + ', 容量: ' `
+ (FormatSize -Size $_.Value['Size'])
Write-Host -Object ''
if ($_.Value['IsBoot']) {
Write-Host -Object $msg -ForegroundColor Red
}
else {
Write-Host -Object $msg
}
}
}
else {
Write-Host -Object '================================'
Write-Host -Object "选择要安装系统的分区编号,推荐 $DefalultSelect"
Write-Host -Object '================================'
}
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt "请输入选择的分区编号(分区编号从 1 开始,默认为 $DefalultSelect),按回车键确认"
if ($InputOption -ieq '') {
Write-Host -Object ''
return $DefalultSelect
}
try {
[System.Int32]$InputOption1 = [System.Int32]$InputOption
if ($InputOption1 -eq 0) {
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
continue
}
if ($InputOption1 -ge 1) {
Write-Host -Object ''
return $InputOption1
}
else {
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
}
}
catch {
Write-Host -Object ''
Write-Warning -Message '输入无效,请重新输入'
}
}
}
function ShowFomatSelect {
Write-Host -Object '================================'
Write-Host -Object '选择是否对所选分区格式化,推荐 2'
Write-Host -Object '================================'
Write-Host -Object ''
Write-Host -Object '1: 否'
Write-Host -Object ''
Write-Host -Object '2: 是,注意:会清除所选分区的数据,请及时备份所选分区的数据' -ForegroundColor Red
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入选择的序号(默认为 2),按回车键确认'
if ($InputOption -ieq '' -or $InputOption -ieq '2') {
Write-Host -Object ''
return $true
}
elseif ($InputOption -ieq '1') {
Write-Host -Object ''
return $false
}
else {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
}
}
function ShowNameInput {
Write-Host -Object '=================================================='
Write-Host -Object '输入系统安装后的登录账号名,账号名建议符合如下要求'
Write-Host -Object '=================================================='
Write-Host -Object ''
Write-Host -Object '1: 推荐英文字母或数字的组合,尽量不使用特殊字符'
Write-Host -Object ''
Write-Host -Object '2: 尽量不使用中文,防止某些应用软件不支持中文而无法使用'
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入登录账号名(默认为 MyPC),按回车键确认'
if ($InputOption -ieq '') {
Write-Host -Object ''
return 'MyPC'
}
else {
Write-Host -Object ''
return $InputOption
}
}
function ShowVentoyDriverLetterSelect {
$CurrentDisks = GetCurrentDisk
Write-Host -Object '==============================='
Write-Host -Object '输入已安装 Ventoy 的 U 盘驱动器'
Write-Host -Object '==============================='
Write-Host -Object ''
Write-Host -Object '当前系统识别到的驱动器如下:'
$CurrentDisks.GetEnumerator() | ForEach-Object {
$msg = '硬盘类型: ' + $_.Value['MediaType'] + ', 接口类型: ' + $_.Value['BusType'] + ', 分区类型: ' `
+ $_.Value['PartitionStyle'] + ', 硬盘容量: ' + (FormatSize -Size $_.Value['Size']) + ', 硬盘名称: ' `
+ $_.Value['FriendlyName']
Write-Host -Object ''
Write-Host -Object $msg
$_.Value['Partitions'].GetEnumerator() | ForEach-Object {
if ($_.Value['DriveLetter']) {
$msg = ' |- 驱动器: ' + $_.Value['DriveLetter'] + ', 文件系统: ' + $_.Value['FileSystem'] `
+ ', 可用空间: ' + (FormatSize -Size $_.Value['SizeRemaining']) + ', 容量: ' `
+ (FormatSize -Size $_.Value['Size'])
Write-Host -Object ' |'
Write-Host -Object $msg
}
}
}
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host `
-Prompt '请输入已安装 Ventoy 的 U 盘驱动器(0 表示将应答文件保存到当前用户的桌面上),按回车键确认'
if ($InputOption -ieq '') {
Write-Host -Object ''
Write-Warning -Message '选择无效,请重新输入'
}
elseif ($InputOption -ieq '0') {
Write-Host -Object ''
return ''
}
elseif (Test-Path -Path $InputOption -PathType Container) {
Write-Host -Object ''
return $InputOption
}
elseif (Test-Path -Path $($InputOption + ':\') -PathType Container) {
Write-Host -Object ''
return $($InputOption + ':\')
}
else {
Write-Host -Object ''
Write-Warning -Message '驱动器不存在,请重新输入'
}
}
}
function ShowGetISOPath {
param($Path)
Write-Host -Object '==============================='
Write-Host -Object '选择使用应答文件的 ISO 镜像文件'
Write-Host -Object '==============================='
Write-Host -Object ''
Write-Host -Object '搜索 ISO 镜像文件中......'
$ISOFiles = [ordered]@{}
$Index = 0;
try {
Get-ChildItem -Path $Path -Include '*.iso' -Recurse -File | ForEach-Object {
$Index = $Index + 1
$ISOFiles.Add([System.String]$Index, $_.FullName)
}
}
catch {
$msg = $Path + ' 无权限'
Write-Warning -Message $msg
}
if ($ISOFiles.Count -gt 0) {
Write-Host -Object ''
Write-Host -Object '搜索到的镜像文件如下:'
$ISOFiles.GetEnumerator() | ForEach-Object {
Write-Host -Object ''
$msg = '镜像文件序号: ' + $_.Key + ', 镜像文件路径: ' + $_.Value
Write-Host -Object $msg
}
while ($true) {
Write-Host -Object ''
$InputOption = Read-Host -Prompt '请输入使用应答文件的 ISO 镜像文件序号,按回车键确认'
$ISOFile = $ISOFiles[$InputOption]
if ($ISOFile) {
Write-Host -Object ''
return $ISOFile
}
else {
Write-Host -Object ''
Write-Warning -Message '镜像文件序号不存在,请重新输入'
}
}
}
else {
Write-Host -Object ''
Write-Host -Object '未搜索到 ISO 镜像文件'
Write-Host -Object ''
return ''
}
}
function UpdateVentoyConfig {
param(
$ISOPath,
$UnattendPath,
$VentoyConfigParentPath
)
if (!$ISOPath) {
return
}
if (!$UnattendPath) {
return
}
if (!$VentoyConfigParentPath) {
return
}
$ISOPath = Split-Path -Path $ISOPath -NoQualifier
$ISOPath = $ISOPath.Replace('\', '/')
$UnattendPath = Split-Path -Path $UnattendPath -NoQualifier
$UnattendPath = $UnattendPath.Replace('\', '/')
$VentoyConfigJsonPath = Join-Path -Path $VentoyConfigParentPath -ChildPath 'ventoy.json'
$JSONContent = $null
if (Test-Path -Path $VentoyConfigJsonPath -PathType Leaf) {
try {
$JSONContent = Get-Content -Path $VentoyConfigJsonPath | ConvertFrom-Json
}
catch {
$msg = $VentoyConfigJsonPath + ' 解析失败'
Write-Warning -Message $msg
}
}
if (!$JSONContent -or !($JSONContent -is [PSCustomObject])) {
$JSONContent = [PSCustomObject]@{
'control' = @(@{
'VTOY_WIN11_BYPASS_CHECK' = '1'
});
'auto_install' = @(@{
'image' = $ISOPath;
'template' = $UnattendPath
})
}
$JSONString = $JSONContent | ConvertTo-Json
$Utf8NoBomEncoding = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false
[System.IO.File]::WriteAllLines($VentoyConfigJsonPath, $JSONString, $Utf8NoBomEncoding)
return
}
$Controls = $JSONContent.'control'
if ($null -eq $Controls -or !($Controls -is [System.Array])) {
Add-Member -InputObject $JSONContent -Force `
-NotePropertyMembers @{ 'control' = @(@{ 'VTOY_WIN11_BYPASS_CHECK' = '1' }) }
}
else {
$AddFlag = $false
foreach ($Control in $Controls) {
if ($null -eq $Control) {
$AddFlag = $true
Add-Member -InputObject $Control -Force -NotePropertyMembers @{ 'VTOY_WIN11_BYPASS_CHECK' = '1' }
break
}
$Check = $Control.'VTOY_WIN11_BYPASS_CHECK'
if ($null -eq $Check) {
continue
}
$AddFlag = $true
Add-Member -InputObject $Control -Force -NotePropertyMembers @{ 'VTOY_WIN11_BYPASS_CHECK' = '1' }
break
}
if (!$AddFlag) {
$Controls += [PSCustomObject]@{ 'VTOY_WIN11_BYPASS_CHECK' = '1' }
$JSONContent.'control' = $Controls
}
}
$Installs = $JSONContent.'auto_install'
if ($null -eq $Installs -or !($Installs -is [System.Array])) {
Add-Member -InputObject $JSONContent -Force `
-NotePropertyMembers @{ 'auto_install' = @(@{ 'image' = $ISOPath; 'template' = $UnattendPath }) }
}
else {
$AddFlag = $false
foreach ($Install in $Installs) {
if ($null -eq $Install) {
Add-Member -InputObject $Install -Force `
-NotePropertyMembers @{ 'image' = $ISOPath; 'template' = $UnattendPath }
$AddFlag = $true
break
}
$Image = $Install.'image'
if ($null -eq $Image) {
Add-Member -InputObject $Install -Force `
-NotePropertyMembers @{ 'image' = $ISOPath; 'template' = $UnattendPath }
$AddFlag = $true
break
}
if ($Image -ne $ISOPath) {
continue
}
Add-Member -InputObject $Install -Force `
-NotePropertyMembers @{ 'image' = $ISOPath; 'template' = $UnattendPath }
$AddFlag = $true
break
}
if (!$AddFlag) {
$Installs += [PSCustomObject]@{ 'image' = $ISOPath; 'template' = $UnattendPath }
$JSONContent.'auto_install' = $Installs
}
}
$JSONString = $JSONContent | ConvertTo-Json
$Utf8NoBomEncoding = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false
[System.IO.File]::WriteAllLines($VentoyConfigJsonPath, $JSONString, $Utf8NoBomEncoding)
}
$VersionInfo = 'v2022.6.11'
if ($Version) {
return $VersionInfo
}
Clear-Host
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
$Host.UI.RawUI.WindowTitle = 'Windows 应答文件生成'
Set-Location -Path $PSScriptRoot
Write-Host -Object "=====> Windows 系统自动安装应答文件生成 $VersionInfo <====="
Write-Host -Object ''
$WipeDisk = $false
$Token = '31bf3856ad364e35'
$WindowsProduct = [ordered]@{
'Enterprise' = @{
'CN' = '企业版';
'US' = 'Enterprise';
'NoSpaceName' = 'Enterprise';
'gvlk' = 'NPPR9-FWDCX-D2C8J-H872K-2YT43'
};
'Education' = @{
'CN' = '教育版';
'US' = 'Education';
'NoSpaceName' = 'Education';
'gvlk' = '6TP4R-GNPTD-KYYHQ-7B7DP-J447Y'
};
'Pro' = @{
'CN' = '专业版';
'US' = 'Pro';
'NoSpaceName' = 'Pro';
'gvlk' = 'W269N-WFGWX-YVC9B-4J6C9-T83GX'
};
'Pro Education' = @{
'CN' = '专业教育版';
'US' = 'Pro Education';
'NoSpaceName' = 'Pro_Education';
'gvlk' = '6TP4R-GNPTD-KYYHQ-7B7DP-J447Y'
};
'Pro For Workstations' = @{
'CN' = '专业工作站版';
'US' = 'Pro For Workstations';
'NoSpaceName' = 'Pro_For_Workstations';
'gvlk' = 'NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J'
}
}
$CurrentDisks = GetCurrentDisk
if ($Interactive) {
$Language = ShowLanguageSelect
$OsVersion = ShowOsVersionSelect
$WindowsProductName = ShowWindowsProductNameSelect
$Architecture = ShowArchitectureSelect -OsVersion $OsVersion
$DiskId = ShowDiskIdSelect
$WipeDisk = ShowWipeDiskSelect -DiskId $DiskId
if ($WipeDisk) {
$CreatePartitionInfo = ShowCreatePartition
}
else {
$PartitionID = ShowPartitionIdSelect -DiskId $DiskId
$NotFormat = !$(ShowFomatSelect)
}
$FullName = ShowNameInput
$VentoyDriverLetter = ShowVentoyDriverLetterSelect
if ($VentoyDriverLetter) {
$ISOPath = ShowGetISOPath -Path $VentoyDriverLetter
}
}
if ($Language -ine 'zh-CN' -and $Language -ine 'en-US') {
Write-Warning -Message '参数 Language 只支持 zh-CN (简体中文), en-US (英文)'
[System.Environment]::Exit(0)
}
if ($Language -ieq 'zh-CN') {
$Language = 'zh-CN'
}
elseif ($Language -ieq 'en-US') {
$Language = 'en-US'
}
if ($OsVersion -ne 10 -and $OsVersion -ne 11) {
Write-Warning -Message '参数 OsVersion 只支持 10 (Windows 10), 11 (Windows 11)'
[System.Environment]::Exit(0)
}
if ($WindowsProductName -and !$WindowsProduct.Contains($WindowsProductName)) {
$WindowsProductNameArray = @($WindowsProduct.GetEnumerator() | ForEach-Object {
$_.Value['US'] + ' (' + $_.Value['CN'] + ')'
})
$AllSupportProductName = [string]::join(', ', $WindowsProductNameArray)
Write-Warning -Message "参数 WindowsProductName 只支持 $AllSupportProductName"
[System.Environment]::Exit(0)
}
if ($OsVersion -eq 10) {
if ($Architecture -ne 'x64' -and $Architecture -ne 'x86') {
Write-Warning -Message '参数 OsVersion 为 Windows 10 时,参数 Architecture 只支持 x64 (64 位系统), x86 (32 位系统)'
[System.Environment]::Exit(0)
}
}
if ($OsVersion -eq 11) {
if ($Architecture -ine 'x64') {
Write-Warning -Message '参数 OsVersion 为 Windows 11 时,参数 Architecture 只支持 x64 (64 位系统)'
[System.Environment]::Exit(0)
}
}
$ArchitectureName = 'amd64'
if ($Architecture -eq 'x86') {
$ArchitectureName = 'x86'
}
if ($DiskId -eq -1) {
$DiskId = GetSystemDiskId
}
if ($PartitionID -eq -1) {
$PartitionID = GetSystemPartitionId
}
if ('' -ieq $VentoyDriverLetter) {
$ParentPath = GetDefaultFolderPath
}
else {
$ParentPath = $VentoyDriverLetter
if ($ISOPath) {
$Letter1 = Split-Path -Path $ISOPath -Qualifier
$Letter2 = Split-Path -Path $ISOPath -Qualifier
if ($Letter1 -ine $Letter2) {
Write-Warning -Message '参数 ISOPath 指定的路径必须和参数 VentoyDriverLetter 指定的驱动器属于同一个驱动器'
[System.Environment]::Exit(0)
}
}
}
$VentoyConfigParentPath = Join-Path -Path $ParentPath -ChildPath 'ventoy'
if (!$(Test-Path -Path $VentoyConfigParentPath -PathType Container)) {
New-Item -Path $VentoyConfigParentPath -ItemType Directory -Force
Write-Host -Object ''
}
$VentoyConfigScriptPath = Join-Path -Path $VentoyConfigParentPath -ChildPath 'script'
if (!$(Test-Path -Path $VentoyConfigScriptPath -PathType Container)) {
New-Item -Path $VentoyConfigScriptPath -ItemType Directory -Force
Write-Host -Object ''
}
$ProductInfo = @{}
if ('' -ieq $WindowsProductName) {
$UnattendPath = $VentoyConfigScriptPath + '\Unattend_Windows_' + $OsVersion + '_' + $Architecture + '_' `
+ $Language + '_' + $FullName + '.xml'
}
else {
$ProductInfo = $WindowsProduct[$WindowsProductName]
$NoSpaceName = $ProductInfo['NoSpaceName']
$UnattendPath = $VentoyConfigScriptPath + '\Unattend_Windows_' + $OsVersion + '_' + $NoSpaceName + '_' `
+ $Architecture + '_' + $Language + '_' + $FullName + '.xml'
}
UpdateVentoyConfig -ISOPath $ISOPath -UnattendPath $UnattendPath -VentoyConfigParentPath $VentoyConfigParentPath
if (Test-Path -Path $UnattendPath -PathType Leaf) {
Remove-Item -Path $UnattendPath -Force
}
Add-Content -Path $UnattendPath -Value '<?xml version="1.0" encoding="utf-8"?>'
Add-Content -Path $UnattendPath -Value '<unattend xmlns="urn:schemas-microsoft-com:unattend">'
Add-Content -Path $UnattendPath -Value ' <settings pass="windowsPE">'
Add-Content -Path $UnattendPath -Value (" <component name=`"Microsoft-Windows-International-Core-WinPE`"" `
+ " processorArchitecture=`"$ArchitectureName`" publicKeyToken=`"$Token`" language=`"neutral`"" `
+ " versionScope=`"nonSxS`" xmlns:wcm=`"http://schemas.microsoft.com/WMIConfig/2002/State`"" `
+ " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`">")
Add-Content -Path $UnattendPath -Value ' <SetupUILanguage>'
Add-Content -Path $UnattendPath -Value " <UILanguage>$Language</UILanguage>"
Add-Content -Path $UnattendPath -Value ' </SetupUILanguage>'
Add-Content -Path $UnattendPath -Value " <InputLocale>$Language</InputLocale>"
Add-Content -Path $UnattendPath -Value " <UILanguage>$Language</UILanguage>"
Add-Content -Path $UnattendPath -Value " <SystemLocale>$Language</SystemLocale>"
Add-Content -Path $UnattendPath -Value " <UserLocale>$Language</UserLocale>"
Add-Content -Path $UnattendPath -Value ' </component>'
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value (" <component name=`"Microsoft-Windows-Setup`"" `
+ " processorArchitecture=`"$ArchitectureName`" publicKeyToken=`"$Token`" language=`"neutral`"" `
+ " versionScope=`"nonSxS`" xmlns:wcm=`"http://schemas.microsoft.com/WMIConfig/2002/State`"" `
+ " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`">")
Add-Content -Path $UnattendPath -Value ' <EnableNetwork>false</EnableNetwork>'
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value ' <UserData>'
Add-Content -Path $UnattendPath -Value ' <AcceptEula>true</AcceptEula>'
if ($FullName) {
Add-Content -Path $UnattendPath -Value " <FullName>$FullName</FullName>"
}
if ($WindowsProductName) {
$key = $ProductInfo['gvlk']
Add-Content -Path $UnattendPath -Value " <ProductKey><Key>$key</Key></ProductKey>"
}
Add-Content -Path $UnattendPath -Value ' </UserData>'
Add-Content -Path $UnattendPath -Value ''
if ($WipeDisk) {
Add-Content -Path $UnattendPath -Value ' <DiskConfiguration>'
Add-Content -Path $UnattendPath -Value ' <Disk wcm:action="add">'
Add-Content -Path $UnattendPath -Value " <DiskID>$DiskId</DiskID>"
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value ' <WillWipeDisk>true</WillWipeDisk>'
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value ' <CreatePartitions>'
$CreatePartitionInfo.GetEnumerator() | ForEach-Object {
Add-Content -Path $UnattendPath -Value ' <CreatePartition>'
if ($_.Value['Extend']) {
Add-Content -Path $UnattendPath -Value ' <Extend>true</Extend>'
}
else {
$Size = $_.Value['Size']
Add-Content -Path $UnattendPath -Value " <Size>$Size</Size>"
}
$Order = $_.Value['Order']
Add-Content -Path $UnattendPath -Value " <Order>$Order</Order>"
$Type = $_.Value['Type']
Add-Content -Path $UnattendPath -Value " <Type>$Type</Type>"
Add-Content -Path $UnattendPath -Value ' </CreatePartition>'
}
Add-Content -Path $UnattendPath -Value ' </CreatePartitions>'
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value ' <ModifyPartitions>'
$CreatePartitionInfo.GetEnumerator() | ForEach-Object {
Add-Content -Path $UnattendPath -Value ' <ModifyPartition>'
$Format = $_.Value['FileSystem']
if ($Format) {
Add-Content -Path $UnattendPath -Value " <Format>$Format</Format>"
}
$Order = $_.Value['Order']
Add-Content -Path $UnattendPath -Value " <Order>$Order</Order>"
Add-Content -Path $UnattendPath -Value " <PartitionID>$Order</PartitionID>"
$Type = $_.Value['Type']
Add-Content -Path $UnattendPath -Value " <TypeID>$Type</TypeID>"
Add-Content -Path $UnattendPath -Value ' </ModifyPartition>'
}
Add-Content -Path $UnattendPath -Value ' </ModifyPartitions>'
Add-Content -Path $UnattendPath -Value ' </Disk>'
Add-Content -Path $UnattendPath -Value ' </DiskConfiguration>'
Add-Content -Path $UnattendPath -Value ''
}
elseif (!$NotFormat) {
Add-Content -Path $UnattendPath -Value ' <DiskConfiguration>'
Add-Content -Path $UnattendPath -Value ' <Disk wcm:action="add">'
Add-Content -Path $UnattendPath -Value " <DiskID>$DiskId</DiskID>"
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value ' <ModifyPartitions>'
Add-Content -Path $UnattendPath -Value ' <ModifyPartition>'
Add-Content -Path $UnattendPath -Value ' <Format>NTFS</Format>'
Add-Content -Path $UnattendPath -Value " <PartitionID>$PartitionID</PartitionID>"
Add-Content -Path $UnattendPath -Value ' </ModifyPartition>'
Add-Content -Path $UnattendPath -Value ' </ModifyPartitions>'
Add-Content -Path $UnattendPath -Value ' </Disk>'
Add-Content -Path $UnattendPath -Value ' </DiskConfiguration>'
Add-Content -Path $UnattendPath -Value ''
}
Add-Content -Path $UnattendPath -Value ' <ImageInstall>'
Add-Content -Path $UnattendPath -Value ' <OSImage>'
if ($WindowsProductName) {
if ($Language -ieq 'zh-CN') {
$ImageName = 'Windows ' + $OsVersion + ' ' + $ProductInfo['CN']
}
else {
$ImageName = 'Windows ' + $OsVersion + ' ' + $ProductInfo['US']
}
Add-Content -Path $UnattendPath -Value ' <InstallFrom>'
Add-Content -Path $UnattendPath -Value ' <MetaData wcm:action="add">'
Add-Content -Path $UnattendPath -Value ' <Key>/IMAGE/NAME</Key>'
Add-Content -Path $UnattendPath -Value " <Value>$ImageName</Value>"
Add-Content -Path $UnattendPath -Value ' </MetaData>'
Add-Content -Path $UnattendPath -Value ' </InstallFrom>'
Add-Content -Path $UnattendPath -Value ''
}
Add-Content -Path $UnattendPath -Value ' <InstallTo>'
Add-Content -Path $UnattendPath -Value " <DiskID>$DiskId</DiskID>"
Add-Content -Path $UnattendPath -Value " <PartitionID>$PartitionID</PartitionID>"
Add-Content -Path $UnattendPath -Value ' </InstallTo>'
Add-Content -Path $UnattendPath -Value ' </OSImage>'
Add-Content -Path $UnattendPath -Value ' </ImageInstall>'
Add-Content -Path $UnattendPath -Value ' </component>'
Add-Content -Path $UnattendPath -Value ' </settings>'
Add-Content -Path $UnattendPath -Value ''
Add-Content -Path $UnattendPath -Value ' <settings pass="oobeSystem">'
Add-Content -Path $UnattendPath -Value (" <component name=`"Microsoft-Windows-Shell-Setup`"" `
+ " processorArchitecture=`"$ArchitectureName`" publicKeyToken=`"$Token`" language=`"neutral`"" `
+ " versionScope=`"nonSxS`" xmlns:wcm=`"http://schemas.microsoft.com/WMIConfig/2002/State`"" `
+ " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`">")
if ($FullName) {
Add-Content -Path $UnattendPath -Value ' <UserAccounts>'
Add-Content -Path $UnattendPath -Value ' <LocalAccounts>'
Add-Content -Path $UnattendPath -Value ' <LocalAccount wcm:action="add">'
Add-Content -Path $UnattendPath -Value " <DisplayName>$FullName</DisplayName>"
Add-Content -Path $UnattendPath -Value ' <Group>Administrators</Group>'
Add-Content -Path $UnattendPath -Value " <Name>$FullName</Name>"
Add-Content -Path $UnattendPath -Value ' </LocalAccount>'
Add-Content -Path $UnattendPath -Value ' </LocalAccounts>'
Add-Content -Path $UnattendPath -Value ' </UserAccounts>'
Add-Content -Path $UnattendPath -Value ''
}
Add-Content -Path $UnattendPath -Value ' <OOBE>'
Add-Content -Path $UnattendPath -Value ' <HideEULAPage>true</HideEULAPage>'
Add-Content -Path $UnattendPath -Value ' <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>'
Add-Content -Path $UnattendPath -Value ' <HideOnlineAccountScreens>true</HideOnlineAccountScreens>'
Add-Content -Path $UnattendPath -Value ' <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>'
Add-Content -Path $UnattendPath -Value ' <HideLocalAccountScreen>true</HideLocalAccountScreen>'
Add-Content -Path $UnattendPath -Value ' <ProtectYourPC>3</ProtectYourPC>'
Add-Content -Path $UnattendPath -Value ' </OOBE>'
Add-Content -Path $UnattendPath -Value ' </component>'
Add-Content -Path $UnattendPath -Value ' </settings>'
Add-Content -Path $UnattendPath -Value '</unattend>'
Write-Host -Object ('生成的应答文件位置: ' + $UnattendPath)
Write-Host -Object ''
Read-Host -Prompt '按回车键关闭此窗口'