<# 2Pintlabs FrontEnd-JSONBasedDynamic.ps1 This is community, no official support Change Log: 26.7.6 - Added Checkbox for P2P Enablement to allow user to choose if they want to enable P2P for the rest of the task sequence - It will default what the peering variable is already set to, but user can uncheck if they do not want to enable P2P #> $ScriptVersion = '26.7.6.8.55' #Region Functions # Helper function to stop transcription function Stop-FrontendTranscription { try { if ((Get-Command Stop-Transcript -ErrorAction SilentlyContinue) -and (Get-Variable -Name transcript -Scope Global -ErrorAction SilentlyContinue)) { Stop-Transcript -ErrorAction SilentlyContinue Write-CMTraceLog -Message "Stopped PowerShell transcription" -Type "Info" -Component "Main" } } catch {} } Function Get-InputFormData { <# .SYNOPSIS Creates a WPF form to collect user input including computer naming strategy and user role selection. .DESCRIPTION This script displays a Windows Presentation Foundation (WPF) form with: - Radio buttons to select computer naming strategy: * Do not set computer name * Use custom computer name (manual entry, max 15 characters) * Use hardware-based name (prefix + serial number or MAC address) - A dropdown list for selecting user role from predefined options The form returns a PSObject with the user's selections. .EXAMPLE $result = .\New-InputForm.ps1 if ($result.FormSubmitted) { Write-Host "Naming Strategy: $($result.NamingStrategy)" Write-Host "Generated Name: $($result.GeneratedComputerName)" Write-Host "User Role: $($result.SelectedUserRole)" } .NOTES Author: Created for 2PintLabs by Gary Blok Date: October 20, 2025 #> Add-Type -AssemblyName PresentationFramework # If no explicit LogoPath was provided earlier, try to use Logo-blue.png located # in the same directory as this script. # Resolve script directory robustly to support dot-sourcing and different PowerShell hosts $scriptDir = $null if ((Get-Module -name "DeployR.Utility") -and (-not (test-path -path "HKLM:\SOFTWARE\2Pint Software\DeployR\GeneralSettings"))) { $scriptDir = ${TSEnv:_CONTENT-CONTENT} Write-Host "Resolved script directory via TS Var _CONTENT-CONTENT: $scriptDir" -ForegroundColor Cyan } if (-not $scriptDir){ try { $scriptDir = $PSScriptRoot } catch {} if (-not $scriptDir) { try { if ($PSCommandPath) { $scriptDir = Split-Path -Parent $PSCommandPath } elseif ($MyInvocation -and $MyInvocation.MyCommand -and $MyInvocation.MyCommand.Definition) { $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition } elseif ($MyInvocation -and $MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) { $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path } else { $scriptDir = (Get-Location).Path } } catch{} } if (-not $scriptDir){ $scriptDir = (Get-Location).Path } Write-Host "Resolved script directory via fallback methods: $scriptDir" -ForegroundColor Cyan } # Load FrontEndConfig.json from the script directory into $JSONConfig $JSONFallbackConfigURL = 'https://raw.githubusercontent.com/gwblok/2PintLabs/refs/heads/main/DeployR/FrontEnd/FrontEndJSONDrivenDynamicApps/FrontEndConfig.json' $JSONConfig = $null try { if ($scriptDir) { Write-Host "Attempting to load FrontEndConfig.json from script directory: $scriptDir" -ForegroundColor Cyan $configPath = Join-Path -Path $scriptDir -ChildPath 'FrontEndConfig.json' if (Test-Path -Path $configPath) { Write-Host "Found FrontEndConfig.json at $configPath" -ForegroundColor Green $JSONConfig = Get-Content -Path $configPath -Raw | ConvertFrom-Json -ErrorAction Stop try { Write-CMTraceLog -Message "Loaded FrontEndConfig.json from $configPath" -Type "Info" -Component "Config" } catch {} } } $configPath = Join-Path -Path $scriptDir -ChildPath 'FrontEndConfig.json' -ErrorAction SilentlyContinue if (-not $JSONConfig) { Write-Verbose "FrontEndConfig.json not found at $configPath" try { Write-CMTraceLog -Message "FrontEndConfig.json not found at $configPath" -Type "Warning" -Component "Config" } catch {} # Attempt to load JSON config from fallback URL. Use Invoke-RestMethod first # (it returns a parsed object). If that fails, fetch raw content and # ConvertFrom-Json explicitly. try { Write-Host "Attempting to load FrontEndConfig.json from fallback URL: $JSONFallbackConfigURL" -ForegroundColor Cyan try { $JSONConfig = Invoke-RestMethod -Uri $JSONFallbackConfigURL -ErrorAction Stop } catch { # Fallback to raw content parsing $content = (Invoke-WebRequest -Uri $JSONFallbackConfigURL -ErrorAction Stop).Content $JSONConfig = $content | ConvertFrom-Json -ErrorAction Stop } Write-Host "Successfully loaded FrontEndConfig.json from fallback URL" -ForegroundColor Green try { Write-CMTraceLog -Message "Loaded FrontEndConfig.json from fallback URL: $JSONFallbackConfigURL" -Type "Info" -Component "Config" } catch {} } catch { Write-Warning "Failed to load FrontEndConfig.json from fallback URL: $_" try { Write-CMTraceLog -Message "Failed to load FrontEndConfig.json from fallback URL: $_" -Type "Warning" -Component "Config" } catch {} } } } catch { Write-Warning "Failed to load or parse FrontEndConfig.json: $_" try { Write-CMTraceLog -Message "Failed to load or parse FrontEndConfig.json: $_" -Type "Warning" -Component "Config" } catch {} } ########################################### #Build Data from JSON ########################################## #Usage (DeployR or ConfigMgr) $Usage = $JSONConfig.Usage if (-not $Usage) { $Usage = "DeployR" } # Default to DeployR if not specified ########################################### #Build Data from JSON ########################################## #Usage (DeployR or ConfigMgr) $Usage = $JSONConfig.Usage if (-not $Usage) { $Usage = "DeployR" } # Default to DeployR if not specified #Logo File Name $LogoFileName = $JSONConfig.LogoFileName if (-not (Get-Variable -Name LogoPath -ErrorAction SilentlyContinue)) { $LogoPath = $null } write-host "Logo file name from JSON config: $LogoFileName" -ForegroundColor Cyan try { if ([string]::IsNullOrWhiteSpace($LogoPath) -and (Test-Path $scriptDir)) { $possibleLogo = Join-Path -Path $scriptDir -ChildPath $LogoFileName if (Test-Path -Path $possibleLogo) { $LogoPath = $possibleLogo Write-Host "Using logo image at $LogoPath" -ForegroundColor Green } } if ([string]::IsNullOrWhiteSpace($LogoPath)) { #Download Default Logo from GitHub and save to temp path $2PintLogoDefaultURL = 'https://raw.githubusercontent.com/gwblok/2PintLabs/refs/heads/main/DeployR/FrontEnd/FrontEndJSONDriven/Logo.png' $tempLogoPath = Join-Path -Path $env:TEMP -ChildPath $LogoFileName Write-Host "Downloading default logo from $2PintLogoDefaultURL to $tempLogoPath" -ForegroundColor Cyan try { Invoke-WebRequest -Uri $2PintLogoDefaultURL -OutFile $tempLogoPath -ErrorAction Stop if (Test-Path -Path $tempLogoPath) { $LogoPath = $tempLogoPath Write-Host "Successfully downloaded default logo to $LogoPath" -ForegroundColor Green } else { Write-Warning "Failed to download default logo to $tempLogoPath" } } catch { Write-Warning "Error downloading default logo $_" } } } catch {} #Default Domain Suffix $DefaultDomainSuffix = $JSONConfig.DomainSuffix #OU Options $OUOptions = @() $JSONConfig.OUs | ForEach-Object { $OUOptions += $_ } #Autopilot Group Tags $AutopilotGroupTagOptions = @() $JSONConfig.AutopilotGroupTags | ForEach-Object { $AutopilotGroupTagOptions += $_ } #Role Options $RoleOptions = @() $JSONConfig.Roles | ForEach-Object { $RoleOptions += $_ } #Finish Action Options $FinishActionOptions = @('Shutdown', 'Restart', 'Reseal', 'Log Off', 'Nothing') # Software options - try to pull dynamically from DeployR, fall back to static list if unavailable # Try to get apps dynamically from DeployR $UseDeployRSoftwareList = $JSONConfig.SoftwareFromDeployR $SoftwareTagForDeployR = $JSONConfig.SoftwareFromDeployRTag $SoftwareOptions = $null $DeployRRetrievalFailed = $false # Only attempt DeployR retrieval if explicitly enabled in JSON config if ($UseDeployRSoftwareList -eq "True") { Write-Host "Attempting to retrieve software list from DeployR..." -ForegroundColor Cyan try { # Call the function that's defined later in this script $script:DeployRApps = Get-DeployRFrontEndApps -Tag $SoftwareTagForDeployR -ErrorAction Stop if ($script:DeployRApps -and $script:DeployRApps.Count -gt 0) { Write-Host "Successfully retrieved $($script:DeployRApps.Count) apps from DeployR" -ForegroundColor Green # Build PSObject array with DisplayName and Id (Id = name without spaces) $SoftwareOptions = @() foreach ($app in $script:DeployRApps) { $SoftwareOptions += [PSCustomObject]@{ DisplayName = $app.Name Id = $app.Id AppID = $app.Id } } } else { # DeployR call succeeded but returned no apps $DeployRRetrievalFailed = $true } } catch { $msg = "Could not retrieve apps from DeployR: $($_.Exception.Message)" Write-Warning $msg try { Write-CMTraceLog -Message $msg -Type "Warning" -Component "DeployR" } catch {} $DeployRRetrievalFailed = $true } } else { Write-Host "DeployR software retrieval disabled in config - using static list from JSON" -ForegroundColor Cyan } # Fall back to static list if: # 1. DeployR was not enabled, OR # 2. DeployR retrieval was attempted but failed if (-not $SoftwareOptions -or $SoftwareOptions.Count -eq 0) { $msg = "Using static software list from JSON config" Write-Host $msg -ForegroundColor Yellow try { Write-CMTraceLog -Message $msg -Type "Info" -Component "Software" } catch {} $SoftwareOptions = @() $JSONConfig.Software | ForEach-Object { $SoftwareOptions += [PSCustomObject]@{ DisplayName = $_.DisplayName Id = $_.Id } } } # Define hardware ID type options (If you change this, you'll need to also update methods to gather this info) $HardwareIdOptions = @( "Serial Number", "MAC Address", "Asset Tag" ) #Region Collection Hardware Information: $LocalInfo = @{} $LocalInfo['IsDesktop'] = "False" $LocalInfo['IsLaptop'] = "False" $LocalInfo['IsServer'] = "False" $LocalInfo['IsSFF'] = "False" $LocalInfo['IsTablet'] = "False" Get-CimInstance -ClassName Win32_SystemEnclosure | ForEach-Object { if ($_.ChassisTypes[0] -in "8", "9", "10", "11", "12", "14", "18", "21") { $LocalInfo['IsLaptop'] = "True"; $LocalInfo['Chassis'] = "Laptop"} if ($_.ChassisTypes[0] -in "3", "4", "5", "6", "7", "15", "16") { $LocalInfo['IsDesktop'] = "True"; $LocalInfo['Chassis'] = "Desktop"} if ($_.ChassisTypes[0] -in "23") { $LocalInfo['IsServer'] = "True"; $LocalInfo['Chassis'] = "Server"} if ($_.ChassisTypes[0] -in "34", "35", "36") { $LocalInfo['IsSFF'] = "True"; $LocalInfo['Chassis'] = "Small Form Factor"} if ($_.ChassisTypes[0] -in "13", "31", "32", "30") {$LocalInfo['IsTablet'] = "True"; $LocalInfo['Chassis'] = "Tablet"} } # Chassis info collected into LocalInfo if needed $macList = @() Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = 1" | ForEach-Object { $_.MacAddress | ForEach-Object { $macList += $_ } } $ipList = @() Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = 1" | ForEach-Object { $_.IPAddress | ForEach-Object { $ipList += $_ } } $gwList = @() Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = 1" | ForEach-Object { if ($_.DefaultIPGateway) { $_.DefaultIPGateway | ForEach-Object { $gwList += $_ } } } $SerialNumber = (Get-CimInstance -ClassName Win32_BIOS).SerialNumber #Round Memory to Nearest GB $Memory = [math]::Round((Get-CimInstance -ClassName Win32_ComputerSystem).TotalPhysicalMemory / 1024 / 1024 / 1024) $LocalInfo = @{} $LocalInfo['Make'] = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer.Trim() $LocalInfo['IsVM'] = "False" Switch -Wildcard ($LocalInfo['Make']) { "*Microsoft*" { $LocalInfo['MakeAlias'] = "Microsoft" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = Get-CimInstance -ClassName MS_SystemInformation -Namespace root\wmi | Select-Object -ExpandProperty SystemSKU # Logic for Hyper-V Testing If ($LocalInfo['ModelAlias'] -eq "Virtual Machine") { $LocalInfo['SystemAlias'] = Get-CimInstance -ClassName MS_SystemInformation -Namespace root\wmi | Select-Object -ExpandProperty SystemVersion $LocalInfo['IsVM'] = "True" } } "*HP*" { $LocalInfo['MakeAlias'] = "HP" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName MS_SystemInformation -NameSpace root\wmi).BaseBoardProduct.Trim() } "*VMWare*" { $LocalInfo['MakeAlias'] = "VMWare" # $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() # Default, sets alias to same as model # $LocalInfo['ModelAlias'] = ((Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim()).replace(",","_") # Remove the "," and replace with "_" $LocalInfo['ModelAlias'] = ((Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim()).replace(" ","_").replace(",","_") # Remove the "," and replace with "_", Remove the " " and replace with "_" $LocalInfo['SystemAlias'] = Get-CimInstance -ClassName MS_SystemInformation -Namespace root\wmi | Select-Object -ExpandProperty SystemSKU $LocalInfo['IsVM'] = "True" } "*QEMU*" { $LocalInfo['MakeAlias'] = "QEMU" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = Get-CimInstance -ClassName MS_SystemInformation -Namespace root\wmi | Select-Object -ExpandProperty SystemSKU $LocalInfo['IsVM'] = "True" } "*Innotek*" { $LocalInfo['MakeAlias'] = "Innotek" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = Get-CimInstance -ClassName MS_SystemInformation -Namespace root\wmi | Select-Object -ExpandProperty SystemSKU $LocalInfo['IsVM'] = "True" } "*Hewlett-Packard*" { $LocalInfo['MakeAlias'] = "HP" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName MS_SystemInformation -NameSpace root\wmi).BaseBoardProduct.Trim() } "*Dell*" { $LocalInfo['MakeAlias'] = "Dell" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName MS_SystemInformation -NameSpace root\wmi ).SystemSku.Trim() } "*Lenovo*" { $LocalInfo['MakeAlias'] = "Lenovo" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystemProduct | Select-Object -ExpandProperty Version).Trim() $LocalInfo['SystemAlias'] = ((Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).SubString(0, 4)).Trim() } "*Intel(R) Client Systems*" { $LocalInfo['MakeAlias'] = "Intel(R) Client Systems" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystemProduct | Select-Object -ExpandProperty Version).Trim() $LocalInfo['SystemAlias'] = ((Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim()) $LocalInfo['SystemAlias'] = $LocalInfo['SystemAlias'].SubString(0, $LocalInfo['SystemAlias'].IndexOf("i")).Trim() } "*Panasonic*" { $LocalInfo['MakeAlias'] = "Panasonic Corporation" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName MS_SystemInformation -NameSpace root\wmi ).BaseBoardProduct.Trim() } "*Viglen*" { $LocalInfo['MakeAlias'] = "Viglen" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName Win32_BaseBoard | Select-Object -ExpandProperty SKU).Trim() } "*AZW*" { $LocalInfo['MakeAlias'] = "AZW" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName MS_SystemInformation -NameSpace root\wmi ).BaseBoardProduct.Trim() } "*Fujitsu*" { $LocalInfo['MakeAlias'] = "Fujitsu" $LocalInfo['ModelAlias'] = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model).Trim() $LocalInfo['SystemAlias'] = (Get-CimInstance -ClassName Win32_BaseBoard | Select-Object -ExpandProperty SKU).Trim() } Default { $LocalInfo['MakeAlias'] = "NA" $LocalInfo['ModelAlias'] = "NA" $LocalInfo['SystemAlias'] = "NA" } # Closing for switch block } $MakeAlias = $LocalInfo['MakeAlias'] $ModelAlias = $LocalInfo['ModelAlias'] $SystemAlias = $LocalInfo['SystemAlias'] $AssetTag = (Get-CimInstance -ClassName Win32_SystemEnclosure).SMBIOSAssetTag.Trim() # Function to get hardware information function Get-HardwareId { param( [string]$Type ) try { if ($Type -eq "Serial Number") { $serial = (Get-CimInstance -ClassName Win32_BIOS -ErrorAction SilentlyContinue).SerialNumber return $serial } elseif ($Type -eq "MAC Address") { $mac = (Get-CimInstance -ClassName Win32_NetworkAdapter -ErrorAction SilentlyContinue | Where-Object { $_.PhysicalAdapter -and $_.MACAddress } | Select-Object -First 1).MACAddress # Remove colons and dashes from MAC address if ($mac) { return $mac -replace '[:-]', '' } } elseif ($Type -eq "Asset Tag") { try { $assetObj = Get-CimInstance -ClassName Win32_SystemEnclosure -ErrorAction SilentlyContinue | Select-Object -First 1 if ($assetObj -and $assetObj.SMBIOSAssetTag -and -not [string]::IsNullOrWhiteSpace($assetObj.SMBIOSAssetTag)) { return $assetObj.SMBIOSAssetTag.Trim() } } catch { # ignore and fall through to UNKNOWN } } } catch { return "UNKNOWN" } return "UNKNOWN" } #endregion Hardware # Build dynamic UI strings based on Usage $WindowTitle = "System Configuration - $Usage OSD" $HeaderText = "System Configuration - $Usage" $DomainSuffixLabel = if ($Usage -eq "ConfigMgr") { "Domain Suffix, used as Domain for Domain Join:" } else { "Domain Suffix (optional):" } $DomainJoinRadioLabel = if ($Usage -eq "ConfigMgr") { "Domain Join" } else { "Offline Domain Join" } # XAML Form Definition [xml]$XAML = @"