Exportar conexiones TCP a CSV con proceso en PowerShell

Para exportar las conexiones TCP de Windows a CSV puedes usar Get-NetTCPConnection, seleccionar sus campos y enviarlos a Export-Csv. Si añades el nombre del proceso asociado a OwningProcess, el archivo permite revisar qué aplicación mantenía cada conexión en el momento de la captura. Es un inventario local: no comprueba desde fuera que un servicio sea accesible.
La guía incluye un script que genera un archivo con fecha, estado, direcciones, puertos y proceso. Está pensado para Windows PowerShell 5.1 con el módulo NetTCPIP. Trabaja primero en tu propio equipo o en un sistema que administras. Los ejemplos no cierran procesos, no cambian el cortafuegos y no envían el inventario a ningún servidor.
Qué diferencia hay entre ver conexiones y probar un puerto
Una conexión TCP establecida, un puerto a la escucha y una prueba de conectividad responden a preguntas distintas. El inventario recoge las entradas que ve Windows localmente. Una prueba desde otro equipo intenta establecer una conexión por una ruta concreta. Ninguno de los dos resultados sustituye una comprobación del funcionamiento de la aplicación.
Si necesitas saber si un servidor acepta una conexión en un puerto, utiliza la guía de Test-NetConnection. Para interpretar la diferencia entre escucha local, redirecciones y acceso desde otra red, consulta cómo comprobar un puerto abierto en Windows.
| Pregunta | Comprobación |
|---|---|
| ¿Qué conexiones registra este Windows? | Inventario local con Get-NetTCPConnection |
| ¿Qué programa figura como propietario? | Relacionar OwningProcess con Get-Process |
| ¿Acepta TCP un servidor desde mi red? | Prueba con Test-NetConnection y un puerto concreto |
| ¿Funciona el sitio o servicio? | Prueba propia de la aplicación, después de revisar la conectividad |
Comprueba que los comandos están disponibles
Abre Windows PowerShell y consulta la disponibilidad antes de copiar el script. Si trabajas desde otra plataforma o desde una sesión con módulos restringidos, no des por hecho que tendrás los mismos comandos. La referencia oficial de Get-NetTCPConnection documenta sus propiedades y filtros.
Get-Command Get-NetTCPConnection, Get-Process, Export-Csv
Get-NetTCPConnection | Select-Object -First 5 LocalAddress, LocalPort, RemoteAddress, RemotePort, State, OwningProcess
El segundo comando muestra una muestra del inventario real del equipo. No esperes las mismas direcciones o puertos en dos ordenadores. Si faltan permisos o la consulta devuelve un error, registra ese problema antes de concluir que no hay conexiones. Un fallo de recopilación no equivale a una lista vacía.
Script para guardar conexiones TCP y proceso en CSV
El siguiente ejemplo crea un directorio nuevo dentro de tu carpeta de usuario y utiliza un nombre con fecha y milisegundos para reducir colisiones. Conserva los objetos durante la transformación y aplica el formato CSV únicamente al final. Copia el bloque completo: las variables se utilizan en los pasos posteriores.
$capturedAt = Get-Date
$reportDir = Join-Path $env:USERPROFILE 'InformesTCP'
$null = New-Item -ItemType Directory -Path $reportDir -Force
$reportName = 'tcp-{0}.csv' -f $capturedAt.ToString('yyyyMMdd-HHmmss-fff')
$reportPath = Join-Path $reportDir $reportName
$connections = @(Get-NetTCPConnection -ErrorAction Stop)
$processNames = @{}
Get-Process -ErrorAction Stop | ForEach-Object {
$processNames[[int]$_.Id] = $_.ProcessName
}
$rows = @($connections | ForEach-Object {
$ownerId = [int]$_.OwningProcess
$processName = $processNames[$ownerId]
if (-not $processName) { $processName = '(no disponible)' }
[pscustomobject]@{
CapturedAt = $capturedAt.ToString('o')
LocalAddress = $_.LocalAddress
LocalPort = $_.LocalPort
RemoteAddress = $_.RemoteAddress
RemotePort = $_.RemotePort
State = [string]$_.State
ProcessId = $ownerId
ProcessName = $processName
}
})
if ($rows.Count -eq 0) {
Write-Warning 'La consulta no devolvio entradas; no se ha creado un CSV.'
} else {
$rows | Sort-Object ProcessName, LocalPort |
Export-Csv -LiteralPath $reportPath -NoTypeInformation -Encoding UTF8 -UseCulture -NoClobber
$reportPath
}
La salida final es la ruta del archivo creado. La carpeta InformesTCP aparece en tu perfil de usuario; no debes sustituirla por una carpeta de Windows. Si ese destino no resulta apropiado, cambia solo la asignación de $reportDir por una carpeta de trabajo donde tengas permiso para escribir.
El script consulta los procesos una vez y construye un diccionario por identificador. Esto evita ejecutar una consulta de proceso por cada fila TCP. Entre ambas capturas un proceso puede terminar y su identificador puede reutilizarse: el resultado es una correlación puntual, no una prueba forense de identidad ni una atribución histórica.
Cómo leer las columnas del archivo
LocalAddress y LocalPort corresponden al extremo local. RemoteAddress y RemotePort describen el otro extremo cuando existe. En entradas a la escucha, no interpretes una dirección remota genérica como si fuera un usuario conectado. Lee primero el estado y después el resto de los campos.
ProcessId es el número utilizado para la relación con el proceso; ProcessName es la etiqueta encontrada durante la captura. No hemos incluido la ruta ejecutable ni la línea de comandos. Si necesitas investigar una aplicación, verifica su identidad en el sistema y conserva el contexto antes de tomar decisiones.
El campo CapturedAt se calcula una vez al comenzar. Sirve para agrupar todas las filas bajo una misma captura, aunque el sistema vaya cambiando mientras se ejecuta la consulta. No representa el instante de creación de cada conexión y no permite calcular por sí solo cuánto duró una sesión.
Filtrar conexiones establecidas o un puerto concreto
Después de ejecutar el bloque, puedes crear un segundo archivo con una selección de $rows. Los filtros siguientes trabajan sobre los objetos ya capturados; no vuelven a consultar el estado de la red. Si quieres datos nuevos, repite la captura completa y conserva otro nombre de archivo.
$established = @($rows | Where-Object { $_.State -eq 'Established' })
$local443 = @($rows | Where-Object { $_.LocalPort -eq 443 })
$remote443 = @($rows | Where-Object { $_.RemotePort -eq 443 })
$listeners = @($rows | Where-Object { $_.State -eq 'Listen' })
$established | Select-Object ProcessName, RemoteAddress, RemotePort
Filtrar LocalPort por 443 sirve para revisar entradas cuyo extremo local usa ese puerto. Filtrar RemotePort por 443 suele responder a otra pregunta, como localizar conexiones hacia un servicio remoto en ese puerto. Un número de puerto habitual sugiere un uso, pero no certifica qué protocolo de aplicación está circulando.
Para guardar una selección, utiliza otro nombre en la misma carpeta. Si el filtro no devuelve filas, comprueba primero que estás usando el extremo correcto. No cambies un filtro local por uno remoto solo para obtener resultados: debes mantener la pregunta que estás investigando.
$filteredPath = Join-Path $reportDir ('establecidas-' + $reportName)
if ($established.Count -gt 0) {
$established | Export-Csv -LiteralPath $filteredPath -NoTypeInformation -Encoding UTF8 -UseCulture -NoClobber
}
Abrir el CSV sin perder la estructura
El ejemplo usa -UseCulture para elegir el separador de lista configurado en Windows. Es útil al abrir el archivo con aplicaciones que siguen esa configuración. Cuando compartas el informe con un sistema que exige comas, sustituye esa opción por -Delimiter ',' tanto al exportar como al importar.
La documentación de Export-Csv explica las opciones de serialización. Evita introducir Format-Table antes de exportar: necesitas propiedades de los objetos, no instrucciones de presentación para la consola. El orden de las columnas del ejemplo se fija al construir cada objeto.
En una hoja de cálculo, utiliza la importación de texto si todo aparece en una sola columna. Selecciona la codificación UTF-8 y el separador correspondiente. Conserva una copia del CSV original antes de ordenar o editar: la aplicación de hojas de cálculo puede cambiar la representación de algunos valores al guardar.
Qué se ha probado y cuáles son los límites
El ejemplo está diseñado para poder validarse sin contactar con servidores externos: una conexión local de prueba permite comprobar que el identificador del proceso y el puerto llegan al CSV. También se puede verificar que una captura vacía no genera un archivo engañoso y que un destino existente queda protegido por -NoClobber. La prueba local realizada el 4 de noviembre de 2026 verificó una escucha temporal en loopback, la relación con su proceso, la lectura del CSV y la protección frente a sobrescritura. Se limitó la consulta a ese puerto de laboratorio; no se probó acceso desde otra red.
El inventario no captura paquetes ni el contenido de las comunicaciones. Tampoco incluye automáticamente conexiones que ya terminaron. Para investigar un incidente necesitas otros registros y una ventana temporal definida. Una captura aislada puede ayudar a formular preguntas, pero no demuestra por sí misma actividad maliciosa.
UDP necesita una consulta diferente. Get-NetUDPEndpoint muestra extremos UDP locales y no debe presentarse como una lista TCP con estados equivalentes. Mantén esos resultados en un archivo separado con sus propios campos.
Problemas habituales y cómo interpretarlos
El nombre del proceso aparece como no disponible
Puede haber terminado entre consultas o no figurar en la lista obtenida. Conserva el identificador y repite la captura si necesitas investigar. La referencia de Get-Process describe la consulta de procesos; no atribuyas automáticamente una conexión a un programa solo porque ahora usa el mismo número.
Hay una escucha local, pero desde fuera no conecta
Revisa dirección de escucha, ruta, cortafuegos y punto desde el que haces la prueba. Una escucha en loopback no es una escucha en todas las interfaces. Si el problema depende del nombre del servidor, continúa con las guías de diagnóstico de Trucos Informáticos y localiza el tutorial de Nslookup para separar resolución DNS y conectividad.
¿Debo publicar el archivo para pedir ayuda?
Revisa antes su contenido: puede revelar direcciones internas, servicios y nombres de aplicaciones. Comparte únicamente las filas y columnas necesarias para explicar el problema, con los datos que deban reservarse sustituidos de forma consistente. Guarda la captura completa en tu entorno de trabajo y documenta qué campos has ocultado en la copia.