{"id":353795,"date":"2024-09-24T07:20:45","date_gmt":"2024-09-24T07:20:45","guid":{"rendered":"https:\/\/www.ninjaone.com\/script-hub\/desinstalacion-de-aplicaciones\/"},"modified":"2024-10-13T19:03:21","modified_gmt":"2024-10-13T19:03:21","slug":"desinstalacion-de-aplicaciones","status":"publish","type":"script_hub","link":"https:\/\/www.ninjaone.com\/es\/script-hub\/desinstalacion-de-aplicaciones\/","title":{"rendered":"C\u00f3mo automatizar la desinstalaci\u00f3n de aplicaciones en Windows mediante PowerShell"},"content":{"rendered":"<p>En el mundo de la inform\u00e1tica, gestionar software en varios equipos es una tarea compleja y cr\u00edtica. Uno de los retos habituales a los que se enfrentan los administradores de TI es desinstalar aplicaciones de forma <a href=\"https:\/\/www.ninjaone.com\/es\/eficiencia\/\" target=\"_blank\" rel=\"noopener\">eficaz<\/a> y estandarizada.<\/p>\n<p>La desinstalaci\u00f3n de aplicaciones manual, especialmente en varios endpoints, no s\u00f3lo lleva mucho tiempo, sino que tambi\u00e9n es propensa a errores. Aqu\u00ed es donde entran en juego los scripts <a href=\"https:\/\/www.ninjaone.com\/it-hub\/endpoint-management\/what-is-powershell\/\" target=\"_blank\" rel=\"noopener\">PowerShell<\/a>, como el que vamos a comentar.<\/p>\n<p>Este script en particular sirve para automatizar <strong>la desinstalaci\u00f3n de aplicaciones<\/strong>\u00a0utilizando UninstallString y argumentos personalizados, proporcionando un enfoque racionalizado para gestionar las eliminaciones de software.<\/p>\n<h2>Necesidad de un script de desinstalaci\u00f3n automatizada<\/h2>\n<p>La desinstalaci\u00f3n de software, especialmente en grandes organizaciones, no es una tarea sencilla. Los profesionales de TI y los <a href=\"https:\/\/www.ninjaone.com\/es\/que-es-un-msp\/\" target=\"_blank\" rel=\"noopener\">proveedores de servicios gestionados (MSP)<\/a> tratan a menudo con un conjunto diverso de aplicaciones instaladas en varios equipos. Cada aplicaci\u00f3n puede requerir diferentes comandos de desinstalaci\u00f3n o seguir protocolos distintos.<\/p>\n<p>El script que estamos analizando est\u00e1 dise\u00f1ado para manejar estos matices, a\u00f1adiendo autom\u00e1ticamente los argumentos necesarios como \/qn \/norestart o \/S, que son esenciales para las desinstalaciones silenciosas y sin reinicios. Esta automatizaci\u00f3n es crucial para mantener la coherencia y la <a href=\"https:\/\/www.ninjaone.com\/es\/eficiencia\/\" target=\"_blank\" rel=\"noopener\">eficacia<\/a> en entornos de TI a gran escala.<\/p>\n<h2>El script para automatizar la desinstalaci\u00f3n de aplicaciones<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">#Requires -Version 5.1\r\n\r\n&lt;#\r\n.SYNOPSIS\r\n    Uninstall an application using the UninstallString and custom arguments. This script will auto-add \/qn \/norestart or \/S arguments.\r\n\r\n    This script will only uninstall apps that follow typical uninstall patterns such as msiexec \/X{GUID} \/qn \/norestart.\r\n.DESCRIPTION\r\n    Uninstall an application using the UninstallString and custom arguments. This script will auto-add \/qn \/norestart or \/S arguments.\r\n\r\n    This script will only uninstall apps that follow typical uninstall patterns such as msiexec \/X{GUID} \/qn \/norestart.\r\n.EXAMPLE\r\n    -Name \"VLC media Player\"\r\n    \r\n    Beginning uninstall of VLC media player using MsiExec.exe \/X{9675011C-2395-4AD7-B1CC-92910F991F58} \/qn \/norestart...\r\n    Exit Code for VLC media player: 0\r\n    Successfully uninstalled your requested apps!\r\n\r\nPARAMETER: -Name \"ReplaceMeWithNameOfApp\"\r\n    Exact name of the application to uninstall, separated by commas. E.g., 'VLC media player, Everything 1.4.1.1024 (x64)'.\r\n\r\nPARAMETER: -Arguments \"\/SILENT, \/NOREBOOT\"\r\n    Additional arguments to use when uninstalling the app, separated by commas. E.g., '\/SILENT, \/NOREBOOT'.\r\n\r\nPARAMETER: -Reboot\r\n    Schedules a reboot for 1 minute after the uninstall process succeeds.\r\n\r\nPARAMETER: -Timeout \"ReplaceMeWithTheNumberOfMinutesToWait\"\r\n    Specify the amount of time in minutes to wait for the uninstall process to complete. \r\n    If the process exceeds this time, the script and uninstall process will be terminated.\r\n\r\n.NOTES\r\n    Minimum OS Architecture Supported: Windows 10, Windows Server 2016\r\n    Release Notes: Initial Release\r\n.COMPONENT\r\n    Misc\r\n#&gt;\r\n\r\n[CmdletBinding()]\r\nparam (\r\n    [Parameter()]\r\n    [String]$Name,\r\n    [Parameter()]\r\n    [String]$Arguments,\r\n    [Parameter()]\r\n    [switch]$Reboot = [System.Convert]::ToBoolean($env:reboot),\r\n    [Parameter()]\r\n    [int]$Timeout = 10\r\n)\r\n\r\nbegin {\r\n    # Replace parameters with dynamic script variables\r\n    if ($env:nameOfAppToUninstall -and $env:nameOfAppToUninstall -notlike \"null\") { $Name = $env:nameOfAppToUninstall }\r\n    if ($env:arguments -and $env:arguments -notlike \"null\") { $Arguments = $env:arguments }\r\n    if ($env:timeoutInMinutes -and $env:timeoutInMinutes -notlike \"null\") { $Timeout = $env:timeoutInMinutes }\r\n\r\n    # Check if application name is provided\r\n    if (-not $Name) {\r\n        Write-Host -Object \"[Error] No name given, please enter in the name of an app to uninstall!\"\r\n        exit 1\r\n    }\r\n\r\n    # Check if timeout is provided\r\n    if (-not $Timeout) {\r\n        Write-Host -Object \"[Error] No timeout given!\"\r\n        Write-Host -Object \"[Error] Please enter in a timeout that's greater than or equal to 1 minute or less than or equal to 60 minutes.\"\r\n        exit 1\r\n    }\r\n\r\n    # Validate the timeout is within the acceptable range\r\n    if ($Timeout -lt 1 -or $Timeout -gt 60) {\r\n        Write-Host -Object \"[Error] An invalid timeout was given of $Timeout minutes.\"\r\n        Write-Host -Object \"[Error] Please enter in a timeout that's greater than or equal to 1 minute or less than or equal to 60 minutes.\"\r\n        exit 1\r\n    }\r\n\r\n    # Create a list to hold application names after splitting\r\n    $AppNames = New-Object System.Collections.Generic.List[String]\r\n    $Name -split ',' | ForEach-Object {\r\n        $AppNames.Add($_.Trim())\r\n    }\r\n\r\n    # Function to check if the script is run with elevated permissions\r\n    function Test-IsElevated {\r\n        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()\r\n        $p = New-Object System.Security.Principal.WindowsPrincipal($id)\r\n        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)\r\n    }\r\n\r\n    # Get all users registry hive locations\r\n    function Get-UserHives {\r\n        param (\r\n            [Parameter()]\r\n            [ValidateSet('AzureAD', 'DomainAndLocal', 'All')]\r\n            [String]$Type = \"All\",\r\n            [Parameter()]\r\n            [String[]]$ExcludedUsers,\r\n            [Parameter()]\r\n            [switch]$IncludeDefault\r\n        )\r\n    \r\n        # User account SID's follow a particular pattern depending on if they're Azure AD, a Domain account, or a local \"workgroup\" account.\r\n        $Patterns = switch ($Type) {\r\n            \"AzureAD\" { \"S-1-12-1-(\\d+-?){4}$\" }\r\n            \"DomainAndLocal\" { \"S-1-5-21-(\\d+-?){4}$\" }\r\n            \"All\" { \"S-1-12-1-(\\d+-?){4}$\" ; \"S-1-5-21-(\\d+-?){4}$\" } \r\n        }\r\n    \r\n        # We'll need the NTUSER.DAT file to load each user's registry hive. So we grab it if their account SID matches the above pattern. \r\n        $UserProfiles = Foreach ($Pattern in $Patterns) { \r\n            Get-ItemProperty \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\*\" |\r\n                Where-Object { $_.PSChildName -match $Pattern } | \r\n                Select-Object @{Name = \"SID\"; Expression = { $_.PSChildName } },\r\n                @{Name = \"UserName\"; Expression = { \"$($_.ProfileImagePath | Split-Path -Leaf)\" } }, \r\n                @{Name = \"UserHive\"; Expression = { \"$($_.ProfileImagePath)\\NTuser.dat\" } }, \r\n                @{Name = \"Path\"; Expression = { $_.ProfileImagePath } }\r\n        }\r\n    \r\n        # There are some situations where grabbing the .Default user's info is needed.\r\n        switch ($IncludeDefault) {\r\n            $True {\r\n                $DefaultProfile = \"\" | Select-Object UserName, SID, UserHive, Path\r\n                $DefaultProfile.UserName = \"Default\"\r\n                $DefaultProfile.SID = \"DefaultProfile\"\r\n                $DefaultProfile.Userhive = \"$env:SystemDrive\\Users\\Default\\NTUSER.DAT\"\r\n                $DefaultProfile.Path = \"C:\\Users\\Default\"\r\n    \r\n                $DefaultProfile | Where-Object { $ExcludedUsers -notcontains $_.UserName }\r\n            }\r\n        }\r\n    \r\n        $UserProfiles | Where-Object { $ExcludedUsers -notcontains $_.UserName }\r\n    }\r\n\r\n    # Function to find the uninstallation key of an application\r\n    function Find-UninstallKey {\r\n        [CmdletBinding()]\r\n        param (\r\n            [Parameter(ValueFromPipeline = $True)]\r\n            [String]$DisplayName,\r\n            [Parameter()]\r\n            [Switch]$UninstallString\r\n        )\r\n        process {\r\n            $UninstallList = New-Object System.Collections.Generic.List[Object]\r\n\r\n            # Search for uninstall key in 32-bit registry location\r\n            $Result = Get-ChildItem \"Registry::HKEY_USERS\\*\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\" | Get-ItemProperty | Where-Object { $_.DisplayName -match \"$([regex]::Escape($DisplayName))\" }\r\n            if ($Result) { $UninstallList.Add($Result) }\r\n\r\n            # Search for uninstall key in 32-bit user locations\r\n            $Result = Get-ChildItem HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* | Get-ItemProperty | Where-Object { $_.DisplayName -match \"$([regex]::Escape($DisplayName))\" }\r\n            if ($Result) { $UninstallList.Add($Result) }\r\n\r\n            # Search for uninstall key in 64-bit registry location\r\n            $Result = Get-ChildItem HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* | Get-ItemProperty | Where-Object { $_.DisplayName -match \"$([regex]::Escape($DisplayName))\" }\r\n            if ($Result) { $UninstallList.Add($Result) }\r\n\r\n            # Search for uninstall key in 64-bit user locations\r\n            $Result = Get-ChildItem \"Registry::HKEY_USERS\\*\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\" | Get-ItemProperty | Where-Object { $_.DisplayName -match \"$([regex]::Escape($DisplayName))\" }\r\n            if ($Result) { $UninstallList.Add($Result) }\r\n    \r\n            # Optionally return the DisplayName and UninstallString\r\n            if ($UninstallString) {\r\n                $UninstallList | ForEach-Object { $_ | Select-Object DisplayName, UninstallString -ErrorAction SilentlyContinue }\r\n            }\r\n            else {\r\n                $UninstallList\r\n            }\r\n        }\r\n    }\r\n\r\n    # Initialize the exit code variable\r\n    $ExitCode = 0\r\n}\r\nprocess {\r\n    # Check for administrative privileges\r\n    if (-not (Test-IsElevated)) {\r\n        Write-Host -Object \"[Error] Access Denied. Please run with Administrator privileges.\"\r\n        exit 1\r\n    }\r\n\r\n    # Load unloaded profiles\r\n    $UserProfiles = Get-UserHives -Type \"All\"\r\n    $ProfileWasLoaded = New-Object System.Collections.Generic.List[string]\r\n\r\n    # Loop through each profile on the machine.\r\n    Foreach ($UserProfile in $UserProfiles) {\r\n        # Load user's NTUSER.DAT if it's not already loaded.\r\n        If ((Test-Path Registry::HKEY_USERS\\$($UserProfile.SID)) -eq $false) {\r\n            Start-Process -FilePath \"cmd.exe\" -ArgumentList \"\/C reg.exe LOAD HKU\\$($UserProfile.SID) `\"$($UserProfile.UserHive)`\"\" -Wait -WindowStyle Hidden\r\n            $ProfileWasLoaded.Add(\"$($UserProfile.SID)\")\r\n        }\r\n    }\r\n\r\n    # Retrieve similar applications based on names provided\r\n    $SimilarAppsToName = $AppNames | ForEach-Object { Find-UninstallKey -DisplayName $_ -UninstallString }\r\n    if (-not $SimilarAppsToName) {\r\n        Write-Host \"[Error] The requested app(s) was not found and none were found that are similar!\"\r\n        exit 1\r\n    }\r\n\r\n    # Unload all hives that were loaded for this script.\r\n    ForEach ($UserHive in $ProfileWasLoaded) {\r\n        If ($ProfileWasLoaded -eq $false) {\r\n            [gc]::Collect()\r\n            Start-Sleep 1\r\n            Start-Process -FilePath \"cmd.exe\" -ArgumentList \"\/C reg.exe UNLOAD HKU\\$($UserHive)\" -Wait -WindowStyle Hidden | Out-Null\r\n        }\r\n    }\r\n\r\n    # Create a list to store apps that are confirmed for uninstallation\r\n    $AppsToUninstall = New-Object System.Collections.Generic.List[Object]\r\n    $SimilarAppsToName | ForEach-Object {\r\n        foreach ($AppName in $AppNames) {\r\n            if ($AppName -eq $_.DisplayName) {\r\n                # A matching app has been found\r\n                $ExactMatch = $True\r\n    \r\n                if ($_.UninstallString) {\r\n                    # Uninstall string is available\r\n                    $UninstallStringFound = $True\r\n                    # Add app to uninstall list\r\n                    $AppsToUninstall.Add($_)\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    # Check if any exact matches were found\r\n    if (-not $ExactMatch) {\r\n        Write-Host \"[Error] Your requested apps were not found. Please see the below list and try again.\"\r\n        $SimilarAppsToName | Format-Table DisplayName | Out-String | Write-Host\r\n        exit 1\r\n    }\r\n\r\n    # Check if uninstall strings were found for the apps\r\n    if (-not $UninstallStringFound) {\r\n        Write-Host \"[Error] No uninstall string found for any of your requested apps!\"\r\n        exit 1\r\n    }\r\n\r\n    # Check if there are apps without uninstall strings or not found at all\r\n    $AppNames | ForEach-Object {\r\n        if ($AppsToUninstall.DisplayName -notcontains $_) {\r\n            Write-Host \"[Error] Either the uninstall string was not present or the app itself was not found for one of your selected apps! See the below list of similar apps and try again.\"\r\n            $SimilarAppsToName | Format-Table DisplayName | Out-String | Write-Host\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    # Convert timeout from minutes to seconds\r\n    $TimeoutInSeconds = $Timeout * 60\r\n    $StartTime = Get-Date\r\n\r\n    # Process each app to uninstall\r\n    $AppsToUninstall | ForEach-Object {\r\n        $AdditionalArguments = New-Object System.Collections.Generic.List[String]\r\n\r\n        # If the uninstall string contains msiexec that's what our executable will be.\r\n        if($_.UninstallString -match \"msiexec\"){\r\n            $Executable = \"msiexec.exe\"\r\n        }\r\n\r\n        # If it contains a filepath we'll use that as our executable.\r\n        if($_.UninstallString -notmatch \"msiexec\" -and $_.UninstallString -match '[a-zA-Z]:\\\\(?:[^\\\\\\\/:*?\"&lt;&gt;|\\r\\n]+\\\\)*[^\\\\\\\/:*?\"&lt;&gt;|\\r\\n]*'){\r\n            $Executable = $Matches[0]\r\n        }\r\n\r\n        # Confirm we have an executable.\r\n        if(-not $Executable){\r\n            Write-Host -Object \"[Error] Unable to find uninstall executable!\"\r\n            exit 1\r\n        }\r\n\r\n        # Split uninstall string into executable and possible arguments\r\n        $PossibleArguments = $_.UninstallString -split ' ' | ForEach-Object { $_.Trim() } | Where-Object { $_ -match \"^\/\"}\r\n\r\n        # Decide executable and additional arguments based on uninstall string analysis\r\n        $i = 0\r\n        foreach ($PossibleArgument in $PossibleArguments) {\r\n            if (-not ($PossibleArgument -match \"^\/I{\") -and $PossibleArgument) {\r\n                $AdditionalArguments.Add($PossibleArgument)\r\n            }\r\n\r\n            if ($PossibleArgument -match \"^\/I{\") {\r\n                $AdditionalArguments.Add(\"$($PossibleArgument -replace '\/I', '\/X')\")\r\n            }\r\n\r\n            $i++\r\n        }\r\n\r\n        # Add custom arguments from the user\r\n        if ($Arguments) {\r\n            $Arguments.Split(',') | ForEach-Object {\r\n                $AdditionalArguments.Add($_.Trim())\r\n            }\r\n        }\r\n\r\n        # Add the usual silent uninstall arguments if not present\r\n        if($Executable -match \"Msiexec\"){\r\n            if($AdditionalArguments -notcontains \"\/qn\"){\r\n                $AdditionalArguments.Add(\"\/qn\")\r\n            }\r\n\r\n            if($AdditionalArguments -notcontains \"\/norestart\"){\r\n                $AdditionalArguments.Add(\"\/norestart\")\r\n            }\r\n        }elseif($Executable -match \"\\.exe\"){\r\n            if($AdditionalArguments -notcontains \"\/S\"){\r\n                $AdditionalArguments.Add(\"\/S\")\r\n            }\r\n\r\n            if($AdditionalArguments -notcontains \"\/norestart\"){\r\n                $AdditionalArguments.Add(\"\/norestart\")\r\n            }\r\n        }\r\n\r\n        # Verify that executable for uninstallation is found\r\n        if (-not $Executable) {\r\n            Write-Host \"[Error] Could not find the executable from the uninstall string!\"\r\n            exit 1\r\n        }\r\n\r\n        # Start the uninstallation process\r\n        Write-Host -Object \"Beginning uninstall of $($_.DisplayName) using $Executable $AdditionalArguments...\"\r\n        try{\r\n            if ($AdditionalArguments) {\r\n                $Uninstall = Start-Process $Executable -ArgumentList $AdditionalArguments -NoNewWindow -PassThru\r\n            }\r\n            else {\r\n                $Uninstall = Start-Process $Executable -NoNewWindow -PassThru\r\n            }\r\n        }catch{\r\n            Write-Host \"[Error] $($_.Exception.Message)\"\r\n            return\r\n        }\r\n\r\n        # Calculate the remaining time for the uninstall process and enforce timeout\r\n        $TimeElapsed = (Get-Date) - $StartTime\r\n        $RemainingTime = $TimeoutInSeconds - $TimeElapsed.TotalSeconds\r\n\r\n        # Wait for the uninstall process to complete within the remaining time\r\n        try {\r\n            $Uninstall | Wait-Process -Timeout $RemainingTime -ErrorAction Stop\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Alert] The uninstall process for $($_.DisplayName) has exceeded the specified timeout of $Timeout minutes.\"\r\n            Write-Host -Object \"[Alert] The script is now terminating.\"\r\n            $Uninstall | Stop-Process -Force\r\n            $ExitCode = 1\r\n        }\r\n\r\n        # Check and report the exit code of the uninstallation process\r\n        Write-Host -Object \"Exit code for $($_.DisplayName): $($Uninstall.ExitCode)\"\r\n        if ($Uninstall.ExitCode -ne 0) {\r\n            Write-Host -Object \"[Error] Exit code does not indicate success!\"\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    # Pause for 30 seconds before final checks\r\n    Start-Sleep -Seconds 30\r\n\r\n    $UserProfiles = Get-UserHives -Type \"All\"\r\n    $ProfileWasLoaded = New-Object System.Collections.Generic.List[string]\r\n\r\n    # Loop through each profile on the machine.\r\n    Foreach ($UserProfile in $UserProfiles) {\r\n        # Load user's NTUSER.DAT if it's not already loaded.\r\n        If ((Test-Path Registry::HKEY_USERS\\$($UserProfile.SID)) -eq $false) {\r\n            Start-Process -FilePath \"cmd.exe\" -ArgumentList \"\/C reg.exe LOAD HKU\\$($UserProfile.SID) `\"$($UserProfile.UserHive)`\"\" -Wait -WindowStyle Hidden\r\n            $ProfileWasLoaded.Add(\"$($UserProfile.SID)\")\r\n        }\r\n    }\r\n\r\n    # Re-check for any remaining apps to confirm they were uninstalled\r\n    $SimilarAppsToName = $AppNames | ForEach-Object { Find-UninstallKey -DisplayName $_ }\r\n    $SimilarAppsToName | ForEach-Object {\r\n        foreach ($AppName in $AppNames) {\r\n            if ($_.DisplayName -eq $AppName) {\r\n                Write-Host -Object \"[Error] Failed to uninstall $($_.DisplayName).\"\r\n                $UninstallFailure = $True\r\n                $ExitCode = 1\r\n            }\r\n        }\r\n    }\r\n\r\n    # Unload all hives that were loaded for this script.\r\n    ForEach ($UserHive in $ProfileWasLoaded) {\r\n        If ($ProfileWasLoaded -eq $false) {\r\n            [gc]::Collect()\r\n            Start-Sleep 1\r\n            Start-Process -FilePath \"cmd.exe\" -ArgumentList \"\/C reg.exe UNLOAD HKU\\$($UserHive)\" -Wait -WindowStyle Hidden | Out-Null\r\n        }\r\n    }\r\n\r\n    # Confirm successful uninstallation if no failures detected\r\n    if (-not $UninstallFailure) {\r\n        Write-Host \"Successfully uninstalled your requested apps!\"\r\n    }\r\n\r\n    # Handle reboot if requested and there were no uninstall failures\r\n    if ($Reboot -and -not $UninstallFailure) {\r\n        Write-Host -Object \"[Alert] a reboot was requested. Scheduling restart for 60 seconds from now...\"\r\n        Start-Process shutdown.exe -ArgumentList \"\/r \/t 60\" -Wait -NoNewWindow\r\n    }\r\n\r\n    # Exit script with the final exit code\r\n    exit $ExitCode\r\n}\r\nend {\r\n    \r\n    \r\n    \r\n}\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n\n<div class=\"in-context-cta\"><\/div>\n<h2>C\u00f3mo funciona el script<\/h2>\n<p>Este script est\u00e1 dise\u00f1ado para desinstalar aplicaciones bas\u00e1ndose en sus nombres y maneja una variedad de escenarios que pueden surgir durante el proceso de desinstalaci\u00f3n de aplicaciones. A continuaci\u00f3n se detalla paso a paso el funcionamiento del script:<\/p>\n<h3>1. Definici\u00f3n de par\u00e1metros y tratamiento de entradas<\/h3>\n<ul>\n<li>El script comienza definiendo varios par\u00e1metros, como -Name para el nombre de la aplicaci\u00f3n, -Arguments para argumentos adicionales de desinstalaci\u00f3n, -Reboot para programar un reinicio y -Timeout para especificar cu\u00e1nto tiempo debe esperar el script el proceso de desinstalaci\u00f3n.<\/li>\n<li>A continuaci\u00f3n, el script comprueba si se proporcionan estos par\u00e1metros y los valida, asegur\u00e1ndose de que cumplen criterios espec\u00edficos, como que el valor del tiempo de espera est\u00e9 entre 1 y 60 minutos.<\/li>\n<\/ul>\n<h3>2. Gesti\u00f3n de perfiles y colmenas de usuarios<\/h3>\n<ul>\n<li>Incluye una funci\u00f3n para cargar y manejar perfiles de usuario y archivos comprimidos del registro, necesarios para acceder a las cadenas de desinstalaci\u00f3n de cada aplicaci\u00f3n. Esto es crucial porque la desinstalaci\u00f3n de una aplicaci\u00f3n a menudo requiere acceder al registro para localizar la ruta de desinstalaci\u00f3n correcta.<\/li>\n<\/ul>\n<h3>3. Identificaci\u00f3n de aplicaciones y recuperaci\u00f3n de claves de desinstalaci\u00f3n<\/h3>\n<ul>\n<li>El script utiliza una funci\u00f3n llamada Find-UninstallKey para buscar en el registro y localizar la clave de desinstalaci\u00f3n asociada con el nombre de la aplicaci\u00f3n proporcionada. Esto se realiza en diferentes ubicaciones del registro, tanto de 32 como de 64 bits, para garantizar una cobertura completa.<\/li>\n<\/ul>\n<h3>4. Proceso de desinstalaci\u00f3n de aplicaciones<\/h3>\n<ul>\n<li>Una vez identificada la cadena de desinstalaci\u00f3n, el script la procesa para extraer la ruta del ejecutable y los argumentos necesarios para la desinstalaci\u00f3n de aplicaciones.<\/li>\n<li>Garantiza que se a\u00f1adan los argumentos de desinstalaci\u00f3n silenciosa necesarios, como \/qn, \/norestart o \/S, si no est\u00e1n ya presentes. Esto garantiza que la desinstalaci\u00f3n se realice sin interacci\u00f3n del usuario y sin forzar el reinicio del sistema, a menos que se especifique lo contrario.<\/li>\n<\/ul>\n<h3>5. Tratamiento de errores y comprobaciones finales<\/h3>\n<ul>\n<li>El script incluye una s\u00f3lida gesti\u00f3n de errores, lo que garantiza que si algo va mal durante la desinstalaci\u00f3n, se informe al administrador y el script salga con un c\u00f3digo de error apropiado.<\/li>\n<li>Tras la desinstalaci\u00f3n, el script vuelve a comprobar que la aplicaci\u00f3n se ha eliminado, asegur\u00e1ndose de que no quedan componentes residuales.<\/li>\n<\/ul>\n<h2>Aplicaci\u00f3n real: un estudio de caso<\/h2>\n<p>Consideremos un escenario en el que un administrador de TI necesita desinstalar una versi\u00f3n obsoleta de VLC Media Player de 200 ordenadores de una organizaci\u00f3n. Hacerlo manualmente llevar\u00eda much\u00edsimo tiempo. En su lugar, el administrador podr\u00eda desplegar este script PowerShell en todos los equipos, especificando \u00abVLC Media Player\u00bb como nombre de la aplicaci\u00f3n.<\/p>\n<p>El script encontrar\u00eda autom\u00e1ticamente la cadena de desinstalaci\u00f3n adecuada, ejecutar\u00eda la desinstalaci\u00f3n en silencio e incluso programar\u00eda un reinicio si fuera necesario. Todo el proceso, que podr\u00eda haber durado d\u00edas, se completa en cuesti\u00f3n de minutos.<\/p>\n<h2>Comparaci\u00f3n con otros m\u00e9todos<\/h2>\n<p>Tradicionalmente, los administradores pueden utilizar el Panel de control o una herramienta de desinstalaci\u00f3n de terceros para eliminar aplicaciones. Sin embargo, estos m\u00e9todos requieren intervenci\u00f3n manual, no son escalables y a menudo carecen de la flexibilidad necesaria para a\u00f1adir argumentos personalizados o gestionar eficazmente las desinstalaciones silenciosas.<\/p>\n<p>Este script, por otro lado, ofrece un enfoque altamente automatizado y personalizable, permitiendo a los profesionales de TI gestionar las desinstalaciones en m\u00faltiples m\u00e1quinas de manera eficiente y con una m\u00ednima intervenci\u00f3n manual.<\/p>\n<h2>Preguntas frecuentes<\/h2>\n<ol>\n<li><strong>\u00bfQu\u00e9 ocurre si no se encuentra el nombre de la aplicaci\u00f3n?<\/strong>\u00a0Si el script no puede encontrar una aplicaci\u00f3n con el nombre exacto proporcionado, buscar\u00e1 nombres similares y proporcionar\u00e1 una lista de posibles coincidencias. Esto permite al administrador ajustar la entrada y volver a intentarlo.<\/li>\n<li><strong>\u00bfPuede el script gestionar varias aplicaciones a la vez?<\/strong>\u00a0S\u00ed, el par\u00e1metro -Name puede aceptar una lista de aplicaciones separadas por comas, lo que permite al script desinstalar varias aplicaciones de una sola vez.<\/li>\n<li><strong>\u00bfQu\u00e9 ocurre si el proceso de desinstalaci\u00f3n supera el tiempo de espera?<\/strong>\u00a0El script terminar\u00e1 el proceso de desinstalaci\u00f3n y saldr\u00e1 con un c\u00f3digo de error, asegurando que ning\u00fan proceso se ejecute indefinidamente.<\/li>\n<\/ol>\n<h2>Implicaciones para la seguridad y la gesti\u00f3n de las TI<\/h2>\n<p>Automatizar el proceso de desinstalaci\u00f3n con scripts como \u00e9ste tiene importantes implicaciones para la <a href=\"https:\/\/www.ninjaone.com\/es\/blog\/lista-de-verificacion-de-seguridad-de-ti\/\" target=\"_blank\" rel=\"noopener\">seguridad y la gesti\u00f3n de TI<\/a>. Al asegurarse de que el software obsoleto o vulnerable se elimina de forma r\u00e1pida y coherente en todos los equipos, las organizaciones pueden reducir el riesgo de <a href=\"https:\/\/www.ninjaone.com\/es\/it-hub\/endpoint-security\/que-es-una-violacion-de-datos\/\" target=\"_blank\" rel=\"noopener\">brechas de seguridad<\/a>. Adem\u00e1s, la automatizaci\u00f3n <a href=\"https:\/\/www.ninjaone.com\/blog\/how-human-error-relates-to-cybersecurity-risks\/\" target=\"_blank\" rel=\"noopener\">reduce los errores humanos<\/a>, garantizando que ninguna aplicaci\u00f3n se quede sin instalar por descuido.<\/p>\n<h2>Pr\u00e1cticas recomendadas para utilizar este script<\/h2>\n<ul>\n<li><strong>Pru\u00e9balo siempre en un entorno controlado:<\/strong>\u00a0antes de desplegar el script en varios equipos, pru\u00e9balo en un entorno controlado para asegurarte de que funciona como se espera.<\/li>\n<li><strong>Realiza registros:<\/strong>\u00a0mant\u00e9n registros de los procesos de desinstalaci\u00f3n con fines de auditor\u00eda y soluci\u00f3n de problemas.<\/li>\n<li><strong>Haz una copia de seguridad antes de proceder a la desinstalaci\u00f3n de aplicaciones:<\/strong>\u00a0aseg\u00farate de hacer una copia de seguridad de los datos importantes antes de ejecutar los scripts de desinstalaci\u00f3n, sobre todo en el caso de las aplicaciones que puedan almacenar datos localmente.<\/li>\n<li><strong>Actualiza el script regularmente:<\/strong>\u00a0a medida que salgan nuevas aplicaciones y actualizaciones, actualiza el script para manejar nuevas cadenas y m\u00e9todos de desinstalaci\u00f3n.<\/li>\n<\/ul>\n<h2>Reflexiones finales<\/h2>\n<p>Los scripts de PowerShell como el comentado son herramientas indispensables para los profesionales de TI que gestionan grandes redes. Al automatizar la desinstalaci\u00f3n de aplicaciones, ahorran tiempo, reducen los errores y mejoran la seguridad. <a href=\"https:\/\/www.ninjaone.com\/es\/\" target=\"_blank\" rel=\"noopener\">NinjaOne<\/a> puede complementar a\u00fan m\u00e1s estos scripts con sus herramientas integrales de gesti\u00f3n de TI, proporcionando <a href=\"https:\/\/www.ninjaone.com\/es\/blog\/todo-sobre-automatizacion-de-ti\/\" target=\"_blank\" rel=\"noopener\">capas adicionales de automatizaci\u00f3n<\/a>, supervisi\u00f3n y control, asegurando que tu infraestructura de TI permanezca robusta, <a href=\"https:\/\/www.ninjaone.com\/es\/?page_id=310645\" target=\"_blank\" rel=\"noopener\">segura<\/a> y actualizada.<\/p>\n","protected":false},"author":35,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"_acf_changed":false,"_relevanssi_hide_post":"","_relevanssi_hide_content":"","_relevanssi_pin_for_all":"","_relevanssi_pin_keywords":"","_relevanssi_unpin_keywords":"","_relevanssi_related_keywords":"","_relevanssi_related_include_ids":"","_relevanssi_related_exclude_ids":"","_relevanssi_related_no_append":"","_relevanssi_related_not_related":"","_relevanssi_related_posts":"","_relevanssi_noindex_reason":"","_lmt_disableupdate":"","_lmt_disable":""},"operating_system":[4212],"use_cases":[4259],"class_list":["post-353795","script_hub","type-script_hub","status-publish","hentry","script_hub_category-windows","use_cases-configuracion-general"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/script_hub\/353795","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/script_hub"}],"about":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/types\/script_hub"}],"author":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/users\/35"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/comments?post=353795"}],"wp:attachment":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/media?parent=353795"}],"wp:term":[{"taxonomy":"script_hub_category","embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/operating_system?post=353795"},{"taxonomy":"use_cases","embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/use_cases?post=353795"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}