Cómo supervisar las conexiones TCP y UDP en Windows mediante PowerShell

En el panorama en constante evolución de la seguridad informática, mantener la visibilidad de las conexiones de red es primordial. Tanto si gestionas una gran red empresarial como si supervisas una pequeña o mediana empresa, es crucial saber qué direcciones IP se comunican activamente con tus sistemas.

Este post profundiza en un script PowerShell especializado diseñado para alertar a los administradores de direcciones IP específicas que están escuchando o en un estado establecido. Esta herramienta tiene un valor incalculable para los profesionales de TI, los proveedores de servicios gestionados (MSP) y los equipos de seguridad que deseen reforzar sus capacidades de supervisión de la red. El siguiente contenido explica cómo supervisar conexiones TCP y UDP en Windows usando PowerShell.

Comprender el script PowerShell

El script proporcionado sirve como mecanismo de alerta para conexiones de red, identificando direcciones IP especificadas en estado ‘Escuchando’ o ‘Establecido’. Va más allá de las comprobaciones básicas del firewall y ofrece información sobre las conexiones activas que pueden eludir los filtros de seguridad tradicionales. El script muestra detalles esenciales como la dirección, el ID del proceso, el estado, el protocolo, la dirección local y el nombre del proceso. Para quienes utilicen NinjaOne, los resultados pueden guardarse automáticamente en un campo personalizado para su posterior análisis.

Importancia para los profesionales de TI y los MSP

En el entorno digital actual, en el que las ciberamenazas están siempre presentes, es esencial tener un control granular de las conexiones de red. Este script responde a la necesidad de supervisar en tiempo real las actividades de la red, lo que permite a los profesionales de TI responder rápidamente a posibles amenazas para la seguridad. Para los MSP que gestionan redes de varios clientes, este script ofrece un método estandarizado para supervisar e informar sobre la actividad de la red, lo que garantiza una supervisión coherente y exhaustiva.

El script para supervisar conexiones TCP y UDP

#Requires -Version 5.1

<#
.SYNOPSIS
    Alert on specified addresses that are Listening or Established and optionally save the results to a custom field.
.DESCRIPTION
    Will alert on addresses, regardless if a firewall is blocking them or not.
    Checks for addresses that are in a 'Listen' or 'Established' state.
    UDP is a stateless protocol and will not have a state.
    Outputs the addresses, process ID, state, protocol, local address, and process name.
    When a Custom Field is provided this will save the results to that custom field.

PARAMETER: -IpAddress "192.168.11.1, 192.168.1.1/24"
    A comma separated list of IP Addresses to check. Can include IPv4 CIDR notation for ranges. IPv6 CIDR notation not supported. (e.g. 192.168.1.0/24, 10.0.10.12)
.EXAMPLE
    -IpAddress "192.168.1.0/24, 10.0.10.12"
    ## EXAMPLE OUTPUT WITH IpAddress ##
    [Info] Valid IP Address: 192.168.11.1
    [Info] Valid IP Network: 192.168.1.1/24
    [Alert] Found Local Address: 192.168.1.18, Local Port: 139, Remote Address: 0.0.0.0, Remote Port: None, PID: 4, Protocol: TCP, State: Listen, Process: System
    [Alert] Found Local Address: 192.168.1.18, Local Port: 138, Remote Address: None, Remote Port: None, PID: 4, Protocol: UDP, State: None, Process: System
    [Alert] Found Local Address: 192.168.1.18, Local Port: 137, Remote Address: None, Remote Port: None, PID: 4, Protocol: UDP, State: None, Process: System

PARAMETER: -CustomField "ReplaceMeWithAnyMultilineCustomField"
    Name of the custom field to save the results to.
.EXAMPLE
    -IpAddress "192.168.11.1, 192.168.1.1/24" -CustomField "ReplaceMeWithAnyMultilineCustomField"
    ## EXAMPLE OUTPUT WITH CustomField ##
    [Info] Valid IP Address: 192.168.11.1
    [Info] Valid IP Network: 192.168.1.1/24
    [Alert] Found Local Address: 192.168.1.18, Local Port: 139, Remote Address: 0.0.0.0, Remote Port: None, PID: 4, Protocol: TCP, State: Listen, Process: System
    [Alert] Found Local Address: 192.168.1.18, Local Port: 138, Remote Address: None, Remote Port: None, PID: 4, Protocol: UDP, State: None, Process: System
    [Alert] Found Local Address: 192.168.1.18, Local Port: 137, Remote Address: None, Remote Port: None, PID: 4, Protocol: UDP, State: None, Process: System
    
    [Info] Saving results to custom field: ReplaceMeWithAnyMultilineCustomField
    [Info] Results saved to custom field: ReplaceMeWithAnyMultilineCustomField
.OUTPUTS
    None
.NOTES
    Supported Operating Systems: Windows 10/Windows Server 2016 or later with PowerShell 5.1
    Release Notes: Initial Release
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://ninjastage2.wpengine.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#>

[CmdletBinding()]
param (
    [Parameter()]
    [String]$IpAddress,
    [String]$CustomFieldName
)

begin {
    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }
    function Set-NinjaProperty {
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory = $True)]
            [String]$Name,
            [Parameter()]
            [String]$Type,
            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
            $Value,
            [Parameter()]
            [String]$DocumentName
        )
    
        $Characters = $Value | Measure-Object -Character | Select-Object -ExpandProperty Characters
        if ($Characters -ge 10000) {
            throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded, value is greater than 10,000 characters.")
        }
        
        # If we're requested to set the field value for a Ninja document we'll specify it here.
        $DocumentationParams = @{}
        if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName }
        
        # This is a list of valid fields that can be set. If no type is given, it will be assumed that the input doesn't need to be changed.
        $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG"
        if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type! Please check here for valid types. https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" }
        
        # The field below requires additional information to be set
        $NeedsOptions = "Dropdown"
        if ($DocumentName) {
            if ($NeedsOptions -contains $Type) {
                # We'll redirect the error output to the success stream to make it easier to error out if nothing was found or something else went wrong.
                $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1
            }
        }
        else {
            if ($NeedsOptions -contains $Type) {
                $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1
            }
        }
        
        # If an error is received it will have an exception property, the function will exit with that error information.
        if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }
        
        # The below type's require values not typically given in order to be set. The below code will convert whatever we're given into a format ninjarmm-cli supports.
        switch ($Type) {
            "Checkbox" {
                # While it's highly likely we were given a value like "True" or a boolean datatype it's better to be safe than sorry.
                $NinjaValue = [System.Convert]::ToBoolean($Value)
            }
            "Date or Date Time" {
                # Ninjarmm-cli expects the  Date-Time to be in Unix Epoch time so we'll convert it here.
                $Date = (Get-Date $Value).ToUniversalTime()
                $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date
                $NinjaValue = $TimeSpan.TotalSeconds
            }
            "Dropdown" {
                # Ninjarmm-cli is expecting the guid of the option we're trying to select. So we'll match up the value we were given with a guid.
                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name"
                $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID
        
                if (-not $Selection) {
                    throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown")
                }
        
                $NinjaValue = $Selection
            }
            default {
                # All the other types shouldn't require additional work on the input.
                $NinjaValue = $Value
            }
        }
        
        # We'll need to set the field differently depending on if its a field in a Ninja Document or not.
        if ($DocumentName) {
            $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1
        }
        else {
            $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2>&1
        }
        
        if ($CustomField.Exception) {
            throw $CustomField
        }
    }
    function Test-IPNetwork {
        param([string]$Text)
        $Ip, $Prefix = $Text -split '/'
        $Ip -as [System.Net.IPAddress] -and
        $Prefix -as [int] -and $Prefix -ge 0 -and $Prefix -le 32
    }
    function Get-IPNetwork {
        [CmdletBinding()]
    
        Param(
            [Parameter(Mandatory, Position = 0)]
            [ValidateScript({ $_ -eq ([IPAddress]$_).IPAddressToString })]
            [string]$IPAddress,
    
            [Parameter(Mandatory, Position = 1, ParameterSetName = "SubnetMask")]
            [ValidateScript({ $_ -eq ([IPAddress]$_).IPAddressToString })]
            [ValidateScript({
                    $SMReversed = [IPAddress]$_
                    $SMReversed = $SMReversed.GetAddressBytes()
                    [array]::Reverse($SMReversed)
                    [IPAddress]$SMReversed = $SMReversed
                    [convert]::ToString($SMReversed.Address, 2) -match "^[1]*0{0,}$"
                })]
            [string]$SubnetMask,
    
            [Parameter(Mandatory, Position = 1, ParameterSetName = "CIDRNotation")]
            [ValidateRange(0, 32)]
            [int]$PrefixLength,
    
            [switch]$ReturnAllIPs
        )
    
        [IPAddress]$IPAddress = $IPAddress
    
        if ($SubnetMask) {
            [IPAddress]$SubnetMask = $SubnetMask
            $SMReversed = $SubnetMask.GetAddressBytes()
            [array]::Reverse($SMReversed)
            [IPAddress]$SMReversed = $SMReversed
    
            [int]$PrefixLength = [convert]::ToString($SMReversed.Address, 2).replace(0, '').length
        } 
        else {
            [IPAddress]$SubnetMask = ([Math]::Pow(2, $PrefixLength) - 1) * [Math]::Pow(2, (32 - $PrefixLength))
        }
    
        
        $FullMask = [UInt32]'0xffffffff'
        $WildcardMask = [IPAddress]($SubnetMask.Address -bxor $FullMask)
        $NetworkId = [IPAddress]($IPAddress.Address -band $SubnetMask.Address)
        $Broadcast = [IPAddress](($FullMask - $NetworkId.Address) -bxor $SubnetMask.Address)
    
        # Used for determining first usable IP Address
        $FirstIPByteArray = $NetworkId.GetAddressBytes()
        [Array]::Reverse($FirstIPByteArray)
    
        # Used for determining last usable IP Address
        $LastIPByteArray = $Broadcast.GetAddressBytes()
        [Array]::Reverse($LastIPByteArray)
    
        # Handler for /31, /30 CIDR prefix values, and default for all others.
        switch ($PrefixLength) {
            31 {
                $TotalIPs = 2
                $UsableIPs = 2
                $FirstIP = $NetworkId
                $LastIP = $Broadcast
                $FirstIPInt = ([IPAddress]$FirstIPByteArray).Address
                $LastIPInt = ([IPAddress]$LastIPByteArray).Address
                break
            }
    
            32 {
                $TotalIPs = 1
                $UsableIPs = 1
                $FirstIP = $IPAddress
                $LastIP = $IPAddress
                $FirstIPInt = ([IPAddress]$FirstIPByteArray).Address
                $LastIPInt = ([IPAddress]$LastIPByteArray).Address
                break
            }
    
            default {
    
                # Usable Address Space
                $TotalIPs = [Math]::pow(2, (32 - $PrefixLength))
                $UsableIPs = $TotalIPs - 2
    
                # First usable IP
                $FirstIPInt = ([IPAddress]$FirstIPByteArray).Address + 1
                $FirstIP = [IPAddress]$FirstIPInt
                $FirstIP = ($FirstIP).GetAddressBytes()
                [Array]::Reverse($FirstIP)
                $FirstIP = [IPAddress]$FirstIP
    
                # Last usable IP
                $LastIPInt = ([IPAddress]$LastIPByteArray).Address - 1
                $LastIP = [IPAddress]$LastIPInt
                $LastIP = ($LastIP).GetAddressBytes()
                [Array]::Reverse($LastIP)
                $LastIP = [IPAddress]$LastIP
            }
        }
    
        $AllIPs = if ($ReturnAllIPs) {
    
            if ($UsableIPs -ge 500000) {
                Write-Host ('[Warn] Generating an array containing {0:N0} IPs, this may take a little while' -f $UsableIPs)
            }
    
            $CurrentIPInt = $FirstIPInt
    
            Do {
                $IP = [IPAddress]$CurrentIPInt
                $IP = ($IP).GetAddressBytes()
                [Array]::Reverse($IP) | Out-Null
                $IP = ([IPAddress]$IP).IPAddressToString
                $IP
    
                $CurrentIPInt++
    
            } While ($CurrentIPInt -le $LastIPInt)
        }
    
    
        $obj = [PSCustomObject]@{
            NetworkId    = ($NetworkId).IPAddressToString
            Broadcast    = ($Broadcast).IPAddressToString
            SubnetMask   = ($SubnetMask).IPAddressToString
            PrefixLength = $PrefixLength
            WildcardMask = ($WildcardMask).IPAddressToString
            FirstIP      = ($FirstIP).IPAddressToString
            LastIP       = ($LastIP).IPAddressToString
            TotalIPs     = $TotalIPs
            UsableIPs    = $UsableIPs
            AllIPs       = $AllIPs
        }
    
        Write-Output $obj
    }
}
process {
    if (-not (Test-IsElevated)) {
        Write-Error -Message "Access Denied. Please run with Administrator privileges."
        exit 1
    }
    if ($env:ipAddress -and $env:ipAddress -ne 'null') {
        $IpAddress = $env:ipAddress
    }
    if ($env:customFieldName -and $env:customFieldName -ne 'null') {
        $CustomFieldName = $env:customFieldName
    }

    # Parse the Addresses to check
    $Addresses = if ($IpAddress) {
        # Validate the IP Address
        $IpAddress -split ',' | ForEach-Object {
            "$_".Trim()
        } | ForEach-Object {
            if (($_ -as [System.Net.IPAddress])) {
                Write-Host "[Info] Valid IP Address: $_"
                [System.Net.IPAddress]::Parse($_)
            }
            elseif ($(Test-IPNetwork $_)) {
                Write-Host "[Info] Valid IP Network: $_"
                $Address, $PrefixLength = $_ -split '/'
                try {
                    Get-IPNetwork -IPAddress $Address -PrefixLength $PrefixLength -ReturnAllIPs | Select-Object -ExpandProperty AllIPs
                }
                catch {
                    Write-Host "[Error] Invalid IP CIDR: $_"
                    exit 1
                }
            }
            else {
                Write-Host "[Error] Invalid IP Address: $_"
                exit 1
            }
        }
    }
    else { $null }

    # Get the open ports
    $FoundAddresses = $(
        Get-NetTCPConnection | Select-Object @(
            'LocalAddress'
            'LocalPort'
            @{Name = "RemoteAddress"; Expression = { if ($_.RemoteAddress) { $_.RemoteAddress }else { "None" } } }
            @{Name = "RemotePort"; Expression = { if ($_.RemotePort) { $_.RemotePort }else { "None" } } }
            'State'
            @{Name = "Protocol"; Expression = { "TCP" } }
            'OwningProcess'
            @{Name = "Process"; Expression = { (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName } }
        )
        Get-NetUDPEndpoint | Select-Object @(
            'LocalAddress'
            'LocalPort'
            @{Name = "RemoteAddress"; Expression = { "None" } }
            @{Name = "RemotePort"; Expression = { "None" } }
            @{Name = "State"; Expression = { "None" } }
            @{Name = "Protocol"; Expression = { "UDP" } }
            'OwningProcess'
            @{Name = "Process"; Expression = { (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName } }
        )
    ) | Where-Object {
        $(
            <# When Addresses are specified select just those addresses. #>
            if ($Addresses) {
                $_.LocalAddress -in $Addresses -or
                $_.RemoteAddress -in $Addresses
            }
            else { $true }
        ) -and
        (
            <# Filter out anything that isn't listening or established. #>
            $(
                $_.Protocol -eq "TCP" -and
                $(
                    $_.State -eq "Listen" -or
                    $_.State -eq "Established"
                )
            ) -or
            <# UDP is stateless, return all UDP connections. #>
            $_.Protocol -eq "UDP"
        )
    } | Sort-Object LocalAddress, RemoteAddress | Select-Object * -Unique

    if (-not $FoundAddresses -or $FoundAddresses.Count -eq 0) {
        Write-Host "[Info] No Addresses were found listening or established with the specified network or address"
    }

    # Output the found Addresses
    $FoundAddresses | ForEach-Object {
        Write-Host "[Alert] Found Local Address: $($_.LocalAddress), Local Port: $($_.LocalPort), Remote Address: $($_.RemoteAddress), Remote Port: $($_.RemotePort), PID: $($_.OwningProcess), Protocol: $($_.Protocol), State: $($_.State), Process: $($_.Process)"
    }
    # Save the results to a custom field if one was provided
    if ($CustomFieldName -and $CustomFieldName -ne 'null') {
        try {
            Write-Host "[Info] Saving results to custom field: $CustomFieldName"
            Set-NinjaProperty -Name $CustomFieldName -Value $(
                $FoundAddresses | ForEach-Object {
                    "Local Address: $($_.LocalAddress), Local Port: $($_.LocalPort), Remote Address: $($_.RemoteAddress), Remote Port: $($_.RemotePort), PID: $($_.OwningProcess), Protocol: $($_.Protocol), State: $($_.State), Process: $($_.Process)"
                } | Out-String
            )
            Write-Host "[Info] Results saved to custom field: $CustomFieldName"
        }
        catch {
            Write-Host $_.Exception.Message
            Write-Host "[Warn] Failed to save results to custom field: $CustomFieldName"
            exit 1
        }
    }
}
end {
    
    
    
}

 

Cómo funciona el script

1. Configuración inicial

El script requiere la versión 5.1 o posterior de PowerShell y está pensado para su uso en Windows 10 o Windows Server 2016 y versiones posteriores. Comienza comprobando si el script se está ejecutando con privilegios de administrador, necesarios para acceder a la información de la conexión de red.

2. Análisis sintáctico de parámetros

El script acepta dos parámetros principales: -IpAddress y -CustomField. El parámetro -IpAddress permite especificar una lista de direcciones IP o rangos de IP anotados con CIDR para monitorizar. El parámetro -CustomField es opcional y se utiliza para guardar los resultados en un campo personalizado de NinjaOne.

3. Validación de direcciones

Una vez proporcionados los parámetros, el script valida cada dirección IP o rango de red. Si detecta una dirección o red válida, procede a recuperar todas las IP correspondientes dentro de ese rango.

4. Supervisión de las conexiones de red

El script utiliza los cmdlets Get-NetTCPConnection y Get-NetUDPEndpoint para capturar todas las conexiones TCP y UDP actuales del sistema. Filtra estas conexiones para identificar aquellas en estado «Escuchando» o «Establecido» para TCP, y captura todas las conexiones UDP debido a su naturaleza sin estado.

5. Salida y ahorro opcional

Las conexiones identificadas se muestran entonces en un formato estructurado, mostrando detalles como direcciones locales y remotas, puertos, ID de procesos, protocolos y estados. Si se especifica un campo personalizado, el script guarda estos resultados para su posterior análisis dentro de NinjaOne.

Caso práctico: aplicaciones reales

Consideremos un escenario en el que un MSP gestiona la infraestructura de TI de una empresa de servicios financieros. La empresa tiene estrictos requisitos de seguridad y necesita supervisar todas las conexiones entrantes y salientes para evitar accesos no autorizados. El MSP despliega este script en todos los servidores y endpoints, especificando los rangos de IP internos de la empresa para garantizar que sólo los dispositivos autorizados se comunican con la red. Si el script detecta una conexión desde una IP no autorizada, alerta al administrador, que puede tomar medidas inmediatas para investigar y mitigar el riesgo.

Comparación con otros métodos

Mientras que otras herramientas como Wireshark o Netstat pueden proporcionar información detallada sobre el tráfico de red, este script de PowerShell ofrece un enfoque simplificado y automatizado. A diferencia de Wireshark, que requiere un análisis manual de los paquetes, este script alerta automáticamente sobre conexiones específicas, lo que lo hace más accesible para una supervisión continua. Comparado con Netstat, el script añade valor filtrando e informando de las conexiones en un formato más estructurado y procesable.

Preguntas frecuentes

P: ¿Este script monitorizar direcciones IPv6?

R: Actualmente, el script sólo admite direcciones y redes IPv4. La compatibilidad con IPv6 requeriría modificaciones en el script para gestionar el diferente formato de las direcciones.

P: ¿Qué ocurre si se introduce una dirección IP inválida?

R: El script incluye comprobaciones de validación y detendrá la ejecución si se detecta una dirección IP o una red no válidas, proporcionando un mensaje de error al usuario.

P: ¿Cómo se guardan los resultados en NinjaOne?

R: Si se proporciona el parámetro -CustomField, el script utiliza la CLI de NinjaOne para guardar los resultados en el campo personalizado especificado, asegurando que los datos sean accesibles para futuras referencias o informes.

Implicaciones de los resultados

El resultado de este script puede tener implicaciones significativas para la seguridad informática. Al identificar y alertar sobre las conexiones activas, los profesionales de TI pueden detectar rápidamente accesos no autorizados, posibles comunicaciones de malware o servicios mal configurados. El uso regular de este script mejora la visibilidad de las actividades de la red, contribuyendo a un entorno informático más seguro y resistente.

Buenas prácticas para utilizar el script

  1. Ejecuta el script con regularidad: programa el script para que se ejecute a intervalos regulares, garantizando así la supervisión continua de las conexiones de red.
  2. Intégralo con NinjaOne: aprovecha las capacidades de NinjaOne para almacenar y analizar los resultados, lo que permite la supervisión a largo plazo y el análisis de tendencias.
  3. Adáptalo a tu entorno: modifica el script según sea necesario para adaptarlo a rangos de IP específicos o a requisitos de registro adicionales.
  4. Prueba en un entorno seguro: antes de desplegarlo en producción, prueba el script en un entorno controlado para asegurarte de que funciona como debería.

Reflexiones finales

Este script PowerShell ofrece una solución robusta para supervisar conexiones TCP y UDP en sistemas Windows. Al integrarlo en tu conjunto de herramientas de monitorización de red, en particular dentro de NinjaOne, puedes lograr una mayor visibilidad y control sobre tu entorno de TI. Para los profesionales de TI y MSP, este script proporciona un enfoque práctico para salvaguardar la integridad de la red, permitiendo respuestas proactivas a las amenazas potenciales.

 

Próximos pasos

La creación de un equipo de TI próspero y eficaz requiere contar con una solución centralizada que se convierta en tu principal herramienta de prestación de servicios. NinjaOne permite a los equipos de TI supervisar, gestionar, proteger y dar soporte a todos sus dispositivos, estén donde estén, sin necesidad de complejas infraestructuras locales.

Obtén más información sobre NinjaOne Endpoint Management, echa un vistazo a un tour en vivo o comienza tu prueba gratuita de la plataforma NinjaOne.

Categorías:

Quizá también te interese…

Ver demo×
×

¡Vean a NinjaOne en acción!

Al enviar este formulario, acepto la política de privacidad de NinjaOne.

Términos y condiciones de NinjaOne

Al hacer clic en el botón «Acepto» que aparece a continuación, estás aceptando los siguientes términos legales, así como nuestras Condiciones de uso:

  • Derechos de propiedad: NinjaOne posee y seguirá poseyendo todos los derechos, títulos e intereses sobre el script (incluidos los derechos de autor). NinjaOne concede al usuario una licencia limitada para utilizar el script de acuerdo con estos términos legales.
  • Limitación de uso: solo podrás utilizar el script para tus legítimos fines personales o comerciales internos, y no podrás compartirlo con terceros.
  • Prohibición de republicación: bajo ninguna circunstancia está permitido volver a publicar el script en ninguna biblioteca de scripts que pertenezca o esté bajo el control de cualquier otro proveedor de software.
  • Exclusión de garantía: el script se proporciona «tal cual» y «según disponibilidad», sin garantía de ningún tipo. NinjaOne no promete ni garantiza que el script esté libre de defectos o que satisfaga las necesidades o expectativas específicas del usuario.
  • Asunción de riesgos: el uso que el usuario haga del script corre por su cuenta y riesgo. El usuario reconoce que existen ciertos riesgos inherentes al uso del script, y entiende y asume cada uno de esos riesgos.
  • Renuncia y exención: el usuario no hará responsable a NinjaOne de cualquier consecuencia adversa o no deseada que resulte del uso del script y renuncia a cualquier derecho o recurso legal o equitativo que pueda tener contra NinjaOne en relación con su uso del script.
  • CLUF: si el usuario es cliente de NinjaOne, su uso del script está sujeto al Contrato de Licencia para el Usuario Final (CLUF).