Public/Get-HardwareReadinessJSON.ps1

function Get-HardwareReadinessJSON {
    <#
    .SYNOPSIS
    Returns raw JSON results of Windows 11 hardware compatibility check.
 
    .DESCRIPTION
    Performs detailed hardware compatibility checks for Windows 11 requirements
    including TPM, SecureBoot, CPU, Memory, and Storage requirements.
    Returns results in JSON format matching the original Microsoft script output.
 
    .EXAMPLE
    Get-HardwareReadinessJSON
 
    Returns:
    {
        "returnCode": 0,
        "returnReason": "",
        "logging": "Storage: OSDiskSize=929GB. PASS; Memory: System_Memory=32GB. PASS; TPM: TPMVersion=2.0, 0, 1.59. PASS;
                   Processor: {AddressWidth=64; MaxClockSpeed=1400; NumberOfLogicalCores=22; Manufacturer=GenuineIntel;
                   Caption=Intel64 Family 6 Model 170 Stepping 4; }. PASS; SecureBoot: Enabled. PASS; ",
        "returnResult": "CAPABLE"
    }
 
    .NOTES
    Based on Microsoft's hardware readiness check script: https://aka.ms/HWReadinessScript
    #>

    [CmdletBinding()]
    param()

    # Constants
    [int]$MinOSDiskSizeGB = 64
    [int]$MinMemoryGB = 4
    [Uint32]$MinClockSpeedMHz = 1000
    [Uint32]$MinLogicalCores = 2
    [Uint16]$RequiredAddressWidth = 64

    $PASS_STRING = "PASS"
    $FAIL_STRING = "FAIL"
    $FAILED_TO_RUN_STRING = "FAILED TO RUN"
    $UNDETERMINED_CAPS_STRING = "UNDETERMINED"
    $UNDETERMINED_STRING = "Undetermined"
    $CAPABLE_STRING = "Capable"
    $NOT_CAPABLE_STRING = "Not capable"
    $CAPABLE_CAPS_STRING = "CAPABLE"
    $NOT_CAPABLE_CAPS_STRING = "NOT CAPABLE"
    $STORAGE_STRING = "Storage"
    $OS_DISK_SIZE_STRING = "OSDiskSize"
    $MEMORY_STRING = "Memory"
    $SYSTEM_MEMORY_STRING = "System_Memory"
    $GB_UNIT_STRING = "GB"
    $TPM_STRING = "TPM"
    $TPM_VERSION_STRING = "TPMVersion"
    $PROCESSOR_STRING = "Processor"
    $SECUREBOOT_STRING = "SecureBoot"
    $I7_7820HQ_CPU_STRING = "i7-7820hq CPU"

    # Log format strings
    $logFormat = '{0}: {1}={2}. {3}; '
    $logFormatWithUnit = '{0}: {1}={2}{3}. {4}; '
    $logFormatReturnReason = '{0}, '
    $logFormatException = '{0}; '
    $logFormatWithBlob = '{0}: {1}. {2}; '

    # Reset output object
    $script:outObject = @{
        returnCode = -2
        returnResult = $FAILED_TO_RUN_STRING
        returnReason = ""
        logging = ""
    }

    try {
        # Check Storage
        $osDrive = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='$($env:SystemDrive)'"
        $osDriveSize = [math]::Round($osDrive.Size / 1GB)
        
        if ($osDriveSize -ge $MinOSDiskSizeGB) {
            $script:outObject.logging += $logFormatWithUnit -f $STORAGE_STRING, $OS_DISK_SIZE_STRING, $osDriveSize, $GB_UNIT_STRING, $PASS_STRING
            UpdateReturnCode 0
        } else {
            $script:outObject.logging += $logFormatWithUnit -f $STORAGE_STRING, $OS_DISK_SIZE_STRING, $osDriveSize, $GB_UNIT_STRING, $FAIL_STRING
            $script:outObject.returnReason += $logFormatReturnReason -f "Insufficient disk space"
            UpdateReturnCode 1
        }

        # Check Memory
        $memory = Get-CimInstance -ClassName Win32_PhysicalMemory | Measure-Object -Property Capacity -Sum
        $memoryGB = [math]::Round($memory.Sum / 1GB)
        
        if ($memoryGB -ge $MinMemoryGB) {
            $script:outObject.logging += $logFormatWithUnit -f $MEMORY_STRING, $SYSTEM_MEMORY_STRING, $memoryGB, $GB_UNIT_STRING, $PASS_STRING
            UpdateReturnCode 0
        } else {
            $script:outObject.logging += $logFormatWithUnit -f $MEMORY_STRING, $SYSTEM_MEMORY_STRING, $memoryGB, $GB_UNIT_STRING, $FAIL_STRING
            $script:outObject.returnReason += $logFormatReturnReason -f "Insufficient memory"
            UpdateReturnCode 1
        }

        # Check TPM
        try {
            $tpm = Get-Tpm

            if ($null -eq $tpm) {
                UpdateReturnCode -ReturnCode 1
                $script:outObject.returnReason += $logFormatReturnReason -f $TPM_STRING
                $script:outObject.logging += $logFormatWithBlob -f $TPM_STRING, "TPM is null", $FAIL_STRING
            }
            elseif ($tpm.TpmPresent) {
                $tpmVersion = Get-WmiObject -Class Win32_Tpm -Namespace root\CIMV2\Security\MicrosoftTpm | 
                             Select-Object -Property SpecVersion

                if ($null -eq $tpmVersion.SpecVersion) {
                    UpdateReturnCode -ReturnCode 1
                    $script:outObject.returnReason += $logFormatReturnReason -f $TPM_STRING
                    $script:outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, "null", $FAIL_STRING
                }

                $majorVersion = $tpmVersion.SpecVersion.Split(",")[0] -as [int]
                if ($majorVersion -lt 2) {
                    UpdateReturnCode -ReturnCode 1
                    $script:outObject.returnReason += $logFormatReturnReason -f $TPM_STRING
                    $script:outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, ($tpmVersion.SpecVersion), $FAIL_STRING
                }
                else {
                    $script:outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, ($tpmVersion.SpecVersion), $PASS_STRING
                    UpdateReturnCode -ReturnCode 0
                }
            }
            else {
                if ($tpm.GetType().Name -eq "String") {
                    UpdateReturnCode -ReturnCode -1
                    $script:outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING
                    $script:outObject.logging += $logFormatException -f $tpm
                }
                else {
                    UpdateReturnCode -ReturnCode 1
                    $script:outObject.returnReason += $logFormatReturnReason -f $TPM_STRING
                    $script:outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, ($tpm.TpmPresent), $FAIL_STRING
                }
            }
        }
        catch {
            UpdateReturnCode -ReturnCode -1
            $script:outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING
            $script:outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)"
        }

        # Check Processor
        $processor = Get-CimInstance -ClassName Win32_Processor
        $cpuCheck = [CpuFamily]::Validate($processor.Manufacturer, $processor.Architecture)
        
        $cpuDetailsLog = "{AddressWidth=$($processor.AddressWidth); MaxClockSpeed=$($processor.MaxClockSpeed); NumberOfLogicalCores=$($processor.NumberOfLogicalProcessors); Manufacturer=$($processor.Manufacturer); Caption=$($processor.Caption); $($cpuCheck.Message)}"
        
        if ($cpuCheck.IsValid -and 
            $processor.AddressWidth -eq $RequiredAddressWidth -and 
            $processor.NumberOfCores -ge $MinLogicalCores -and 
            $processor.MaxClockSpeed -ge $MinClockSpeedMHz) {
            $script:outObject.logging += $logFormatWithBlob -f $PROCESSOR_STRING, $cpuDetailsLog, $PASS_STRING
            UpdateReturnCode 0
        } else {
            $script:outObject.logging += $logFormatWithBlob -f $PROCESSOR_STRING, $cpuDetailsLog, $FAIL_STRING
            $script:outObject.returnReason += $logFormatReturnReason -f "Processor not compatible"
            UpdateReturnCode 1
        }

        # Check SecureBoot
        try {
            $secureBoot = Confirm-SecureBootUEFI
            if ($secureBoot) {
                $script:outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, "Enabled", $PASS_STRING
                UpdateReturnCode 0
            } else {
                $script:outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, "Disabled", $FAIL_STRING
                $script:outObject.returnReason += $logFormatReturnReason -f "SecureBoot disabled"
                UpdateReturnCode 1
            }
        } catch {
            $script:outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, "Error", $FAILED_TO_RUN_STRING
            $script:outObject.returnReason += $logFormatException -f $_.Exception.Message
            UpdateReturnCode -1
        }

        # Set final result
        if ($script:outObject.returnCode -eq 0) {
            $script:outObject.returnResult = $CAPABLE_CAPS_STRING
        } elseif ($script:outObject.returnCode -eq 1) {
            $script:outObject.returnResult = $NOT_CAPABLE_CAPS_STRING
        } else {
            $script:outObject.returnResult = $FAILED_TO_RUN_STRING
        }

    } catch {
        $script:outObject.returnReason = $_.Exception.Message
        $script:outObject.returnCode = -1
        $script:outObject.returnResult = $FAILED_TO_RUN_STRING
    }

    # Return JSON result
    $script:outObject | ConvertTo-Json -Compress
}