#requires -Version 5.1 # SPDX-License-Identifier: MIT # Copyright (c) 2026 LaptopsRenovadas.com <# LaptopsRenovadas - Auditoria Express Collector Version 1.0.4 License: MIT. Source is intentionally readable for inspection and redistribution under that license. Privacy and safety design: - Read-only hardware/system queries plus one local JSON output file. - No network requests. - Does not collect user name, computer name, serial number, UUID, MAC address, IP address, domain name, tenant ID, email address or raw dsregcmd output. - Every unsupported field is left null instead of guessed. #> Set-StrictMode -Version 2.0 $ErrorActionPreference = 'Stop' function Try-Value { param([ScriptBlock]$Script) try { return (& $Script) } catch { return $null } } function To-NullableNumber { param($Value) if ($null -eq $Value -or "$Value" -eq '') { return $null } try { return [double]$Value } catch { return $null } } function Trim-Text { param($Value) if ($null -eq $Value) { return $null } $s = [string]$Value $s = $s.Trim() if ($s.Length -eq 0) { return $null } return $s } function Get-OptionalProperty { param($Object, [string]$Name) if ($null -eq $Object -or [string]::IsNullOrWhiteSpace($Name)) { return $null } try { $prop = $Object.PSObject.Properties[$Name] if ($null -eq $prop) { return $null } return $prop.Value } catch { return $null } } function Parse-DsregBoolean { param([string]$Text, [string]$Name) if ([string]::IsNullOrWhiteSpace($Text)) { return $null } $pattern = '(?im)^\s*' + [regex]::Escape($Name) + '\s*:\s*(YES|NO)\s*$' $m = [regex]::Match($Text, $pattern) if (-not $m.Success) { return $null } return ($m.Groups[1].Value.ToUpperInvariant() -eq 'YES') } function Normalize-BatteryNumber { param($Value, [switch]$AllowZero) $n = To-NullableNumber $Value if ($null -eq $n) { return $null } if ($n -eq 4294967295) { return $null } if ($AllowZero) { if ($n -lt 0) { return $null } } elseif ($n -le 0) { return $null } return $n } function Get-BatteryReportFallback { $result = [ordered]@{ status = 'not-run' batteries = @() } $tempName = 'LRAuditBattery-' + [guid]::NewGuid().ToString('N') + '.xml' $tempPath = Join-Path ([System.IO.Path]::GetTempPath()) $tempName try { $powercfg = Join-Path $env:SystemRoot 'System32\powercfg.exe' if (-not (Test-Path $powercfg)) { $result['status'] = 'powercfg-missing'; return $result } & $powercfg /batteryreport /output $tempPath /xml 2>$null | Out-Null if (-not (Test-Path $tempPath)) { $result['status'] = 'report-not-created'; return $result } [xml]$xml = Get-Content -LiteralPath $tempPath -Raw -ErrorAction Stop # batteryreport XML uses a default namespace on several Windows builds. # local-name() keeps parsing stable across those namespace variations. $nodes = @($xml.SelectNodes("/*[local-name()='BatteryReport']/*[local-name()='Batteries']/*[local-name()='Battery']")) if ($nodes.Count -eq 0) { $nodes = @($xml.SelectNodes("//*[local-name()='Batteries']/*[local-name()='Battery']")) } foreach ($node in $nodes) { if ($null -eq $node) { continue } $designNode = $node.SelectSingleNode("*[local-name()='DesignCapacity']") $fullNode = $node.SelectSingleNode("*[local-name()='FullChargeCapacity']") $cycleNode = $node.SelectSingleNode("*[local-name()='CycleCount']") $design = if ($null -ne $designNode) { Normalize-BatteryNumber ([string]$designNode.InnerText) } else { $null } $full = if ($null -ne $fullNode) { Normalize-BatteryNumber ([string]$fullNode.InnerText) } else { $null } $cycles = if ($null -ne $cycleNode) { Normalize-BatteryNumber ([string]$cycleNode.InnerText) -AllowZero } else { $null } if ($null -eq $design -and $null -eq $full -and $null -eq $cycles) { continue } $result['batteries'] += [ordered]@{ designedCapacityMWh = $design fullChargeCapacityMWh = $full cycleCount = $cycles cycleCounterSupported = ($null -ne $cycles -and [double]$cycles -gt 0) source = 'powercfg-batteryreport-xml' } } $result['status'] = if ($result['batteries'].Count -gt 0) { 'parsed' } else { 'no-capacity-data' } } catch { $result['status'] = 'parse-error' } finally { try { if (Test-Path $tempPath) { Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue } } catch {} } return $result } function Get-LegacySmartSummary { $summary = [ordered]@{ available = $false predictFailure = $null devicesChecked = 0 activeDevices = 0 queryStatus = 'not-run' providerSource = $null } $rows = @() $firstError = $null try { $rows = @(Get-CimInstance -Namespace root\wmi -ClassName MSStorageDriver_FailurePredictStatus -ErrorAction Stop) $summary['providerSource'] = 'CIM' } catch { $firstError = [string]$_.Exception.Message try { if (Get-Command Get-WmiObject -ErrorAction SilentlyContinue) { $rows = @(Get-WmiObject -Namespace root\wmi -Class MSStorageDriver_FailurePredictStatus -ErrorAction Stop) $summary['providerSource'] = 'WMI' } } catch { $msg = [string]$_.Exception.Message if ($msg -match '(?i)access.*denied|acceso.*denegado|unauthorized') { $summary['queryStatus'] = 'permission-denied' } elseif ($msg -match '(?i)invalid class|clase.*no vĂ¡lida|not found|no se encuentra') { $summary['queryStatus'] = 'provider-unavailable' } else { $summary['queryStatus'] = 'query-failed' } return $summary } } if ($rows.Count -le 0) { $summary['queryStatus'] = if ($null -ne $firstError) { 'no-data-after-fallback' } else { 'no-data' } return $summary } $summary['available'] = $true $summary['queryStatus'] = 'available' $summary['devicesChecked'] = $rows.Count $seen = $false $failure = $false foreach ($row in $rows) { $active = Get-OptionalProperty $row 'Active' if ($null -ne $active -and [bool]$active) { $summary['activeDevices']++ } $v = Get-OptionalProperty $row 'PredictFailure' if ($null -eq $v) { continue } $seen = $true if ([bool]$v) { $failure = $true } } if ($seen) { $summary['predictFailure'] = $failure } return $summary } function Get-TpmNativeFallback { $result = [ordered]@{ present = $null ready = $null version = $null source = $null status = 'not-run' } # tpmtool.exe is included with supported Windows 10/11 builds and can expose # basic TPM state even when the PowerShell/WMI provider is inaccessible. try { $tool = Join-Path $env:SystemRoot 'System32\tpmtool.exe' if (Test-Path $tool) { $text = (& $tool getdeviceinformation 2>$null | Out-String) if (-not [string]::IsNullOrWhiteSpace($text)) { $presentMatch = [regex]::Match($text, '(?im)^\s*-?\s*(?:TPM\s+Present|TPM\s+presente|Presencia\s+(?:del|de)\s+TPM)\s*:\s*(True|False|Verdadero|Falso|S\u00ED|Si|No)\s*$') if ($presentMatch.Success) { $value = $presentMatch.Groups[1].Value.ToLowerInvariant() $result['present'] = -not ($value -in @('false','falso','no')) } $versionMatch = [regex]::Match($text, '(?im)^\s*-?\s*(?:TPM\s+Version|Versi(?:o|\u00F3)n\s+(?:del|de)\s+TPM)\s*:\s*([^\r\n]+)') if ($versionMatch.Success) { $result['version'] = (Trim-Text $versionMatch.Groups[1].Value) } $readyMatch = [regex]::Match($text, '(?im)^\s*-?\s*(?:Ready\s+For\s+Storage|Listo\s+para\s+almacenamiento)\s*:\s*(True|False|Verdadero|Falso|S\u00ED|Si|No)\s*$') if ($readyMatch.Success) { $value = $readyMatch.Groups[1].Value.ToLowerInvariant() $result['ready'] = -not ($value -in @('false','falso','no')) } if ($null -ne $result['present'] -or $null -ne $result['version']) { $result['source'] = 'tpmtool' $result['status'] = 'available' return $result } $result['status'] = 'tpmtool-unparsed' } else { $result['status'] = 'tpmtool-no-output' } } else { $result['status'] = 'tpmtool-missing' } } catch { $result['status'] = 'tpmtool-failed' } # Last native fallback: only inspect the generic PnP device display name. # Do not export PNPDeviceID or any device identifier. try { $devices = @(Get-CimInstance -ClassName Win32_PnPEntity -ErrorAction Stop | Where-Object { $n = Trim-Text (Get-OptionalProperty $_ 'Name') $c = Trim-Text (Get-OptionalProperty $_ 'Caption') (($n -match '(?i)Trusted Platform Module|\bTPM\b|M(?:o|\u00F3)dulo de plataforma segura') -or ($c -match '(?i)Trusted Platform Module|\bTPM\b|M(?:o|\u00F3)dulo de plataforma segura')) }) if ($devices.Count -gt 0) { $name = Trim-Text (Get-OptionalProperty $devices[0] 'Name') if ($null -eq $name) { $name = Trim-Text (Get-OptionalProperty $devices[0] 'Caption') } $result['present'] = $true if ($null -ne $name) { $vm = [regex]::Match($name, '\b(1\.2|2\.0)\b') if ($vm.Success) { $result['version'] = $vm.Groups[1].Value } } $result['source'] = 'Win32_PnPEntity' $result['status'] = 'available' } } catch {} return $result } $collectorVersion = '1.0.4' $collectedAt = (Get-Date).ToUniversalTime().ToString('o') $isAdministrator = $false try { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($identity) $isAdministrator = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch {} $computer = Try-Value { Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop } $bios = Try-Value { Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop } $os = Try-Value { Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop } $processors = @() try { $processors = @(Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop) } catch {} $videoControllers = @() try { $videoControllers = @(Get-CimInstance -ClassName Win32_VideoController -ErrorAction Stop) } catch {} $memoryModules = @() try { $memoryModules = @(Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop) } catch {} $cpuOut = @() foreach ($cpu in $processors) { $cpuOut += [ordered]@{ name = Trim-Text (Get-OptionalProperty $cpu 'Name') manufacturer = Trim-Text (Get-OptionalProperty $cpu 'Manufacturer') cores = To-NullableNumber (Get-OptionalProperty $cpu 'NumberOfCores') logicalProcessors = To-NullableNumber (Get-OptionalProperty $cpu 'NumberOfLogicalProcessors') maxClockMHz = To-NullableNumber (Get-OptionalProperty $cpu 'MaxClockSpeed') } } $gpuOut = @() foreach ($gpu in $videoControllers) { $name = Trim-Text (Get-OptionalProperty $gpu 'Name') if ($null -eq $name) { continue } $gpuOut += [ordered]@{ name = $name adapterRamBytes = To-NullableNumber (Get-OptionalProperty $gpu 'AdapterRAM') driverVersion = Trim-Text (Get-OptionalProperty $gpu 'DriverVersion') } } $ramOut = @() $totalRamBytes = 0.0 foreach ($ram in $memoryModules) { $cap = To-NullableNumber (Get-OptionalProperty $ram 'Capacity') if ($null -ne $cap) { $totalRamBytes += $cap } $ramOut += [ordered]@{ capacityBytes = $cap manufacturer = Trim-Text (Get-OptionalProperty $ram 'Manufacturer') partNumber = Trim-Text (Get-OptionalProperty $ram 'PartNumber') speedMHz = To-NullableNumber (Get-OptionalProperty $ram 'Speed') configuredClockMHz = To-NullableNumber (Get-OptionalProperty $ram 'ConfiguredClockSpeed') formFactor = To-NullableNumber (Get-OptionalProperty $ram 'FormFactor') deviceLocator = Trim-Text (Get-OptionalProperty $ram 'DeviceLocator') } } $diskOut = @() $storageSource = 'Get-PhysicalDisk' try { $physicalDisks = @(Get-PhysicalDisk -ErrorAction Stop) foreach ($disk in $physicalDisks) { $rel = $null try { $rel = Get-StorageReliabilityCounter -PhysicalDisk $disk -ErrorAction Stop } catch {} $diskOut += [ordered]@{ model = Trim-Text (Get-OptionalProperty $disk 'FriendlyName') mediaType = Trim-Text (Get-OptionalProperty $disk 'MediaType') busType = Trim-Text (Get-OptionalProperty $disk 'BusType') healthStatus = Trim-Text (Get-OptionalProperty $disk 'HealthStatus') operationalStatus = if ($null -ne (Get-OptionalProperty $disk 'OperationalStatus')) { (@((Get-OptionalProperty $disk 'OperationalStatus')) -join ', ') } else { $null } sizeBytes = To-NullableNumber (Get-OptionalProperty $disk 'Size') firmwareVersion = Trim-Text (Get-OptionalProperty $disk 'FirmwareVersion') reliability = [ordered]@{ temperatureC = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'Temperature') } else { $null } temperatureMaxC = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'TemperatureMax') } else { $null } wearPercent = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'Wear') } else { $null } powerOnHours = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'PowerOnHours') } else { $null } readErrorsTotal = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'ReadErrorsTotal') } else { $null } readErrorsUncorrected = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'ReadErrorsUncorrected') } else { $null } writeErrorsTotal = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'WriteErrorsTotal') } else { $null } writeErrorsUncorrected = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'WriteErrorsUncorrected') } else { $null } readLatencyMaxMs = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'ReadLatencyMax') } else { $null } writeLatencyMaxMs = if ($null -ne $rel) { To-NullableNumber (Get-OptionalProperty $rel 'WriteLatencyMax') } else { $null } } } } } catch { $storageSource = 'Win32_DiskDrive-fallback' try { $fallbackDisks = @(Get-CimInstance -ClassName Win32_DiskDrive -ErrorAction Stop) foreach ($disk in $fallbackDisks) { $diskOut += [ordered]@{ model = Trim-Text (Get-OptionalProperty $disk 'Model') mediaType = Trim-Text (Get-OptionalProperty $disk 'MediaType') busType = Trim-Text (Get-OptionalProperty $disk 'InterfaceType') healthStatus = Trim-Text (Get-OptionalProperty $disk 'Status') operationalStatus = $null sizeBytes = To-NullableNumber (Get-OptionalProperty $disk 'Size') firmwareVersion = Trim-Text (Get-OptionalProperty $disk 'FirmwareRevision') reliability = [ordered]@{ temperatureC = $null temperatureMaxC = $null wearPercent = $null powerOnHours = $null readErrorsTotal = $null readErrorsUncorrected = $null writeErrorsTotal = $null writeErrorsUncorrected = $null readLatencyMaxMs = $null writeLatencyMaxMs = $null } } } } catch {} } $batteryStatic = @() $batteryFull = @() $batteryCycles = @() try { $batteryStatic = @(Get-CimInstance -Namespace root\wmi -ClassName BatteryStaticData -ErrorAction Stop) } catch {} try { $batteryFull = @(Get-CimInstance -Namespace root\wmi -ClassName BatteryFullChargedCapacity -ErrorAction Stop) } catch {} try { $batteryCycles = @(Get-CimInstance -Namespace root\wmi -ClassName BatteryCycleCount -ErrorAction Stop) } catch {} $batteryWin32 = @() try { $batteryWin32 = @(Get-CimInstance -ClassName Win32_Battery -ErrorAction Stop) } catch {} $batteryOut = @() for ($i = 0; $i -lt $batteryStatic.Count; $i++) { $s = $batteryStatic[$i] $instanceName = Trim-Text (Get-OptionalProperty $s 'InstanceName') $f = $null $c = $null if ($null -ne $instanceName) { $f = $batteryFull | Where-Object { (Trim-Text (Get-OptionalProperty $_ 'InstanceName')) -eq $instanceName } | Select-Object -First 1 $c = $batteryCycles | Where-Object { (Trim-Text (Get-OptionalProperty $_ 'InstanceName')) -eq $instanceName } | Select-Object -First 1 } if ($null -eq $f -and $i -lt $batteryFull.Count) { $f = $batteryFull[$i] } if ($null -eq $c -and $i -lt $batteryCycles.Count) { $c = $batteryCycles[$i] } $win = if ($i -lt $batteryWin32.Count) { $batteryWin32[$i] } else { $null } $cyclesValue = if ($null -ne $c) { To-NullableNumber (Get-OptionalProperty $c 'CycleCount') } else { $null } $batteryOut += [ordered]@{ designedCapacityMWh = To-NullableNumber (Get-OptionalProperty $s 'DesignedCapacity') fullChargeCapacityMWh = if ($null -ne $f) { To-NullableNumber (Get-OptionalProperty $f 'FullChargedCapacity') } else { $null } cycleCount = $cyclesValue cycleCounterSupported = ($null -ne $cyclesValue -and $cyclesValue -gt 0) estimatedChargeRemainingPercent = if ($null -ne $win) { To-NullableNumber (Get-OptionalProperty $win 'EstimatedChargeRemaining') } else { $null } status = if ($null -ne $win) { Trim-Text (Get-OptionalProperty $win 'Status') } else { $null } chemistry = To-NullableNumber (Get-OptionalProperty $s 'Chemistry') source = 'wmi' } } if ($batteryOut.Count -eq 0 -and $batteryWin32.Count -gt 0) { foreach ($win in $batteryWin32) { $batteryOut += [ordered]@{ designedCapacityMWh = To-NullableNumber (Get-OptionalProperty $win 'DesignCapacity') fullChargeCapacityMWh = To-NullableNumber (Get-OptionalProperty $win 'FullChargeCapacity') cycleCount = $null cycleCounterSupported = $false estimatedChargeRemainingPercent = To-NullableNumber (Get-OptionalProperty $win 'EstimatedChargeRemaining') status = Trim-Text (Get-OptionalProperty $win 'Status') chemistry = To-NullableNumber (Get-OptionalProperty $win 'Chemistry') source = 'win32-battery' } } } # Some OEM WMI providers expose a battery object but omit design/full capacity. # powercfg /batteryreport /xml is a local Windows fallback that often exposes # exactly those two values without requiring network access or retaining battery IDs. $batteryReportResult = Get-BatteryReportFallback $batteryReportFallback = @($batteryReportResult['batteries']) if ($batteryReportFallback.Count -gt 0) { if ($batteryOut.Count -eq 0) { foreach ($fallback in $batteryReportFallback) { $batteryOut += [ordered]@{ designedCapacityMWh = $fallback['designedCapacityMWh'] fullChargeCapacityMWh = $fallback['fullChargeCapacityMWh'] cycleCount = $fallback['cycleCount'] cycleCounterSupported = [bool]$fallback['cycleCounterSupported'] estimatedChargeRemainingPercent = $null status = $null chemistry = $null source = 'powercfg-batteryreport-xml' } } } else { for ($i = 0; $i -lt $batteryOut.Count; $i++) { if ($i -ge $batteryReportFallback.Count) { break } $current = $batteryOut[$i] $fallback = $batteryReportFallback[$i] $usedFallback = $false $designNow = $current['designedCapacityMWh'] $fullNow = $current['fullChargeCapacityMWh'] $cyclesNow = $current['cycleCount'] if ($null -eq $designNow -or [double]$designNow -le 0) { $v = $fallback['designedCapacityMWh'] if ($null -ne $v -and [double]$v -gt 0) { $current['designedCapacityMWh'] = $v; $usedFallback = $true } } if ($null -eq $fullNow -or [double]$fullNow -le 0) { $v = $fallback['fullChargeCapacityMWh'] if ($null -ne $v -and [double]$v -gt 0) { $current['fullChargeCapacityMWh'] = $v; $usedFallback = $true } } if ($null -eq $cyclesNow) { $v = $fallback['cycleCount'] if ($null -ne $v -and [double]$v -ge 0) { $current['cycleCount'] = $v $current['cycleCounterSupported'] = [bool]$fallback['cycleCounterSupported'] $usedFallback = $true } } if ($usedFallback) { $current['source'] = 'wmi+powercfg-batteryreport-xml' } } } } $legacySmart = Get-LegacySmartSummary if ($isAdministrator -and $legacySmart['queryStatus'] -eq 'permission-denied') { $legacySmart['queryStatus'] = 'provider-access-denied' } # TPM: prefer Get-Tpm, then fall back to the Windows TPM provider through CIM/WMI. $tpm = Try-Value { Get-Tpm -ErrorAction Stop } $tpmPresent = $null $tpmReady = $null $tpmEnabled = $null $tpmActivated = $null $tpmSpecVersion = $null $tpmSource = $null $tpmQueryStatus = 'not-run' if ($null -ne $tpm) { $v = Get-OptionalProperty $tpm 'TpmPresent'; if ($null -ne $v) { $tpmPresent = [bool]$v } $v = Get-OptionalProperty $tpm 'TpmReady'; if ($null -ne $v) { $tpmReady = [bool]$v } $tpmSource = 'Get-Tpm' $tpmQueryStatus = if ($null -ne $tpmPresent) { 'available' } else { 'incomplete-object' } } if ($null -eq $tpmPresent) { $tpmRows = @() try { $tpmRows = @(Get-CimInstance -Namespace 'root\CIMV2\Security\MicrosoftTpm' -ClassName Win32_Tpm -ErrorAction Stop) $tpmSource = 'Win32_Tpm-CIM' $tpmQueryStatus = 'available' } catch { try { if (Get-Command Get-WmiObject -ErrorAction SilentlyContinue) { $tpmRows = @(Get-WmiObject -Namespace 'root\CIMV2\Security\MicrosoftTpm' -Class Win32_Tpm -ErrorAction Stop) $tpmSource = 'Win32_Tpm-WMI' $tpmQueryStatus = 'available' } } catch { $msg = [string]$_.Exception.Message if ($msg -match '(?i)access.*denied|acceso.*denegado|unauthorized') { $tpmQueryStatus = 'permission-denied' } elseif ($msg -match '(?i)invalid namespace|invalid class|no se encuentra|not found') { $tpmQueryStatus = 'provider-unavailable' } else { $tpmQueryStatus = 'query-failed' } } } if ($tpmRows.Count -gt 0) { $tpmPresent = $true $row = $tpmRows[0] $v = Get-OptionalProperty $row 'IsEnabled_InitialValue'; if ($null -ne $v) { $tpmEnabled = [bool]$v } $v = Get-OptionalProperty $row 'IsActivated_InitialValue'; if ($null -ne $v) { $tpmActivated = [bool]$v } $tpmSpecVersion = Trim-Text (Get-OptionalProperty $row 'SpecVersion') } elseif ($tpmQueryStatus -eq 'available') { $tpmPresent = $false $tpmQueryStatus = 'not-detected' } } if ($null -eq $tpmPresent) { $nativeTpm = Get-TpmNativeFallback if ($null -ne $nativeTpm['present']) { $tpmPresent = [bool]$nativeTpm['present'] if ($null -ne $nativeTpm['ready']) { $tpmReady = [bool]$nativeTpm['ready'] } if ($null -ne $nativeTpm['version']) { $tpmSpecVersion = Trim-Text $nativeTpm['version'] } $tpmSource = $nativeTpm['source'] $tpmQueryStatus = if ($tpmPresent) { 'available' } else { 'not-detected' } } elseif ($isAdministrator -and $tpmQueryStatus -eq 'permission-denied') { $tpmQueryStatus = 'provider-access-denied' } } # Secure Boot: first use the Windows-maintained registry state so standard-user # runs can often get a result; use Confirm-SecureBootUEFI only as a fallback. $firmwareMode = $null try { $fw = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control' -Name 'PEFirmwareType' -ErrorAction Stop $fwValue = To-NullableNumber (Get-OptionalProperty $fw 'PEFirmwareType') if ($fwValue -eq 1) { $firmwareMode = 'BIOS' } elseif ($fwValue -eq 2) { $firmwareMode = 'UEFI' } } catch {} $secureBoot = $null $secureBootStatus = 'unknown' $secureBootSource = $null if ($firmwareMode -eq 'BIOS') { $secureBootStatus = 'unsupported-legacy-bios' $secureBootSource = 'firmware-mode' } else { try { $sb = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name 'UEFISecureBootEnabled' -ErrorAction Stop $sbValue = To-NullableNumber (Get-OptionalProperty $sb 'UEFISecureBootEnabled') if ($sbValue -eq 1) { $secureBoot = $true; $secureBootStatus = 'enabled'; $secureBootSource = 'registry' } elseif ($sbValue -eq 0) { $secureBoot = $false; $secureBootStatus = 'disabled'; $secureBootSource = 'registry' } } catch {} } if ($null -eq $secureBoot -and $secureBootStatus -ne 'unsupported-legacy-bios') { if ($firmwareMode -eq 'BIOS') { $secureBootStatus = 'unsupported-legacy-bios' $secureBootSource = 'firmware-mode' } else { try { $secureBoot = [bool](Confirm-SecureBootUEFI -ErrorAction Stop) $secureBootStatus = if ($secureBoot) { 'enabled' } else { 'disabled' } $secureBootSource = 'Confirm-SecureBootUEFI' } catch { $message = [string]$_.Exception.Message if ($message -match '(?i)not supported|no compatible|no se admite') { $secureBootStatus = 'unsupported' } elseif ($message -match '(?i)access.*denied|acceso.*denegado|privilege') { $secureBootStatus = 'permission-denied' } else { $secureBootStatus = 'query-failed' } $secureBootSource = 'Confirm-SecureBootUEFI' } } } $dsregText = $null try { $dsregExe = Join-Path $env:SystemRoot 'System32\dsregcmd.exe' if (Test-Path $dsregExe) { $dsregText = (& $dsregExe /status 2>$null | Out-String) } } catch {} $autopilotSignal = $false try { $ap = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Provisioning\Diagnostics\AutoPilot' -Name 'CloudAssignedTenantId' -ErrorAction Stop if ($null -ne (Get-OptionalProperty $ap 'CloudAssignedTenantId') -and ([string](Get-OptionalProperty $ap 'CloudAssignedTenantId')).Trim().Length -gt 0) { $autopilotSignal = $true } } catch {} $report = [ordered]@{ schema = 'lr-audit-express/1' collectorVersion = $collectorVersion collectedAt = $collectedAt execution = [ordered]@{ isAdministrator = $isAdministrator } privacy = [ordered]@{ networkRequests = $false userIdentityCollected = $false serialNumbersCollected = $false rawDsregOutputCollected = $false } system = [ordered]@{ manufacturer = if ($null -ne $computer) { Trim-Text (Get-OptionalProperty $computer 'Manufacturer') } else { $null } model = if ($null -ne $computer) { Trim-Text (Get-OptionalProperty $computer 'Model') } else { $null } systemType = if ($null -ne $computer) { Trim-Text (Get-OptionalProperty $computer 'SystemType') } else { $null } totalMemoryBytes = if ($totalRamBytes -gt 0) { $totalRamBytes } elseif ($null -ne $computer) { To-NullableNumber (Get-OptionalProperty $computer 'TotalPhysicalMemory') } else { $null } } bios = [ordered]@{ manufacturer = if ($null -ne $bios) { Trim-Text (Get-OptionalProperty $bios 'Manufacturer') } else { $null } smbiosVersion = if ($null -ne $bios) { Trim-Text (Get-OptionalProperty $bios 'SMBIOSBIOSVersion') } else { $null } releaseDate = if ($null -ne $bios -and $null -ne (Get-OptionalProperty $bios 'ReleaseDate')) { try { ([datetime](Get-OptionalProperty $bios 'ReleaseDate')).ToUniversalTime().ToString('o') } catch { $null } } else { $null } } operatingSystem = [ordered]@{ caption = if ($null -ne $os) { Trim-Text (Get-OptionalProperty $os 'Caption') } else { $null } version = if ($null -ne $os) { Trim-Text (Get-OptionalProperty $os 'Version') } else { $null } buildNumber = if ($null -ne $os) { Trim-Text (Get-OptionalProperty $os 'BuildNumber') } else { $null } architecture = if ($null -ne $os) { Trim-Text (Get-OptionalProperty $os 'OSArchitecture') } else { $null } } cpu = $cpuOut gpu = $gpuOut memory = [ordered]@{ totalBytes = if ($totalRamBytes -gt 0) { $totalRamBytes } else { $null } modules = $ramOut } storage = [ordered]@{ source = $storageSource disks = $diskOut legacySmart = $legacySmart } battery = [ordered]@{ batteries = $batteryOut batteryReportStatus = $batteryReportResult['status'] } security = [ordered]@{ tpmPresent = $tpmPresent tpmReady = $tpmReady tpmEnabled = $tpmEnabled tpmActivated = $tpmActivated tpmSpecVersion = $tpmSpecVersion tpmSource = $tpmSource tpmQueryStatus = $tpmQueryStatus firmwareMode = $firmwareMode secureBootEnabled = $secureBoot secureBootStatus = $secureBootStatus secureBootSource = $secureBootSource } management = [ordered]@{ azureAdJoined = Parse-DsregBoolean $dsregText 'AzureAdJoined' domainJoined = Parse-DsregBoolean $dsregText 'DomainJoined' workplaceJoined = Parse-DsregBoolean $dsregText 'WorkplaceJoined' autopilotAssignmentSignal = $autopilotSignal } } $prettyJson = $report | ConvertTo-Json -Depth 12 $compactJson = $report | ConvertTo-Json -Depth 12 -Compress $downloads = Join-Path $env:USERPROFILE 'Downloads' if (-not (Test-Path $downloads)) { $downloads = [Environment]::GetFolderPath('Desktop') } $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' $outPath = Join-Path $downloads ("LRAuditExpress-$stamp.json") Set-Content -Path $outPath -Value $prettyJson -Encoding UTF8 $bytes = [System.Text.Encoding]::UTF8.GetBytes($compactJson) $portableCode = 'LR1:' + [Convert]::ToBase64String($bytes) $copied = $false try { if (Get-Command Set-Clipboard -ErrorAction Stop) { Set-Clipboard -Value $portableCode $copied = $true } } catch {} Write-Host '' Write-Host 'LaptopsRenovadas Auditoria Express completada.' -ForegroundColor Green Write-Host ('Archivo: ' + $outPath) if ($copied) { Write-Host 'Codigo LR1 copiado al portapapeles.' -ForegroundColor Green } else { Write-Host 'El portapapeles no estuvo disponible; importa el archivo JSON.' -ForegroundColor Yellow } Write-Host 'No se hicieron solicitudes de red y no se recopilaron seriales ni identidad de usuario.'