Private/core-device/Initialize-OSDCoreDevice.ps1
|
function Initialize-OSDCoreDevice { <# .SYNOPSIS Initializes the OSDCore device inventory and diagnostics context. .DESCRIPTION Initialize-OSDCoreDevice collects local hardware, firmware, TPM, keyboard, and network information from CIM, environment variables, and UEFI checks. It creates diagnostic artifacts in $env:TEMP\osdcloud-logs, normalizes key identity values such as manufacturer/model/product, and builds $global:OSDCoreDevice as an ordered property bag used by deployment and workflow orchestration functions. The function is intended for internal module initialization and is called before workflow execution so downstream steps can rely on a consistent device state snapshot. .EXAMPLE Initialize-OSDCoreDevice Collects current device metadata, creates or updates $global:OSDCoreDevice, and writes log artifacts for troubleshooting. .EXAMPLE Initialize-OSDCoreDevice -Verbose Runs initialization and emits additional details about discovered disks, network adapters, keyboard selection, and support checks. .INPUTS None. You cannot pipe input to this function. .OUTPUTS None. This function does not emit pipeline output. .NOTES Side effects: - Clears the current PowerShell error collection. - Attempts to sync date/time (best effort) through Sync-OSDCoreDateTime. - Writes diagnostic logs to $env:TEMP\osdcloud-logs. - Attempts to stage logs in an OSDCloudLogs destination when available. - Updates global state in $global:OSDCoreDevice. Changelog: - 2026-08-05 | pending | Resolve OS catalog provider selection overwrite. Replaced unconditional dual assignment of OSDCoreOperatingSystems with command-aware selection logic that uses the available provider function and throws a descriptive error when neither provider is present. - 2026-08-05 | ef3e4fb | Core catalog initialization wiring update. Integrated initializer into the broader core startup path and aligned device state dependencies for downstream catalog and workflow logic. - 2026-08-04 | 538d747 | Initial OSDCoreDevice initializer introduced. Added core CIM collection, log export pipeline, normalization helpers, and baseline global property map for workflow consumers. Maintainer guidance: - Add a new changelog entry in this NOTES section for every functional behavior change in this function. - Keep entries in reverse chronological order using this format: YYYY-MM-DD | <short-hash> | <summary> #> [CmdletBinding()] param () #================================================= $Error.Clear() Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)]" #================================================= try { Sync-OSDCoreDateTime -ThresholdMinutes 5 -Force -ErrorAction Stop } catch { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to sync date/time: $($_.Exception.Message)" } #================================================= # Set the osdcloud-logs Path $LogsPath = "$env:TEMP\osdcloud-logs" if (-not (Test-Path -Path $LogsPath)) { New-Item -Path $LogsPath -ItemType Directory -Force | Out-Null } #================================================= # Real Architecture $ProcessorArchitecture = $env:PROCESSOR_ARCHITECTURE #================================================= # ipconfig ipconfig | Out-File (Join-Path -Path $LogsPath -ChildPath 'Network_IPConfig.txt') -Width 4096 -Force #================================================= # Win32_BaseBoard $classWin32BaseBoard = Get-CimInstance -ClassName Win32_BaseBoard | Select-Object -Property * $classWin32BaseBoard | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_BaseBoard.txt') -Width 4096 -Force $BaseBoardProduct = $classWin32BaseBoard.Product | ConvertTo-TrimmedString #================================================= # Win32_Battery try { $classWin32Battery = Get-CimInstance -ClassName Win32_Battery | Select-Object -Property * $classWin32Battery | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_Battery.txt') -Width 4096 -Force } catch { $classWin32Battery = $null } [System.Boolean]$IsOnBattery = $false if ($classWin32Battery) { $IsOnBattery = ($classWin32Battery.BatteryStatus -contains 1) } #================================================= # Win32_BIOS $classWin32BIOS = Get-CimInstance -ClassName Win32_BIOS | Select-Object -Property * $classWin32BIOS | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_BIOS.txt') -Width 4096 -Force #================================================= # Win32_ComputerSystem $classWin32ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -Property * $classWin32ComputerSystem | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_ComputerSystem.txt') -Width 4096 -Force $ComputerManufacturer = $classWin32ComputerSystem.Manufacturer | ConvertTo-TrimmedString $ComputerModel = $classWin32ComputerSystem.Model | ConvertTo-TrimmedString $ComputerSystemFamily = $classWin32ComputerSystem.SystemFamily | ConvertTo-TrimmedString $ComputerSystemSKU = $classWin32ComputerSystem.SystemSKUNumber | ConvertTo-TrimmedString #================================================= # Win32_ComputerSystemProduct $classWin32ComputerSystemProduct = Get-CimInstance -ClassName Win32_ComputerSystemProduct | Select-Object -Property * $classWin32ComputerSystemProduct | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_ComputerSystemProduct.txt') -Width 4096 -Force $ComputerSystemProduct = $classWin32ComputerSystemProduct.Version | ConvertTo-TrimmedString #================================================= # Win32_DiskDrive $classWin32DiskDrive = Get-CimInstance -ClassName Win32_DiskDrive | Select-Object -Property * $classWin32DiskDrive | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_DiskDrive.txt') -Width 4096 -Force foreach ($Item in $classWin32DiskDrive) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Disk: $($Item.Model) [$($Item.DeviceID)]" } #================================================= # Win32_Environment $classWin32Environment = Get-CimInstance -ClassName Win32_Environment | Select-Object -Property * | Sort-Object Name $classWin32Environment | Where-Object { $_.SystemVariable -eq $true } | Select-Object -Property Name, VariableValue | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_Environment-System.txt') -Width 4096 -Force #================================================= # Win32_Keyboard try { $classWin32Keyboard = Get-CimInstance -ClassName Win32_Keyboard -ErrorAction Stop | Select-Object -Property * $classWin32Keyboard | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_Keyboard.txt') -Width 4096 -Force # A device can expose multiple keyboard endpoints. Prefer entries with # a populated layout and stable ordering, then use the first candidate. $keyboardCandidates = @($classWin32Keyboard) | Where-Object { -not [string]::IsNullOrWhiteSpace($_.Layout) -or -not [string]::IsNullOrWhiteSpace($_.Name) } | Sort-Object -Property @( @{ Expression = { [string]::IsNullOrWhiteSpace($_.Layout) } ; Descending = $false }, @{ Expression = { [string]$_.Name } ; Descending = $false }, @{ Expression = { [string]$_.DeviceID } ; Descending = $false } ) $primaryKeyboard = $keyboardCandidates | Select-Object -First 1 if ($primaryKeyboard) { $KeyboardLayout = [System.String]$primaryKeyboard.Layout $KeyboardName = [System.String]$primaryKeyboard.Name if ($keyboardCandidates.Count -gt 1) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Multiple keyboards detected ($($keyboardCandidates.Count)). Using '$KeyboardName' with layout '$KeyboardLayout'." } } else { $KeyboardLayout = $null $KeyboardName = $null } } catch { $classWin32Keyboard = $null $KeyboardLayout = $null $KeyboardName = $null } #================================================= # Win32_NetworkAdapter $classWin32NetworkAdapter = Get-CimInstance -ClassName Win32_NetworkAdapter | Select-Object -Property * $classWin32NetworkAdapter | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_NetworkAdapter.txt') -Width 4096 -Force $NetworkAdapterGuid = $classWin32NetworkAdapter | Where-Object { $null -ne $_.GUID } if ($NetworkAdapterGuid) { foreach ($Item in $NetworkAdapterGuid) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] NetAdapter: $($Item.Name) [$($Item.MACAddress)]" } } #================================================= # Win32_NetworkAdapterConfiguration $classWin32NetworkAdapterConfiguration = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true } | Select-Object -Property * $classWin32NetworkAdapterConfiguration | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_NetworkAdapterConfiguration.txt') -Width 4096 -Force foreach ($Item in $classWin32NetworkAdapterConfiguration) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] NetAdapterConfig: $($Item.IPAddress) [$($Item.Description)]" } $NetIPAddress = @() $NetMacAddress = @() $NetGateways = @() $classWin32NetworkAdapterConfiguration | ForEach-Object { $_.IPAddress | ForEach-Object { $NetIPAddress += $_ } $_.MacAddress | ForEach-Object { $NetMacAddress += $_ } $_.DefaultIPGateway | ForEach-Object { $NetGateways += $_ } } #================================================= # Win32_OperatingSystem $classWin32OperatingSystem = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -Property * $classWin32OperatingSystem | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_OperatingSystem.txt') -Width 4096 -Force $OSArchitecture = $classWin32OperatingSystem.OSArchitecture | ConvertTo-TrimmedString #================================================= # Win32_PnPEntity $classWin32PnPEntity = Get-CimInstance -ClassName Win32_PnPEntity | Select-Object -Property * $classWin32PnPEntityError = $classWin32PnPEntity | Where-Object { $_.Status -eq 'Error' } | Sort-Object HardwareID -Unique | Sort-Object Name if ($classWin32PnPEntityError) { $classWin32PnPEntityError | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_PnPEntityError.txt') -Width 4096 -Force } $SystemFirmwareDevice = $classWin32PnPEntity | Where-Object ClassGuid -eq '{f2e7dd72-6468-4e36-b6f1-6488f42c1b52}' | Where-Object Caption -match 'System' if ($SystemFirmwareDevice) { $GuidPattern = '\{?(([0-9a-f]){8}-([0-9a-f]){4}-([0-9a-f]){4}-([0-9a-f]){4}-([0-9a-f]){12})\}?' $SystemFirmwareResource = ($SystemFirmwareDevice.PNPDeviceID | Select-String -Pattern $GuidPattern -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value) $SystemFirmwareHardwareId = $SystemFirmwareResource -replace '[{}]','' } else { $SystemFirmwareDevice = $null $SystemFirmwareResource = $null $SystemFirmwareHardwareId = $null } #================================================= # Win32_Processor $classWin32Processor = Get-CimInstance -ClassName Win32_Processor | Select-Object -Property * $classWin32Processor | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_Processor.txt') -Width 4096 -Force foreach ($Item in $classWin32Processor) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Processor: $($Item.Name) [$($Item.NumberOfLogicalProcessors) Logical]" } #================================================= # Win32_SystemEnclosure $classWin32SystemEnclosure = Get-CimInstance -ClassName Win32_SystemEnclosure | Select-Object -Property * $classWin32SystemEnclosure | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_SystemEnclosure.txt') -Width 4096 -Force #================================================= # Win32_SystemTimeZone $classWin32SystemTimeZone = Get-CimInstance -ClassName Win32_SystemTimeZone | Select-Object -Property * $classWin32SystemTimeZone | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_SystemTimeZone.txt') -Width 4096 -Force #================================================= # Win32_TimeZone $classWin32TimeZone = Get-CimInstance -ClassName Win32_TimeZone | Select-Object -Property * $classWin32TimeZone | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_TimeZone.txt') -Width 4096 -Force #================================================= # TPM $IsAutopilotSpec = $false $IsTpmSpec = $false $classWin32Tpm = @{} try { $classWin32Tpm = Get-CimInstance -Namespace 'ROOT\cimv2\Security\MicrosoftTpm' -ClassName Win32_Tpm -ErrorAction Stop $classWin32Tpm | Out-File (Join-Path -Path $LogsPath -ChildPath 'Win32_Tpm.txt') -Width 4096 -Force $DeviceTpmIsActivated = $($classWin32Tpm.IsActivated_InitialValue) $DeviceTpmIsEnabled = $($classWin32Tpm.IsEnabled_InitialValue) $DeviceTpmIsOwned = $($classWin32Tpm.IsOwned_InitialValue) $DeviceTpmManufacturerIdTxt = $($classWin32Tpm.ManufacturerIdTxt) $DeviceTpmManufacturerVersion = $($classWin32Tpm.ManufacturerVersion) $DeviceTpmSpecVersion = $($classWin32Tpm.SpecVersion) } catch { $classWin32Tpm = $null $DeviceTpmIsActivated = $false $DeviceTpmIsEnabled = $false $DeviceTpmIsOwned = $false $DeviceTpmManufacturerIdTxt = $null $DeviceTpmManufacturerVersion = $null $DeviceTpmSpecVersion = $null Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] [NOT SUPPORTED] TPM is not supported on this device." Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] [NOT SUPPORTED] Intune Autopilot is not supported on this device." } if ($DeviceTpmSpecVersion) { $majorVersion = $DeviceTpmSpecVersion.Split(',')[0] -as [int] if ($majorVersion -lt 2) { Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] [NOT SUPPORTED] TPM version is lower than 2.0 on this device." Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] [NOT SUPPORTED] Intune Autopilot is not supported on this device." } else { Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [OK] TPM 2.0 is supported on this device." Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [OK] Intune Autopilot is supported on this device." $IsAutopilotSpec = $true $IsTpmSpec = $true } } #================================================= # Secure Boot Information # https://github.com/richardhicks/uefi/blob/main/Get-UEFICertificate.ps1 try { $SecureBootStatus = Confirm-SecureBootUEFI } catch { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [UNAVAILABLE] Unable to access UEFI Secure Boot information." Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [UNAVAILABLE] This system may not support UEFI or Secure Boot." } if ($SecureBootStatus -eq $true) { Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [OK] Secure Boot is enabled on this device." if (Get-Command -Name Get-SecureBootUEFI -ErrorAction SilentlyContinue) { try { $dbVariable = Get-SecureBootUEFI -Name DB -ErrorAction Stop $kekVariable = Get-SecureBootUEFI -Name KEK -ErrorAction Stop $dbBytes = $dbVariable.Bytes $kekBytes = $kekVariable.Bytes if (-not $dbBytes -or -not $kekBytes) { Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] [WARN] Secure Boot certificates could not be read (missing DB or KEK bytes). Skipping certificate presence checks." } else { $utf8Encoding = [System.Text.Encoding]::GetEncoding( 'utf-8', [System.Text.EncoderFallback]::ReplacementFallback, [System.Text.DecoderFallback]::ReplacementFallback ) $dbText = $utf8Encoding.GetString($dbBytes) if ([string]::IsNullOrWhiteSpace($dbText)) { $dbText = [System.Text.Encoding]::ASCII.GetString($dbBytes) } $WinUEFIca2023 = $dbText -match 'Windows UEFI CA 2023' if ($WinUEFIca2023) { Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [OK] Windows UEFI CA 2023 is present." } else { Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] [WARN] Windows UEFI CA 2023 is not present." } $MsUEFIca2023 = $dbText -match 'Microsoft UEFI CA 2023' if ($MsUEFIca2023) { Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [OK] Microsoft UEFI CA 2023 is present." } else { Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] [WARN] Microsoft UEFI CA 2023 is not present." } $kekText = $utf8Encoding.GetString($kekBytes) if ([string]::IsNullOrWhiteSpace($kekText)) { $kekText = [System.Text.Encoding]::ASCII.GetString($kekBytes) } $MsKEKca2023 = $kekText -match 'Microsoft Corporation KEK 2K CA 2023' if ($MsKEKca2023) { Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [OK] Microsoft Corporation KEK 2K CA 2023 is present." } else { Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] [WARN] Microsoft Corporation KEK 2K CA 2023 is not present." } } } catch { Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format s)] [WARN] Unable to query Secure Boot certificate variables (DB/KEK): $($_.Exception.Message). Skipping certificate presence checks." } } } elseif ($SecureBootStatus -eq $false) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [FAIL] Secure Boot is not enabled." } #================================================= # Identify Serial Number with multiple fallback methods due to variability in how different manufacturers populate WMI classes $serialNumberCandidates = @( $classWin32BIOS.SerialNumber, $classWin32SystemEnclosure.SerialNumber, $classWin32ComputerSystemProduct.IdentifyingNumber, $classWin32BaseBoard.SerialNumber ) $SerialNumber = $null foreach ($candidate in $serialNumberCandidates) { $SerialNumber = $candidate | ConvertTo-TrimmedString if (-not [string]::IsNullOrWhiteSpace($SerialNumber)) { break } } #================================================= # IsUEFI [System.Boolean]$IsUEFI = $false if ($env:firmware_type -eq 'UEFI') { $IsUEFI = $true } elseif ($env:firmware_type -eq 'Legacy') { $IsUEFI = $false } elseif ($env:SystemDrive -eq 'X:') { Start-Process -WindowStyle Hidden -FilePath wpeutil.exe -ArgumentList ('updatebootinfo') -Wait $IsUEFI = (Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Control).PEFirmwareType -eq 2 } else { if ($null -eq (Get-ItemProperty HKLM:\System\CurrentControlSet\Control\SecureBoot\State -ErrorAction SilentlyContinue)) { $IsUEFI = $false } else { $IsUEFI = $true } } #================================================= # IsVM [System.Boolean]$IsVM = $false $vmDetectionSources = @( $classWin32ComputerSystem.Model, $classWin32ComputerSystem.Manufacturer, $classWin32ComputerSystem.SystemFamily, $classWin32ComputerSystemProduct.Name, $classWin32ComputerSystemProduct.Vendor, $classWin32ComputerSystemProduct.Version ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } $vmPattern = '(?i)(virtual machine|vmware|hyper-v|hyperv|kvm|qemu|xen|virtualbox|bhyve|parallels|gce|google compute engine|amazon ec2|azure|bochs|openstack|ovirt|rhev|kubevirt|ahv|nutanix)' [System.Boolean]$IsVM = ($vmDetectionSources -join ' ') -match $vmPattern #================================================= # ChassisType $IsDesktop = $false $IsLaptop = $false $IsServer = $false $IsSFF = $false $IsTablet = $false $ComputerSystemType = $classWin32SystemEnclosure | ForEach-Object { if ($_.ChassisTypes[0] -in "8", "9", "10", "11", "12", "14", "18", "21") { $IsLaptop = $true; "Laptop" } if ($_.ChassisTypes[0] -in "3", "4", "5", "6", "7", "15", "16") { $IsDesktop = $true; "Desktop" } if ($_.ChassisTypes[0] -in "23") { $IsServer = $true; "Server" } if ($_.ChassisTypes[0] -in "34", "35", "36") { $IsSFF = $true; "Small Form Factor" } if ($_.ChassisTypes[0] -in "13", "31", "32", "30") { $IsTablet = $true; "Tablet" } } #================================================= # TotalPhysicalMemoryGB $TotalPhysicalMemoryGB = [math]::Round( $classWin32ComputerSystem.TotalPhysicalMemory / 1GB, 0, [System.MidpointRounding]::AwayFromZero ) if ($TotalPhysicalMemoryGB -lt 6) { Write-Warning "[$(Get-Date -format s)] OSDCloud Workflow requires at least 8 GB of memory to function properly. Errors are expected." } #================================================= # OA3Tool for Hardware Hash (Autopilot) $HardwareHash = $null if (Get-Command 'oa3tool.exe' -ErrorAction SilentlyContinue) { $oa3cfg = @" <OA3> <FileBased> <InputKeyXMLFile>$env:TEMP\OA3_Input.xml</InputKeyXMLFile> </FileBased> <OutputData> <AssembledBinaryFile>$env:TEMP\OA3.bin</AssembledBinaryFile> <ReportedXMLFile>$env:TEMP\OA3.xml</ReportedXMLFile> </OutputData> </OA3> "@ $oa3input = @" <?xml version="1.0"?> <Key> <ProductKey>XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</ProductKey> <ProductKeyID>0000000000000</ProductKeyID> <ProductKeyState>0</ProductKeyState> </Key> "@ $oa3cfg | Out-File -FilePath "$env:TEMP\OA3.cfg" -Encoding utf8 -Force $oa3input | Out-File -FilePath "$env:TEMP\OA3_Input.xml" -Encoding utf8 -Force $null = oa3tool.exe /Report /ConfigFile="$env:TEMP\OA3.cfg" /LogTrace="$env:TEMP\OA3_Report.xml" /NoKeyCheck if (Test-Path "$env:TEMP\OA3.xml") { $HardwareHash = Get-Content "$env:TEMP\OA3.xml" -Raw | Select-String -Pattern '<HardwareHash>(.*?)</HardwareHash>' -AllMatches | ForEach-Object { $_.Matches } | ForEach-Object { $_.Groups[1].Value } Copy-Item -Path "$env:TEMP\OA3.xml" -Destination (Join-Path -Path $LogsPath -ChildPath 'OA3.xml') -Force -ErrorAction SilentlyContinue Copy-Item -Path "$env:TEMP\OA3_Report.xml" -Destination (Join-Path -Path $LogsPath -ChildPath 'OA3_Report.xml') -Force -ErrorAction SilentlyContinue if ($HardwareHash) { $null = oa3tool.exe /DecodeHwHash="$HardwareHash" /LogTrace="$env:TEMP\OA3_Decode.xml" $null = oa3tool.exe /ValidateHwHash="$HardwareHash" /LogTrace="$env:TEMP\OA3_Validate.xml" $csvContent = @() $csvContent += [PSCustomObject]@{ 'Device Serial Number' = $SerialNumber 'Windows Product ID' = '' 'Hardware Hash' = $HardwareHash } $csvContent | ConvertTo-Csv -NoTypeInformation | ForEach-Object { $_ -replace '"', '' } | Out-File -FilePath "$env:TEMP\Autopilot.csv" -Force -Encoding utf8 Copy-Item -Path "$env:TEMP\Autopilot.csv" -Destination (Join-Path -Path $LogsPath -ChildPath 'Autopilot.csv') -Force -ErrorAction SilentlyContinue Copy-Item -Path "$env:TEMP\OA3_Decode.xml" -Destination (Join-Path -Path $LogsPath -ChildPath 'OA3_Decode.xml') -Force -ErrorAction SilentlyContinue Copy-Item -Path "$env:TEMP\OA3_Validate.xml" -Destination (Join-Path -Path $LogsPath -ChildPath 'OA3_Validate.xml') -Force -ErrorAction SilentlyContinue } } } #================================================= # OSD Properties with normalization and aliasing for known manufacturers and models to ensure consistent values for OSDCloud workflows and reporting $OSDManufacturer = $classWin32ComputerSystem.Manufacturer | ConvertTo-TrimmedString $OSDModel = $classWin32ComputerSystem.Model | ConvertTo-TrimmedString $OSDProduct = $classWin32ComputerSystemProduct.Version | ConvertTo-TrimmedString #================================================= # Normalize Aliases for Known Manufacturers and Models switch -Regex ($OSDManufacturer) { 'Acer' { $OSDManufacturer = 'Acer' $OSDProduct = $BaseBoardProduct break } 'ASUS|ASUSTeK' { $OSDManufacturer = 'ASUS' $OSDProduct = $BaseBoardProduct break } 'Dell' { $OSDManufacturer = 'Dell' $OSDProduct = $ComputerSystemSKU break } 'Fujitsu' { $OSDManufacturer = 'Fujitsu' $OSDProduct = $BaseBoardProduct break } 'Gigabyte' { $OSDManufacturer = 'Gigabyte' $OSDProduct = $BaseBoardProduct break } 'Lenovo' { $OSDManufacturer = 'Lenovo' $OSDModel = $ComputerSystemProduct if (-not [string]::IsNullOrWhiteSpace($ComputerModel) -and $ComputerModel.Length -ge 4) { $OSDProduct = $ComputerModel.Substring(0, 4) } else { $OSDProduct = $ComputerModel } break } 'Hewlett|Packard|^HP$' { $OSDManufacturer = 'HP' $OSDProduct = $BaseBoardProduct break } 'Microsoft' { $OSDManufacturer = 'Microsoft' if ($OSDModel -match 'Virtual') { $OSDProduct = $ComputerSystemProduct } else { $OSDProduct = $ComputerSystemSKU } break } 'Panasonic' { $OSDManufacturer = 'Panasonic'; break } 'to be filled' { $OSDManufacturer = 'OEM'; break } } if ([string]::IsNullOrWhiteSpace($OSDManufacturer) -or $OSDManufacturer -match '^to be filled$') { $OSDManufacturer = 'OEM' } if ([string]::IsNullOrWhiteSpace($OSDModel) -or $OSDModel -match '^to be filled$') { $OSDModel = 'OEM' } if ([string]::IsNullOrWhiteSpace($OSDProduct)) { $OSDProduct = 'Unknown' } #================================================= # Disk Information # Include only USB disks that are online and available. $GetDisk = Get-Disk | Where-Object { $_.BusType -eq 'USB' -and $_.IsOffline -eq $false -and $_.OperationalStatus -eq 'Online' } | Sort-Object DiskNumber | Select-Object -Property * $usbDiskNumbers = [System.Collections.Generic.HashSet[int]]::new() foreach ($disk in $GetDisk) { [void]$usbDiskNumbers.Add([int]$disk.DiskNumber) } # Partition Information $GetPartition = Get-Partition | Sort-Object DiskNumber, PartitionNumber | Select-Object -Property *, @{ Name = 'IsUSB' Expression = { $usbDiskNumbers.Contains([int]$_.DiskNumber) } } # USB Partitions $USBPartitions = $GetPartition | Where-Object { $_.IsUSB -eq $true } # USBVolumes $usbDriveLetters = $USBPartitions | ForEach-Object { $_.AccessPaths } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { if ($_ -match '^(?<DriveLetter>[A-Z]):\\$') { $Matches.DriveLetter } } | Sort-Object -Unique $USBVolumes = @() if ($usbDriveLetters) { $USBVolumes = Get-Volume -DriveLetter $usbDriveLetters -ErrorAction SilentlyContinue | Sort-Object DriveLetter -Unique } #================================================= # OSDCloudEnv #================================================= # Use OSDCloudEnv to override these properties: # OSDManufacturer # OSDModel # OSDProduct # OSArchitecture if ($global:OSDCloudEnv) { if ($global:OSDCloudEnv.OSDManufacturer) { $OSDManufacturer = $global:OSDCloudEnv.OSDManufacturer } if ($global:OSDCloudEnv.OSDModel) { $OSDModel = $global:OSDCloudEnv.OSDModel } if ($global:OSDCloudEnv.OSDProduct) { $OSDProduct = $global:OSDCloudEnv.OSDProduct } if ($global:OSDCloudEnv.OSArchitecture) { $OSArchitecture = $global:OSDCloudEnv.OSArchitecture } if ($global:OSDCloudEnv.ProcessorArchitecture) { $ProcessorArchitecture = $global:OSDCloudEnv.ProcessorArchitecture } } #================================================= # Pass Variables to OSDCoreDevice #================================================= $global:OSDCoreDevice = $null $global:OSDCoreDevice = [ordered]@{ OSDManufacturer = [System.String]$OSDManufacturer OSDModel = [System.String]$OSDModel OSDProduct = [System.String]$OSDProduct ComputerName = $classWin32ComputerSystem.Name BaseBoardProduct = [System.String]$BaseBoardProduct BiosReleaseDate = [System.String]$classWin32BIOS.ReleaseDate BiosVersion = [System.String]$classWin32BIOS.SMBIOSBIOSVersion ComputerManufacturer = [System.String]$ComputerManufacturer ComputerModel = [System.String]$ComputerModel ComputerSystemFamily = [System.String]$ComputerSystemFamily ComputerSystemProduct = [System.String]$ComputerSystemProduct ComputerSystemSKU = [System.String]$ComputerSystemSKU ComputerSystemType = [System.String]$ComputerSystemType HardwareHash = [System.String]$HardwareHash IsAutopilotSpec = [System.Boolean]$IsAutopilotSpec IsDesktop = [System.Boolean]$IsDesktop IsLaptop = [System.Boolean]$IsLaptop IsOnBattery = [System.Boolean]$IsOnBattery IsServer = [System.Boolean]$IsServer IsSFF = [System.Boolean]$IsSFF IsTablet = [System.Boolean]$IsTablet IsTpmSpec = [System.Boolean]$IsTpmSpec IsVM = [System.Boolean]$IsVM IsUEFI = [System.Boolean]$IsUEFI KeyboardLayout = $KeyboardLayout KeyboardName = $KeyboardName NetGateways = $NetGateways NetIPAddress = $NetIPAddress NetMacAddress = $NetMacAddress OSArchitecture = $OSArchitecture OSVersion = $classWin32OperatingSystem.Version ProcessorArchitecture = $ProcessorArchitecture SerialNumber = $SerialNumber SystemFirmwareHardwareId = $SystemFirmwareHardwareId TimeZone = $classWin32TimeZone.StandardName TotalPhysicalMemoryGB = $TotalPhysicalMemoryGB TpmIsActivated = $DeviceTpmIsActivated TpmIsEnabled = $DeviceTpmIsEnabled TpmIsOwned = $DeviceTpmIsOwned TpmManufacturerIdTxt = $DeviceTpmManufacturerIdTxt TpmManufacturerVersion = $DeviceTpmManufacturerVersion TpmSpecVersion = $DeviceTpmSpecVersion USBPartitions = $USBPartitions USBVolumes = $USBVolumes UUID = $classWin32ComputerSystemProduct.UUID } $global:OSDCoreDevice | ConvertTo-Json -Depth 10 | Out-File "$LogsPath\OSDCoreDevice.json" -Force -Encoding utf8 #================================================= # OSDCoreCacheContent $global:OSDCoreCacheContent = Get-OSDCoreCacheContent #================================================= # ModuleCoreOperatingSystems $global:ModuleCoreOperatingSystems = Initialize-ModuleCoreOperatingSystems # Validate ModuleCoreOperatingSystems if (-not ($global:ModuleCoreOperatingSystems)) { throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to load Module Core Operating Systems." } #================================================= # OSDCoreOperatingSystems $global:OSDCoreOperatingSystems = Get-OSDCoreOperatingSystems | Where-Object { ($_.Architecture -match "$ProcessorArchitecture") -or ($_.OSArchitecture -match "$ProcessorArchitecture") } $null = Set-OSDCoreOperatingSystemCloudObject -OSArchitecture $ProcessorArchitecture #================================================= # OSDCoreDriverPacks $global:ModuleCoreDriverPacks = Initialize-ModuleCoreDriverPacks -OSDManufacturer $OSDManufacturer $global:OSDCoreDriverPackCloudObject = $global:ModuleCoreDriverPacks | Where-Object { $_.SystemId -match $OSDProduct } | Select-Object -First 1 #================================================= # OSDCloudLogs # Look for available drives (USB, mapped network drives, and local drives) with at least 1 GB of free space and write permissions for the current user to copy logs. $OSDCloudLogsDrive = $null foreach ($Drive in (Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -match '^[A-Z]:\\$' })) { $DriveLetter = $Drive.Name $OSDCloudLogsCheckPath = "$DriveLetter`:\OSDCloudLogs" try { if (Test-Path -Path $OSDCloudLogsCheckPath) { $DriveInfo = New-Object System.IO.DriveInfo($DriveLetter) if ($DriveInfo.AvailableFreeSpace -ge 1GB) { $OSDCloudLogsDrive = [PSCustomObject]@{ DriveLetter = $DriveLetter AvailableFreeSpaceGB = [math]::Round($DriveInfo.AvailableFreeSpace / 1GB, 1) } break } } } catch { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to access drive $DriveLetter" } } if ($OSDCloudLogsDrive) { $OSDCloudLogsPath = "$($OSDCloudLogsDrive.DriveLetter):\OSDCloudLogs\$SerialNumber" if (-not (Test-Path -Path $OSDCloudLogsPath)) { New-Item -Path $OSDCloudLogsPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null } # If folder doesn't exist, then the drive likely doesn't have write permissions for the current user, so skip copying logs to avoid errors if (Test-Path -Path $OSDCloudLogsPath) { Get-ChildItem -Path $LogsPath -File | ForEach-Object { Copy-Item -Path $_.FullName -Destination $(Join-Path -Path $OSDCloudLogsPath -ChildPath $_.Name) -Force } } } #================================================= Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" #================================================= } |