Public/Get-TechCareDiagnostics.ps1
|
function Get-TechCareDiagnostics { <# .SYNOPSIS Recopila el diagnostico del equipo en modo solo lectura. .DESCRIPTION Reune sistema operativo, CPU, memoria, discos, salud fisica de los discos, tiempo encendido, errores criticos recientes, actualizaciones pendientes y programas de inicio. No modifica nada, por lo que no declara ShouldProcess ni requiere privilegios de administrador; sin elevacion simplemente hay apartados que el sistema no deja leer, y eso se refleja en los hallazgos. .EXAMPLE Get-TechCareDiagnostics Diagnostico completo del equipo local. .EXAMPLE (Get-TechCareDiagnostics).Recommendations Solo las recomendaciones, para pegarlas en el parte de trabajo. .OUTPUTS PSCustomObject .LINK https://github.com/earbona23/TechCare #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseSingularNouns', '', Justification = 'El cmdlet devuelve un lote de diagnosticos, no uno solo, y el nombre publico esta congelado por el manifiesto 0.1.0. Renombrarlo romperia a los consumidores sin aportar claridad.')] [CmdletBinding()] [OutputType([pscustomobject])] param() if (-not (Test-TechCarePlatform)) { throw $script:PlatformMessage } $findings = New-Object -TypeName System.Collections.ArrayList $recs = New-Object -TypeName System.Collections.ArrayList $status = 'Green' $mod = 'Diagnose' Write-Progress -Activity 'TechCare' -Status 'Diagnostico del equipo' -PercentComplete 5 Write-TechCareLog -ModuleName $mod -Message 'Iniciando diagnostico (solo lectura).' try { $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cpu = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | Select-Object -First 1 [void]$findings.Add(('Equipo: {0} / usuario: {1}' -f $cs.Name, $cs.UserName)) [void]$findings.Add(('SO: {0} (build {1})' -f $os.Caption, $os.BuildNumber)) [void]$findings.Add(('CPU: {0} - {1} nucleos / {2} hilos' -f $cpu.Name, $cpu.NumberOfCores, $cpu.NumberOfLogicalProcessors)) # FreePhysicalMemory viene en kilobytes, de ahi la division por 1MB. [void]$findings.Add(('RAM: {0:N1} GB totales, {1:N1} GB libres' -f ($cs.TotalPhysicalMemory / 1GB), ($os.FreePhysicalMemory / 1MB))) $uptime = (Get-Date) - $os.LastBootUpTime [void]$findings.Add(('Tiempo encendido: {0:N1} dias' -f $uptime.TotalDays)) if ($uptime.TotalDays -gt 30) { $status = 'Yellow' [void]$recs.Add('Mas de 30 dias sin reiniciar: programar un reinicio planificado. TechCare no reinicia nunca.') } $disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -ErrorAction Stop foreach ($disk in $disks) { if ($disk.Size -le 0) { continue } $freePct = [math]::Round(($disk.FreeSpace / $disk.Size) * 100, 1) [void]$findings.Add(('Disco {0}: {1:N1} GB libres de {2:N1} GB ({3}%)' -f $disk.DeviceID, ($disk.FreeSpace / 1GB), ($disk.Size / 1GB), $freePct)) if ($freePct -lt 10) { $status = 'Red' [void]$recs.Add("Disco $($disk.DeviceID) por debajo del 10% libre.") } elseif ($freePct -lt 20) { if ($status -ne 'Red') { $status = 'Yellow' } [void]$recs.Add("Disco $($disk.DeviceID) con menos del 20% libre.") } } } catch { $status = 'Yellow' [void]$findings.Add('No se pudo completar la recopilacion basica: ' + $_.Exception.Message) Write-TechCareLog -ModuleName $mod -Level Error -Message 'Fallo en recopilacion basica.' -ErrorText $_.Exception.Message } # Get-PhysicalDisk solo existe con el modulo Storage; en ediciones donde falta # se cae a la clase SMART de WMI, que lleva en Windows desde XP. try { $physical = Get-PhysicalDisk -ErrorAction Stop foreach ($pd in $physical) { [void]$findings.Add(('Disco fisico: {0} - salud {1} - estado {2}' -f $pd.FriendlyName, $pd.HealthStatus, ($pd.OperationalStatus -join ','))) if ($pd.HealthStatus -ne 'Healthy') { $status = 'Red' [void]$recs.Add("Disco fisico $($pd.FriendlyName) no reporta salud correcta: respaldar antes de cualquier reparacion.") } } } catch { try { $smart = Get-CimInstance -Namespace 'root\wmi' -ClassName MSStorageDriver_FailurePredictStatus -ErrorAction Stop foreach ($s in $smart) { [void]$findings.Add(('SMART: prediccion de fallo = {0}' -f $s.PredictFailure)) if ($s.PredictFailure) { $status = 'Red' [void]$recs.Add('SMART predice fallo de disco: respaldar antes de cualquier reparacion.') } } } catch { [void]$findings.Add('Salud SMART no disponible en este equipo.') } } try { $events = @(Get-WinEvent -FilterHashtable @{ LogName = 'System'; Level = 1, 2; StartTime = (Get-Date).AddDays(-7) } -MaxEvents 15 -ErrorAction Stop) [void]$findings.Add(('Errores y criticos en System (7 dias): {0}' -f $events.Count)) foreach ($ev in ($events | Select-Object -First 5)) { [void]$findings.Add((' - {0} | ID {1} | {2}' -f $ev.TimeCreated, $ev.Id, $ev.ProviderName)) } if ($events.Count -ge 10) { if ($status -ne 'Red') { $status = 'Yellow' } [void]$recs.Add('Volumen alto de errores en el Visor de eventos: revisar proveedor y origen.') } } catch { [void]$findings.Add('Visor de eventos: sin errores recientes legibles o acceso insuficiente.') } try { $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $pending = $searcher.Search("IsInstalled=0 and IsHidden=0 and Type='Software'") [void]$findings.Add(('Actualizaciones de software pendientes: {0}' -f $pending.Updates.Count)) if ($pending.Updates.Count -gt 0) { if ($status -eq 'Green') { $status = 'Yellow' } [void]$recs.Add('Hay actualizaciones pendientes: usar el modulo Update.') } } catch { [void]$findings.Add('Busqueda de actualizaciones no disponible: ' + $_.Exception.Message) } try { $runKeys = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' ) $startup = New-Object -TypeName System.Collections.ArrayList foreach ($key in $runKeys) { if (-not (Test-Path -LiteralPath $key)) { continue } $item = Get-ItemProperty -LiteralPath $key -ErrorAction SilentlyContinue if ($null -eq $item) { continue } # Las propiedades PS* las anade PowerShell al objeto, no son entradas # de arranque reales. $item.PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' } | ForEach-Object { [void]$startup.Add($_.Name) } } [void]$findings.Add(('Programas de inicio (registro): {0}' -f $startup.Count)) foreach ($s in $startup) { [void]$findings.Add(' - ' + $s) } if ($startup.Count -gt 12) { if ($status -eq 'Green') { $status = 'Yellow' } [void]$recs.Add('Muchos programas de inicio: revisar con el modulo Performance.') } } catch { [void]$findings.Add('No se pudo enumerar el arranque automatico: ' + $_.Exception.Message) } Write-Progress -Activity 'TechCare' -Status 'Diagnostico completado' -Completed Write-TechCareLog -ModuleName $mod -Level Success -Message ('Diagnostico finalizado con estado {0}.' -f $status) return Add-TechCareResult -Module $mod -Status $status -Findings $findings.ToArray() -Recommendations $recs.ToArray() } |