<# Informe del equipo Herramientas BAIT · https://bait.cl/herramientas/script-informe-equipo/ Qué hace: Reúne en un solo archivo HTML los datos que pide soporte técnico o un inventario: fabricante, modelo, número de serie, BIOS, procesador, memoria (total y módulos), discos (SSD o HDD, salud y espacio), Windows (edición, versión, compilación y activación), antivirus, últimas actualizaciones, usuarios locales, red y programas instalados. El informe queda en el Escritorio y se abre en el navegador. No envía nada a internet y no cambia ninguna configuración. No necesita administrador (algunos datos, como la salud de los discos, pueden salir más completos si lo ejecutas como administrador). Uso: Clic derecho > Ejecutar con PowerShell o bien: powershell -ExecutionPolicy Bypass -File .\informe-equipo.ps1 #> $ErrorActionPreference = 'SilentlyContinue' $Host.UI.RawUI.WindowTitle = 'Informe del equipo (BAIT)' Write-Host '' Write-Host '=== Informe del equipo ===' -ForegroundColor Cyan Write-Host 'Reuniendo datos, puede tardar hasta un minuto...' # ---------- utilidades ---------- # Escapa texto para ponerlo dentro del HTML function H($Texto) { if ($null -eq $Texto) { return '' } return [System.Net.WebUtility]::HtmlEncode([string]$Texto) } function GB([double]$Bytes, [int]$Decimales = 1) { return ("{0:N$Decimales} GB" -f ($Bytes / 1GB)) } # Arma una tabla HTML a partir de una lista de objetos y las columnas indicadas function Tabla($Filas, [string[]]$Columnas) { if (-not $Filas) { return '

Sin datos.

' } $html = '
' foreach ($c in $Columnas) { $html += '' } $html += '' foreach ($f in $Filas) { $html += '' foreach ($c in $Columnas) { $html += '' } $html += '' } return $html + '
' + (H $c) + '
' + (H $f.$c) + '
' } # Lista de pares nombre y valor function Datos($Pares) { $html = '
' foreach ($k in $Pares.Keys) { $html += '
' + (H $k) + '
' + (H $Pares[$k]) + '
' } return $html + '
' } function Paso([string]$Texto) { Write-Host " $Texto" -ForegroundColor Gray } # ---------- equipo y BIOS ---------- Paso 'Equipo y BIOS' $cs = Get-CimInstance Win32_ComputerSystem $bios = Get-CimInstance Win32_BIOS $prod = Get-CimInstance Win32_ComputerSystemProduct $equipo = [ordered]@{ 'Nombre del equipo' = $env:COMPUTERNAME 'Fabricante' = $cs.Manufacturer 'Modelo' = $cs.Model 'Número de serie' = $bios.SerialNumber 'UUID' = $prod.UUID 'Dominio o grupo' = $cs.Domain 'BIOS' = "$($bios.Manufacturer) $($bios.SMBIOSBIOSVersion)" 'Fecha de la BIOS' = $(if ($bios.ReleaseDate) { $bios.ReleaseDate.ToString('dd-MM-yyyy') } else { '' }) } # ---------- procesador y memoria ---------- Paso 'Procesador y memoria' $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 $modulos = Get-CimInstance Win32_PhysicalMemory | ForEach-Object { [pscustomobject]@{ 'Ranura' = $_.DeviceLocator 'Capacidad' = GB $_.Capacity 0 'Velocidad' = $(if ($_.ConfiguredClockSpeed) { "$($_.ConfiguredClockSpeed) MHz" } else { "$($_.Speed) MHz" }) 'Fabricante' = $_.Manufacturer 'Modelo' = ([string]$_.PartNumber).Trim() } } $ranurasTotales = (Get-CimInstance Win32_PhysicalMemoryArray | Measure-Object -Property MemoryDevices -Sum).Sum $hardware = [ordered]@{ 'Procesador' = ([string]$cpu.Name).Trim() 'Núcleos e hilos' = "$($cpu.NumberOfCores) núcleos, $($cpu.NumberOfLogicalProcessors) hilos" 'Memoria total' = GB $cs.TotalPhysicalMemory 'Ranuras de RAM' = "$(@($modulos).Count) ocupadas de $ranurasTotales" } # ---------- discos ---------- Paso 'Discos' $discosFisicos = @() if (Get-Command Get-PhysicalDisk -ErrorAction SilentlyContinue) { $discosFisicos = Get-PhysicalDisk | Sort-Object DeviceId | ForEach-Object { $tipo = [string]$_.MediaType if ($tipo -eq 'Unspecified' -or -not $tipo) { $tipo = 'Sin dato' } $salud = switch ([string]$_.HealthStatus) { 'Healthy' { 'Buena' } 'Warning' { 'Advertencia' } 'Unhealthy' { 'Con fallas' } default { [string]$_.HealthStatus } } [pscustomobject]@{ 'Disco' = $_.FriendlyName 'Tipo' = $tipo 'Conexión' = [string]$_.BusType 'Tamaño' = GB $_.Size 0 'Salud' = $salud } } } $volumenes = Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3' | ForEach-Object { $pct = 0 if ($_.Size) { $pct = [math]::Round(($_.FreeSpace / $_.Size) * 100) } [pscustomobject]@{ 'Unidad' = $_.DeviceID 'Nombre' = $_.VolumeName 'Tamaño' = GB $_.Size 'Libre' = GB $_.FreeSpace '% libre' = "$pct %" } } # ---------- Windows y licencia ---------- Paso 'Windows y licencia' $os = Get-CimInstance Win32_OperatingSystem $cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' # Consulta la licencia de Windows (ApplicationID fijo de Windows) $lic = Get-CimInstance SoftwareLicensingProduct -Filter "ApplicationID='55c92734-d682-4d71-983e-d6ec3f16059f' AND PartialProductKey IS NOT NULL" | Select-Object -First 1 $estadoLicencia = 'No se pudo consultar' if ($lic) { $estadoLicencia = switch ([int]$lic.LicenseStatus) { 0 { 'Sin licencia' } 1 { 'Activado' } 2 { 'Período de gracia inicial' } 3 { 'Período de gracia adicional' } 4 { 'Período de gracia (no genuino)' } 5 { 'Notificación: no activado' } 6 { 'Período de gracia extendido' } default { 'Desconocido' } } $estadoLicencia += " (clave terminada en $($lic.PartialProductKey))" } $windows = [ordered]@{ 'Edición' = $os.Caption 'Versión' = $cv.DisplayVersion 'Compilación' = "$($os.BuildNumber).$($cv.UBR)" 'Arquitectura' = $os.OSArchitecture 'Instalado el' = $os.InstallDate.ToString('dd-MM-yyyy') 'Último encendido' = $os.LastBootUpTime.ToString('dd-MM-yyyy HH:mm') 'Activación' = $estadoLicencia } # ---------- antivirus ---------- Paso 'Antivirus' # productState es un número donde algunos dígitos indican si está activo y si las firmas están al día $antivirus = Get-CimInstance -Namespace 'root/SecurityCenter2' -ClassName AntiVirusProduct | ForEach-Object { $hex = '{0:x6}' -f [int]$_.productState $activo = $hex.Substring(2, 2) -in @('10', '11') $alDia = $hex.Substring(4, 2) -eq '00' [pscustomobject]@{ 'Antivirus' = $_.displayName 'Estado' = $(if ($activo) { 'Activo' } else { 'Inactivo' }) 'Firmas' = $(if ($alDia) { 'Al día' } else { 'Desactualizadas' }) } } # ---------- actualizaciones ---------- Paso 'Actualizaciones' $actualizaciones = Get-HotFix | Where-Object { $_.InstalledOn } | Sort-Object InstalledOn -Descending | Select-Object -First 10 | ForEach-Object { [pscustomobject]@{ 'Código' = $_.HotFixID 'Tipo' = $_.Description 'Instalada el' = $_.InstalledOn.ToString('dd-MM-yyyy') } } # ---------- usuarios locales ---------- Paso 'Usuarios locales' $admins = @() try { $admins = Get-LocalGroupMember -SID 'S-1-5-32-544' -ErrorAction Stop | ForEach-Object { ($_.Name -split '\\')[-1] } } catch { } $usuarios = Get-LocalUser | ForEach-Object { [pscustomobject]@{ 'Usuario' = $_.Name 'Habilitado' = $(if ($_.Enabled) { 'Sí' } else { 'No' }) 'Administrador' = $(if ($admins -contains $_.Name) { 'Sí' } else { 'No' }) 'Último inicio' = $(if ($_.LastLogon) { $_.LastLogon.ToString('dd-MM-yyyy HH:mm') } else { 'Nunca' }) } } # ---------- red ---------- Paso 'Red' $red = Get-CimInstance Win32_NetworkAdapterConfiguration -Filter 'IPEnabled=True' | ForEach-Object { [pscustomobject]@{ 'Adaptador' = $_.Description 'IP' = (@($_.IPAddress) | Where-Object { $_ -match '^\d+\.' }) -join ', ' 'MAC' = $_.MACAddress 'Puerta de enlace' = (@($_.DefaultIPGateway) -join ', ') 'DNS' = (@($_.DNSServerSearchOrder) -join ', ') 'DHCP' = $(if ($_.DHCPEnabled) { 'Sí' } else { 'No' }) } } # ---------- programas instalados (registro, sin usar Win32_Product que es lento) ---------- Paso 'Programas instalados' $rutasDesinstalar = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' ) $programas = Get-ItemProperty -Path $rutasDesinstalar | Where-Object { $_.DisplayName -and $_.SystemComponent -ne 1 -and -not $_.ParentKeyName } | Sort-Object DisplayName -Unique | ForEach-Object { $fecha = '' if ($_.InstallDate -match '^(\d{4})(\d{2})(\d{2})$') { $fecha = "$($Matches[3])-$($Matches[2])-$($Matches[1])" } [pscustomobject]@{ 'Programa' = $_.DisplayName 'Versión' = $_.DisplayVersion 'Editor' = $_.Publisher 'Instalado' = $fecha } } # ---------- HTML ---------- Paso 'Generando el informe' $fechaInforme = Get-Date -Format 'dd-MM-yyyy HH:mm' $css = @' :root{--bg:#F5F4EF;--surface:#fff;--alt:#ECEAE3;--text:#131614;--body:#454B47;--muted:#666B66;--line:#DEDBD2;--accent:#0B6B5D} @media (prefers-color-scheme:dark){:root{--bg:#0F1211;--surface:#181C1A;--alt:#151917;--text:#EEEFEA;--body:#B9BEB8;--muted:#8F958F;--line:#2A302D;--accent:#4FD1B8}} *{box-sizing:border-box} body{margin:0;background:var(--bg);color:var(--body);font:15px/1.55 "Segoe UI",system-ui,sans-serif} main{max-width:1080px;margin:0 auto;padding:32px 18px 60px} h1{color:var(--text);font-size:30px;margin:0 0 4px;letter-spacing:-.02em} h2{color:var(--text);font-size:19px;margin:0 0 12px} .sub{color:var(--muted);margin:0 0 28px} section{background:var(--surface);border-radius:14px;padding:20px 22px;margin-bottom:14px} .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px} .grid section{margin:0} dl{margin:0} dl div{display:flex;justify-content:space-between;gap:16px;padding:8px 0} dl div+div{border-top:1px solid var(--line)} dt{color:var(--muted)} dd{margin:0;color:var(--text);text-align:right;word-break:break-word} .scroll{overflow-x:auto} table{width:100%;border-collapse:collapse;font-size:14px} th{text-align:left;color:var(--muted);font-weight:500;padding:6px 10px 6px 0;white-space:nowrap} td{padding:8px 10px 8px 0;border-top:1px solid var(--line);color:var(--text)} .vacio{color:var(--muted);margin:0} footer{color:var(--muted);font-size:13px;margin-top:24px} footer a{color:var(--accent)} @media print{body{background:#fff}section{padding:8px 0}} '@ $cuerpo = @"

Informe de $(H $env:COMPUTERNAME)

$(H $equipo['Fabricante']) $(H $equipo['Modelo']) · Serie $(H $equipo['Número de serie']) · Generado el $fechaInforme

Equipo

$(Datos $equipo)

Hardware

$(Datos $hardware)

Windows

$(Datos $windows)

Módulos de memoria

$(Tabla $modulos @('Ranura','Capacidad','Velocidad','Fabricante','Modelo'))

Discos físicos

$(Tabla $discosFisicos @('Disco','Tipo','Conexión','Tamaño','Salud'))

Unidades

$(Tabla $volumenes @('Unidad','Nombre','Tamaño','Libre','% libre'))

Antivirus

$(Tabla $antivirus @('Antivirus','Estado','Firmas'))

Últimas actualizaciones

$(Tabla $actualizaciones @('Código','Tipo','Instalada el'))

Usuarios locales

$(Tabla $usuarios @('Usuario','Habilitado','Administrador','Último inicio'))

Red

$(Tabla $red @('Adaptador','IP','MAC','Puerta de enlace','DNS','DHCP'))

Programas instalados ($(@($programas).Count))

$(Tabla $programas @('Programa','Versión','Editor','Instalado'))
"@ $html = "`nInforme de $(H $env:COMPUTERNAME)$cuerpo" # Escritorio real del usuario (funciona aunque OneDrive lo haya movido) $escritorio = [Environment]::GetFolderPath('Desktop') $archivo = Join-Path $escritorio ("Informe-{0}-{1}.html" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyyMMdd-HHmm')) [System.IO.File]::WriteAllText($archivo, $html, (New-Object System.Text.UTF8Encoding($true))) Write-Host '' Write-Host "Informe guardado en: $archivo" -ForegroundColor Green Start-Process $archivo Read-Host 'Presiona Enter para cerrar'