Speed up uninstallation with PowerShell - powershell

I use PowerShell script to uninstall software, remove services and delete installation folder. Full clean up. This software has core app and 11 addins. So I use this code for addins:
$appAddIns = #(Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -match "SE0008*" })
foreach ($appAddIn in $appAddIns)
{
Write-Host "Uninstalling: " $appAddIn.Name
$appAddIn.Uninstall() | out-null
}
But it's terribly slow to even start the script. I run it, and it's just blank. My colleague at work didn't use my script because after 10 seconds he assumed that it doesn't work and terminated it.
Is there any way to write it better, or just add:
Write-Host "Sit and wait you impatient bastard"
at the beginning?

win32_product is slow ,you can utilize this registry path (HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall) to get the uninstallation command and run it

You could use Write-Progress and then update it with the name of the program you are uninstalling and a calculated percentage based on the number of programs being uninstalled.
Here is an example:
Write-Progress -Activity "Uninstalling programs..."
for ($i = 1; $i -le 5; $i++) {
Write-Progress -Activity "Uninstalling programs..." -Status "Program $i" -PercentComplete ($i / 5 * 100)
Start-Sleep -Seconds 1
}

Win32_product is broken and should not be used (even Microsoft wrote some articles about this broken WMI class). Besides it being very slow, it also causes problems with the current installed MSI packages since it forces a re-register on all registered MSI packages.
The easiest way to uninstall software is nowadays Powershell Desired State Configuration (note that you need at least Windows 8.1 for this). With DSC you can also change Service behaviour, remove files, install / uninstall MSI packages, run Powershell scripts etc.

Related

Is there a way of detecting whether Microsoft Teams is in use before running an update script?

I'm deploying a script across my organization to keep software patched with our chocolatey repo, I'm including tests so it doesn't upgrade applications that are currently running on workstations however I'm struggling to detect whether Teams is actively being used by the user or just running in the background. Here's what I've got so far:
# First attempt - Check if the Teams process is running and if not, upgrade it
$teams = Get-Process | Where {$_.ProcessName -Like "*teams*"} -ErrorAction SilentlyContinue
if (-Not $teams) {
Write-Host "Teams not running, upgrading..."
choco upgrade microsoft-teams.install -y
}
# Second attempt - Upgrade Teams if there are less than 5 running processes
if ((get-process -ea silentlycontinue teams).count -gt 5) {
Write-Host "Teams running, aborting"
exit 1
} else {
Write-Host "Updating Teams"
choco upgrade microsoft-teams.install -y
}
They both don't work reliably as Teams is often running with multiple processes even when it's not in active use, is there any way of telling whether it's present in the Taskbar/minimised to the tray that would be a good indication of whether an update would interrupt the user?
There is no such cmdlet available and provided by Microsoft. I have also gone through other docs and its not there.

PowerShell script for software installation using chocolatey

enter code here
# Step 1) install Chocolatey when needed
if (-not (Test-Path -Path "$env:ProgramData\Chocolatey\choco.exe" -PathType Leaf)) {
# from https://chocolatey.org/install
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
}
# Step 2) define the array of packages you are offering
$Packages = 'googlechrome','firefox','codeblocks','windbg','nasm',
'explorersuite','pestudio','vscode','sysinternals','python'
# Step 3) define the Show-Menu function
function Show-Menu {
Clear-Host
Write-Host "**********************************************"
Write-Host "LIST OF SOFTWARES"
# write the options using the array of packages
for ($i = 0; $i -lt $Packages.Count; $i++) {
# {0,2} means right align with spaces to max 2 characters
Write-Host ('{0,2}. {1}' -f ($i + 1), $Packages[$i])
}
Write-Host " q. Exit the script"
Write-Host "*************************************************"
Write-Host
}
# Step 4) enter an endless loop you only exit if the user enters 'q'
while ($true) {
Show-Menu
$UserInput = Read-Host "Enter the software number to be installed"
# test if the user wants to quit and if so, break the loop
if ($UserInput -eq 'q') { break }
# test if the user entered a number between 1 and the total number of packages (inclusive)
if ([int]::TryParse($UserInput,[ref]$null) -and 1..$Packages.Count -contains [int]$UserInput) {
# here you install the chosen package using the array index number (= user input number minus 1)
$packageIndex = [int]$UserInput - 1
Write-Host "Installing $($Packages[$packageIndex])"
choco install $Packages[$packageIndex] -y
}
else {
$availableOptions = 1..$Packages.Count -join ','
Write-Host "Error in selection, choose $availableOptions or q" -ForegroundColor Red
}
$null = Read-Host "Press Enter to continue"
}
Problem Statement:
I am writing power shell script using chocolatey for software downloading and installing automatically. My script will display a menu with the list of software's whenever users enter the number the corresponding software will download and install.
When i run the script the executable files are not showing in control panel and desktop icon is also not creating but i am getting the message in power-shell terminal that the software is installed but i am not able to see that application.
I need to put some conditions in my script they are:
If the software is already installed then the software should not be downloaded
When the software is installed it has to shown in control panel and desktop icon should be created.
If any upgrade is their like for googlechrome and firefox generally up gradation takes place then the script has to upgrade.
Please help me and edit my script if anything is wrong
Thanks in advance
Referencing this answer, check to see if the relevant software is installed by checking the following registry keys:
$InstalledSoftware = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
$InstalledSoftware += Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
If it's installed, you can either skip the installation altogether, or...
You can get the package managed under Chocolatey by "installing" the package anyways, but provide the -n (--skippowershell) parameter:
choco install -n packageName
This will download the package metadata but skip the actual PowerShell code (chocolateyInstall.ps1, which is embedded into all Chocolatey packages). This is the piece which runs MSIEXEC, an EXE installer, extracts zip archives for packages without a proper installer, etc. This has the benefit of allowing future updates to be manageable by Chocolatey without reinstalling the software which already exists.
Note: There may be some nuance surrounding specific packages you'll have to figure out, but for the majority of what is available as a Chocolatey package this approach will work.

ps1 to exe deployment of Powershell Script, Cannot find all the information required

I'm trying to create a .exe file that will run a powershell script that I have to install various components of an application. I'm working on step 1 of 4 to get this entire application installed and want to get it into one package. Things are not going well with it. I am using Ps1 to Exe by f2ko. There is no documentation that I can find on the software, but it does what I want it to. The issue is in the packaged files and how it is run. I am running into a couple issues. The primary issue seems to be with Start-Process, When the exe is running I get an error that states
Start-Process : This command cannot be run completely because the system cannot find all the information required.
At C:\Users\adminjp\AppData\Local\Temp\2605.tml\2606.tmp\2607.ps1:9 char:16
The remainder of the error is obscured by a status bar for the installer. The status bar runs, but the process does not. I initially had an issue with ExecutionPolicy but that is rectified by manually changing the value in order to run the script. I have been having multiple issues getting this script to run and am running out of articles online that I haven't read. Here is my powershell script:
Set-ExecutionPolicy -Force remotesigned
$NETfile = "env:p2eincfilepath\1 - NDP471-KB4033342-x86-x64-AllOS-ENU.exe"
$NETargs = "/q"
$SQLfile = "env:p2eincfilepath\setup.exe"
$SQLargs = "/ConfigurationFile=`".\ConfigurationFile_SQLExpress.ini`""
function Show-Progress ($file, $arguments, $component){
$process = Start-Process $file $arguments -PassThru
for($i = 0; $i -le 100; $i = ($i + 1) % 100)
{
Write-Progress -Activity "Installer" -PercentComplete $i -Status "Installing $component"
Start-Sleep -Milliseconds 100
if ($process.HasExited) {
Write-Progress -Activity "Installer" -Completed
break
}
}
}
Show-Progress $NETfile $NETargs ".NET 4.7.2"
Show-Progress $SQLfile $SQLargs "SQL Express"
The program that converts this script to an exe is what sets the extraction location and, to the best of my knowledge, I have no way to set that. Attached is a screenshot of their explanation of how to reference the embeded files:
I am using env:p2eincfilepath because that is the powershell method of accessing environment variables and the application appears to create or use an environment variable. Please let me know if there is any other information you might need. The Ps1 appears to work correctly if I run it directly from powershell and put the files in the root directory, calling them with ./ I would really like to keep this function in use since this is what gives me my status bar letting the user know what is being installed:
I think you need to have a $ sign in front of the env variable for it to fill in. e.g.
$NETfile = "$env:p2eincfilepath\1 - NDP471-KB4033342-x86-x64-AllOS-ENU.exe"
$NETargs = "/q"
$SQLfile = "$env:p2eincfilepath\setup.exe"
$SQLargs = "/ConfigurationFile=`".\ConfigurationFile_SQLExpress.ini`""

silent installation using powershell

I am trying to install software using powershell silent scripting. To install this software we need to have JRE installed on machine. For this first we need to check weather JRE installed or not, if not installed then it needs to be installed. What approach needs to be followed?
I have tried with the below of code.
$LASTEXITCODE = 0
$workdir = "C:\Program Files (x86)\Java"
If (!(Test-Path $workdir))
{
$LASTEXITCODE = (Start-Process "D:\jre-6u26-windows-i586.exe" -ArgumentList "/s" -Wait -PassThru).Exitcode
}
If($LASTEXITCODE -eq 0)
{
$DCBdir = "C:\Program Files (x86)\Compart"
If (!(Test-Path $DCBdir))
{
$Installer="D:\sw.exe"
$responsefile="D:\Sresponse.varfile"
$a=#("-q", "-varfile", "$responsefile")
start-process $Installer -ArgumentList $a -wait
}
}
$chkdir = "C:\Program Files (x86)\SWFolder"
if(Test-Path -eq $chkdir)
{
[System.Windows.MessageBox]::Show('Installation completed successfully')
}
When I run script its workingfine as it is checking the previous installation and performing installation if not found the installation. But here I am getting as issue with this code.
If Java installed alredy means it should start the other installation. but here in my case its stopping the complete installation.
after installation completed, I need to display the message like " Installation completed". But here its not working. AnNy wrong in the above code..??
One package manager that I like to use is Chocolatey which has an approved package for JRE, it looks like. A quick check wmi will tell you whether or not java is installed:
$x = Get-WmiObject -Class Win32_Product -Filter "Name like 'Java(TM)%'" | Select -Expand Version
You could also use Test-Path pointed at registry keys you know exist for the package. Once you verify that JRE is not on the machine, then you can call out to Chocolatey to install it.

Automating WSUS updates with PowerShell/PowerCLI

I'm looking to automate the process of installing WSUS updates on my VMs. To give a short overview, here are the things I want to accomplish (please let me know if my methods are moronic, I'd love to learn the right way for all of this.):
Check if the particular VM has any WSUS updates to install
If there are updates available, take a snapshot of the VM
Begin the WSUS install
Reboot the system, if necessary
I am currently able to check if the particular VM has updates and take a snapshot. Now I know I could just have this portion of the script run and configure a GPO to accomplish the rest of the tasks, but my thought process is that if I can do it all in the script, I will be able to check that the snapshot of the VM exists prior to installing the update. Below you can see what my script does as of now.
foreach ($vm in $vms) {
if ($vm.PowerState -eq "poweredOn") {
$output = Invoke-VMScript -ScriptText $script -VM $vm -GuestCredential $guestCred
if ($output.ScriptOutput -Notlike '0*') {
New-Snapshot -VM $vm -Name BeforeWSUS
}
}
}
After this I would like to perform a check to see if the snapshot exists for a vm, then install the WSUS update. If a reboot is necessary, then reboot.
Is there a way to do this? A better way to do this? Would really appreciate some insight, I'm new to Powershell.
Edit: I've checked on the PSWindowsUpdate Module, would that need to be on each VM I plan to update?
Yes, you would need PSWindowsUpdate installed on each VM.
You could include something like this in your script to check if PSWindowsUpdate is installed and if not, install it.
Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted
$Modules = "PSWindowsUpdate"
$Modules | ForEach-Object {
If (!(Get-Module -ListAvailable -Name $_)) {
Install-Module $_ -Verbose -Confirm:$false
}
}
I think that Install-Module requires PowerShell version 5.0.
Then you would use Get-WUInstall to install updates from your WSUS server. (It looks like it defaults to WSUS if configured via GPO.)
Probably throw in a -Confirm:$False to avoid it prompting you to allow each update.
More info on PSWindowsUpdate: https://github.com/joeypiccola/PSWindowsUpdate