1 votes

Tuer le processus, puis désinstaller l'application - Puis-je faire cela avec PowerShell, sur plus de 400 ordinateurs, à partir d'un fichier txt ?

Je suis assez novice en matière de PowerShell, mais j'essaie de m'y retrouver. Voici le résumé de ce que je veux faire :

  • 400 ordinateurs, dont pas plus de 5 sur un site physique, connectés par des vitesses assez faibles à travers un VPN.
  • Tous ont une seule application avec une seule version (mais sur Windows XP et Windows 7) qui doit être supprimée en raison d'exigences de conformité.
  • J'ai essayé d'utiliser quelque chose comme ce qui suit, sans grand succès :

    function Terminate-Process {

    param( 
    \[Parameter(Mandatory=$true,valuefrompipeline=$true)\] 
    \[string\]$compname) 
    begin {$processname = Read-Host "Process Name I Want To Kill"} 
    process { 

    $result = Get-WmiObject -Class win32_Process -Filter "name='$processname'" -ComputerName (Get-Content computers.txt) | ForEach-Object { $_.Terminate() } if ($result.ReturnValue -eq 0 )
    { Write-Output " $($processname) terminated on $($compname) "} else { Write-Output "could not terminate $($processname) on $($compname) "}

                } 

    end{Write-Output "Script ...END"}

    }

    Start-Sleep -s 60

    Get-Content Computers.txt | .\Get-InstalledSoftware.ps1 | Where {$_.AppName -match “SoftwareName” } | .\Uninstall-InstalledSoftware.ps1

\============================================

La dernière ligne appelle deux autres scripts powershell.

Get-InstalledSoftware.ps1 est :

\[cmdletbinding()\]            

\[cmdletbinding()\]            
param(            
 \[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)\]            
 \[string\[\]\]$ComputerName = $env:computername            

)            

begin {            
 $UninstallRegKey="SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall"             
}            

process {            
 foreach($Computer in $ComputerName) {            
  Write-Verbose "Working on $Computer"            
  if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {            
   $HKLM   = \[microsoft.win32.registrykey\]::OpenRemoteBaseKey('LocalMachine',$computer)            
   $UninstallRef  = $HKLM.OpenSubKey($UninstallRegKey)            
   $Applications = $UninstallRef.GetSubKeyNames()            

   foreach ($App in $Applications) {            
    $AppRegistryKey  = $UninstallRegKey + "\\\\" + $App            
    $AppDetails   = $HKLM.OpenSubKey($AppRegistryKey)            
    $AppGUID   = $App            
    $AppDisplayName  = $($AppDetails.GetValue("DisplayName"))            
    $AppVersion   = $($AppDetails.GetValue("DisplayVersion"))            
    $AppPublisher  = $($AppDetails.GetValue("Publisher"))            
    $AppInstalledDate = $($AppDetails.GetValue("InstallDate"))            
    $AppUninstall  = $($AppDetails.GetValue("UninstallString"))            
    if(!$AppDisplayName) { continue }            
    $OutputObj = New-Object -TypeName PSobject             
    $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.ToUpper()            
    $OutputObj | Add-Member -MemberType NoteProperty -Name AppName -Value $AppDisplayName            
    $OutputObj | Add-Member -MemberType NoteProperty -Name AppVersion -Value $AppVersion            
    $OutputObj | Add-Member -MemberType NoteProperty -Name AppVendor -Value $AppPublisher            
    $OutputObj | Add-Member -MemberType NoteProperty -Name InstalledDate -Value $AppInstalledDate            
    $OutputObj | Add-Member -MemberType NoteProperty -Name UninstallKey -Value $AppUninstall            
    $OutputObj | Add-Member -MemberType NoteProperty -Name AppGUID -Value $AppGUID            
    $OutputObj# | Select ComputerName, DriveName            
   }            
  }            
 }            
}            

end {}

et Uninstall-InstalledSoftware.ps1 :

\[cmdletbinding()\]            

param (            

 \[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)\]
 \[string\]$ComputerName = $env:computername,
 \[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)\]
 \[string\]$AppGUID
)            

 try {
  $returnval = (\[WMICLASS\]"\\\\$computerName\\ROOT\\CIMV2:win32\_process").Create("msiexec \`/x$AppGUID \`/qn")
 } catch {
  write-error "Failed to trigger the uninstallation. Review the error message"
  $\_
  exit
 }
 switch ($($returnval.returnvalue)){
  0 { "Uninstallation command triggered successfully" }
  2 { "You don't have sufficient permissions to trigger the command on $Computer" }
  3 { "You don't have sufficient permissions to trigger the command on $Computer" }
  8 { "An unknown error has occurred" }
  9 { "Path Not Found" }
  9 { "Invalid Parameter"}
 }

Je reçois toutes sortes d'erreurs bizarres, et je ne suis même pas sûr que ce qui précède puisse fonctionner. J'ai trouvé la plupart de ces informations sur techibee.com, ici : http://techibee.com/powershell/powershell-uninstall-software-on-remote-computer/1400

Existe-t-il un moyen plus simple de procéder ? Je m'arrache un peu les cheveux ! Sinon, je pourrais me connecter par RDP à 400 ordinateurs, tuer le processus et désinstaller... mais je n'ai vraiment, vraiment, vraiment pas envie de faire ça.

1 votes

Quelques éléments qui me viennent immédiatement à l'esprit : 1. Si vous avez 400 machines sur une liaison lente, j'exécuterais l'intégralité du script dans un Invoke-Command scriptblock sur l'hôte distant, plutôt que de tout communiquer en retour. 2. Y a-t-il un danger à tenter de supprimer le logiciel, même s'il n'est pas installé ? Plutôt que d'obtenir une liste des logiciels installés et de la vérifier, ce qui est propre et net, pourriez-vous simplement tenter la désinstallation, et si elle échoue, supposer qu'il n'a jamais été installé en premier lieu ?

1voto

HopelessN00b Points 53075

Bien que cela soit techniquement possible, il y a probablement une meilleure façon de procéder.

Et en parlant de meilleures façons de procéder, vous pourriez le faire dans un GPO avec quelques lignes de code comme un script de démarrage ou d'arrêt, ce qui est la façon dont je gère cela. Avec quelques lignes de code supplémentaires, vous pourriez enregistrer les résultats de la vérification de la présence de cette chose et/ou de sa désinstallation, ce qui serait sans aucun doute utile dans vos efforts de conformité.

Si un script de démarrage/arrêt lié à GPO n'est pas une option pour une raison quelconque, je pense que j'utiliserais PSExec pour tuer le processus sur une liste d'ordinateurs lue dans un fichier et ensuite script la désinstallation dans un langage approprié. Il me semble que c'est beaucoup plus facile en VB, par exemple.

a=WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{2318C2B1-4965-11d4-9B18-009027A5CD4F}\UninstallString")

If a<>"" Then

WshShell.Run(a&" /S"),1,True
i=i+1

end if

(Adieu la barre d'outils Google, dans cet exemple que j'ai écrit ou copié il y a quelques années. Copié, probablement. Je suis plutôt paresseux).

Sans déboguer le script PS que vous avez copié, je vous signalerais que vous pourriez utiliser une version différente de PS, différents modules PS installés/chargés et/ou il pourrait y avoir des dépendances que vos machines XP n'ont pas en place et qui causent des problèmes.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X