Come automatizzare la disinstallazione di applicazioni in Windows utilizzando PowerShell

Nel mondo IT, la gestione del software su più macchine è un compito complesso e critico. Una delle sfide più comuni che gli amministratori IT devono affrontare è la disinstallazione delle applicazioni in modo efficiente e standardizzato.

La disinstallazione manuale, soprattutto su più endpoint, non solo richiede molto tempo, ma è anche soggetta a errori. È qui che entrano in gioco gli script PowerShell, come quello di cui parleremo in questo articolo.

Questo particolare script è utile per automatizzare la disinstallazione di applicazioni, e lo fa utilizzando il cmdlet UninstallString e argomenti personalizzati, fornendo un approccio semplificato alla gestione della rimozione del software.

La necessità di uno script per automatizzare la disinstallazione di applicazioni

La disinstallazione del software, soprattutto nelle grandi organizzazioni, non è un compito semplice. I professionisti IT e i Managed Service Provider (MSP) hanno spesso a che fare con una serie di applicazioni diverse installate su vari computer. Ogni applicazione potrebbe richiedere comandi di disinstallazione diversi o seguire protocolli diversi.

Lo script che stiamo analizzando è progettato per gestire queste sfumature, aggiungendo automaticamente gli argomenti necessari come /qn /norestart o /S, che sono essenziali per le disinstallazioni silenziose e senza riavvio. Questa automazione è fondamentale per mantenere la coerenza e l’efficienza degli ambienti IT su larga scala.

Lo script per automatizzare la disinstallazione delle applicazioni:

#Requires -Version 5.1

<#
.SYNOPSIS
    Uninstall an application using the UninstallString and custom arguments. This script will auto-add /qn /norestart or /S arguments.

    This script will only uninstall apps that follow typical uninstall patterns such as msiexec /X{GUID} /qn /norestart.
.DESCRIPTION
    Uninstall an application using the UninstallString and custom arguments. This script will auto-add /qn /norestart or /S arguments.

    This script will only uninstall apps that follow typical uninstall patterns such as msiexec /X{GUID} /qn /norestart.
.EXAMPLE
    -Name "VLC media Player"
    
    Beginning uninstall of VLC media player using MsiExec.exe /X{9675011C-2395-4AD7-B1CC-92910F991F58} /qn /norestart...
    Exit Code for VLC media player: 0
    Successfully uninstalled your requested apps!

PARAMETER: -Name "ReplaceMeWithNameOfApp"
    Exact name of the application to uninstall, separated by commas. E.g., 'VLC media player, Everything 1.4.1.1024 (x64)'.

PARAMETER: -Arguments "/SILENT, /NOREBOOT"
    Additional arguments to use when uninstalling the app, separated by commas. E.g., '/SILENT, /NOREBOOT'.

PARAMETER: -Reboot
    Schedules a reboot for 1 minute after the uninstall process succeeds.

PARAMETER: -Timeout "ReplaceMeWithTheNumberOfMinutesToWait"
    Specify the amount of time in minutes to wait for the uninstall process to complete. 
    If the process exceeds this time, the script and uninstall process will be terminated.

.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows Server 2016
    Release Notes: Initial Release
.COMPONENT
    Misc
#>

[CmdletBinding()]
param (
    [Parameter()]
    [String]$Name,
    [Parameter()]
    [String]$Arguments,
    [Parameter()]
    [switch]$Reboot = [System.Convert]::ToBoolean($env:reboot),
    [Parameter()]
    [int]$Timeout = 10
)

begin {
    # Replace parameters with dynamic script variables
    if ($env:nameOfAppToUninstall -and $env:nameOfAppToUninstall -notlike "null") { $Name = $env:nameOfAppToUninstall }
    if ($env:arguments -and $env:arguments -notlike "null") { $Arguments = $env:arguments }
    if ($env:timeoutInMinutes -and $env:timeoutInMinutes -notlike "null") { $Timeout = $env:timeoutInMinutes }

    # Check if application name is provided
    if (-not $Name) {
        Write-Host -Object "[Error] No name given, please enter in the name of an app to uninstall!"
        exit 1
    }

    # Check if timeout is provided
    if (-not $Timeout) {
        Write-Host -Object "[Error] No timeout given!"
        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."
        exit 1
    }

    # Validate the timeout is within the acceptable range
    if ($Timeout -lt 1 -or $Timeout -gt 60) {
        Write-Host -Object "[Error] An invalid timeout was given of $Timeout minutes."
        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."
        exit 1
    }

    # Create a list to hold application names after splitting
    $AppNames = New-Object System.Collections.Generic.List[String]
    $Name -split ',' | ForEach-Object {
        $AppNames.Add($_.Trim())
    }

    # Function to check if the script is run with elevated permissions
    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    # Get all users registry hive locations
    function Get-UserHives {
        param (
            [Parameter()]
            [ValidateSet('AzureAD', 'DomainAndLocal', 'All')]
            [String]$Type = "All",
            [Parameter()]
            [String[]]$ExcludedUsers,
            [Parameter()]
            [switch]$IncludeDefault
        )
    
        # User account SID's follow a particular pattern depending on if they're Azure AD, a Domain account, or a local "workgroup" account.
        $Patterns = switch ($Type) {
            "AzureAD" { "S-1-12-1-(\d+-?){4}$" }
            "DomainAndLocal" { "S-1-5-21-(\d+-?){4}$" }
            "All" { "S-1-12-1-(\d+-?){4}$" ; "S-1-5-21-(\d+-?){4}$" } 
        }
    
        # 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. 
        $UserProfiles = Foreach ($Pattern in $Patterns) { 
            Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" |
                Where-Object { $_.PSChildName -match $Pattern } | 
                Select-Object @{Name = "SID"; Expression = { $_.PSChildName } },
                @{Name = "UserName"; Expression = { "$($_.ProfileImagePath | Split-Path -Leaf)" } }, 
                @{Name = "UserHive"; Expression = { "$($_.ProfileImagePath)\NTuser.dat" } }, 
                @{Name = "Path"; Expression = { $_.ProfileImagePath } }
        }
    
        # There are some situations where grabbing the .Default user's info is needed.
        switch ($IncludeDefault) {
            $True {
                $DefaultProfile = "" | Select-Object UserName, SID, UserHive, Path
                $DefaultProfile.UserName = "Default"
                $DefaultProfile.SID = "DefaultProfile"
                $DefaultProfile.Userhive = "$env:SystemDrive\Users\Default\NTUSER.DAT"
                $DefaultProfile.Path = "C:\Users\Default"
    
                $DefaultProfile | Where-Object { $ExcludedUsers -notcontains $_.UserName }
            }
        }
    
        $UserProfiles | Where-Object { $ExcludedUsers -notcontains $_.UserName }
    }

    # Function to find the uninstallation key of an application
    function Find-UninstallKey {
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline = $True)]
            [String]$DisplayName,
            [Parameter()]
            [Switch]$UninstallString
        )
        process {
            $UninstallList = New-Object System.Collections.Generic.List[Object]

            # Search for uninstall key in 32-bit registry location
            $Result = Get-ChildItem "Registry::HKEY_USERS\*\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -match "$([regex]::Escape($DisplayName))" }
            if ($Result) { $UninstallList.Add($Result) }

            # Search for uninstall key in 32-bit user locations
            $Result = Get-ChildItem HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Get-ItemProperty | Where-Object { $_.DisplayName -match "$([regex]::Escape($DisplayName))" }
            if ($Result) { $UninstallList.Add($Result) }

            # Search for uninstall key in 64-bit registry location
            $Result = Get-ChildItem HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Get-ItemProperty | Where-Object { $_.DisplayName -match "$([regex]::Escape($DisplayName))" }
            if ($Result) { $UninstallList.Add($Result) }

            # Search for uninstall key in 64-bit user locations
            $Result = Get-ChildItem "Registry::HKEY_USERS\*\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -match "$([regex]::Escape($DisplayName))" }
            if ($Result) { $UninstallList.Add($Result) }
    
            # Optionally return the DisplayName and UninstallString
            if ($UninstallString) {
                $UninstallList | ForEach-Object { $_ | Select-Object DisplayName, UninstallString -ErrorAction SilentlyContinue }
            }
            else {
                $UninstallList
            }
        }
    }

    # Initialize the exit code variable
    $ExitCode = 0
}
process {
    # Check for administrative privileges
    if (-not (Test-IsElevated)) {
        Write-Host -Object "[Error] Access Denied. Please run with Administrator privileges."
        exit 1
    }

    # Load unloaded profiles
    $UserProfiles = Get-UserHives -Type "All"
    $ProfileWasLoaded = New-Object System.Collections.Generic.List[string]

    # Loop through each profile on the machine.
    Foreach ($UserProfile in $UserProfiles) {
        # Load user's NTUSER.DAT if it's not already loaded.
        If ((Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe LOAD HKU\$($UserProfile.SID) `"$($UserProfile.UserHive)`"" -Wait -WindowStyle Hidden
            $ProfileWasLoaded.Add("$($UserProfile.SID)")
        }
    }

    # Retrieve similar applications based on names provided
    $SimilarAppsToName = $AppNames | ForEach-Object { Find-UninstallKey -DisplayName $_ -UninstallString }
    if (-not $SimilarAppsToName) {
        Write-Host "[Error] The requested app(s) was not found and none were found that are similar!"
        exit 1
    }

    # Unload all hives that were loaded for this script.
    ForEach ($UserHive in $ProfileWasLoaded) {
        If ($ProfileWasLoaded -eq $false) {
            [gc]::Collect()
            Start-Sleep 1
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe UNLOAD HKU\$($UserHive)" -Wait -WindowStyle Hidden | Out-Null
        }
    }

    # Create a list to store apps that are confirmed for uninstallation
    $AppsToUninstall = New-Object System.Collections.Generic.List[Object]
    $SimilarAppsToName | ForEach-Object {
        foreach ($AppName in $AppNames) {
            if ($AppName -eq $_.DisplayName) {
                # A matching app has been found
                $ExactMatch = $True
    
                if ($_.UninstallString) {
                    # Uninstall string is available
                    $UninstallStringFound = $True
                    # Add app to uninstall list
                    $AppsToUninstall.Add($_)
                }
            }
        }
    }

    # Check if any exact matches were found
    if (-not $ExactMatch) {
        Write-Host "[Error] Your requested apps were not found. Please see the below list and try again."
        $SimilarAppsToName | Format-Table DisplayName | Out-String | Write-Host
        exit 1
    }

    # Check if uninstall strings were found for the apps
    if (-not $UninstallStringFound) {
        Write-Host "[Error] No uninstall string found for any of your requested apps!"
        exit 1
    }

    # Check if there are apps without uninstall strings or not found at all
    $AppNames | ForEach-Object {
        if ($AppsToUninstall.DisplayName -notcontains $_) {
            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."
            $SimilarAppsToName | Format-Table DisplayName | Out-String | Write-Host
            $ExitCode = 1
        }
    }

    # Convert timeout from minutes to seconds
    $TimeoutInSeconds = $Timeout * 60
    $StartTime = Get-Date

    # Process each app to uninstall
    $AppsToUninstall | ForEach-Object {
        $AdditionalArguments = New-Object System.Collections.Generic.List[String]

        # If the uninstall string contains msiexec that's what our executable will be.
        if($_.UninstallString -match "msiexec"){
            $Executable = "msiexec.exe"
        }

        # If it contains a filepath we'll use that as our executable.
        if($_.UninstallString -notmatch "msiexec" -and $_.UninstallString -match '[a-zA-Z]:\\(?:[^\\\/:*?"<>|\r\n]+\\)*[^\\\/:*?"<>|\r\n]*'){
            $Executable = $Matches[0]
        }

        # Confirm we have an executable.
        if(-not $Executable){
            Write-Host -Object "[Error] Unable to find uninstall executable!"
            exit 1
        }

        # Split uninstall string into executable and possible arguments
        $PossibleArguments = $_.UninstallString -split ' ' | ForEach-Object { $_.Trim() } | Where-Object { $_ -match "^/"}

        # Decide executable and additional arguments based on uninstall string analysis
        $i = 0
        foreach ($PossibleArgument in $PossibleArguments) {
            if (-not ($PossibleArgument -match "^/I{") -and $PossibleArgument) {
                $AdditionalArguments.Add($PossibleArgument)
            }

            if ($PossibleArgument -match "^/I{") {
                $AdditionalArguments.Add("$($PossibleArgument -replace '/I', '/X')")
            }

            $i++
        }

        # Add custom arguments from the user
        if ($Arguments) {
            $Arguments.Split(',') | ForEach-Object {
                $AdditionalArguments.Add($_.Trim())
            }
        }

        # Add the usual silent uninstall arguments if not present
        if($Executable -match "Msiexec"){
            if($AdditionalArguments -notcontains "/qn"){
                $AdditionalArguments.Add("/qn")
            }

            if($AdditionalArguments -notcontains "/norestart"){
                $AdditionalArguments.Add("/norestart")
            }
        }elseif($Executable -match "\.exe"){
            if($AdditionalArguments -notcontains "/S"){
                $AdditionalArguments.Add("/S")
            }

            if($AdditionalArguments -notcontains "/norestart"){
                $AdditionalArguments.Add("/norestart")
            }
        }

        # Verify that executable for uninstallation is found
        if (-not $Executable) {
            Write-Host "[Error] Could not find the executable from the uninstall string!"
            exit 1
        }

        # Start the uninstallation process
        Write-Host -Object "Beginning uninstall of $($_.DisplayName) using $Executable $AdditionalArguments..."
        try{
            if ($AdditionalArguments) {
                $Uninstall = Start-Process $Executable -ArgumentList $AdditionalArguments -NoNewWindow -PassThru
            }
            else {
                $Uninstall = Start-Process $Executable -NoNewWindow -PassThru
            }
        }catch{
            Write-Host "[Error] $($_.Exception.Message)"
            return
        }

        # Calculate the remaining time for the uninstall process and enforce timeout
        $TimeElapsed = (Get-Date) - $StartTime
        $RemainingTime = $TimeoutInSeconds - $TimeElapsed.TotalSeconds

        # Wait for the uninstall process to complete within the remaining time
        try {
            $Uninstall | Wait-Process -Timeout $RemainingTime -ErrorAction Stop
        }
        catch {
            Write-Host -Object "[Alert] The uninstall process for $($_.DisplayName) has exceeded the specified timeout of $Timeout minutes."
            Write-Host -Object "[Alert] The script is now terminating."
            $Uninstall | Stop-Process -Force
            $ExitCode = 1
        }

        # Check and report the exit code of the uninstallation process
        Write-Host -Object "Exit code for $($_.DisplayName): $($Uninstall.ExitCode)"
        if ($Uninstall.ExitCode -ne 0) {
            Write-Host -Object "[Error] Exit code does not indicate success!"
            $ExitCode = 1
        }
    }

    # Pause for 30 seconds before final checks
    Start-Sleep -Seconds 30

    $UserProfiles = Get-UserHives -Type "All"
    $ProfileWasLoaded = New-Object System.Collections.Generic.List[string]

    # Loop through each profile on the machine.
    Foreach ($UserProfile in $UserProfiles) {
        # Load user's NTUSER.DAT if it's not already loaded.
        If ((Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe LOAD HKU\$($UserProfile.SID) `"$($UserProfile.UserHive)`"" -Wait -WindowStyle Hidden
            $ProfileWasLoaded.Add("$($UserProfile.SID)")
        }
    }

    # Re-check for any remaining apps to confirm they were uninstalled
    $SimilarAppsToName = $AppNames | ForEach-Object { Find-UninstallKey -DisplayName $_ }
    $SimilarAppsToName | ForEach-Object {
        foreach ($AppName in $AppNames) {
            if ($_.DisplayName -eq $AppName) {
                Write-Host -Object "[Error] Failed to uninstall $($_.DisplayName)."
                $UninstallFailure = $True
                $ExitCode = 1
            }
        }
    }

    # Unload all hives that were loaded for this script.
    ForEach ($UserHive in $ProfileWasLoaded) {
        If ($ProfileWasLoaded -eq $false) {
            [gc]::Collect()
            Start-Sleep 1
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe UNLOAD HKU\$($UserHive)" -Wait -WindowStyle Hidden | Out-Null
        }
    }

    # Confirm successful uninstallation if no failures detected
    if (-not $UninstallFailure) {
        Write-Host "Successfully uninstalled your requested apps!"
    }

    # Handle reboot if requested and there were no uninstall failures
    if ($Reboot -and -not $UninstallFailure) {
        Write-Host -Object "[Alert] a reboot was requested. Scheduling restart for 60 seconds from now..."
        Start-Process shutdown.exe -ArgumentList "/r /t 60" -Wait -NoNewWindow
    }

    # Exit script with the final exit code
    exit $ExitCode
}
end {
    
    
    
}

 

Come funziona lo script

Questo script è stato progettato per automatizzare la disinstallazione delle applicazioni in base ai loro nomi e gestisce una serie di scenari che potrebbero verificarsi durante il processo di disinstallazione. Di seguito viene illustrato passo per passo il funzionamento dello script:

1. Definizione dei parametri e gestione dell’input:

  • Lo script per automatizzare la disinstallazione delle applicazioni inizia definendo diversi parametri, come -Name per il nome dell’applicazione, -Arguments per gli argomenti aggiuntivi della disinstallazione, -Reboot per pianificare un riavvio e -Timeout per specificare quanto tempo lo script deve attendere per il processo di disinstallazione.
  • Lo script per automatizzare la disinstallazione delle applicazioni controlla quindi se questi parametri sono stati forniti e li convalida, assicurandosi che soddisfino criteri specifici, come ad esempio che il valore di timeout sia compreso tra 1 e 60 minuti.

2. Gestione del profilo e dell’hive utente:

  • Include una funzione per caricare e gestire i profili utente e gli hive del registro di sistema, necessari per accedere alle stringhe di disinstallazione per ogni applicazione. Questo è fondamentale perché la disinstallazione di un’applicazione richiede spesso l’accesso al registro di sistema per individuare il percorso di disinstallazione corretto.

3. Identificazione dell’applicazione e recupero della chiave di disinstallazione:

  • Lo script per automatizzare la disinstallazione delle applicazioni utilizza una funzione chiamata Find-UninstallKey per cercare nel registro di sistema e individuare la chiave di disinstallazione associata al nome dell’applicazione fornito. Questa operazione viene eseguita in diverse posizioni del registro, sia a 32 che a 64 bit, per garantire una copertura completa.

4. Processo di disinstallazione:

  • Una volta identificata la stringa di disinstallazione, lo script per automatizzare la disinstallazione delle applicazioni la elabora per estrarre il percorso dell’eseguibile e gli argomenti necessari per la disinstallazione.
  • Assicura che vengano aggiunti gli argomenti necessari per la disinstallazione silenziosa, come /qn, /norestart o /S, se non sono già presenti. Questo assicura che la disinstallazione venga eseguita senza l’interazione dell’utente e senza forzare il riavvio del sistema, a meno che non sia specificato.

5. Gestione degli errori e controlli finali:

  • Lo script per automatizzare la disinstallazione delle applicazioni include una solida gestione degli errori, che garantisce che, se qualcosa dovesse storto durante la disinstallazione, l’amministratore ne verrebbe informato e lo script terminerebbe con un codice di errore appropriato.
  • Dopo la disinstallazione, lo script controlla nuovamente che l’applicazione sia stata effettivamente rimossa, assicurandosi che non rimangano componenti residui.

Campi di applicazione pratici: Un caso di studio

Immagina uno scenario in cui un amministratore IT deve disinstallare una versione obsoleta di VLC Media Player da 200 computer di un’organizzazione. Fare questa operazione manualmente richiederebbe molto tempo. L’amministratore potrebbe invece distribuire questo script PowerShell per automatizzare la disinstallazione delle applicazioni su tutti i computer, specificando “VLC Media Player” come nome dell’applicazione.

Lo script individuerebbe automaticamente la stringa di disinstallazione appropriata, eseguirebbe la disinstallazione in modo silenzioso e, se necessario, programmerebbe un riavvio. L’intero processo, che avrebbe potuto richiedere giorni, grazie allo script per automatizzare la disinstallazione delle applicazioni viene completato in pochi minuti.

Confronto con altri metodi

Tradizionalmente, gli amministratori possono utilizzare il Pannello di controllo o uno strumento di disinstallazione di terze parti per rimuovere le applicazioni. Tuttavia, questi metodi richiedono un intervento manuale, non sono scalabili e spesso mancano della flessibilità necessaria per aggiungere argomenti personalizzati o gestire efficacemente le disinstallazioni silenziose.

Questo script, invece, offre un approccio altamente automatizzato e personalizzabile, consentendo ai professionisti IT di gestire le disinstallazioni su più macchine in modo efficiente e con una necessità minima di input manuale.

Domande frequenti

  1. Cosa succede se il nome dell’applicazione non viene trovato? Se lo script per automatizzare la disinstallazione delle applicazioni non riesce a trovare un’applicazione con il nome esatto fornito, cercherà nomi simili e fornirà un elenco di possibili corrispondenze. In questo modo l’amministratore può modificare l’input e riprovare.
  2. Lo script può gestire più applicazioni contemporaneamente? Sì, il parametro -Name può accettare un elenco di applicazioni separate da virgole, consentendo allo script di disinstallare più applicazioni in una sola volta.
  3. Cosa succede se il processo di disinstallazione supera il periodo di timeout? Lo script terminerà il processo di disinstallazione ed uscirà con un codice di errore, assicurando che nessun processo venga eseguito all’infinito.

Implicazioni per la sicurezza e la gestione dell’IT

L’automazione del processo di disinstallazione con script di questo tipo ha implicazioni significative per la gestione e la sicurezza IT. Assicurandosi che il software obsoleto o vulnerabile venga rimosso rapidamente e in modo coerente su tutti i computer, le organizzazioni possono ridurre il rischio di violazioni di sicurezza. Inoltre, l’automazione riduce l’errore umano, garantendo che nessuna applicazione venga disinstallata a causa di una svista.

Best practice per l’utilizzo dello script

  • Testa sempre lo script per automatizzare la disinstallazione delle applicazioni in un ambiente controllato: Prima di distribuire lo script per automatizzare la disinstallazione delle applicazioni su più macchine, testalo in un ambiente controllato per assicurarti che funzioni come previsto.
  • Conserva i log: Conserva i log dei processi di disinstallazione per scopi di revisione e risoluzione dei problemi.
  • Esegui il backup prima della disinstallazione: Assicurati di eseguire il backup dei dati importanti prima di eseguire gli script di disinstallazione, in particolare per le applicazioni che potrebbero memorizzare i dati in locale.
  • Aggiorna regolarmente lo script per automatizzare la disinstallazione delle applicazioni: Quando vengono rese disponibili nuove applicazioni e aggiornamenti, aggiorna lo script per gestire le nuove stringhe e i nuovi metodi di disinstallazione.

Considerazioni finali

Gli script PowerShell come quello per automatizzare la disinstallazione delle applicazioni sono strumenti indispensabili per i professionisti IT che gestiscono reti di grandi dimensioni. Automatizzando la disinstallazione delle applicazioni, permettono di risparmiare tempo, di ridurre gli errori e di migliorare la sicurezza. NinjaOne può integrare questi script con i suoi strumenti completi di gestione IT, fornendo ulteriori livelli di automazione, monitoraggio e controllo, e assicurando che la tua infrastruttura IT rimanga solida, sicura e aggiornata.

Passi successivi

La creazione di un team IT efficiente ed efficace richiede una soluzione centralizzata che funga da principale strumento per la fornitura di servizi. NinjaOne consente ai team IT di monitorare, gestire, proteggere e supportare tutti i dispositivi, ovunque essi si trovino, senza la necessità di una complessa infrastruttura locale.

Per saperne di più su NinjaOne Endpoint Management, fai un tour dal vivo, o inizia la tua prova gratuita della piattaforma NinjaOne.

Categorie:

Ti potrebbe interessare anche

×

Guarda NinjaOne in azione!

Inviando questo modulo, accetto La politica sulla privacy di NinjaOne.

Termini e condizioni NinjaOne

Cliccando sul pulsante “Accetto” qui sotto, dichiari di accettare i seguenti termini legali e le nostre condizioni d’uso:

  • Diritti di proprietà: NinjaOne possiede e continuerà a possedere tutti i diritti, i titoli e gli interessi relativi allo script (compreso il copyright). NinjaOne ti concede una licenza limitata per l’utilizzo dello script in conformità con i presenti termini legali.
  • Limitazione d’uso: Puoi utilizzare lo script solo per legittimi scopi personali o aziendali interni e non puoi condividere lo script con altri soggetti.
  • Divieto di ripubblicazione: In nessun caso ti è consentito ripubblicare lo script in una libreria di script appartenente o sotto il controllo di un altro fornitore di software.
  • Esclusione di garanzia: Lo script viene fornito “così com’è” e “come disponibile”, senza garanzie di alcun tipo. NinjaOne non promette né garantisce che lo script sia privo di difetti o che soddisfi le tue esigenze o aspettative specifiche.
  • Assunzione del rischio: L’uso che farai dello script è da intendersi a tuo rischio. Riconosci che l’utilizzo dello script comporta alcuni rischi intrinseci, che comprendi e sei pronto ad assumerti.
  • Rinuncia e liberatoria: Non riterrai NinjaOne responsabile di eventuali conseguenze negative o indesiderate derivanti dall’uso dello script e rinuncerai a qualsiasi diritto legale o di equità e a qualsiasi rivalsa nei confronti di NinjaOne in relazione all’uso dello script.
  • EULA: Se sei un cliente NinjaOne, l’uso dello script è soggetto al Contratto di licenza con l’utente finale (EULA) applicabile.