mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-13 18:07:20 -06:00
Merge branch 'Naming' into Fluent
This commit is contained in:
+506
-31
@@ -70,7 +70,31 @@ When set to $true, enables adding WinPE drivers. By default copies drivers from
|
||||
When set to $true, will copy the provisioning package from the $FFUDevelopmentPath\PPKG folder to the Deployment partition of the USB drive. Default is $false.
|
||||
|
||||
.PARAMETER CopyUnattend
|
||||
When set to $true, will copy the $FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is $false.
|
||||
When set to $true, stages the selected architecture-specific unattend XML file as Unattend.xml on the deployment partition of the USB drive. Default is $false.
|
||||
|
||||
.PARAMETER DeviceNamingMode
|
||||
Controls how device naming is handled when unattend content is copied to USB media or injected into the FFU. Supported values are Legacy, None, Prompt, Template, Prefixes, and SerialComputerNames.
|
||||
|
||||
.PARAMETER DeviceNameTemplate
|
||||
Sets the device name used when DeviceNamingMode is Template. Supports a static name or the %serial% token when CopyUnattend is used.
|
||||
|
||||
.PARAMETER DeviceNamePrefixes
|
||||
Sets the prefixes used when DeviceNamingMode is Prefixes. Each entry becomes a line in prefixes.txt on the deployment media.
|
||||
|
||||
.PARAMETER DeviceNamePrefixesPath
|
||||
Path to the source prefixes file used for legacy copy or when DeviceNamePrefixes is not supplied. Default is $FFUDevelopmentPath\Unattend\prefixes.txt.
|
||||
|
||||
.PARAMETER DeviceNameSerialComputerNames
|
||||
Sets the CSV content used when DeviceNamingMode is SerialComputerNames. The CSV must include SerialNumber and ComputerName headers.
|
||||
|
||||
.PARAMETER DeviceNameSerialComputerNamesPath
|
||||
Path to the source CSV file used when DeviceNamingMode is SerialComputerNames and DeviceNameSerialComputerNames is not supplied. Default is $FFUDevelopmentPath\Unattend\SerialComputerNames.csv.
|
||||
|
||||
.PARAMETER UnattendX64FilePath
|
||||
Path to the x64 unattend XML source file. Default is $FFUDevelopmentPath\Unattend\unattend_x64.xml.
|
||||
|
||||
.PARAMETER UnattendArm64FilePath
|
||||
Path to the arm64 unattend XML source file. Default is $FFUDevelopmentPath\Unattend\unattend_arm64.xml.
|
||||
|
||||
.PARAMETER CreateDeploymentMedia
|
||||
When set to $true, this will create WinPE deployment media for use when deploying to a physical device.
|
||||
@@ -106,7 +130,7 @@ Prefix for the generated FFU file. Default is _FFU.
|
||||
Headers to use when downloading files. Not recommended to modify.
|
||||
|
||||
.PARAMETER InjectUnattend
|
||||
When set to $true and InstallApps is also $true, copies unattend_[arch].xml from $FFUDevelopmentPath\unattend to $FFUDevelopmentPath\Apps\Unattend\Unattend.xml so sysprep can use it inside the VM. Default is $false.
|
||||
When set to $true and InstallApps is also $true, stages the selected architecture-specific unattend XML file to $FFUDevelopmentPath\Apps\Unattend\Unattend.xml so sysprep can use it inside the VM. Default is $false.
|
||||
|
||||
.PARAMETER InstallApps
|
||||
When set to $true, the script will create an Apps.iso file from the $FFUDevelopmentPath\Apps folder. It will also create a VM, mount the Apps.iso, install the apps, sysprep, and capture the VM. When set to $false, the FFU is created from a VHDX file, and no VM is created.
|
||||
@@ -407,6 +431,15 @@ param(
|
||||
[bool]$AllowVHDXCaching,
|
||||
[bool]$CopyPPKG,
|
||||
[bool]$CopyUnattend,
|
||||
[ValidateSet('Legacy', 'None', 'Prompt', 'Template', 'Prefixes', 'SerialComputerNames')]
|
||||
[string]$DeviceNamingMode = 'Legacy',
|
||||
[string]$DeviceNameTemplate,
|
||||
[string[]]$DeviceNamePrefixes,
|
||||
[string]$DeviceNamePrefixesPath,
|
||||
[string[]]$DeviceNameSerialComputerNames,
|
||||
[string]$DeviceNameSerialComputerNamesPath,
|
||||
[string]$UnattendX64FilePath,
|
||||
[string]$UnattendArm64FilePath,
|
||||
[bool]$CopyAutopilot,
|
||||
[bool]$CompactOS = $true,
|
||||
[bool]$CleanupDeployISO = $true,
|
||||
@@ -447,7 +480,7 @@ param(
|
||||
[switch]$Cleanup
|
||||
)
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$version = '2603.2'
|
||||
$version = '2604.1'
|
||||
|
||||
# Remove any existing modules to avoid conflicts
|
||||
if (Get-Module -Name 'FFU.Common.Core' -ErrorAction SilentlyContinue) {
|
||||
@@ -505,6 +538,173 @@ if ($ConfigFile -and (Test-Path -Path $ConfigFile)) {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-UnattendSourcePath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$UnattendFolder,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WindowsArch,
|
||||
[string]$UnattendX64FilePath,
|
||||
[string]$UnattendArm64FilePath
|
||||
)
|
||||
|
||||
$resolvedArch = if ($WindowsArch -ieq 'arm64') { 'arm64' } else { 'x64' }
|
||||
$resolvedSourcePath = if ($resolvedArch -eq 'arm64') {
|
||||
if ([string]::IsNullOrWhiteSpace($UnattendArm64FilePath)) {
|
||||
Join-Path $UnattendFolder 'unattend_arm64.xml'
|
||||
}
|
||||
else {
|
||||
$UnattendArm64FilePath
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ([string]::IsNullOrWhiteSpace($UnattendX64FilePath)) {
|
||||
Join-Path $UnattendFolder 'unattend_x64.xml'
|
||||
}
|
||||
else {
|
||||
$UnattendX64FilePath
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog "Resolved unattend source path for ${resolvedArch}: $resolvedSourcePath"
|
||||
return $resolvedSourcePath
|
||||
}
|
||||
|
||||
function Initialize-UnattendComputerNamePath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[xml]$UnattendXml,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WindowsArch
|
||||
)
|
||||
|
||||
$unattendRoot = $UnattendXml.DocumentElement
|
||||
if (($null -eq $unattendRoot) -or ($unattendRoot.LocalName -ne 'unattend')) {
|
||||
throw 'Unattend XML is missing the unattend root element.'
|
||||
}
|
||||
|
||||
$unattendNamespace = $unattendRoot.NamespaceURI
|
||||
if ([string]::IsNullOrWhiteSpace($unattendNamespace)) {
|
||||
throw 'Unattend XML is missing the default unattend namespace.'
|
||||
}
|
||||
|
||||
$namespaceManager = New-Object System.Xml.XmlNamespaceManager($UnattendXml.NameTable)
|
||||
$namespaceManager.AddNamespace('un', $unattendNamespace)
|
||||
|
||||
$specializeSettings = $unattendRoot.SelectSingleNode("un:settings[@pass='specialize']", $namespaceManager)
|
||||
$createdSpecializeSettings = $false
|
||||
if ($null -eq $specializeSettings) {
|
||||
$specializeSettings = $UnattendXml.CreateElement('settings', $unattendNamespace)
|
||||
$null = $specializeSettings.SetAttribute('pass', 'specialize')
|
||||
$firstSettingsNode = $unattendRoot.SelectSingleNode('un:settings', $namespaceManager)
|
||||
if ($null -ne $firstSettingsNode) {
|
||||
$null = $unattendRoot.InsertBefore($specializeSettings, $firstSettingsNode)
|
||||
}
|
||||
else {
|
||||
$null = $unattendRoot.AppendChild($specializeSettings)
|
||||
}
|
||||
$createdSpecializeSettings = $true
|
||||
}
|
||||
|
||||
$shellSetupComponent = $specializeSettings.SelectSingleNode("un:component[@name='Microsoft-Windows-Shell-Setup']", $namespaceManager)
|
||||
$createdShellSetupComponent = $false
|
||||
if ($null -eq $shellSetupComponent) {
|
||||
$processorArchitecture = if ($WindowsArch -ieq 'arm64') { 'arm64' } else { 'amd64' }
|
||||
$shellSetupComponent = $UnattendXml.CreateElement('component', $unattendNamespace)
|
||||
$null = $shellSetupComponent.SetAttribute('name', 'Microsoft-Windows-Shell-Setup')
|
||||
$null = $shellSetupComponent.SetAttribute('processorArchitecture', $processorArchitecture)
|
||||
$null = $shellSetupComponent.SetAttribute('publicKeyToken', '31bf3856ad364e35')
|
||||
$null = $shellSetupComponent.SetAttribute('language', 'neutral')
|
||||
$null = $shellSetupComponent.SetAttribute('versionScope', 'nonSxS')
|
||||
$null = $shellSetupComponent.SetAttribute('xmlns:wcm', 'http://www.w3.org/2000/xmlns/', 'http://schemas.microsoft.com/WMIConfig/2002/State')
|
||||
$null = $shellSetupComponent.SetAttribute('xmlns:xsi', 'http://www.w3.org/2000/xmlns/', 'http://www.w3.org/2001/XMLSchema-instance')
|
||||
|
||||
$firstComponentNode = $specializeSettings.SelectSingleNode('un:component', $namespaceManager)
|
||||
if ($null -ne $firstComponentNode) {
|
||||
$null = $specializeSettings.InsertBefore($shellSetupComponent, $firstComponentNode)
|
||||
}
|
||||
else {
|
||||
$null = $specializeSettings.AppendChild($shellSetupComponent)
|
||||
}
|
||||
$createdShellSetupComponent = $true
|
||||
}
|
||||
|
||||
$computerNameElement = $shellSetupComponent.SelectSingleNode('un:ComputerName', $namespaceManager)
|
||||
$createdComputerNameElement = $false
|
||||
if ($null -eq $computerNameElement) {
|
||||
$computerNameElement = $UnattendXml.CreateElement('ComputerName', $unattendNamespace)
|
||||
$null = $shellSetupComponent.AppendChild($computerNameElement)
|
||||
$createdComputerNameElement = $true
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
ComputerNameElement = $computerNameElement
|
||||
CreatedSpecializeSettings = $createdSpecializeSettings
|
||||
CreatedShellSetupComponent = $createdShellSetupComponent
|
||||
CreatedComputerNameElement = $createdComputerNameElement
|
||||
}
|
||||
}
|
||||
|
||||
function Save-StagedUnattendFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SourcePath,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$DestinationPath,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('Legacy', 'None', 'Prompt', 'Template', 'Prefixes', 'SerialComputerNames')]
|
||||
[string]$DeviceNamingMode,
|
||||
[string]$DeviceNameTemplate,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WindowsArch,
|
||||
[bool]$LegacyPrefixesWillBeStaged = $false
|
||||
)
|
||||
|
||||
if ($DeviceNamingMode -eq 'None') {
|
||||
Copy-Item -Path $SourcePath -Destination $DestinationPath -Force | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
[xml]$unattendXml = Get-Content -Path $SourcePath
|
||||
$computerNamePath = Initialize-UnattendComputerNamePath -UnattendXml $unattendXml -WindowsArch $WindowsArch
|
||||
|
||||
if ($computerNamePath.CreatedSpecializeSettings -or $computerNamePath.CreatedShellSetupComponent -or $computerNamePath.CreatedComputerNameElement) {
|
||||
$createdParts = @()
|
||||
if ($computerNamePath.CreatedSpecializeSettings) {
|
||||
$createdParts += 'specialize settings'
|
||||
}
|
||||
if ($computerNamePath.CreatedShellSetupComponent) {
|
||||
$createdParts += 'Microsoft-Windows-Shell-Setup component'
|
||||
}
|
||||
if ($computerNamePath.CreatedComputerNameElement) {
|
||||
$createdParts += 'ComputerName element'
|
||||
}
|
||||
WriteLog "Created $($createdParts -join ', ') while staging unattend file $DestinationPath"
|
||||
}
|
||||
|
||||
if ($DeviceNamingMode -eq 'Prompt') {
|
||||
$computerNamePath.ComputerNameElement.InnerText = 'MyComputer'
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'Template') {
|
||||
$computerNamePath.ComputerNameElement.InnerText = $DeviceNameTemplate
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'Prefixes') {
|
||||
if ($computerNamePath.CreatedComputerNameElement) {
|
||||
$computerNamePath.ComputerNameElement.InnerText = '*'
|
||||
}
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'SerialComputerNames') {
|
||||
if ($computerNamePath.CreatedComputerNameElement) {
|
||||
$computerNamePath.ComputerNameElement.InnerText = '*'
|
||||
}
|
||||
}
|
||||
elseif (($DeviceNamingMode -eq 'Legacy') -and $computerNamePath.CreatedComputerNameElement) {
|
||||
$computerNamePath.ComputerNameElement.InnerText = if ($LegacyPrefixesWillBeStaged) { '*' } else { 'MyComputer' }
|
||||
}
|
||||
|
||||
$unattendXml.Save($DestinationPath)
|
||||
}
|
||||
|
||||
$vmSwitchWasExplicitlyBound = $PSBoundParameters.ContainsKey('VMSwitchName')
|
||||
$enableVmNetworkingWasExplicitlyBound = $PSBoundParameters.ContainsKey('EnableVMNetworking')
|
||||
if (-not $EnableVMNetworking -and $vmSwitchWasExplicitlyBound -and -not $enableVmNetworkingWasExplicitlyBound) {
|
||||
@@ -512,6 +712,101 @@ if (-not $EnableVMNetworking -and $vmSwitchWasExplicitlyBound -and -not $enableV
|
||||
WriteLog 'EnableVMNetworking not explicitly set. Enabling VM networking because -VMSwitchName was supplied on the command line.'
|
||||
}
|
||||
|
||||
$normalizedDeviceNameTemplate = if ($null -ne $DeviceNameTemplate) { $DeviceNameTemplate.Trim() } else { $null }
|
||||
$effectiveDeviceNamePrefixes = @($DeviceNamePrefixes | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||
$resolvedDeviceNamePrefixesPath = if ([string]::IsNullOrWhiteSpace($DeviceNamePrefixesPath)) {
|
||||
Join-Path (Join-Path $FFUDevelopmentPath 'Unattend') 'prefixes.txt'
|
||||
}
|
||||
else {
|
||||
$DeviceNamePrefixesPath
|
||||
}
|
||||
$effectiveDeviceNameSerialComputerNames = @($DeviceNameSerialComputerNames | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||
$resolvedDeviceNameSerialComputerNamesPath = if ([string]::IsNullOrWhiteSpace($DeviceNameSerialComputerNamesPath)) {
|
||||
Join-Path (Join-Path $FFUDevelopmentPath 'Unattend') 'SerialComputerNames.csv'
|
||||
}
|
||||
else {
|
||||
$DeviceNameSerialComputerNamesPath
|
||||
}
|
||||
|
||||
if (($DeviceNamingMode -eq 'Prefixes') -and ($effectiveDeviceNamePrefixes.Count -eq 0) -and (Test-Path -Path $resolvedDeviceNamePrefixesPath -PathType Leaf)) {
|
||||
$effectiveDeviceNamePrefixes = @(Get-Content -Path $resolvedDeviceNamePrefixesPath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||
WriteLog "Loaded device name prefixes from $resolvedDeviceNamePrefixesPath"
|
||||
}
|
||||
|
||||
if (($DeviceNamingMode -eq 'SerialComputerNames') -and ($effectiveDeviceNameSerialComputerNames.Count -eq 0) -and (Test-Path -Path $resolvedDeviceNameSerialComputerNamesPath -PathType Leaf)) {
|
||||
$effectiveDeviceNameSerialComputerNames = @(Get-Content -Path $resolvedDeviceNameSerialComputerNamesPath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||
WriteLog "Loaded serial computer-name mappings from $resolvedDeviceNameSerialComputerNamesPath"
|
||||
}
|
||||
|
||||
if ($CopyUnattend -and $InjectUnattend) {
|
||||
throw 'CopyUnattend and InjectUnattend cannot both be set to `$true. Select only one unattend delivery method.'
|
||||
}
|
||||
|
||||
if ($DeviceNamingMode -eq 'Template') {
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedDeviceNameTemplate)) {
|
||||
throw 'DeviceNamingMode Template requires DeviceNameTemplate.'
|
||||
}
|
||||
|
||||
$templateWithoutSupportedVariables = $normalizedDeviceNameTemplate -replace '(?i)%serial%', ''
|
||||
if ($templateWithoutSupportedVariables -match '%') {
|
||||
throw 'Only the %serial% device name variable is supported.'
|
||||
}
|
||||
|
||||
if (-not ($CopyUnattend -or $InjectUnattend)) {
|
||||
throw 'DeviceNamingMode Template requires either CopyUnattend or InjectUnattend.'
|
||||
}
|
||||
|
||||
if ($InjectUnattend -and (-not $CopyUnattend) -and $normalizedDeviceNameTemplate -match '(?i)%serial%') {
|
||||
throw 'The %serial% device name variable is only supported when CopyUnattend is used.'
|
||||
}
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'Prompt') {
|
||||
if (-not $CopyUnattend) {
|
||||
throw 'DeviceNamingMode Prompt requires CopyUnattend. Prompt-based naming is not supported with InjectUnattend.'
|
||||
}
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'Prefixes') {
|
||||
if (-not $CopyUnattend) {
|
||||
throw 'DeviceNamingMode Prefixes requires CopyUnattend. Prefix-based naming is not supported with InjectUnattend.'
|
||||
}
|
||||
|
||||
if ($effectiveDeviceNamePrefixes.Count -eq 0) {
|
||||
throw 'DeviceNamingMode Prefixes requires at least one DeviceNamePrefixes entry or a valid DeviceNamePrefixesPath.'
|
||||
}
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'SerialComputerNames') {
|
||||
if (-not $CopyUnattend) {
|
||||
throw 'DeviceNamingMode SerialComputerNames requires CopyUnattend. Serial-to-computer-name mapping is not supported with InjectUnattend.'
|
||||
}
|
||||
|
||||
if ($effectiveDeviceNameSerialComputerNames.Count -eq 0) {
|
||||
throw 'DeviceNamingMode SerialComputerNames requires DeviceNameSerialComputerNames content or a valid DeviceNameSerialComputerNamesPath.'
|
||||
}
|
||||
|
||||
try {
|
||||
$serialComputerNameMappings = @($effectiveDeviceNameSerialComputerNames | ConvertFrom-Csv -ErrorAction Stop)
|
||||
}
|
||||
catch {
|
||||
throw "DeviceNamingMode SerialComputerNames requires valid CSV content with SerialNumber and ComputerName headers. $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
if ($serialComputerNameMappings.Count -eq 0) {
|
||||
throw 'DeviceNamingMode SerialComputerNames requires at least one CSV data row.'
|
||||
}
|
||||
|
||||
$serialComputerNameHeaders = @($serialComputerNameMappings[0].PSObject.Properties.Name)
|
||||
if ((-not ($serialComputerNameHeaders -contains 'SerialNumber')) -or (-not ($serialComputerNameHeaders -contains 'ComputerName'))) {
|
||||
throw 'DeviceNamingMode SerialComputerNames requires SerialNumber and ComputerName headers.'
|
||||
}
|
||||
|
||||
$validSerialComputerNameMappings = @($serialComputerNameMappings | Where-Object {
|
||||
-not [string]::IsNullOrWhiteSpace([string]$_.SerialNumber) -and -not [string]::IsNullOrWhiteSpace([string]$_.ComputerName)
|
||||
})
|
||||
if ($validSerialComputerNameMappings.Count -eq 0) {
|
||||
throw 'DeviceNamingMode SerialComputerNames requires at least one row with both SerialNumber and ComputerName values.'
|
||||
}
|
||||
}
|
||||
|
||||
# Validate that the selected Windows SKU is compatible with the chosen Windows release and ensure an ISO is provided for unsupported releases
|
||||
$clientSKUs = @(
|
||||
'Home',
|
||||
@@ -705,6 +1000,8 @@ if (-not $EdgePath) { $EdgePath = "$AppsPath\Edge" }
|
||||
if (-not $DriversFolder) { $DriversFolder = "$FFUDevelopmentPath\Drivers" }
|
||||
if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" }
|
||||
if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" }
|
||||
if ([string]::IsNullOrWhiteSpace($UnattendX64FilePath)) { $UnattendX64FilePath = Join-Path $UnattendFolder 'unattend_x64.xml' }
|
||||
if ([string]::IsNullOrWhiteSpace($UnattendArm64FilePath)) { $UnattendArm64FilePath = Join-Path $UnattendFolder 'unattend_arm64.xml' }
|
||||
if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" }
|
||||
if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" }
|
||||
if (-not $VHDXCacheFolder) { $VHDXCacheFolder = "$FFUDevelopmentPath\VHDXCache" }
|
||||
@@ -4184,6 +4481,164 @@ Function New-DeploymentUSB {
|
||||
Import-Module "$($using:PSScriptRoot)\FFU.Common" -Force
|
||||
Set-CommonCoreLogPath -Path $using:LogFile
|
||||
|
||||
function Get-LocalUnattendSourcePath {
|
||||
param(
|
||||
[string]$UnattendFolder,
|
||||
[string]$WindowsArch,
|
||||
[string]$UnattendX64FilePath,
|
||||
[string]$UnattendArm64FilePath
|
||||
)
|
||||
|
||||
$resolvedArch = if ($WindowsArch -ieq 'arm64') { 'arm64' } else { 'x64' }
|
||||
$resolvedSourcePath = if ($resolvedArch -eq 'arm64') {
|
||||
if ([string]::IsNullOrWhiteSpace($UnattendArm64FilePath)) {
|
||||
Join-Path $UnattendFolder 'unattend_arm64.xml'
|
||||
}
|
||||
else {
|
||||
$UnattendArm64FilePath
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ([string]::IsNullOrWhiteSpace($UnattendX64FilePath)) {
|
||||
Join-Path $UnattendFolder 'unattend_x64.xml'
|
||||
}
|
||||
else {
|
||||
$UnattendX64FilePath
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog "Resolved unattend source path for ${resolvedArch}: $resolvedSourcePath"
|
||||
return $resolvedSourcePath
|
||||
}
|
||||
|
||||
function Initialize-UnattendComputerNamePath {
|
||||
param(
|
||||
[xml]$UnattendXml,
|
||||
[string]$WindowsArch
|
||||
)
|
||||
|
||||
$unattendRoot = $UnattendXml.DocumentElement
|
||||
if (($null -eq $unattendRoot) -or ($unattendRoot.LocalName -ne 'unattend')) {
|
||||
throw 'Unattend XML is missing the unattend root element.'
|
||||
}
|
||||
|
||||
$unattendNamespace = $unattendRoot.NamespaceURI
|
||||
if ([string]::IsNullOrWhiteSpace($unattendNamespace)) {
|
||||
throw 'Unattend XML is missing the default unattend namespace.'
|
||||
}
|
||||
|
||||
$namespaceManager = New-Object System.Xml.XmlNamespaceManager($UnattendXml.NameTable)
|
||||
$namespaceManager.AddNamespace('un', $unattendNamespace)
|
||||
|
||||
$specializeSettings = $unattendRoot.SelectSingleNode("un:settings[@pass='specialize']", $namespaceManager)
|
||||
$createdSpecializeSettings = $false
|
||||
if ($null -eq $specializeSettings) {
|
||||
$specializeSettings = $UnattendXml.CreateElement('settings', $unattendNamespace)
|
||||
$null = $specializeSettings.SetAttribute('pass', 'specialize')
|
||||
$firstSettingsNode = $unattendRoot.SelectSingleNode('un:settings', $namespaceManager)
|
||||
if ($null -ne $firstSettingsNode) {
|
||||
$null = $unattendRoot.InsertBefore($specializeSettings, $firstSettingsNode)
|
||||
}
|
||||
else {
|
||||
$null = $unattendRoot.AppendChild($specializeSettings)
|
||||
}
|
||||
$createdSpecializeSettings = $true
|
||||
}
|
||||
|
||||
$shellSetupComponent = $specializeSettings.SelectSingleNode("un:component[@name='Microsoft-Windows-Shell-Setup']", $namespaceManager)
|
||||
$createdShellSetupComponent = $false
|
||||
if ($null -eq $shellSetupComponent) {
|
||||
$processorArchitecture = if ($WindowsArch -ieq 'arm64') { 'arm64' } else { 'amd64' }
|
||||
$shellSetupComponent = $UnattendXml.CreateElement('component', $unattendNamespace)
|
||||
$null = $shellSetupComponent.SetAttribute('name', 'Microsoft-Windows-Shell-Setup')
|
||||
$null = $shellSetupComponent.SetAttribute('processorArchitecture', $processorArchitecture)
|
||||
$null = $shellSetupComponent.SetAttribute('publicKeyToken', '31bf3856ad364e35')
|
||||
$null = $shellSetupComponent.SetAttribute('language', 'neutral')
|
||||
$null = $shellSetupComponent.SetAttribute('versionScope', 'nonSxS')
|
||||
$null = $shellSetupComponent.SetAttribute('xmlns:wcm', 'http://www.w3.org/2000/xmlns/', 'http://schemas.microsoft.com/WMIConfig/2002/State')
|
||||
$null = $shellSetupComponent.SetAttribute('xmlns:xsi', 'http://www.w3.org/2000/xmlns/', 'http://www.w3.org/2001/XMLSchema-instance')
|
||||
|
||||
$firstComponentNode = $specializeSettings.SelectSingleNode('un:component', $namespaceManager)
|
||||
if ($null -ne $firstComponentNode) {
|
||||
$null = $specializeSettings.InsertBefore($shellSetupComponent, $firstComponentNode)
|
||||
}
|
||||
else {
|
||||
$null = $specializeSettings.AppendChild($shellSetupComponent)
|
||||
}
|
||||
$createdShellSetupComponent = $true
|
||||
}
|
||||
|
||||
$computerNameElement = $shellSetupComponent.SelectSingleNode('un:ComputerName', $namespaceManager)
|
||||
$createdComputerNameElement = $false
|
||||
if ($null -eq $computerNameElement) {
|
||||
$computerNameElement = $UnattendXml.CreateElement('ComputerName', $unattendNamespace)
|
||||
$null = $shellSetupComponent.AppendChild($computerNameElement)
|
||||
$createdComputerNameElement = $true
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
ComputerNameElement = $computerNameElement
|
||||
CreatedSpecializeSettings = $createdSpecializeSettings
|
||||
CreatedShellSetupComponent = $createdShellSetupComponent
|
||||
CreatedComputerNameElement = $createdComputerNameElement
|
||||
}
|
||||
}
|
||||
|
||||
function Save-LocalStagedUnattendFile {
|
||||
param(
|
||||
[string]$SourcePath,
|
||||
[string]$DestinationPath,
|
||||
[string]$DeviceNamingMode,
|
||||
[string]$DeviceNameTemplate,
|
||||
[string]$WindowsArch,
|
||||
[bool]$LegacyPrefixesWillBeStaged = $false
|
||||
)
|
||||
|
||||
if ($DeviceNamingMode -eq 'None') {
|
||||
Copy-Item -Path $SourcePath -Destination $DestinationPath -Force | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
[xml]$unattendXml = Get-Content -Path $SourcePath
|
||||
$computerNamePath = Initialize-UnattendComputerNamePath -UnattendXml $unattendXml -WindowsArch $WindowsArch
|
||||
|
||||
if ($computerNamePath.CreatedSpecializeSettings -or $computerNamePath.CreatedShellSetupComponent -or $computerNamePath.CreatedComputerNameElement) {
|
||||
$createdParts = @()
|
||||
if ($computerNamePath.CreatedSpecializeSettings) {
|
||||
$createdParts += 'specialize settings'
|
||||
}
|
||||
if ($computerNamePath.CreatedShellSetupComponent) {
|
||||
$createdParts += 'Microsoft-Windows-Shell-Setup component'
|
||||
}
|
||||
if ($computerNamePath.CreatedComputerNameElement) {
|
||||
$createdParts += 'ComputerName element'
|
||||
}
|
||||
WriteLog "Created $($createdParts -join ', ') while staging unattend file $DestinationPath"
|
||||
}
|
||||
|
||||
if ($DeviceNamingMode -eq 'Prompt') {
|
||||
$computerNamePath.ComputerNameElement.InnerText = 'MyComputer'
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'Template') {
|
||||
$computerNamePath.ComputerNameElement.InnerText = $DeviceNameTemplate
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'Prefixes') {
|
||||
if ($computerNamePath.CreatedComputerNameElement) {
|
||||
$computerNamePath.ComputerNameElement.InnerText = '*'
|
||||
}
|
||||
}
|
||||
elseif ($DeviceNamingMode -eq 'SerialComputerNames') {
|
||||
if ($computerNamePath.CreatedComputerNameElement) {
|
||||
$computerNamePath.ComputerNameElement.InnerText = '*'
|
||||
}
|
||||
}
|
||||
elseif (($DeviceNamingMode -eq 'Legacy') -and $computerNamePath.CreatedComputerNameElement) {
|
||||
$computerNamePath.ComputerNameElement.InnerText = if ($LegacyPrefixesWillBeStaged) { '*' } else { 'MyComputer' }
|
||||
}
|
||||
|
||||
$unattendXml.Save($DestinationPath)
|
||||
}
|
||||
|
||||
$DiskNumber = $USBDrive.DeviceID.Replace("\\.\PHYSICALDRIVE", "")
|
||||
WriteLog "Thread $([System.Threading.Thread]::CurrentThread.ManagedThreadId) processing DiskNumber $DiskNumber ($($USBDrive.Model))"
|
||||
|
||||
@@ -4244,15 +4699,20 @@ Function New-DeploymentUSB {
|
||||
$UnattendPathOnUSB = Join-Path $DeployPartitionDriveLetter "Unattend"
|
||||
WriteLog "Copying unattend file to $UnattendPathOnUSB"
|
||||
New-Item -Path $UnattendPathOnUSB -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
|
||||
if ($using:WindowsArch -eq 'x64') {
|
||||
Copy-Item -Path (Join-Path $using:UnattendFolder 'unattend_x64.xml') -Destination (Join-Path $UnattendPathOnUSB 'Unattend.xml') -Force | Out-Null
|
||||
$unattendSource = Get-LocalUnattendSourcePath -UnattendFolder $using:UnattendFolder -WindowsArch $using:WindowsArch -UnattendX64FilePath $using:UnattendX64FilePath -UnattendArm64FilePath $using:UnattendArm64FilePath
|
||||
$legacyPrefixesWillBeStaged = ($using:DeviceNamingMode -eq 'Legacy') -and (Test-Path -Path $using:resolvedDeviceNamePrefixesPath -PathType Leaf)
|
||||
Save-LocalStagedUnattendFile -SourcePath $unattendSource -DestinationPath (Join-Path $UnattendPathOnUSB 'Unattend.xml') -DeviceNamingMode $using:DeviceNamingMode -DeviceNameTemplate $using:normalizedDeviceNameTemplate -WindowsArch $using:WindowsArch -LegacyPrefixesWillBeStaged $legacyPrefixesWillBeStaged
|
||||
if ($using:DeviceNamingMode -eq 'Prefixes') {
|
||||
WriteLog "Writing prefixes.txt file to $UnattendPathOnUSB"
|
||||
$using:effectiveDeviceNamePrefixes | Set-Content -Path (Join-Path $UnattendPathOnUSB 'prefixes.txt') -Encoding UTF8
|
||||
}
|
||||
elseif ($using:WindowsArch -eq 'arm64') {
|
||||
Copy-Item -Path (Join-Path $using:UnattendFolder 'unattend_arm64.xml') -Destination (Join-Path $UnattendPathOnUSB 'Unattend.xml') -Force | Out-Null
|
||||
elseif ($using:DeviceNamingMode -eq 'SerialComputerNames') {
|
||||
WriteLog "Writing SerialComputerNames.csv file to $UnattendPathOnUSB"
|
||||
$using:effectiveDeviceNameSerialComputerNames | Set-Content -Path (Join-Path $UnattendPathOnUSB 'SerialComputerNames.csv') -Encoding UTF8
|
||||
}
|
||||
if (Test-Path (Join-Path $using:UnattendFolder 'prefixes.txt')) {
|
||||
elseif ($legacyPrefixesWillBeStaged) {
|
||||
WriteLog "Copying prefixes.txt file to $UnattendPathOnUSB"
|
||||
Copy-Item -Path (Join-Path $using:UnattendFolder 'prefixes.txt') -Destination (Join-Path $UnattendPathOnUSB 'prefixes.txt') -Force | Out-Null
|
||||
Copy-Item -Path $using:resolvedDeviceNamePrefixesPath -Destination (Join-Path $UnattendPathOnUSB 'prefixes.txt') -Force | Out-Null
|
||||
}
|
||||
WriteLog 'Copy completed'
|
||||
}
|
||||
@@ -5506,18 +5966,32 @@ if ($CopyAutopilot) {
|
||||
WriteLog 'Autopilot validation complete'
|
||||
}
|
||||
|
||||
#Validate Unattend folder
|
||||
if ($CopyUnattend) {
|
||||
# Validate unattend source file
|
||||
if ($CopyUnattend -or $InjectUnattend) {
|
||||
WriteLog 'Doing Unattend validation'
|
||||
if (!(Test-Path -Path $UnattendFolder)) {
|
||||
WriteLog "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing"
|
||||
throw "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing"
|
||||
$selectedUnattendMode = if ($CopyUnattend) { 'CopyUnattend' } else { 'InjectUnattend' }
|
||||
$unattendSourcePath = Get-UnattendSourcePath -UnattendFolder $UnattendFolder -WindowsArch $WindowsArch -UnattendX64FilePath $UnattendX64FilePath -UnattendArm64FilePath $UnattendArm64FilePath
|
||||
if (!(Test-Path -Path $unattendSourcePath -PathType Leaf)) {
|
||||
WriteLog "-$selectedUnattendMode is set to `$true, but the selected unattend XML file is missing: $unattendSourcePath"
|
||||
throw "-$selectedUnattendMode is set to `$true, but the selected unattend XML file is missing: $unattendSourcePath"
|
||||
}
|
||||
#Check for .XML file
|
||||
if (!(Get-ChildItem -Path $UnattendFolder -Filter unattend_*.xml)) {
|
||||
WriteLog "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing a .XML file"
|
||||
throw "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing a .XML file"
|
||||
|
||||
$selectedUnattendFile = Get-Item -Path $unattendSourcePath -ErrorAction SilentlyContinue
|
||||
if (($null -eq $selectedUnattendFile) -or ($selectedUnattendFile.Length -le 0)) {
|
||||
WriteLog "-$selectedUnattendMode is set to `$true, but the selected unattend XML file is empty: $unattendSourcePath"
|
||||
throw "-$selectedUnattendMode is set to `$true, but the selected unattend XML file is empty: $unattendSourcePath"
|
||||
}
|
||||
|
||||
if ($DeviceNamingMode -ne 'None') {
|
||||
try {
|
||||
[xml]$validationUnattendXml = Get-Content -Path $unattendSourcePath
|
||||
$null = Initialize-UnattendComputerNamePath -UnattendXml $validationUnattendXml -WindowsArch $WindowsArch
|
||||
}
|
||||
catch {
|
||||
throw "DeviceNamingMode $DeviceNamingMode requires a valid specialize/Microsoft-Windows-Shell-Setup/ComputerName path in $unattendSourcePath. $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog 'Unattend validation complete'
|
||||
}
|
||||
|
||||
@@ -6418,9 +6892,7 @@ if ($InstallApps) {
|
||||
#Create Apps ISO
|
||||
# Inject Unattend.xml into Apps if requested and applicable
|
||||
if ($InstallApps -and $InjectUnattend) {
|
||||
# Determine source unattend.xml based on architecture
|
||||
$archSuffix = if ($WindowsArch -ieq 'arm64') { 'arm64' } else { 'x64' }
|
||||
$unattendSource = Join-Path $UnattendFolder "unattend_$archSuffix.xml"
|
||||
$unattendSource = Get-UnattendSourcePath -UnattendFolder $UnattendFolder -WindowsArch $WindowsArch -UnattendX64FilePath $UnattendX64FilePath -UnattendArm64FilePath $UnattendArm64FilePath
|
||||
|
||||
# Ensure target folder exists under Apps
|
||||
$targetFolder = Join-Path $AppsPath 'Unattend'
|
||||
@@ -6431,7 +6903,7 @@ if ($InstallApps) {
|
||||
# Copy if source exists; otherwise log and skip
|
||||
if (Test-Path -Path $unattendSource -PathType Leaf) {
|
||||
$destination = Join-Path $targetFolder 'Unattend.xml'
|
||||
Copy-Item -Path $unattendSource -Destination $destination -Force | Out-Null
|
||||
Save-StagedUnattendFile -SourcePath $unattendSource -DestinationPath $destination -DeviceNamingMode $DeviceNamingMode -DeviceNameTemplate $normalizedDeviceNameTemplate -WindowsArch $WindowsArch
|
||||
WriteLog "Injected unattend file into Apps: $unattendSource -> $destination"
|
||||
}
|
||||
else {
|
||||
@@ -6998,10 +7470,8 @@ try {
|
||||
if ($WindowsRelease -eq 2016 -and $installationType -eq "Server") {
|
||||
WriteLog 'WindowsRelease is 2016, adding SSU first'
|
||||
WriteLog "Adding SSU to $WindowsPartition"
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
|
||||
# Commenting out -preventpending as it causes an issue with the SSU being applied
|
||||
# Seems to be because of the registry being mounted per dism.log
|
||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
|
||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Image:$WindowsPartition /Add-Package /PackagePath:$SSUFilePath" | Out-Null
|
||||
WriteLog "SSU added to $WindowsPartition"
|
||||
# WriteLog "Removing $SSUFilePath"
|
||||
# Remove-Item -Path $SSUFilePath -Force | Out-Null
|
||||
@@ -7010,7 +7480,8 @@ try {
|
||||
if ($WindowsRelease -in 2016, 2019, 2021 -and $isLTSC) {
|
||||
WriteLog "WindowsRelease is $WindowsRelease and is $WindowsSKU, adding SSU first"
|
||||
WriteLog "Adding SSU to $WindowsPartition"
|
||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
|
||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Image:$WindowsPartition /Add-Package /PackagePath:$SSUFilePath" | Out-Null
|
||||
WriteLog "SSU added to $WindowsPartition"
|
||||
# WriteLog "Removing $SSUFilePath"
|
||||
# Remove-Item -Path $SSUFilePath -Force | Out-Null
|
||||
@@ -7023,23 +7494,27 @@ try {
|
||||
}
|
||||
else {
|
||||
WriteLog "Adding $CUPath to $WindowsPartition"
|
||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $CUPath | Out-Null
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $CUPath | Out-Null
|
||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Image:$WindowsPartition /Add-Package /PackagePath:$CUPath" | Out-Null
|
||||
WriteLog "$CUPath added to $WindowsPartition"
|
||||
}
|
||||
}
|
||||
if ($UpdatePreviewCU) {
|
||||
WriteLog "Adding $CUPPath to $WindowsPartition"
|
||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $CUPPath | Out-Null
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $CUPPath | Out-Null
|
||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Image:$WindowsPartition /Add-Package /PackagePath:$CUPPath" | Out-Null
|
||||
WriteLog "$CUPPath added to $WindowsPartition"
|
||||
}
|
||||
if ($UpdateLatestNet) {
|
||||
WriteLog "Adding $NETPath to $WindowsPartition"
|
||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $NETPath | Out-Null
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $NETPath | Out-Null
|
||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Image:$WindowsPartition /Add-Package /PackagePath:$NETPath" | Out-Null
|
||||
WriteLog "$NETPath added to $WindowsPartition"
|
||||
}
|
||||
if ($UpdateLatestMicrocode -and $WindowsRelease -in 2016, 2019) {
|
||||
WriteLog "Adding $MicrocodePath to $WindowsPartition"
|
||||
Add-WindowsPackage -Path $WindowsPartition -PackagePath $MicrocodePath | Out-Null
|
||||
# Add-WindowsPackage -Path $WindowsPartition -PackagePath $MicrocodePath | Out-Null
|
||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Image:$WindowsPartition /Add-Package /PackagePath:$MicrocodePath" | Out-Null
|
||||
WriteLog "$MicrocodePath added to $WindowsPartition"
|
||||
}
|
||||
WriteLog "KBs added to $WindowsPartition"
|
||||
|
||||
@@ -46,7 +46,8 @@ $script:uiState = [PSCustomObject]@{
|
||||
logStreamReader = $null;
|
||||
pollTimer = $null;
|
||||
currentBuildProcess = $null;
|
||||
lastConfigFilePath = $null
|
||||
lastConfigFilePath = $null;
|
||||
loadedDeviceNamingMode = $null
|
||||
};
|
||||
Flags = @{
|
||||
installAppsForcedByUpdates = $false;
|
||||
@@ -56,7 +57,9 @@ $script:uiState = [PSCustomObject]@{
|
||||
lastSortAscending = $true;
|
||||
isBuilding = $false;
|
||||
isCleanupRunning = $false;
|
||||
isFluentSupported = $false
|
||||
isFluentSupported = $false;
|
||||
deviceNamingModeWasExplicitlyChanged = $false;
|
||||
suppressDeviceNamingChangeTracking = $false
|
||||
};
|
||||
Defaults = @{};
|
||||
LogFilePath = "$FFUDevelopmentPath\FFUDevelopment_UI.log"
|
||||
@@ -432,6 +435,116 @@ $script:uiState.Controls.btnRun.Add_Click({
|
||||
return
|
||||
}
|
||||
|
||||
if ($config.CopyUnattend -and $config.InjectUnattend) {
|
||||
[System.Windows.MessageBox]::Show("Copy Unattend.xml and Inject Unattend.xml cannot both be selected. Choose only one unattend delivery method.", "Unattend Selection Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: choose only one unattend delivery method."
|
||||
return
|
||||
}
|
||||
|
||||
if ($config.CopyUnattend -or $config.InjectUnattend) {
|
||||
$selectedUnattendArch = if ($config.WindowsArch -ieq 'arm64') { 'arm64' } else { 'x64' }
|
||||
$selectedUnattendSourcePath = if ($selectedUnattendArch -eq 'arm64') {
|
||||
[string]$config.UnattendArm64FilePath
|
||||
}
|
||||
else {
|
||||
[string]$config.UnattendX64FilePath
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($selectedUnattendSourcePath)) {
|
||||
[System.Windows.MessageBox]::Show("Select a valid $selectedUnattendArch unattend XML file before using Copy Unattend.xml or Inject Unattend.xml.", "Unattend File Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: unattend file path required."
|
||||
return
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $selectedUnattendSourcePath -PathType Leaf)) {
|
||||
[System.Windows.MessageBox]::Show("The selected $selectedUnattendArch unattend XML file was not found:`n$selectedUnattendSourcePath", "Unattend File Missing", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: unattend file missing."
|
||||
return
|
||||
}
|
||||
|
||||
$selectedUnattendFileInfo = Get-Item -Path $selectedUnattendSourcePath -ErrorAction SilentlyContinue
|
||||
if (($null -eq $selectedUnattendFileInfo) -or ($selectedUnattendFileInfo.Length -le 0)) {
|
||||
[System.Windows.MessageBox]::Show("The selected $selectedUnattendArch unattend XML file is empty:`n$selectedUnattendSourcePath", "Unattend File Empty", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: unattend file empty."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ($config.DeviceNamingMode -eq 'Prompt') {
|
||||
if (-not $config.CopyUnattend) {
|
||||
[System.Windows.MessageBox]::Show("Select Copy Unattend.xml before using 'Prompt for Device Name'.", "Copy Unattend Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: prompt naming requires Copy Unattend.xml."
|
||||
return
|
||||
}
|
||||
}
|
||||
elseif ($config.DeviceNamingMode -eq 'Template') {
|
||||
if ([string]::IsNullOrWhiteSpace([string]$config.DeviceNameTemplate)) {
|
||||
[System.Windows.MessageBox]::Show("Specify a device name before using 'Specify Device Name'.", "Device Name Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: device name required."
|
||||
return
|
||||
}
|
||||
|
||||
if (-not ($config.CopyUnattend -or $config.InjectUnattend)) {
|
||||
[System.Windows.MessageBox]::Show("Select Copy Unattend.xml or Inject Unattend.xml before using 'Specify Device Name'.", "Unattend Selection Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: unattend delivery method required for device naming."
|
||||
return
|
||||
}
|
||||
|
||||
$templateWithoutSupportedVariables = ([string]$config.DeviceNameTemplate) -replace '(?i)%serial%', ''
|
||||
if ($templateWithoutSupportedVariables -match '%') {
|
||||
[System.Windows.MessageBox]::Show("Only the %serial% device name variable is supported.", "Unsupported Device Name Variable", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: unsupported device name variable."
|
||||
return
|
||||
}
|
||||
|
||||
if ($config.InjectUnattend -and (-not $config.CopyUnattend) -and ([string]$config.DeviceNameTemplate -match '(?i)%serial%')) {
|
||||
[System.Windows.MessageBox]::Show("The %serial% device name variable is only supported when Copy Unattend.xml is selected.", "Unsupported Inject Unattend Setting", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: %serial% requires Copy Unattend.xml."
|
||||
return
|
||||
}
|
||||
}
|
||||
elseif ($config.DeviceNamingMode -eq 'Prefixes') {
|
||||
if (-not $config.CopyUnattend) {
|
||||
[System.Windows.MessageBox]::Show("Select Copy Unattend.xml before using 'Specify a list of Prefixes'.", "Copy Unattend Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: prefixes require Copy Unattend.xml."
|
||||
return
|
||||
}
|
||||
|
||||
$hasSavedPrefixesPath = -not [string]::IsNullOrWhiteSpace([string]$config.DeviceNamePrefixesPath) -and (Test-Path -Path $config.DeviceNamePrefixesPath -PathType Leaf)
|
||||
if ((($null -eq $config.DeviceNamePrefixes) -or ($config.DeviceNamePrefixes.Count -eq 0)) -and -not $hasSavedPrefixesPath) {
|
||||
[System.Windows.MessageBox]::Show("Enter at least one prefix or choose a valid prefixes file before using 'Specify a list of Prefixes'.", "Prefixes Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: prefixes required."
|
||||
return
|
||||
}
|
||||
}
|
||||
elseif ($config.DeviceNamingMode -eq 'SerialComputerNames') {
|
||||
if (-not $config.CopyUnattend) {
|
||||
[System.Windows.MessageBox]::Show("Select Copy Unattend.xml before using 'Specify Serial to Device Name Mapping'.", "Copy Unattend Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: serial computer-name mapping requires Copy Unattend.xml."
|
||||
return
|
||||
}
|
||||
|
||||
$hasSavedSerialComputerNamesPath = -not [string]::IsNullOrWhiteSpace([string]$config.DeviceNameSerialComputerNamesPath) -and (Test-Path -Path $config.DeviceNameSerialComputerNamesPath -PathType Leaf)
|
||||
if ((($null -eq $config.DeviceNameSerialComputerNames) -or ($config.DeviceNameSerialComputerNames.Count -eq 0)) -and -not $hasSavedSerialComputerNamesPath) {
|
||||
[System.Windows.MessageBox]::Show("Enter CSV content or choose a valid SerialComputerNames.csv file before using 'Specify Serial to Device Name Mapping'.", "Serial Mapping Required", "OK", "Warning") | Out-Null
|
||||
$btnRun.IsEnabled = $true
|
||||
$script:uiState.Controls.txtStatus.Text = "Build canceled: serial computer-name mapping required."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$configFilePath = Join-Path $config.FFUDevelopmentPath "\config\FFUConfig.json"
|
||||
# Sort top-level keys alphabetically for consistent output
|
||||
$sortedConfig = [ordered]@{}
|
||||
|
||||
@@ -836,9 +836,13 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Row 9: General Build Options Checkboxes -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Row 10: Build USB Drive Section -->
|
||||
<!-- Row 10: Unattend.xml Options -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Row 11: Post-Build Cleanup -->
|
||||
<!-- Row 11: Device Naming -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Row 12: Build USB Drive Section -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Row 13: Post-Build Cleanup -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -898,13 +902,90 @@
|
||||
<CheckBox x:Name="chkOptimize" Content="Optimize" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will optimize the OS when building the FFU."/>
|
||||
<CheckBox x:Name="chkAllowVHDXCaching" Content="Allow VHDX Caching" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will cache the VHDX file to cache folder and create a config json file to track Windows build information."/>
|
||||
<CheckBox x:Name="chkCreateDeploymentMedia" Content="Create Deployment Media" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, this will create WinPE deployment media for use when deploying to a physical device."/>
|
||||
<CheckBox x:Name="chkInjectUnattend" Content="Inject Unattend.xml" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true and Install Apps is enabled, copies unattend_[arch].xml from $FFUDevelopmentPath\unattend into Apps\Unattend\Unattend.xml to be used by sysprep."/>
|
||||
<CheckBox x:Name="chkVerbose" Content="Verbose" Margin="0" VerticalAlignment="Center" Tag="When set to $true, will enable write-verbose output to the console for the build script."/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<!-- Row 10: Build USB Drive Section -->
|
||||
<Expander Grid.Row="10" x:Name="usbDriveSection" Header="Build USB Drive Options" IsExpanded="False" Margin="0,0,0,20" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||
<!-- Row 10: Unattend.xml Options -->
|
||||
<Expander Grid.Row="10" Header="Unattend.xml Options" IsExpanded="False" Margin="0,0,0,20" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<TextBlock Text="Choose how unattend content is staged and which architecture-specific source files are used." Margin="0,0,0,12" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="x64 Unattend File Path" Margin="0,0,0,8" TextWrapping="Wrap"/>
|
||||
<Grid Margin="0,0,0,16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="txtUnattendX64FilePath" Grid.Column="0" VerticalAlignment="Center" ToolTip="Path to the x64 unattend XML source file. You can use any file name."/>
|
||||
<Button x:Name="btnBrowseUnattendX64FilePath" Grid.Column="1" Content="Browse..." Padding="12,4" Margin="8,0,0,0" VerticalAlignment="Center" ToolTip="Browse to the x64 unattend XML source file."/>
|
||||
</Grid>
|
||||
<TextBlock Text="arm64 Unattend File Path" Margin="0,0,0,8" TextWrapping="Wrap"/>
|
||||
<Grid Margin="0,0,0,16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="txtUnattendArm64FilePath" Grid.Column="0" VerticalAlignment="Center" ToolTip="Path to the arm64 unattend XML source file. You can use any file name."/>
|
||||
<Button x:Name="btnBrowseUnattendArm64FilePath" Grid.Column="1" Content="Browse..." Padding="12,4" Margin="8,0,0,0" VerticalAlignment="Center" ToolTip="Browse to the arm64 unattend XML source file."/>
|
||||
</Grid>
|
||||
<CheckBox x:Name="chkInjectUnattend" Content="Inject Unattend.xml" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true and Install Apps is enabled, uses the selected architecture-specific unattend XML file and copies it into Apps\Unattend\Unattend.xml to be used by sysprep."/>
|
||||
<CheckBox x:Name="chkCopyUnattend" Content="Copy Unattend.xml" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, uses the selected architecture-specific unattend XML file and copies it to the USB drive as Unattend.xml."/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<!-- Row 11: Device Naming -->
|
||||
<Expander Grid.Row="11" x:Name="deviceNamingSection" Header="Device Naming" IsExpanded="False" Margin="0,0,0,20" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<TextBlock Text="Choose how the device should be named when unattend is applied." Margin="0,0,0,12" TextWrapping="Wrap"/>
|
||||
<RadioButton x:Name="rbDeviceNamingNone" Content="No Device Name" GroupName="DeviceNamingMode" IsChecked="True" Margin="0,0,0,8" ToolTip="Apply unattend without setting a specific computer name."/>
|
||||
<RadioButton x:Name="rbDeviceNamingPrompt" Content="Prompt for Device Name" GroupName="DeviceNamingMode" Margin="0,0,0,8" ToolTip="Prompt the technician to enter a device name during deployment. This option requires Copy Unattend.xml."/>
|
||||
<RadioButton x:Name="rbDeviceNamingTemplate" Content="Specify Device Name" GroupName="DeviceNamingMode" Margin="0,0,0,8" ToolTip="Use a static device name or the %serial% variable when Copy Unattend.xml is selected."/>
|
||||
<StackPanel x:Name="deviceNameTemplatePanel" Margin="32,0,0,16" Visibility="Collapsed">
|
||||
<TextBlock Text="Use static text, %serial%, or both together, for example Comp-%serial%." Margin="0,0,0,4" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="Choose Copy Unattend.xml or Inject Unattend.xml for static names. %serial% requires Copy Unattend.xml." Margin="0,0,0,8" TextWrapping="Wrap" Opacity="0.75"/>
|
||||
<TextBox x:Name="txtDeviceNameTemplate" VerticalAlignment="Center" ToolTip="Examples: KIOSK-01 or Comp-%serial%"/>
|
||||
</StackPanel>
|
||||
<RadioButton x:Name="rbDeviceNamingPrefixes" Content="Specify a list of Prefixes" GroupName="DeviceNamingMode" Margin="0,0,0,8" ToolTip="Enter one prefix per line or import an existing prefixes file. This option requires Copy Unattend.xml."/>
|
||||
<StackPanel x:Name="deviceNamePrefixesPanel" Margin="32,0,0,0" Visibility="Collapsed">
|
||||
<TextBlock Text="Prefixes File Path" Margin="0,0,0,8" TextWrapping="Wrap"/>
|
||||
<Grid Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="txtDeviceNamePrefixesPath" Grid.Column="0" VerticalAlignment="Center" ToolTip="Path to the prefixes source file. You can use any file name."/>
|
||||
<Button x:Name="btnBrowseDeviceNamePrefixesPath" Grid.Column="1" Content="Browse..." Padding="12,4" Margin="8,0,0,0" VerticalAlignment="Center" ToolTip="Browse to a prefixes source file path."/>
|
||||
</Grid>
|
||||
<TextBlock Text="Enter one prefix per line. Each prefix is combined with the device serial number during deployment." Margin="0,0,0,4" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="If you enter a single prefix, it is used automatically. If you enter multiple prefixes, the technician is prompted to choose one during deployment." Margin="0,0,0,8" TextWrapping="Wrap" Opacity="0.75"/>
|
||||
<TextBox x:Name="txtDeviceNamePrefixes" MinHeight="120" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" ToolTip="Each line becomes a prefix option in prefixes.txt."/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8,0,0" HorizontalAlignment="Left">
|
||||
<Button x:Name="btnSaveDeviceNamePrefixes" Content="Save Prefixes" Padding="12,4" ToolTip="Save the current prefixes list to the Prefixes File Path."/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<RadioButton x:Name="rbDeviceNamingSerialComputerNames" Content="Specify Serial to Device Name Mapping" GroupName="DeviceNamingMode" Margin="0,8,0,8" ToolTip="Create or import a SerialComputerNames.csv file. This option requires Copy Unattend.xml."/>
|
||||
<StackPanel x:Name="deviceNameSerialComputerNamesPanel" Margin="32,0,0,0" Visibility="Collapsed">
|
||||
<TextBlock Text="SerialComputerNames.csv File Path" Margin="0,0,0,8" TextWrapping="Wrap"/>
|
||||
<Grid Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="txtDeviceNameSerialComputerNamesPath" Grid.Column="0" VerticalAlignment="Center" ToolTip="Path to the serial-to-device-name mapping CSV file. You can use any file name."/>
|
||||
<Button x:Name="btnBrowseDeviceNameSerialComputerNamesPath" Grid.Column="1" Content="Browse..." Padding="12,4" Margin="8,0,0,0" VerticalAlignment="Center" ToolTip="Browse to a SerialComputerNames.csv source file path."/>
|
||||
</Grid>
|
||||
<TextBlock Text="Enter CSV content with SerialNumber and ComputerName headers. Each row maps one BIOS serial number to one computer name during deployment." Margin="0,0,0,4" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="Example: SerialNumber,ComputerName" Margin="0,0,0,8" TextWrapping="Wrap" Opacity="0.75"/>
|
||||
<TextBox x:Name="txtDeviceNameSerialComputerNames" MinHeight="120" AcceptsReturn="True" TextWrapping="NoWrap" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" ToolTip="Each CSV row maps a serial number to a computer name."/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8,0,0" HorizontalAlignment="Left">
|
||||
<Button x:Name="btnSaveDeviceNameSerialComputerNames" Content="Save Serial Mapping" Padding="12,4" ToolTip="Save the current serial-to-device-name mapping to the CSV file path."/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<!-- Row 12: Build USB Drive Section -->
|
||||
<Expander Grid.Row="12" x:Name="usbDriveSection" Header="Build USB Drive Options" IsExpanded="False" Margin="0,0,0,20" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<CheckBox x:Name="chkBuildUSBDriveEnable" Content="Build USB Drive" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will partition and format a USB drive and copy the captured FFU to the drive."/>
|
||||
<CheckBox x:Name="chkAllowExternalHardDiskMedia" Content="Allow External Hard Disk Media" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will allow the use of external hard disk media."/>
|
||||
@@ -912,7 +993,6 @@
|
||||
<CheckBox x:Name="chkSelectSpecificUSBDrives" Content="Select Specific USB Drives" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Enable to select specific USB drives for building"/>
|
||||
<!-- Added Missing Checkboxes -->
|
||||
<CheckBox x:Name="chkCopyAutopilot" Content="Copy Autopilot Profile" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will copy the Autopilot profile to the USB drive."/>
|
||||
<CheckBox x:Name="chkCopyUnattend" Content="Copy Unattend.xml" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will copy the Unattend.xml file to the USB drive."/>
|
||||
<CheckBox x:Name="chkCopyPPKG" Content="Copy Provisioning Package" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will copy the provisioning package to the USB drive."/>
|
||||
<CheckBox x:Name="chkCopyAdditionalFFUFiles" Content="Copy Additional FFU Files" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, allows selecting existing FFU files in the capture folder to also copy to the USB drive."/>
|
||||
|
||||
@@ -969,8 +1049,8 @@
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<!-- Row 11: Post-Build Cleanup -->
|
||||
<Expander Grid.Row="11" Header="Post-Build Cleanup" IsExpanded="False" Margin="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||
<!-- Row 13: Post-Build Cleanup -->
|
||||
<Expander Grid.Row="13" Header="Post-Build Cleanup" IsExpanded="False" Margin="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<CheckBox x:Name="chkCleanupAppsISO" Content="Cleanup Apps ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove Apps ISO after FFU capture."/>
|
||||
<CheckBox x:Name="chkCleanupDeployISO" Content="Cleanup Deploy ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove WinPE deployment ISO after FFU capture."/>
|
||||
|
||||
@@ -36,9 +36,17 @@ function Get-UIConfig {
|
||||
UseDriversAsPEDrivers = $State.Controls.chkUseDriversAsPEDrivers.IsChecked
|
||||
CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked
|
||||
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
|
||||
DeviceNamingMode = Get-ConfiguredDeviceNamingMode -State $State
|
||||
DeviceNameTemplate = $State.Controls.txtDeviceNameTemplate.Text
|
||||
DeviceNamePrefixesPath = $State.Controls.txtDeviceNamePrefixesPath.Text
|
||||
DeviceNamePrefixes = @(Get-DeviceNamePrefixes -State $State)
|
||||
DeviceNameSerialComputerNamesPath = $State.Controls.txtDeviceNameSerialComputerNamesPath.Text
|
||||
DeviceNameSerialComputerNames = @(Get-SerialComputerNamesLines -State $State)
|
||||
CopyAdditionalFFUFiles = $State.Controls.chkCopyAdditionalFFUFiles.IsChecked
|
||||
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
|
||||
InjectUnattend = $State.Controls.chkInjectUnattend.IsChecked
|
||||
UnattendX64FilePath = $State.Controls.txtUnattendX64FilePath.Text
|
||||
UnattendArm64FilePath = $State.Controls.txtUnattendArm64FilePath.Text
|
||||
CustomFFUNameTemplate = $State.Controls.txtCustomFFUNameTemplate.Text
|
||||
Disksize = [int64]$State.Controls.txtDiskSize.Text * 1GB
|
||||
DownloadDrivers = $State.Controls.chkDownloadDrivers.IsChecked
|
||||
@@ -450,12 +458,53 @@ function Update-UIFromConfig {
|
||||
Set-UIValue -ControlName 'chkCopyAdditionalFFUFiles' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyAdditionalFFUFiles' -State $State
|
||||
Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State
|
||||
Set-UIValue -ControlName 'chkInjectUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InjectUnattend' -State $State
|
||||
Set-UIValue -ControlName 'txtUnattendX64FilePath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'UnattendX64FilePath' -State $State
|
||||
Set-UIValue -ControlName 'txtUnattendArm64FilePath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'UnattendArm64FilePath' -State $State
|
||||
Set-UIValue -ControlName 'chkVerbose' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'Verbose' -State $State
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($State.Controls.txtUnattendX64FilePath.Text)) {
|
||||
$State.Controls.txtUnattendX64FilePath.Text = Get-DefaultUnattendFilePath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text -WindowsArch 'x64'
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($State.Controls.txtUnattendArm64FilePath.Text)) {
|
||||
$State.Controls.txtUnattendArm64FilePath.Text = Get-DefaultUnattendFilePath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text -WindowsArch 'arm64'
|
||||
}
|
||||
|
||||
# USB Drive Modification group (Build Tab)
|
||||
Set-UIValue -ControlName 'chkCopyAutopilot' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyAutopilot' -State $State
|
||||
Set-UIValue -ControlName 'chkCopyUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyUnattend' -State $State
|
||||
Set-UIValue -ControlName 'chkCopyPPKG' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyPPKG' -State $State
|
||||
Set-UIValue -ControlName 'txtDeviceNamePrefixesPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DeviceNamePrefixesPath' -State $State
|
||||
Set-UIValue -ControlName 'txtDeviceNameSerialComputerNamesPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DeviceNameSerialComputerNamesPath' -State $State
|
||||
Set-UIValue -ControlName 'txtDeviceNameTemplate' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DeviceNameTemplate' -State $State
|
||||
Set-UIValue -ControlName 'txtDeviceNamePrefixes' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DeviceNamePrefixes' -TransformValue { param($val) if ($val -is [System.Array]) { $val -join [System.Environment]::NewLine } else { [string]$val } } -State $State
|
||||
Set-UIValue -ControlName 'txtDeviceNameSerialComputerNames' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'DeviceNameSerialComputerNames' -TransformValue { param($val) if ($val -is [System.Array]) { $val -join [System.Environment]::NewLine } else { [string]$val } } -State $State
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($State.Controls.txtDeviceNamePrefixesPath.Text)) {
|
||||
$State.Controls.txtDeviceNamePrefixesPath.Text = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($State.Controls.txtDeviceNameSerialComputerNamesPath.Text)) {
|
||||
$State.Controls.txtDeviceNameSerialComputerNamesPath.Text = Get-DefaultSerialComputerNamesPath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text
|
||||
}
|
||||
|
||||
$loadedDeviceNamingMode = $null
|
||||
if ($ConfigContent.PSObject.Properties.Name -contains 'DeviceNamingMode') {
|
||||
$candidateDeviceNamingMode = [string]$ConfigContent.DeviceNamingMode
|
||||
if ($candidateDeviceNamingMode -in @('Legacy', 'None', 'Prompt', 'Template', 'Prefixes', 'SerialComputerNames')) {
|
||||
$loadedDeviceNamingMode = $candidateDeviceNamingMode
|
||||
}
|
||||
}
|
||||
$displayDeviceNamingMode = if ($loadedDeviceNamingMode -in @('Prompt', 'Template', 'Prefixes', 'SerialComputerNames')) {
|
||||
$loadedDeviceNamingMode
|
||||
}
|
||||
else {
|
||||
'None'
|
||||
}
|
||||
Set-DeviceNamingModeState -State $State -DisplayMode $displayDeviceNamingMode -LoadedMode $loadedDeviceNamingMode
|
||||
Import-DeviceNamePrefixesFromConfiguredPath -State $State
|
||||
Import-SerialComputerNamesFromConfiguredPath -State $State
|
||||
Update-DeviceNamingControls -State $State
|
||||
|
||||
# Post Build Cleanup group (Build Tab)
|
||||
Set-UIValue -ControlName 'chkCleanupAppsISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupAppsISO' -State $State
|
||||
|
||||
@@ -27,6 +27,319 @@ function Update-VMNetworkingControls {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SelectedDeviceNamingMode {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
if ($true -eq $State.Controls.rbDeviceNamingPrompt.IsChecked) {
|
||||
return 'Prompt'
|
||||
}
|
||||
|
||||
if ($true -eq $State.Controls.rbDeviceNamingTemplate.IsChecked) {
|
||||
return 'Template'
|
||||
}
|
||||
|
||||
if ($true -eq $State.Controls.rbDeviceNamingPrefixes.IsChecked) {
|
||||
return 'Prefixes'
|
||||
}
|
||||
|
||||
if ($true -eq $State.Controls.rbDeviceNamingSerialComputerNames.IsChecked) {
|
||||
return 'SerialComputerNames'
|
||||
}
|
||||
|
||||
return 'None'
|
||||
}
|
||||
|
||||
function Set-DeviceNamingMode {
|
||||
param(
|
||||
[PSCustomObject]$State,
|
||||
[ValidateSet('None', 'Prompt', 'Template', 'Prefixes', 'SerialComputerNames')]
|
||||
[string]$Mode
|
||||
)
|
||||
|
||||
$State.Controls.rbDeviceNamingNone.IsChecked = $Mode -eq 'None'
|
||||
$State.Controls.rbDeviceNamingPrompt.IsChecked = $Mode -eq 'Prompt'
|
||||
$State.Controls.rbDeviceNamingTemplate.IsChecked = $Mode -eq 'Template'
|
||||
$State.Controls.rbDeviceNamingPrefixes.IsChecked = $Mode -eq 'Prefixes'
|
||||
$State.Controls.rbDeviceNamingSerialComputerNames.IsChecked = $Mode -eq 'SerialComputerNames'
|
||||
}
|
||||
|
||||
function Set-DeviceNamingModeState {
|
||||
param(
|
||||
[PSCustomObject]$State,
|
||||
[ValidateSet('None', 'Prompt', 'Template', 'Prefixes', 'SerialComputerNames')]
|
||||
[string]$DisplayMode,
|
||||
[AllowNull()]
|
||||
[string]$LoadedMode
|
||||
)
|
||||
|
||||
if ($null -eq $State.Flags) {
|
||||
$State.Flags = @{}
|
||||
}
|
||||
|
||||
if ($null -eq $State.Data) {
|
||||
$State.Data = @{}
|
||||
}
|
||||
|
||||
$previousSuppressionState = $true -eq $State.Flags.suppressDeviceNamingChangeTracking
|
||||
$State.Flags.suppressDeviceNamingChangeTracking = $true
|
||||
try {
|
||||
Set-DeviceNamingMode -State $State -Mode $DisplayMode
|
||||
}
|
||||
finally {
|
||||
$State.Flags.suppressDeviceNamingChangeTracking = $previousSuppressionState
|
||||
}
|
||||
|
||||
$State.Data.loadedDeviceNamingMode = if ([string]::IsNullOrWhiteSpace($LoadedMode)) {
|
||||
$null
|
||||
}
|
||||
else {
|
||||
$LoadedMode.Trim()
|
||||
}
|
||||
$State.Flags.deviceNamingModeWasExplicitlyChanged = $false
|
||||
}
|
||||
|
||||
function Get-ConfiguredDeviceNamingMode {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
if (($null -ne $State.Flags) -and ($true -eq $State.Flags.deviceNamingModeWasExplicitlyChanged)) {
|
||||
return Get-SelectedDeviceNamingMode -State $State
|
||||
}
|
||||
|
||||
if (($null -ne $State.Data) -and -not [string]::IsNullOrWhiteSpace([string]$State.Data.loadedDeviceNamingMode)) {
|
||||
return [string]$State.Data.loadedDeviceNamingMode
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-DeviceNamePrefixes {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
if ($null -eq $State.Controls.txtDeviceNamePrefixes) {
|
||||
return @()
|
||||
}
|
||||
|
||||
return @(
|
||||
$State.Controls.txtDeviceNamePrefixes.Text -split "\r?\n" |
|
||||
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
|
||||
ForEach-Object { $_.Trim() }
|
||||
)
|
||||
}
|
||||
|
||||
function Get-SerialComputerNamesLines {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
if ($null -eq $State.Controls.txtDeviceNameSerialComputerNames) {
|
||||
return @()
|
||||
}
|
||||
|
||||
return @(
|
||||
$State.Controls.txtDeviceNameSerialComputerNames.Text -split "\r?\n" |
|
||||
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
|
||||
ForEach-Object { $_.Trim() }
|
||||
)
|
||||
}
|
||||
|
||||
function Import-DeviceNamePrefixesFile {
|
||||
param(
|
||||
[PSCustomObject]$State,
|
||||
[string]$FilePath
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FilePath) -or -not (Test-Path -Path $FilePath -PathType Leaf)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$prefixLines = @(Get-Content -Path $FilePath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||
if ($null -ne $State.Controls.txtDeviceNamePrefixesPath) {
|
||||
$State.Controls.txtDeviceNamePrefixesPath.Text = $FilePath
|
||||
}
|
||||
$State.Controls.txtDeviceNamePrefixes.Text = $prefixLines -join [System.Environment]::NewLine
|
||||
WriteLog "Imported device name prefixes from $FilePath"
|
||||
return $true
|
||||
}
|
||||
|
||||
function Import-SerialComputerNamesFile {
|
||||
param(
|
||||
[PSCustomObject]$State,
|
||||
[string]$FilePath
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FilePath) -or -not (Test-Path -Path $FilePath -PathType Leaf)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$serialMappingLines = @(Get-Content -Path $FilePath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||
if ($null -ne $State.Controls.txtDeviceNameSerialComputerNamesPath) {
|
||||
$State.Controls.txtDeviceNameSerialComputerNamesPath.Text = $FilePath
|
||||
}
|
||||
$State.Controls.txtDeviceNameSerialComputerNames.Text = $serialMappingLines -join [System.Environment]::NewLine
|
||||
WriteLog "Imported serial computer-name mappings from $FilePath"
|
||||
return $true
|
||||
}
|
||||
|
||||
function Get-DefaultDeviceNamePrefixesPath {
|
||||
param([string]$FFUDevelopmentPath)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FFUDevelopmentPath)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return Join-Path (Join-Path $FFUDevelopmentPath 'unattend') 'prefixes.txt'
|
||||
}
|
||||
|
||||
function Get-DefaultSerialComputerNamesPath {
|
||||
param([string]$FFUDevelopmentPath)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FFUDevelopmentPath)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return Join-Path (Join-Path $FFUDevelopmentPath 'unattend') 'SerialComputerNames.csv'
|
||||
}
|
||||
|
||||
function Get-DefaultUnattendFilePath {
|
||||
param(
|
||||
[string]$FFUDevelopmentPath,
|
||||
[ValidateSet('x64', 'arm64')]
|
||||
[string]$WindowsArch
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($FFUDevelopmentPath)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$fileName = if ($WindowsArch -ieq 'arm64') { 'unattend_arm64.xml' } else { 'unattend_x64.xml' }
|
||||
return Join-Path (Join-Path $FFUDevelopmentPath 'unattend') $fileName
|
||||
}
|
||||
|
||||
function Import-DeviceNamePrefixesFromConfiguredPath {
|
||||
param(
|
||||
[PSCustomObject]$State,
|
||||
[switch]$SkipIfTextPresent
|
||||
)
|
||||
|
||||
if ($SkipIfTextPresent -and -not [string]::IsNullOrWhiteSpace($State.Controls.txtDeviceNamePrefixes.Text)) {
|
||||
return
|
||||
}
|
||||
|
||||
$prefixFilePath = $State.Controls.txtDeviceNamePrefixesPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($prefixFilePath)) {
|
||||
$prefixFilePath = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text
|
||||
if (-not [string]::IsNullOrWhiteSpace($prefixFilePath) -and $null -ne $State.Controls.txtDeviceNamePrefixesPath) {
|
||||
$State.Controls.txtDeviceNamePrefixesPath.Text = $prefixFilePath
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-Path -Path $prefixFilePath -PathType Leaf) {
|
||||
Import-DeviceNamePrefixesFile -State $State -FilePath $prefixFilePath | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Import-SerialComputerNamesFromConfiguredPath {
|
||||
param(
|
||||
[PSCustomObject]$State,
|
||||
[switch]$SkipIfTextPresent
|
||||
)
|
||||
|
||||
if ($SkipIfTextPresent -and -not [string]::IsNullOrWhiteSpace($State.Controls.txtDeviceNameSerialComputerNames.Text)) {
|
||||
return
|
||||
}
|
||||
|
||||
$serialComputerNamesPath = $State.Controls.txtDeviceNameSerialComputerNamesPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($serialComputerNamesPath)) {
|
||||
$serialComputerNamesPath = Get-DefaultSerialComputerNamesPath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text
|
||||
if (-not [string]::IsNullOrWhiteSpace($serialComputerNamesPath) -and $null -ne $State.Controls.txtDeviceNameSerialComputerNamesPath) {
|
||||
$State.Controls.txtDeviceNameSerialComputerNamesPath.Text = $serialComputerNamesPath
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-Path -Path $serialComputerNamesPath -PathType Leaf) {
|
||||
Import-SerialComputerNamesFile -State $State -FilePath $serialComputerNamesPath | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Test-DeviceNameTemplateUsesSerialToken {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
return ((Get-SelectedDeviceNamingMode -State $State) -eq 'Template') -and ($State.Controls.txtDeviceNameTemplate.Text -match '(?i)%serial%')
|
||||
}
|
||||
|
||||
function Update-UnattendSelectionControls {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
$selectedDeviceNamingMode = Get-SelectedDeviceNamingMode -State $State
|
||||
$isCopyUnattendSelected = $true -eq $State.Controls.chkCopyUnattend.IsChecked
|
||||
$isInjectUnattendSelected = $true -eq $State.Controls.chkInjectUnattend.IsChecked
|
||||
$deviceNameTemplateUsesSerialToken = Test-DeviceNameTemplateUsesSerialToken -State $State
|
||||
$requiresCopiedUnattend = ($selectedDeviceNamingMode -in @('Prompt', 'Prefixes', 'SerialComputerNames')) -or $deviceNameTemplateUsesSerialToken
|
||||
|
||||
if ($isCopyUnattendSelected -and $isInjectUnattendSelected) {
|
||||
if ($requiresCopiedUnattend) {
|
||||
$State.Controls.chkInjectUnattend.IsChecked = $false
|
||||
$isInjectUnattendSelected = $false
|
||||
}
|
||||
else {
|
||||
$State.Controls.chkCopyUnattend.IsChecked = $false
|
||||
$isCopyUnattendSelected = $false
|
||||
}
|
||||
}
|
||||
|
||||
if ($requiresCopiedUnattend) {
|
||||
if (-not $isCopyUnattendSelected) {
|
||||
$State.Controls.chkCopyUnattend.IsChecked = $true
|
||||
$isCopyUnattendSelected = $true
|
||||
}
|
||||
|
||||
if ($isInjectUnattendSelected) {
|
||||
$State.Controls.chkInjectUnattend.IsChecked = $false
|
||||
$isInjectUnattendSelected = $false
|
||||
}
|
||||
|
||||
$State.Controls.chkCopyUnattend.IsEnabled = $false
|
||||
$State.Controls.chkInjectUnattend.IsEnabled = $false
|
||||
return
|
||||
}
|
||||
|
||||
if ($isCopyUnattendSelected) {
|
||||
$State.Controls.chkCopyUnattend.IsEnabled = $true
|
||||
$State.Controls.chkInjectUnattend.IsEnabled = $false
|
||||
}
|
||||
elseif ($isInjectUnattendSelected) {
|
||||
$State.Controls.chkCopyUnattend.IsEnabled = $false
|
||||
$State.Controls.chkInjectUnattend.IsEnabled = $true
|
||||
}
|
||||
else {
|
||||
$State.Controls.chkCopyUnattend.IsEnabled = $true
|
||||
$State.Controls.chkInjectUnattend.IsEnabled = $true
|
||||
}
|
||||
}
|
||||
|
||||
function Update-DeviceNamingControls {
|
||||
param([PSCustomObject]$State)
|
||||
|
||||
if (($true -eq $State.Controls.chkInjectUnattend.IsChecked) -and (($true -eq $State.Controls.rbDeviceNamingPrompt.IsChecked) -or ($true -eq $State.Controls.rbDeviceNamingPrefixes.IsChecked) -or ($true -eq $State.Controls.rbDeviceNamingSerialComputerNames.IsChecked))) {
|
||||
$State.Controls.rbDeviceNamingNone.IsChecked = $true
|
||||
}
|
||||
|
||||
$selectedDeviceNamingMode = Get-SelectedDeviceNamingMode -State $State
|
||||
$State.Controls.deviceNameTemplatePanel.Visibility = if ($selectedDeviceNamingMode -eq 'Template') { 'Visible' } else { 'Collapsed' }
|
||||
$State.Controls.deviceNamePrefixesPanel.Visibility = if ($selectedDeviceNamingMode -eq 'Prefixes') { 'Visible' } else { 'Collapsed' }
|
||||
$State.Controls.deviceNameSerialComputerNamesPanel.Visibility = if ($selectedDeviceNamingMode -eq 'SerialComputerNames') { 'Visible' } else { 'Collapsed' }
|
||||
$State.Controls.rbDeviceNamingPrompt.IsEnabled = -not ($true -eq $State.Controls.chkInjectUnattend.IsChecked)
|
||||
$State.Controls.rbDeviceNamingPrefixes.IsEnabled = -not ($true -eq $State.Controls.chkInjectUnattend.IsChecked)
|
||||
$State.Controls.rbDeviceNamingSerialComputerNames.IsEnabled = -not ($true -eq $State.Controls.chkInjectUnattend.IsChecked)
|
||||
|
||||
if ($selectedDeviceNamingMode -eq 'Prefixes') {
|
||||
Import-DeviceNamePrefixesFromConfiguredPath -State $State -SkipIfTextPresent
|
||||
}
|
||||
elseif ($selectedDeviceNamingMode -eq 'SerialComputerNames') {
|
||||
Import-SerialComputerNamesFromConfiguredPath -State $State -SkipIfTextPresent
|
||||
}
|
||||
|
||||
Update-UnattendSelectionControls -State $State
|
||||
}
|
||||
|
||||
function Register-EventHandlers {
|
||||
param([PSCustomObject]$State)
|
||||
WriteLog "Registering UI event handlers..."
|
||||
@@ -242,7 +555,34 @@ function Register-EventHandlers {
|
||||
$localState = $window.Tag
|
||||
$selectedPath = Invoke-BrowseAction -Type 'Folder' -Title "Select FFU Development Path"
|
||||
if ($selectedPath) {
|
||||
$currentPrefixesPath = $localState.Controls.txtDeviceNamePrefixesPath.Text
|
||||
$currentSerialComputerNamesPath = $localState.Controls.txtDeviceNameSerialComputerNamesPath.Text
|
||||
$currentUnattendX64FilePath = $localState.Controls.txtUnattendX64FilePath.Text
|
||||
$currentUnattendArm64FilePath = $localState.Controls.txtUnattendArm64FilePath.Text
|
||||
$previousDefaultPrefixesPath = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text
|
||||
$previousDefaultSerialComputerNamesPath = Get-DefaultSerialComputerNamesPath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text
|
||||
$previousDefaultUnattendX64FilePath = Get-DefaultUnattendFilePath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text -WindowsArch 'x64'
|
||||
$previousDefaultUnattendArm64FilePath = Get-DefaultUnattendFilePath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text -WindowsArch 'arm64'
|
||||
$localState.Controls.txtFFUDevPath.Text = $selectedPath
|
||||
$newDefaultPrefixesPath = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $selectedPath
|
||||
$newDefaultSerialComputerNamesPath = Get-DefaultSerialComputerNamesPath -FFUDevelopmentPath $selectedPath
|
||||
$newDefaultUnattendX64FilePath = Get-DefaultUnattendFilePath -FFUDevelopmentPath $selectedPath -WindowsArch 'x64'
|
||||
$newDefaultUnattendArm64FilePath = Get-DefaultUnattendFilePath -FFUDevelopmentPath $selectedPath -WindowsArch 'arm64'
|
||||
if ([string]::IsNullOrWhiteSpace($currentPrefixesPath) -or $currentPrefixesPath -ieq $previousDefaultPrefixesPath) {
|
||||
$localState.Controls.txtDeviceNamePrefixesPath.Text = $newDefaultPrefixesPath
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath) -or $currentSerialComputerNamesPath -ieq $previousDefaultSerialComputerNamesPath) {
|
||||
$localState.Controls.txtDeviceNameSerialComputerNamesPath.Text = $newDefaultSerialComputerNamesPath
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($currentUnattendX64FilePath) -or $currentUnattendX64FilePath -ieq $previousDefaultUnattendX64FilePath) {
|
||||
$localState.Controls.txtUnattendX64FilePath.Text = $newDefaultUnattendX64FilePath
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($currentUnattendArm64FilePath) -or $currentUnattendArm64FilePath -ieq $previousDefaultUnattendArm64FilePath) {
|
||||
$localState.Controls.txtUnattendArm64FilePath.Text = $newDefaultUnattendArm64FilePath
|
||||
}
|
||||
Import-DeviceNamePrefixesFromConfiguredPath -State $localState
|
||||
Import-SerialComputerNamesFromConfiguredPath -State $localState
|
||||
Update-DeviceNamingControls -State $localState
|
||||
}
|
||||
})
|
||||
|
||||
@@ -256,6 +596,234 @@ function Register-EventHandlers {
|
||||
}
|
||||
})
|
||||
|
||||
$State.Controls.rbDeviceNamingNone.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) {
|
||||
$localState.Flags.deviceNamingModeWasExplicitlyChanged = $true
|
||||
$localState.Data.loadedDeviceNamingMode = $null
|
||||
}
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.rbDeviceNamingPrompt.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) {
|
||||
$localState.Flags.deviceNamingModeWasExplicitlyChanged = $true
|
||||
$localState.Data.loadedDeviceNamingMode = $null
|
||||
}
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.rbDeviceNamingTemplate.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) {
|
||||
$localState.Flags.deviceNamingModeWasExplicitlyChanged = $true
|
||||
$localState.Data.loadedDeviceNamingMode = $null
|
||||
}
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.txtDeviceNameTemplate.Add_TextChanged({
|
||||
param($eventSource, $textChangedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
if ($null -ne $window -and $null -ne $window.Tag) {
|
||||
Update-DeviceNamingControls -State $window.Tag
|
||||
}
|
||||
})
|
||||
$State.Controls.rbDeviceNamingPrefixes.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) {
|
||||
$localState.Flags.deviceNamingModeWasExplicitlyChanged = $true
|
||||
$localState.Data.loadedDeviceNamingMode = $null
|
||||
}
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.rbDeviceNamingSerialComputerNames.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) {
|
||||
$localState.Flags.deviceNamingModeWasExplicitlyChanged = $true
|
||||
$localState.Data.loadedDeviceNamingMode = $null
|
||||
}
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.btnBrowseDeviceNamePrefixesPath.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$currentPrefixesPath = $localState.Controls.txtDeviceNamePrefixesPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($currentPrefixesPath)) {
|
||||
$currentPrefixesPath = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text
|
||||
}
|
||||
$initialDirectory = if ([string]::IsNullOrWhiteSpace($currentPrefixesPath)) {
|
||||
$null
|
||||
}
|
||||
else {
|
||||
Split-Path $currentPrefixesPath -Parent
|
||||
}
|
||||
$fileName = if ([string]::IsNullOrWhiteSpace($currentPrefixesPath)) { 'prefixes.txt' } else { Split-Path $currentPrefixesPath -Leaf }
|
||||
$selectedPath = Invoke-BrowseAction -Type 'OpenFile' -Title 'Select prefixes file path' -Filter 'Text files (*.txt)|*.txt|All files (*.*)|*.*' -InitialDirectory $initialDirectory -FileName $fileName
|
||||
if (Import-DeviceNamePrefixesFile -State $localState -FilePath $selectedPath) {
|
||||
Update-DeviceNamingControls -State $localState
|
||||
}
|
||||
})
|
||||
$State.Controls.btnBrowseDeviceNameSerialComputerNamesPath.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$currentSerialComputerNamesPath = $localState.Controls.txtDeviceNameSerialComputerNamesPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath)) {
|
||||
$currentSerialComputerNamesPath = Get-DefaultSerialComputerNamesPath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text
|
||||
}
|
||||
$initialDirectory = if ([string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath)) {
|
||||
$null
|
||||
}
|
||||
else {
|
||||
Split-Path $currentSerialComputerNamesPath -Parent
|
||||
}
|
||||
$fileName = if ([string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath)) { 'SerialComputerNames.csv' } else { Split-Path $currentSerialComputerNamesPath -Leaf }
|
||||
$selectedPath = Invoke-BrowseAction -Type 'OpenFile' -Title 'Select SerialComputerNames.csv file path' -Filter 'CSV files (*.csv)|*.csv|All files (*.*)|*.*' -InitialDirectory $initialDirectory -FileName $fileName
|
||||
if (Import-SerialComputerNamesFile -State $localState -FilePath $selectedPath) {
|
||||
Update-DeviceNamingControls -State $localState
|
||||
}
|
||||
})
|
||||
$State.Controls.btnBrowseUnattendX64FilePath.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$currentUnattendX64FilePath = $localState.Controls.txtUnattendX64FilePath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($currentUnattendX64FilePath)) {
|
||||
$currentUnattendX64FilePath = Get-DefaultUnattendFilePath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text -WindowsArch 'x64'
|
||||
}
|
||||
$initialDirectory = if ([string]::IsNullOrWhiteSpace($currentUnattendX64FilePath)) {
|
||||
$null
|
||||
}
|
||||
else {
|
||||
Split-Path $currentUnattendX64FilePath -Parent
|
||||
}
|
||||
$fileName = if ([string]::IsNullOrWhiteSpace($currentUnattendX64FilePath)) { 'unattend_x64.xml' } else { Split-Path $currentUnattendX64FilePath -Leaf }
|
||||
$selectedPath = Invoke-BrowseAction -Type 'OpenFile' -Title 'Select x64 unattend XML file' -Filter 'XML files (*.xml)|*.xml|All files (*.*)|*.*' -InitialDirectory $initialDirectory -FileName $fileName
|
||||
if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
|
||||
$localState.Controls.txtUnattendX64FilePath.Text = $selectedPath
|
||||
}
|
||||
})
|
||||
$State.Controls.btnBrowseUnattendArm64FilePath.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$currentUnattendArm64FilePath = $localState.Controls.txtUnattendArm64FilePath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($currentUnattendArm64FilePath)) {
|
||||
$currentUnattendArm64FilePath = Get-DefaultUnattendFilePath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text -WindowsArch 'arm64'
|
||||
}
|
||||
$initialDirectory = if ([string]::IsNullOrWhiteSpace($currentUnattendArm64FilePath)) {
|
||||
$null
|
||||
}
|
||||
else {
|
||||
Split-Path $currentUnattendArm64FilePath -Parent
|
||||
}
|
||||
$fileName = if ([string]::IsNullOrWhiteSpace($currentUnattendArm64FilePath)) { 'unattend_arm64.xml' } else { Split-Path $currentUnattendArm64FilePath -Leaf }
|
||||
$selectedPath = Invoke-BrowseAction -Type 'OpenFile' -Title 'Select arm64 unattend XML file' -Filter 'XML files (*.xml)|*.xml|All files (*.*)|*.*' -InitialDirectory $initialDirectory -FileName $fileName
|
||||
if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
|
||||
$localState.Controls.txtUnattendArm64FilePath.Text = $selectedPath
|
||||
}
|
||||
})
|
||||
$State.Controls.btnSaveDeviceNamePrefixes.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$prefixLines = @(Get-DeviceNamePrefixes -State $localState)
|
||||
|
||||
if ($prefixLines.Count -eq 0) {
|
||||
[System.Windows.MessageBox]::Show("Enter at least one prefix before saving the prefixes file.", "Prefixes Required", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
$currentPrefixesPath = $localState.Controls.txtDeviceNamePrefixesPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($currentPrefixesPath)) {
|
||||
$currentPrefixesPath = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text
|
||||
if (-not [string]::IsNullOrWhiteSpace($currentPrefixesPath)) {
|
||||
$localState.Controls.txtDeviceNamePrefixesPath.Text = $currentPrefixesPath
|
||||
}
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($currentPrefixesPath)) {
|
||||
[System.Windows.MessageBox]::Show("Select a valid Prefixes File Path before saving prefixes.", "Prefixes File Path Required", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
$prefixLines | Set-Content -Path $currentPrefixesPath -Encoding UTF8
|
||||
$localState.Controls.txtDeviceNamePrefixesPath.Text = $currentPrefixesPath
|
||||
WriteLog "Saved device name prefixes to $currentPrefixesPath"
|
||||
}
|
||||
catch {
|
||||
[System.Windows.MessageBox]::Show("Saving prefixes failed for '$currentPrefixesPath'. $($_.Exception.Message)", "Save Prefixes Failed", "OK", "Error") | Out-Null
|
||||
}
|
||||
})
|
||||
$State.Controls.btnSaveDeviceNameSerialComputerNames.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$serialComputerNameLines = @(Get-SerialComputerNamesLines -State $localState)
|
||||
|
||||
if ($serialComputerNameLines.Count -eq 0) {
|
||||
[System.Windows.MessageBox]::Show("Enter CSV content before saving the serial mapping file.", "Serial Mapping Required", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
$currentSerialComputerNamesPath = $localState.Controls.txtDeviceNameSerialComputerNamesPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath)) {
|
||||
$currentSerialComputerNamesPath = Get-DefaultSerialComputerNamesPath -FFUDevelopmentPath $localState.Controls.txtFFUDevPath.Text
|
||||
if (-not [string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath)) {
|
||||
$localState.Controls.txtDeviceNameSerialComputerNamesPath.Text = $currentSerialComputerNamesPath
|
||||
}
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($currentSerialComputerNamesPath)) {
|
||||
[System.Windows.MessageBox]::Show("Select a valid SerialComputerNames.csv file path before saving the serial mapping.", "Serial Mapping File Path Required", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
$serialComputerNameLines | Set-Content -Path $currentSerialComputerNamesPath -Encoding UTF8
|
||||
$localState.Controls.txtDeviceNameSerialComputerNamesPath.Text = $currentSerialComputerNamesPath
|
||||
WriteLog "Saved serial computer-name mappings to $currentSerialComputerNamesPath"
|
||||
}
|
||||
catch {
|
||||
[System.Windows.MessageBox]::Show("Saving serial mapping failed for '$currentSerialComputerNamesPath'. $($_.Exception.Message)", "Save Serial Mapping Failed", "OK", "Error") | Out-Null
|
||||
}
|
||||
})
|
||||
$State.Controls.chkCopyUnattend.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$localState.Controls.chkInjectUnattend.IsChecked = $false
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.chkCopyUnattend.Add_Unchecked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
Update-DeviceNamingControls -State $window.Tag
|
||||
})
|
||||
$State.Controls.chkInjectUnattend.Add_Checked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
$localState = $window.Tag
|
||||
$localState.Controls.chkCopyUnattend.IsChecked = $false
|
||||
Update-DeviceNamingControls -State $localState
|
||||
})
|
||||
$State.Controls.chkInjectUnattend.Add_Unchecked({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||
Update-DeviceNamingControls -State $window.Tag
|
||||
})
|
||||
|
||||
# Build USB Drive Settings Event Handlers
|
||||
# The USB Expander is always visible; the checkbox controls child settings only
|
||||
$State.Controls.chkBuildUSBDriveEnable.Add_Checked({
|
||||
|
||||
@@ -220,6 +220,27 @@ function Initialize-UIControls {
|
||||
$State.Controls.chkAllowVHDXCaching = $window.FindName('chkAllowVHDXCaching')
|
||||
$State.Controls.chkCreateDeploymentMedia = $window.FindName('chkCreateDeploymentMedia')
|
||||
$State.Controls.chkInjectUnattend = $window.FindName('chkInjectUnattend')
|
||||
$State.Controls.txtUnattendX64FilePath = $window.FindName('txtUnattendX64FilePath')
|
||||
$State.Controls.btnBrowseUnattendX64FilePath = $window.FindName('btnBrowseUnattendX64FilePath')
|
||||
$State.Controls.txtUnattendArm64FilePath = $window.FindName('txtUnattendArm64FilePath')
|
||||
$State.Controls.btnBrowseUnattendArm64FilePath = $window.FindName('btnBrowseUnattendArm64FilePath')
|
||||
$State.Controls.rbDeviceNamingNone = $window.FindName('rbDeviceNamingNone')
|
||||
$State.Controls.rbDeviceNamingPrompt = $window.FindName('rbDeviceNamingPrompt')
|
||||
$State.Controls.rbDeviceNamingTemplate = $window.FindName('rbDeviceNamingTemplate')
|
||||
$State.Controls.rbDeviceNamingPrefixes = $window.FindName('rbDeviceNamingPrefixes')
|
||||
$State.Controls.rbDeviceNamingSerialComputerNames = $window.FindName('rbDeviceNamingSerialComputerNames')
|
||||
$State.Controls.deviceNameTemplatePanel = $window.FindName('deviceNameTemplatePanel')
|
||||
$State.Controls.deviceNamePrefixesPanel = $window.FindName('deviceNamePrefixesPanel')
|
||||
$State.Controls.deviceNameSerialComputerNamesPanel = $window.FindName('deviceNameSerialComputerNamesPanel')
|
||||
$State.Controls.txtDeviceNameTemplate = $window.FindName('txtDeviceNameTemplate')
|
||||
$State.Controls.txtDeviceNamePrefixesPath = $window.FindName('txtDeviceNamePrefixesPath')
|
||||
$State.Controls.btnBrowseDeviceNamePrefixesPath = $window.FindName('btnBrowseDeviceNamePrefixesPath')
|
||||
$State.Controls.txtDeviceNamePrefixes = $window.FindName('txtDeviceNamePrefixes')
|
||||
$State.Controls.btnSaveDeviceNamePrefixes = $window.FindName('btnSaveDeviceNamePrefixes')
|
||||
$State.Controls.txtDeviceNameSerialComputerNamesPath = $window.FindName('txtDeviceNameSerialComputerNamesPath')
|
||||
$State.Controls.btnBrowseDeviceNameSerialComputerNamesPath = $window.FindName('btnBrowseDeviceNameSerialComputerNamesPath')
|
||||
$State.Controls.txtDeviceNameSerialComputerNames = $window.FindName('txtDeviceNameSerialComputerNames')
|
||||
$State.Controls.btnSaveDeviceNameSerialComputerNames = $window.FindName('btnSaveDeviceNameSerialComputerNames')
|
||||
$State.Controls.chkVerbose = $window.FindName('chkVerbose')
|
||||
$State.Controls.chkCopyAutopilot = $window.FindName('chkCopyAutopilot')
|
||||
$State.Controls.chkCopyUnattend = $window.FindName('chkCopyUnattend')
|
||||
@@ -376,6 +397,8 @@ function Initialize-UIDefaults {
|
||||
$State.Controls.chkOptimize.IsChecked = $State.Defaults.generalDefaults.Optimize
|
||||
$State.Controls.chkAllowVHDXCaching.IsChecked = $State.Defaults.generalDefaults.AllowVHDXCaching
|
||||
$State.Controls.chkInjectUnattend.IsChecked = $State.Defaults.generalDefaults.InjectUnattend
|
||||
$State.Controls.txtUnattendX64FilePath.Text = $State.Defaults.generalDefaults.UnattendX64FilePath
|
||||
$State.Controls.txtUnattendArm64FilePath.Text = $State.Defaults.generalDefaults.UnattendArm64FilePath
|
||||
$State.Controls.chkCreateDeploymentMedia.IsChecked = $State.Defaults.generalDefaults.CreateDeploymentMedia
|
||||
$State.Controls.chkAllowExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.AllowExternalHardDiskMedia
|
||||
$State.Controls.chkPromptExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.PromptExternalHardDiskMedia
|
||||
@@ -383,6 +406,21 @@ function Initialize-UIDefaults {
|
||||
$State.Controls.chkCopyAutopilot.IsChecked = $State.Defaults.generalDefaults.CopyAutopilot
|
||||
$State.Controls.chkCopyUnattend.IsChecked = $State.Defaults.generalDefaults.CopyUnattend
|
||||
$State.Controls.chkCopyPPKG.IsChecked = $State.Defaults.generalDefaults.CopyPPKG
|
||||
$defaultDeviceNamingMode = if ($State.Defaults.generalDefaults.DeviceNamingMode -in @('None', 'Prompt', 'Template', 'Prefixes', 'SerialComputerNames')) {
|
||||
$State.Defaults.generalDefaults.DeviceNamingMode
|
||||
}
|
||||
else {
|
||||
'None'
|
||||
}
|
||||
Set-DeviceNamingModeState -State $State -DisplayMode $defaultDeviceNamingMode -LoadedMode $null
|
||||
$State.Controls.txtDeviceNameTemplate.Text = $State.Defaults.generalDefaults.DeviceNameTemplate
|
||||
$State.Controls.txtDeviceNamePrefixesPath.Text = $State.Defaults.generalDefaults.DeviceNamePrefixesPath
|
||||
$State.Controls.txtDeviceNamePrefixes.Text = ($State.Defaults.generalDefaults.DeviceNamePrefixes -join [System.Environment]::NewLine)
|
||||
$State.Controls.txtDeviceNameSerialComputerNamesPath.Text = $State.Defaults.generalDefaults.DeviceNameSerialComputerNamesPath
|
||||
$State.Controls.txtDeviceNameSerialComputerNames.Text = ($State.Defaults.generalDefaults.DeviceNameSerialComputerNames -join [System.Environment]::NewLine)
|
||||
Import-DeviceNamePrefixesFromConfiguredPath -State $State
|
||||
Import-SerialComputerNamesFromConfiguredPath -State $State
|
||||
Update-DeviceNamingControls -State $State
|
||||
$State.Controls.chkCleanupAppsISO.IsChecked = $State.Defaults.generalDefaults.CleanupAppsISO
|
||||
$State.Controls.chkCleanupDeployISO.IsChecked = $State.Defaults.generalDefaults.CleanupDeployISO
|
||||
$State.Controls.chkCleanupDrivers.IsChecked = $State.Defaults.generalDefaults.CleanupDrivers
|
||||
|
||||
@@ -1173,6 +1173,9 @@ function Invoke-BrowseAction {
|
||||
if (-not [string]::IsNullOrWhiteSpace($InitialDirectory)) {
|
||||
$dialog.InitialDirectory = $InitialDirectory
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($FileName)) {
|
||||
$dialog.FileName = $FileName
|
||||
}
|
||||
if ($dialog.ShowDialog()) {
|
||||
return $dialog.FileName
|
||||
}
|
||||
|
||||
@@ -106,10 +106,15 @@ function Get-GeneralDefaults {
|
||||
$peDriversPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "PEDrivers"
|
||||
$vmLocationPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "VM"
|
||||
$ffuCapturePath = Join-Path -Path $FFUDevelopmentPath -ChildPath "FFU"
|
||||
$unattendPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "unattend"
|
||||
$officePath = Join-Path -Path $appsPath -ChildPath "Office"
|
||||
$appListJsonPath = Join-Path -Path $appsPath -ChildPath "AppList.json"
|
||||
$userAppListPath = Join-Path -Path $appsPath -ChildPath "UserAppList.json"
|
||||
$driversJsonPath = Join-Path -Path $driversPath -ChildPath "Drivers.json"
|
||||
$deviceNamePrefixesPath = Join-Path -Path $unattendPath -ChildPath "prefixes.txt"
|
||||
$deviceNameSerialComputerNamesPath = Join-Path -Path $unattendPath -ChildPath "SerialComputerNames.csv"
|
||||
$unattendX64FilePath = Join-Path -Path $unattendPath -ChildPath "unattend_x64.xml"
|
||||
$unattendArm64FilePath = Join-Path -Path $unattendPath -ChildPath "unattend_arm64.xml"
|
||||
|
||||
return [PSCustomObject]@{
|
||||
# Build Tab Defaults
|
||||
@@ -132,6 +137,14 @@ function Get-GeneralDefaults {
|
||||
CopyUnattend = $false
|
||||
CopyPPKG = $false
|
||||
InjectUnattend = $false
|
||||
UnattendX64FilePath = $unattendX64FilePath
|
||||
UnattendArm64FilePath = $unattendArm64FilePath
|
||||
DeviceNamingMode = 'None'
|
||||
DeviceNameTemplate = ''
|
||||
DeviceNamePrefixesPath = $deviceNamePrefixesPath
|
||||
DeviceNamePrefixes = @()
|
||||
DeviceNameSerialComputerNamesPath = $deviceNameSerialComputerNamesPath
|
||||
DeviceNameSerialComputerNames = @()
|
||||
CleanupAppsISO = $true
|
||||
CleanupDeployISO = $true
|
||||
CleanupDrivers = $false
|
||||
|
||||
@@ -41,6 +41,59 @@ function WriteLog($LogText) {
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Read-MenuSelection {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Prompt,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$InvalidInputMessage,
|
||||
|
||||
[Parameter()]
|
||||
[int[]]$ValidSelections,
|
||||
|
||||
[Parameter()]
|
||||
[int]$Minimum = [int]::MinValue,
|
||||
|
||||
[Parameter()]
|
||||
[int]$Maximum = [int]::MaxValue,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$AllowSkip
|
||||
)
|
||||
|
||||
do {
|
||||
$userInput = Read-Host $Prompt
|
||||
if ([string]::IsNullOrWhiteSpace($userInput)) {
|
||||
Write-Host $InvalidInputMessage
|
||||
continue
|
||||
}
|
||||
|
||||
$selection = 0
|
||||
if (-not [int]::TryParse($userInput, [ref]$selection)) {
|
||||
Write-Host $InvalidInputMessage
|
||||
continue
|
||||
}
|
||||
|
||||
if ($AllowSkip -and $selection -eq 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('ValidSelections')) {
|
||||
if ($ValidSelections -notcontains $selection) {
|
||||
Write-Host $InvalidInputMessage
|
||||
continue
|
||||
}
|
||||
}
|
||||
elseif ($selection -lt $Minimum -or $selection -gt $Maximum) {
|
||||
Write-Host $InvalidInputMessage
|
||||
continue
|
||||
}
|
||||
|
||||
return $selection
|
||||
} until ($false)
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile, $DiskID) {
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
@@ -64,6 +117,68 @@ function Set-Computername($computername) {
|
||||
return $computername
|
||||
}
|
||||
|
||||
function Get-UnattendComputerNameValue {
|
||||
if ($null -eq $UnattendFile) {
|
||||
return $null
|
||||
}
|
||||
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
foreach ($component in $xml.unattend.settings.component) {
|
||||
if ($component.ComputerName) {
|
||||
return [string]$component.ComputerName
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Test-LegacyPromptComputerName($computername) {
|
||||
if ([string]::IsNullOrWhiteSpace($computername)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$normalizedName = $computername.Trim().ToLowerInvariant()
|
||||
return $normalizedName -in @('mycomputer', 'default')
|
||||
}
|
||||
|
||||
function Get-NormalizedComputerName($computername) {
|
||||
if ([string]::IsNullOrWhiteSpace($computername)) {
|
||||
throw 'Computer name cannot be empty.'
|
||||
}
|
||||
|
||||
$normalizedName = ($computername -replace "\s", '').Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedName)) {
|
||||
throw 'Computer name cannot be empty after removing spaces.'
|
||||
}
|
||||
|
||||
if ($normalizedName.Length -gt 15) {
|
||||
$normalizedName = $normalizedName.Substring(0, 15)
|
||||
}
|
||||
|
||||
return $normalizedName
|
||||
}
|
||||
|
||||
function Resolve-ComputerNameTemplate($computerNameTemplate, $serialNumber) {
|
||||
if ([string]::IsNullOrWhiteSpace($computerNameTemplate)) {
|
||||
throw 'Computer name template cannot be empty.'
|
||||
}
|
||||
|
||||
$resolvedName = $computerNameTemplate -replace '(?i)%serial%', $serialNumber
|
||||
if ($resolvedName -match '%') {
|
||||
throw 'Unsupported device name variable found. Only %serial% is supported.'
|
||||
}
|
||||
|
||||
return Get-NormalizedComputerName($resolvedName)
|
||||
}
|
||||
|
||||
function Set-ConfiguredComputerName($computername) {
|
||||
$normalizedName = Get-NormalizedComputerName($computername)
|
||||
$normalizedName = Set-Computername($normalizedName)
|
||||
Writelog "Computer name will be set to $normalizedName"
|
||||
Write-Host "Computer name will be set to $normalizedName"
|
||||
return $normalizedName
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
@@ -835,7 +950,7 @@ $LogFileName = 'ScriptLog.txt'
|
||||
$USBDrive = Get-USBDrive
|
||||
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
|
||||
$LogFile = $USBDrive + $LogFilename
|
||||
$version = '2603.2'
|
||||
$version = '2604.1'
|
||||
WriteLog 'Begin Logging'
|
||||
WriteLog "Script version: $version"
|
||||
|
||||
@@ -891,21 +1006,7 @@ else {
|
||||
}
|
||||
$displayList | Format-Table -AutoSize -Property Disk, 'Size (GB)', Sector, 'Bus Type', Model
|
||||
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$diskSelection = Read-Host 'Enter the disk number to apply the FFU to'
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid disk number'
|
||||
$var = $false
|
||||
}
|
||||
# Validate selected disk is in the list of available disks
|
||||
if ($var -and $validDiskIndexes -notcontains $diskSelection) {
|
||||
Write-Host "Invalid disk number. Please select from the available disks."
|
||||
$var = $false
|
||||
}
|
||||
} until ($var)
|
||||
$diskSelection = Read-MenuSelection -Prompt 'Enter the disk number to apply the FFU to' -InvalidInputMessage 'Invalid disk number. Please select from the available disks.' -ValidSelections $validDiskIndexes
|
||||
|
||||
$selectedDisk = $diskDriveCandidates | Where-Object { $_.Index -eq $diskSelection }
|
||||
WriteLog "Disk selection: DiskNumber=$($selectedDisk.Index), Model=$($selectedDisk.Model), SizeGB=$([math]::Round(($selectedDisk.Size / 1GB), 2)), BusType=$($selectedDisk.InterfaceType)"
|
||||
@@ -949,18 +1050,8 @@ If ($FFUCount -gt 1) {
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile | Out-Host
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount - 1) -and $var)
|
||||
$FFUSelected = Read-MenuSelection -Prompt 'Enter the FFU number to install' -InvalidInputMessage 'Input was not in correct format. Please enter a valid FFU number.' -Minimum 1 -Maximum $FFUCount
|
||||
$FFUSelected = $FFUSelected - 1
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
WriteLog "$FFUFileToInstall was selected"
|
||||
@@ -1023,13 +1114,26 @@ If (Test-Path -Path $UnattendComputerNamePath) {
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
If ($Unattend -or $UnattendPrefix -or $UnattendComputerName) {
|
||||
$UnattendConfiguredComputerName = $null
|
||||
$RequiresLegacyDeviceNamePrompt = $false
|
||||
$RequiresTemplateDeviceName = $false
|
||||
if ($Unattend) {
|
||||
$UnattendConfiguredComputerName = Get-UnattendComputerNameValue
|
||||
$RequiresLegacyDeviceNamePrompt = Test-LegacyPromptComputerName($UnattendConfiguredComputerName)
|
||||
if (-not [string]::IsNullOrWhiteSpace($UnattendConfiguredComputerName) -and $UnattendConfiguredComputerName -match '(?i)%serial%') {
|
||||
$RequiresTemplateDeviceName = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if naming is explicitly required
|
||||
If ($UnattendPrefix -or $UnattendComputerName -or $RequiresTemplateDeviceName -or $RequiresLegacyDeviceNamePrompt) {
|
||||
Write-SectionHeader 'Device Name Selection'
|
||||
if ($Unattend -and $UnattendPrefix) {
|
||||
Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
$skipPrefixSelection = $false
|
||||
$PrefixToUse = $null
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
@@ -1038,20 +1142,18 @@ If ($Unattend -or $UnattendPrefix -or $UnattendComputerName) {
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected - 1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount - 1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
Write-Host "`n$PrefixToUse was selected as device name prefix"
|
||||
$prefixSelection = Read-MenuSelection -Prompt 'Enter the prefix number to use for the device name (0 to skip)' -InvalidInputMessage 'Input was not in correct format. Please enter 0 to skip or a valid prefix number.' -Minimum 1 -Maximum $UnattendPrefixCount -AllowSkip
|
||||
if ($prefixSelection -eq 0) {
|
||||
$skipPrefixSelection = $true
|
||||
WriteLog 'User chose to skip device name prefix selection. Existing unattend computer name will remain unchanged.'
|
||||
Write-Host "`nDevice name prefix selection was skipped. The existing unattend computer name will remain unchanged."
|
||||
}
|
||||
else {
|
||||
$PrefixSelected = $prefixSelection - 1
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
Write-Host "`n$PrefixToUse was selected as device name prefix"
|
||||
}
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
@@ -1060,17 +1162,10 @@ If ($Unattend -or $UnattendPrefix -or $UnattendComputerName) {
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
Write-Host "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = ($PrefixToUse + $serial) -replace "\s", "" # Remove spaces because windows does not support spaces in the computer names
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15) {
|
||||
$computername = $computername.substring(0, 15)
|
||||
if (-not $skipPrefixSelection) {
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
$computername = Set-ConfiguredComputerName($PrefixToUse + $serial)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name will be set to $computername"
|
||||
Write-Host "Computer name will be set to $computername"
|
||||
}
|
||||
elseif ($Unattend -and $UnattendComputerName) {
|
||||
Writelog 'Unattend file found with SerialComputerNames.csv. Getting name for current computer.'
|
||||
@@ -1080,32 +1175,31 @@ If ($Unattend -or $UnattendPrefix -or $UnattendComputerName) {
|
||||
$SCName = $SerialComputerNames | Where-Object { $_.SerialNumber -eq $SerialNumber }
|
||||
|
||||
If ($SCName) {
|
||||
[string]$computername = $SCName.ComputerName
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name will be set to $computername"
|
||||
Write-Host "Computer name will be set to $computername"
|
||||
[string]$computername = Set-ConfiguredComputerName($SCName.ComputerName)
|
||||
}
|
||||
else {
|
||||
Writelog 'No matching serial number found in SerialComputerNames.csv. Setting random computer name to complete setup.'
|
||||
Write-Host 'No matching serial number found in SerialComputerNames.csv. Setting random computer name to complete setup.'
|
||||
[string]$computername = ("FFU-" + ( -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 11 | ForEach-Object { [char]$_ })))
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name will be set to $computername"
|
||||
Write-Host "Computer name will be set to $computername"
|
||||
[string]$computername = Set-ConfiguredComputerName(("FFU-" + ( -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 11 | ForEach-Object { [char]$_ }))))
|
||||
}
|
||||
}
|
||||
elseif ($Unattend) {
|
||||
elseif ($Unattend -and $RequiresTemplateDeviceName) {
|
||||
Writelog 'Unattend file found with a %serial% computer name template. Resolving the template.'
|
||||
$serialNumber = (Get-CimInstance -ClassName Win32_Bios).SerialNumber.Trim()
|
||||
[string]$computername = Set-ConfiguredComputerName((Resolve-ComputerNameTemplate -computerNameTemplate $UnattendConfiguredComputerName -serialNumber $serialNumber))
|
||||
}
|
||||
elseif ($Unattend -and $RequiresLegacyDeviceNamePrompt) {
|
||||
Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
Write-Host 'Unattend file found but no prefixes.txt. Please enter a device name.'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name will be set to $computername"
|
||||
Write-Host "Computer name will be set to $computername"
|
||||
[string]$computername = Set-ConfiguredComputerName((Read-Host 'Enter device name'))
|
||||
}
|
||||
else {
|
||||
WriteLog 'Device naming assets detected without unattend.xml. Skipping device naming prompts.'
|
||||
}
|
||||
}
|
||||
elseif ($Unattend) {
|
||||
WriteLog 'Unattend file found. Device naming is not required, but unattend settings will still be applied.'
|
||||
}
|
||||
else {
|
||||
WriteLog 'No unattend folder found. Device name will be set via PPKG, AP JSON, or default OS name.'
|
||||
}
|
||||
@@ -1114,17 +1208,7 @@ else {
|
||||
If ($autopilot -eq $true -and $PPKG -eq $true) {
|
||||
WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APorPPKG = Read-Host 'Enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
$APorPPKG = Read-MenuSelection -Prompt 'Enter 1 for Autopilot or 2 for Provisioning Package' -InvalidInputMessage 'Incorrect value. Please enter 1 for Autopilot or 2 for Provisioning Package.' -Minimum 1 -Maximum 2
|
||||
If ($APorPPKG -eq 1) {
|
||||
$PPKG = $false
|
||||
}
|
||||
@@ -1143,22 +1227,20 @@ If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$APFileSelected = Read-Host 'Enter the AP json file number to install'
|
||||
$APFileSelected = $APFileSelected - 1
|
||||
}
|
||||
$APFileSelection = Read-MenuSelection -Prompt 'Enter the AP json file number to install (0 to skip)' -InvalidInputMessage 'Input was not in correct format. Please enter 0 to skip or a valid AP json file number.' -Minimum 1 -Maximum $APFilesCount -AllowSkip
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount - 1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
WriteLog "$APFileToInstall was selected"
|
||||
if ($APFileSelection -eq 0) {
|
||||
$APFileToInstall = $null
|
||||
$APFileName = $null
|
||||
WriteLog 'User chose to skip Autopilot JSON selection.'
|
||||
Write-Host "`nAutopilot JSON selection was skipped."
|
||||
}
|
||||
else {
|
||||
$APFileSelected = $APFileSelection - 1
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
WriteLog "$APFileToInstall was selected"
|
||||
}
|
||||
}
|
||||
elseif ($APFilesCount -eq 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount AP File"
|
||||
@@ -1181,22 +1263,19 @@ If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PPKGFileSelected = Read-Host 'Enter the PPKG file number to install'
|
||||
$PPKGFileSelected = $PPKGFileSelected - 1
|
||||
}
|
||||
$PPKGFileSelection = Read-MenuSelection -Prompt 'Enter the PPKG file number to install (0 to skip)' -InvalidInputMessage 'Input was not in correct format. Please enter 0 to skip or a valid PPKG file number.' -Minimum 1 -Maximum $PPKGFilesCount -AllowSkip
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount - 1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
Write-Host "`n$PPKGFileToInstall will be used"
|
||||
if ($PPKGFileSelection -eq 0) {
|
||||
$PPKGFileToInstall = $null
|
||||
WriteLog 'User chose to skip Provisioning Package selection.'
|
||||
Write-Host "`nProvisioning Package selection was skipped."
|
||||
}
|
||||
else {
|
||||
$PPKGFileSelected = $PPKGFileSelection - 1
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
Write-Host "`n$PPKGFileToInstall will be used"
|
||||
}
|
||||
}
|
||||
elseif ($PPKGFilesCount -eq 1 -and $PPKG -eq $true) {
|
||||
Write-SectionHeader -Title 'Provisioning Package Selection'
|
||||
@@ -1312,7 +1391,7 @@ if ($null -eq $DriverSourcePath) {
|
||||
if ([string]::IsNullOrWhiteSpace($relativeSegment)) {
|
||||
return Split-Path -Path $normalizedPath -Leaf
|
||||
}
|
||||
return $relativePath = $relativeSegment
|
||||
return $relativeSegment
|
||||
}
|
||||
return $normalizedPath
|
||||
}
|
||||
@@ -1388,25 +1467,17 @@ if ($null -eq $DriverSourcePath) {
|
||||
}
|
||||
}
|
||||
$displayArray | Format-Table -Property Number, Type, RelativePath -AutoSize
|
||||
|
||||
|
||||
$DriverSelected = -1
|
||||
$skipDriverInstall = $false
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$userSelection = Read-Host 'Enter the number of the driver source to install (0 to skip)'
|
||||
if ($userSelection -eq 0) {
|
||||
$skipDriverInstall = $true
|
||||
break
|
||||
}
|
||||
$DriverSelected = $userSelection - 1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid number.'
|
||||
$var = $false
|
||||
}
|
||||
} until ((($DriverSelected -ge 0 -and $DriverSelected -lt $DriverSourcesCount) -or $skipDriverInstall) -and $var)
|
||||
|
||||
$userSelection = Read-MenuSelection -Prompt 'Enter the number of the driver source to install (0 to skip)' -InvalidInputMessage 'Input was not in correct format. Please enter 0 to skip or a valid number.' -Minimum 1 -Maximum $DriverSourcesCount -AllowSkip
|
||||
if ($userSelection -eq 0) {
|
||||
$skipDriverInstall = $true
|
||||
}
|
||||
else {
|
||||
$DriverSelected = $userSelection - 1
|
||||
}
|
||||
|
||||
if ($skipDriverInstall) {
|
||||
$DriverSourcePath = $null
|
||||
$DriverSourceType = $null
|
||||
@@ -1568,8 +1639,9 @@ If ($PPKGFileToInstall) {
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($computername) {
|
||||
Write-SectionHeader -Title 'Applying Computer Name and Unattend Configuration'
|
||||
If ($Unattend) {
|
||||
$unattendSectionTitle = if ($computername) { 'Applying Computer Name and Unattend Configuration' } else { 'Applying Unattend Configuration' }
|
||||
Write-SectionHeader -Title $unattendSectionTitle
|
||||
try {
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir) {
|
||||
@@ -1590,8 +1662,8 @@ If ($computername) {
|
||||
}
|
||||
}
|
||||
catch {
|
||||
WriteLog "Copying Unattend.xml to name device failed"
|
||||
Stop-Script -Message "Copying Unattend.xml to name device failed with error: $_"
|
||||
WriteLog 'Copying Unattend.xml to Panther failed'
|
||||
Stop-Script -Message "Copying Unattend.xml to Panther failed with error: $_"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
SerialNumber,ComputerName
|
||||
ABC12345,CORP-001
|
||||
DEF67890,KIOSK-010
|
||||
XYZ24680,STORE-015
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<settings pass="specialize">
|
||||
<!--<ComputerName> must be in the first Component Element "Microsoft-Windows-Shell-Setup" . Do not change the order or remove it -->
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ComputerName>MYCOMPUTER</ComputerName><!--Leave Default will be renamed-->
|
||||
<ComputerName>*</ComputerName><!--Leave Default will be renamed-->
|
||||
<TimeZone>Eastern Standard Time</TimeZone><!--Add Your Local TimeZone-->
|
||||
</component>
|
||||
<!-- Place additional Components Elements and Settings below here: -->
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<settings pass="specialize">
|
||||
<!--<ComputerName> must be in the first Component Element "Microsoft-Windows-Shell-Setup" . Do not change the order or remove it -->
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ComputerName>MyComputer</ComputerName>
|
||||
<ComputerName>*</ComputerName>
|
||||
</component>
|
||||
<!--Place addtional Components Elements and settings below here. -->
|
||||
</settings>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<settings pass="specialize">
|
||||
<!--<ComputerName> must be in the first Component Element "Microsoft-Windows-Shell-Setup" . Do not change the order or remove it -->
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ComputerName>MyComputer</ComputerName>
|
||||
<ComputerName>*</ComputerName>
|
||||
</component>
|
||||
<!--Place addtional Components Elements and settings below here. -->
|
||||
</settings>
|
||||
|
||||
Reference in New Issue
Block a user