I have a script to uninstall McAfee antivirus and the agent associated with it.
The issue i'm having is that the script provides an exit code too early and doesn't continue through. If I run the script multiple times I get the desired result, but as we're trying to push it out via PDQ remotely, we need it to run through the script and only provide an exit code at the end of the script.
I'm a powershell novice so there's probably a much better and easier way to write this script but any advice would be greatly appreciated.
Start-Process -FilePath "msiexec.exe" -ArgumentList "/x {CE15D1B6-19B6-4D4D-8F43-CF5D2C3356FF} REMOVE=ALL REBOOT=R /q"; Write-Host "Uninstalling McAfee VirusScan Enterprise 8.8..."
$version = (Get-WmiObject -class Win32_OperatingSystem).Caption
Write-Host "Detected OS as $version"
if ($version -like '*Windows 7*')
{
Write-Host "Uninstalling McAfee Agent..."
Start-Process -FilePath "C:\Program Files (x86)\McAfee\Common Framework\frminst.exe" -ArgumentList "/forceuninstall"
}
elseif ($version -like '*Windows 10*')
{
Write-Host "Unmanaging McAfee Agent for Uninstall Process.."
Start-Process -FilePath "C:\Program Files\McAfee\Agent\maconfig.exe" -ArgumentList "/provision /unmanaged";
Write-Host "Uninstalling McAfee Agent..."
Start-Process -FilePath "C:\Program Files\McAfee\Agent\x86\frminst.exe" -ArgumentList "/forceuninstall"
}
else
{
exit
}
Start-Process reports a return code as soon as it starts the process indicating whether it was successful or not. Either use -wait to force the script to wait until it finishes or capture the output and proceed based on what the returnvalue is. See the docs for Start-Process
Related
Thank you for all the help I get here!
I researched a lot on the Internet (and StackOverFlow) but I believe there is no way I can add a System Variable within a script and somehow "restart" or "refresh" so that $env:MyTools can be populated :( I'll have to exit and request the user to restart PS.
$AddCheck = Start-Process powershell.exe -verb runAs -ArgumentList '-Command', "`"[System.Environment]::SetEnvironmentVariable('MyTools','C:\SomeFolder',[System.EnvironmentVariableTarget]::Machine)`";" -PassThru -Wait
if($AddCheck.ExitCode -eq 0)
{
Write-Host "[+] Addition Successful. Please restart PowerShell for changes to take effect!" -foregroundcolor green
""
"Exiting.."
sleep -s 5
stop-process -Id $PID
}
else
{"Something went wrong."}
Any help/inputs would be appreciated!
We are tying to install an MSI file on Windows servers using the following script and are able to install the MSI file in a Windows server. The Following code IS working fine for some MSI files, but it's failing for others. getting exit-code as 1603. If we do clean installation it works fine, but while trying to reinstall, we are getting an exit-code:1603 error. All the configuration settings are same for all services.
As mentioned on the Microsoft web site, we verified that following conditions and none are applied to our case.
Windows Installer is attempting to install an app that is already installed on your PC.
The folder that you are trying to install the Windows
Installer package to is encrypted.
The drive that contains the folder that you are trying to install the Windows Installer package to is accessed as a substitute drive.
The SYSTEM account does not have Full Control permissions on the folder that you are trying to install the Windows Installer package to. You notice the error message because the Windows Installer service uses the SYSTEM account to install software.
Code:
:outer for($i=1; $i -le $attempts; $i++) {
$timeout = $null
$proc = Start-Process -filePath $InstallerPath -ArgumentList $InstallCommand -PassThru
$proc | Wait-Process -Timeout $SecondsToWait -ea 0 -ev timeout
If (($timeout) -or ($proc.ExitCode -ne 0)) {
$proc | kill
$error = "`tFailed To Run $($ProcessTitle)Operations: Exit-Code = $($proc.ExitCode)"
If(($i+1) -le $attempts) {
WriteLog -Message($error) -MainLoggingConfigs $MainLoggingConfigs
Start-Sleep -s $WaitTimePerAttempt
}
Else {
throw $error
}
}
Else {
break outer
}
If using an MSI, you'll want to use Start-Process msiexec.exe -wait -NoNewWindow instead of Wait-Process . If you are really worried about it running forever, consider using PowerShell jobs:
Start-Job -Name MyInstall -scriptBlock {
Start-Process msiexec.exe -NoNewWindow -ArgumentList $MSIArguments
}
Wait-Job -Name MyInstall
Then check the job Get-Job MyInstall for output, status messages, state, errors, and especially child jobs.
The error you get may be due to competing installation attempts if your Start-Process creates child processes that haven't ended. Try out using something like Kevin Marquette's solution to save off the verbose MSI logs as well:
$MSI = 'C:\path\to\msi.msi'
$DateStamp = get-date -Format yyyyMMddTHHmmss
$logFile = "$MSI-$DateStamp.log"
$MSIArguments = #(
"/i"
"`"$MSI`""
"/qn"
"/norestart"
"/L*v"
$logFile
)
Start-Process "msiexec.exe" -ArgumentList $MSIArguments -Wait -NoNewWindow
For the installation of the "Self-Hosted Integration runtime software, using PowerShell command
Start-Process "msiexec.exe" "/i $path /quiet /passive" -Wait, error comes up for "DIAHostService" not having "LocalSystem" access.
I have the code to change the login to the LocalSystem ("sc.exe config "ServiceName" obj="LocalSystem") But How can I do this during the Installation Process, how do I catch the error and do the required change and continue for the installation automatically?
Code:-
param([string]$path, [string]$authKey)
function Install-Gateway([string] $gwPath)
{
# uninstall any existing gateway
UnInstall-Gateway
Write-Host "Start Gateway installation"
Start-Process "msiexec.exe" "/i $path /quiet /passive" -Wait
Start-Sleep -Seconds 30
Write-Host "Succeed to install gateway"
}
Since I am in no position to replicate what you are doing, take this generic approach as an example.
param([string]$path, [string]$authKey)
function Install-Gateway([string] $gwPath)
{
Write-Warning -Message 'Starting Gateway uninstallation'
UnInstall-Gateway
Try
{
'Starting Gateway installation'
Start-Process -FilePath 'msiexec.exe' -ArgumentList "/i $path /quiet /passive" -Wait -ErrorAction Stop
Start-Sleep -Seconds 30
Write-Verbose -Message 'Gateway installation successful.' -Verbose
}
Catch
{
Write-Warning -Message 'AN error occurred'
$PSItem.Exception.Message
}
}
If you need to run this with a different user, then you need to add the RunAs as part of the Start-Process error logic. Yet using RunAs means starting a new Powershell instance. It will not run in the same process.
References
• PowerShell: Running Executables
Direct - Using the environment path or local folder
Invoke-Expression (IEX)
Invoke-Command (ICM)
Invoke-Item (II)
The Call Operator &
cmd /c - Using the old cmd shell
Start-Process (start/saps)
[Diagnostics.Process] Start()
WMI Win32_Process Create() Method
Stop-Parsing Symbol --%
see also:
'powershell start-process msiexec'
Powershell: Installing MSI files
'PowerShell start-process msiexec' try/catch
about_Try_Catch_Finally - PowerShell | Microsoft Docs
'PowerShell error preference'
Handling Errors the PowerShell Way | Scripting Blog
-ErrorAction and -ErrorVariable
You could also look at using PowerShell jobs for this use case.
start-process 'about PowerShell jobs'
I have a powershell script that I am trying to run at computer startup through a GPO using the new tab for powershell scripts that can be found in the group policy editor.
No matter what, it does not seem to be running at all, and I suspect the problem might for some reason be with the script itself using some var or calling to something that is not available under NT Authority\System impersonation.
Should something in the following script need to be edited in order to actually work as a startup script via GPO?
$sysdrivelocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
#If the drive is encrypted and ready, exit script and do nothing.
if(($sysdrivelocker.VolumeStatus -eq "FullyEncrypted") -or ($sysdrivelocker -eq "EncryptionInProgress")){
exit
}
#If the drive has been prepared with bdehdcfg, start bitlocker encryption and restart the computer.
else if($sysdrivelocker.VolumeStatus -eq "FullyDecrypted"){
#Creating the recovery key
Start-Process 'manage-bde.exe' -ArgumentList " -protectors -add $env:SystemDrive -recoverypassword" -Verb runas -Wait
#Adding TPM key.
Start-Process 'manage-bde.exe' -ArgumentList " -protectors -add $env:SystemDrive -tpm" -Verb runas -Wait
sleep -Seconds 15 #This is to give sufficient time for the protectors to fully take effect.
#Getting Recovery Key GUID.
$RecoveryKeyGUID = (Get-BitLockerVolume -MountPoint $env:SystemDrive).keyprotector | where {$_.Keyprotectortype -eq 'RecoveryPassword'} | Select-Object -ExpandProperty KeyProtectorID
#Backing up the Recovery to AD.
Start-Process 'manage-bde.exe' -ArgumentList " -protectors $env:SystemDrive -adbackup -id $RecoveryKeyGUID" -Verb runas -Wait
#Enabling Encryption.
Start-Process 'manage-bde.exe' -ArgumentList " -on $env:SystemDrive" -Verb runas -Wait
#Restarting the computer, to begin the encryption process.
Restart-Computer
}
#If the drive is not bitlocker ready, prepare it and restart the computer.
else if([string]::IsNullOrEmpty($sysdrivelocker.VolumeStatus) -eq $true)
#Starting the defrag service, required in the next step.
Get-Service -Name defragsvc -ErrorAction SilentlyContinue | Set-Service -Status Running -ErrorAction SilentlyContinue
#Preparing the systemdrive for bitlocker activation, and restarting the computer.
BdeHdCfg -target $env:SystemDrive shrink -quiet -restart | Out-Null
}
#Exit in case the volume status is anything else (e.g. paused or decryption in progress).
else{
exit
}
And yes, before anyone asks, I have set it up correctly as any guide I could find tells me, the script is located under \\domain.local\SysVol\domain.local\Policies\{GPO-GUID}\Machine\Scripts\Startup and for troubleshooting purposes I even set my machines execution policy to unrestricted.
Sometimes when I'm writing a script on my Windows 10 Machine (PowerShell 5.0) I use commands, parameters or aliases which are not available on earlier versions of PowerShell, e.g the -persist parameter of new-psdrive is not available on PowerShell 2.0 which all of our Win7 machines use.
to set my #requires -version x statement correctly, I need to know if there are commands in my script which aren't available to earlier PowerShell Versions. When you wrote a code with 1000 lines it could be a little difficult to find unavailable commands in your script.
Is there a way to check this programmatically, other than just run the script in different PowerShell environments and see what's happening?
Have you considered developing on your Windows 10 machine but set your powershell profile to always run a powershell -version 2?
You'll launch powershell which will launch version 2 to develop in and if there are errors in the script you'll know when they're created and commands that would run in version 5 (or whatever version your Win10 machine has) would fail.
It should be noted that launching powershell like this:
powershell -version 2
will keep the logic the same and act like the version 2 powershell but the help file and output from commmands Get-Help will still show the version 5(or whatever) syntax that is true powershell version.
Setting your Powershell Profile:
http://www.howtogeek.com/50236/customizing-your-powershell-profile/
You can check the running version with
$PSVersionTable
This doesnt really answer your question
but i use the "Script Analyser" that comes with https://gallery.technet.microsoft.com/scriptcenter/Getting-started-with-57e15ada
I had previously thought that it would be good if it was enhance to check at different version levels. either output the minimum version for your script. or you specify a target version and it would tell you what was wrong.
We decided to make Powershell v.4 baseline for all systems. So I wrote a powershell script to ensure v.4 is install on all systems. This is set to run via GPO shutdown script under machine configuration.
IF ($PSVersionTable.PSVersion.Major -like "4*"){$StringToWrite | Out-File -FilePath $Logpath\PwrShl\Powershell_UpToDate_$hostname.log -Append; IF (Test-Path -Path $Logpath\PwrShl\Powershell_OutofDate_$hostname.log){Remove-Item $Logpath\PwrShl\Powershell_OutofDate_$hostname.log -Force}; exit}
IF (($PSVersionTable.PSVersion.Major -like "2*") -or ($PSVersionTable.PSVersion.Major -like "3*")){$StringToWrite | Out-File -FilePath $Logpath\PwrShl\Powershell_OutofDate_$hostname.log -Append}
if ($env:PROCESSOR_ARCHITECTURE -eq "x86"){
if (!(Test-Path C:\SchTsk\Temp\Windows6.1-KB2819745-x86-MultiPkg.msu)){Copy-Item "\\ad.dcpds.cpms.osd.mil\SYSVOL\ad.dcpds.cpms.osd.mil\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Scripts\Startup\Windows6.1-KB2819745-x86-MultiPkg.msu" -Destination C:\SchTsk\Temp\Windows6.1-KB2819745-x86-MultiPkg.msu -Force}
}
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64"){
if (!(Test-Path C:\SchTsk\Temp\Windows6.1-KB2819745-x64-MultiPkg.msu)){Copy-Item "\\ad.dcpds.cpms.osd.mil\SYSVOL\ad.dcpds.cpms.osd.mil\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Scripts\Startup\Windows6.1-KB2819745-x64-MultiPkg.msu" -Destination C:\SchTsk\Temp\Windows6.1-KB2819745-x64-MultiPkg.msu -Force}
}
IF ($env:PROCESSOR_ARCHITECTURE -eq "x86"){
Set-Location C:\SchTsk\Temp
if (!(Test-Path C:\SchTsk\Temp\Windows6.1-KB2819745-x86-MultiPkg.msu)){exit}
expand -F:* .\Windows6.1-KB2819745-x86-MultiPkg.msu C:\SchTsk\Temp
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2872035-x86.cab /NoRestart' -Wait
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2872047-x86.cab /NoRestart' -Wait
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2819745-x86.cab /NoRestart' -Wait
}
IF ($env:PROCESSOR_ARCHITECTURE -eq "amd64"){
Set-Location C:\SchTsk\Temp
if (!(Test-Path C:\SchTsk\Temp\Windows6.1-KB2819745-x64-MultiPkg.msu)){exit}
expand -F:* .\Windows6.1-KB2819745-x64-MultiPkg.msu C:\SchTsk\Temp
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2809215-x64.cab /NoRestart' -Wait
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2872035-x64.cab /NoRestart' -Wait
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2872047-x64.cab /NoRestart' -Wait
Start-Process dism.exe -ArgumentList '/online /add-package /PackagePath:C:\SchTsk\Temp\Windows6.1-KB2819745-x64.cab /NoRestart' -Wait
}
Are network speed is kind of crapy and I was having problems with it trying to continue to run and the file not be existant. So I simply had it exit if the file isn't there to extract and install. Since it takes a restart, shutdown script made the most since. This was pretty successful for me, had a few trouble childs.