Qué vas a conseguir
Un script que, dada una lista de equipos, se conecta a cada uno, saca modelo, número de serie, versión de Windows, disco libre y último arranque, y deja un CSV con una fila por equipo, incluidos los que no respondieron y por qué. Es el patrón que repetirás en la mitad de los scripts de IT.
Antes de empezar
- PowerShell 5.1 (viene con Windows) o PowerShell 7 (
winget install Microsoft.PowerShell). El script vale en los dos. - WinRM activado en los equipos de destino (en dominio suele estarlo por GPO; si no,
Enable-PSRemoting -Forceen cada uno, o la GPO Permitir la administración remota del servidor a través de WinRM). - Tu cuenta con administrador local en los equipos, o una credencial para pasarla con
-Credential. - Un editor con PowerShell integrado: VS Code con la extensión de PowerShell marca errores mientras escribes.
1. Objetos, no texto
La diferencia con cmd y con Bash: un comando de PowerShell devuelve objetos con propiedades, no líneas. Por eso puedes seleccionar, filtrar y exportar sin cortar cadenas.
Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, LastBootUpTimeCaption Version LastBootUpTime------- ------- --------------Microsoft Windows 11 Enterprise 10.0.26100 03/10/2026 8:12:40(Get-CimInstance Win32_OperatingSystem).LastBootUpTime -lt (Get-Date).AddDays(-14)TrueGet-CimInstance sustituye a Get-WmiObject (obsoleto). Get-Member sobre cualquier objeto te enseña qué propiedades tiene.
2. El script
#Requires -Version 5.1<#.SYNOPSIS Inventario básico de equipos por WinRM, con un CSV de salida..EXAMPLE .\Get-DeviceInventory.ps1 -ComputerName (Get-Content equipos.txt) -OutFile inventario.csv#>[CmdletBinding()]param( [Parameter(Mandatory)] [string[]] $ComputerName, [string] $OutFile = "inventario-$(Get-Date -Format yyyyMMdd).csv", [pscredential] $Credential) $remote = { $os = Get-CimInstance Win32_OperatingSystem $cs = Get-CimInstance Win32_ComputerSystem $bio = Get-CimInstance Win32_BIOS $c = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" [pscustomobject]@{ Computer = $env:COMPUTERNAME Model = "$($cs.Manufacturer) $($cs.Model)" Serial = $bio.SerialNumber OS = $os.Caption Build = $os.Version FreeGB = [math]::Round($c.FreeSpace / 1GB, 1) LastBoot = $os.LastBootUpTime LastUser = $cs.UserName }} $results = foreach ($pc in $ComputerName) { Write-Verbose "Consultando $pc" try { $p = @{ ComputerName = $pc; ScriptBlock = $remote; ErrorAction = 'Stop' } if ($Credential) { $p.Credential = $Credential } $r = Invoke-Command @p $r | Select-Object * -ExcludeProperty PSComputerName, RunspaceId, PSShowComputerName | Add-Member -NotePropertyName Status -NotePropertyValue OK -PassThru } catch { [pscustomobject]@{ Computer = $pc; Model = $null; Serial = $null; OS = $null; Build = $null FreeGB = $null; LastBoot = $null; LastUser = $null Status = "ERROR: $($_.Exception.Message -replace '\s+',' ')" } }} $results | Export-Csv -Path $OutFile -NoTypeInformation -Encoding UTF8 -Delimiter ';'$ok = ($results | Where-Object Status -eq 'OK').CountWrite-Host "$ok de $($ComputerName.Count) equipos respondieron. CSV: $OutFile"Lo que hace que sea un script y no un apaño: param con tipos (así -ComputerName acepta una lista), [CmdletBinding()] que da -Verbose gratis, el bloque remoto que devuelve un objeto por equipo, try/catch con ErrorAction Stop para que un equipo apagado no pare el bucle, y el CSV con ; como separador para que Excel en español lo abra en columnas.
3. Ejecutarlo
.\Get-DeviceInventory.ps1 -ComputerName LAPTOP-7F2K9Q, DESK-MAD-201, LAPTOP-OLD-1 -VerboseVERBOSE: Consultando LAPTOP-7F2K9QVERBOSE: Consultando DESK-MAD-201VERBOSE: Consultando LAPTOP-OLD-12 de 3 equipos respondieron. CSV: inventario-20261021.csvImport-Csv .\inventario-20261021.csv -Delimiter ';' | Format-Table Computer, FreeGB, LastBoot, StatusComputer FreeGB LastBoot Status-------- ------ -------- ------LAPTOP-7F2K9Q 21,4 03/10/2026 8:12:40 OKDESK-MAD-201 188,0 20/10/2026 7:55:02 OKLAPTOP-OLD-1 ERROR: Connecting to remote server LAPTOP-OLD-1 failed with the following error message : WinRM cannot complete the operation...Para cientos de equipos, Invoke-Command acepta la lista entera a la vez y los consulta en paralelo (-ThrottleLimit 32); el bucle de arriba es más lento pero deja claro qué equipo falló y por qué.
4. Política de ejecución y firma
Si al ejecutarlo aparece "no se puede cargar porque la ejecución de scripts está deshabilitada", es la política de ejecución. No es seguridad contra atacantes; es un seguro contra ejecutar scripts por accidente.
Get-ExecutionPolicy -List Scope ExecutionPolicy ----- --------------- MachinePolicy Undefined UserPolicy Undefined Process Undefined CurrentUser Undefined LocalMachine RestrictedSet-ExecutionPolicy -Scope CurrentUser RemoteSignedRemoteSigned ejecuta lo que escribes tú y exige firma a lo descargado. Si la empresa la fija por GPO en AllSigned, los scripts del equipo se firman con un certificado de firma de código de la CA interna (Set-AuthenticodeSignature). Es lo correcto en producción y encaja con el repositorio de la guía de Git: solo lo revisado se firma.
Siguientes pasos
- Cambia la lista de equipos por
Get-ADComputer -Filter {OperatingSystem -like "Windows 11*"}y tendrás el inventario de todo el dominio. - Pasa el resultado por
PSScriptAnalyzer(Invoke-ScriptAnalyzer .\Get-DeviceInventory.ps1) antes de subirlo al repo. - En el Nivel 3 verás Microsoft Graph desde PowerShell: el mismo patrón, contra Entra ID e Intune en lugar de WinRM.
¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.
Tech Career Lab · newsletter
¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.
Apúntate y llévate las 33 ideas →