Install Multiple msi files consecutively - powershell

I have just recently started using (and learning how to) script. I am using Powershell ISE and am trying to create a script that can be run on a new computer to install multiple/various programs. I have the programs in both .exe and .msi and want the programs to install silently, and consecutively. Again I am a beginner, but have put together the script below to get this done. I am trying to find out what variable/command will ensure that the programs install one by one.
msiexec.exe /q /i '\\Server\Folder\Applications\msi files\file.msi'
msiexec.exe /q /i '\\Server\Folder\Applications\msi files\file.msi'
msiexec.exe /q /i '\\Server\Folder\Applications\msi files\file.msi'
msiexec.exe /q /i '\\Server\Folder\Applications\msi files\file.msi'
msiexec.exe /q /i '\\Server\Folder\Applications\msi files\file.msi'
msiexec.exe /q /i '\\Server\Folder\Applications\msi files\file.msi'
I originally started this as a .bat file, and can run it to install the .exe files just fine, however they all run at the same time. Therefore, I figured creating a script (rather than a .bat file) would be my best bet. Any and all input and or help is greatly appreciated!

You cannot run multiple .msi files all at once if that's what you're wanting. If you want to run them consecutively it would look like this in Powershell:
If it's an msi:
Start-Process msiexec.exe -Wait -ArgumentList '/I ProgramName.msi /quiet'
If it's an exe:
Start-Process programname.exe -Wait -ArgumentList '/I /quiet'
And basically the -Wait parameter will wait until the windows installer closes until it proceeds to the next line of code. Some msi's do have different ways of classifying args depending on the developer. Sometimes it's /q, /qn or /quiet.

Windows Installer: the msiexec.exe engine should wait for your installation to complete before it exits. I suspect your command lines are wrong and that is why it looks like they all run and exit simultaneously.
Sample Command Line: Maybe try this command line (maybe put in MyTest.cmd and run):
msiexec.exe /i MySetup.msi /L*V C:\MyLog.log /qn ADDLOCAL=ALL REBOOT=ReallySuppress ALLUSERS=1
Repeat command line for each MSI you need to install.
Logging:The log files should have unique names, obviously. You can enable logging for all MSI installations (section 'Globally for all setups on a machine'). Then you will find a new MSI-log file with a random name in the system's %TEMP% folder after each MSI operation. Sort by change date to find the latest one. To find errors in MSI logs, try searching for "value 3".
More information available on request. Please do NOT add your own answer, edit your original question instead. Just add more info, delete or whatever you need. We have versioning so we can find what you delete as well if need be.

Related

Is there a way to open a CMD window with a running command through Powershell?

I know it is possible to use a batch file, but due to a suggestion I switched a lot of my script over to PowerShell. The problem I ran into is that PowerShell is still missing some commands from CMD and has lower permissions when run as an Admin. Below is the current line I have been using.
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList 'del /s "C:\Users\*.mp3"'"
I ran it in both 5.1 and 7.
It works with a simple command like opening the calculator or sending a ping, but I can't get the del command to work. The goal is to open a CMD window which will then delete all mp3 files. I know there are probably better ways to do this, but I more so want to know if it's possible than efficient. Thank you for your time!
This works somewhat
Start-Process cmd "/c del /S C:\Users\*.mp3"

Removal of Office

I'm currently trying to run a batch file as a startup script to detect and remove whatever version of office a user has installed and then to install Office 365. I have the install working however, when I attempted to uninstall Office 2013 I received the following error:
Input Error: Can not find script file "C:\Windows\OffScrub_O15msi.vbs"
The Offscrub file is in the same location as the script, is someone able to tell me why it's looking in C:\Windows for it?
Update
Please find my current script which now works for Office 2013, I previously added the line Remove2016Installs $true when using -Command to remove Office 2016, this worked. Since using -File to work around my initial problem I've been unable to get the script to remove Office 2016 and would like some advice on how to do this, I've read that whatever command is after -File needs to be the last which I believe might be why it's failing.
My full script is below:
start "----NOTICE----" cmd.exe /t:ec /Q /k "echo OFFICE 365 IS BEING INSTALLED. THIS WINDOW WILL CLOSE WHEN COMPLETE&&prompt $h"
#echo off
pushd "%~dp0"
powershell.exe -executionpolicy bypass -NoExit -File "Remove-PreviousOfficeInstalls.ps1"
popd
reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set OS=32BIT || set OS=64BIT
if %OS%==32BIT "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\setup.exe" /configure "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\configuration-Office365-x86.xml"
if %OS%==64BIT "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\setup.exe" /configure "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\configuration-Office365-x64.xml"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ ----NOTICE----"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ Administrator: ----NOTICE----"
echo %date% %time% Setup ended with error code %errorlevel%. >> %LogLocation%\%computername%.txt
Update Finished
There's a line that calls the Powershell script Remove-PreviousOfficeInstalls, this is a file from GitHub that is very popular for the removal of whichever Office version you have installed.
I can run this command if say I copy these files to the desktop and amend the locations in the scripts, I'm not sure what this reference to C:\Windows is though when run from \domain\SYSVOL\domain\Policies{Policy Number}\Machine\Scripts\Startup\?
If you run a default instances of PowerShell it always starts in a certain directory. It depends on how and who it is started by. For instance an administrative PowerShell usually starts in C:\Windows\System32. If you use any paths that are not absolute they're applied relative to this directory.
To work around this you need to change the directory it's using. For instance by using cd to change the directory. My guess would be that your script Remove-PreviousOfficeInstalls.ps1 contains a relative call to the VBS.
An easy fix would be to run a script block instead of a single command and just cd to \\domain\SYSVOL\domain\Policies\'{Policy Number}'\Machine\Scripts\Startup\ prior to running the ps1.
The PowerShell help you can view by following [this] link or running powershell -h has the following information in regards to using the -Command switch.
...
Script blocks must be enclosed in braces ({}). You can specify a script block only when running PowerShell.exe in PowerShell. If you want to use a script block when running from another shell you must use the format:
"& {}"
...
The other important parameter for your use case is -File.
Runs the specified script in the local scope ("dot-sourced"), so that the functions and variables that the script creates are available in the current session. Enter the script file path and any parameters.
...
Your batch contains the following line:
powershell.exe -executionpolicy bypass -Command "\\domain\SYSVOL\domain\Policies\'{Policy Number}'\Machine\Scripts\Startup\Remove-PreviousOfficeInstalls.ps1 -Remove2016Installs $true"
What you do is run a single command to invoke a script with a parameter. The problems is that said script checks its locations based on certain function and with your invocation that location is wrongly detected.
There are multiple ways to fix this. One would be to change the directory before invoking the script. To do this you'd need to use a script block as the parameter for -Command. An example for this would be:
powershell.exe -Command "& {Write-Output 'Hello'; Write-Output 'World';}"
As you can see there are two independent Write-Output commands being run. You'd change this to a cd \\domain\SYSVOL\domain\Policies\'{Policy Number}'\Machine\Scripts\Startup\ and the invocation of your script. As a bonus you wouldn't need to put the whole path in front of the script anymore.
The other option would be to run powershell -File with your current invocation of the script. That should also mean that the script is read from the file and the corresponding parameters are populated accordingly.
If neither of these options work you will have to check what $PSScriptRoot is being populated with and/or what the return of (Get-Item -Path ".\").FullName is as those are the two commands used to determine the location of the script that's being executed. To do this you could use a script block.
thanks for your help regarding this. My resolution was to use the following bat command:
`start "----NOTICE----" cmd.exe /t:ec /Q /k "echo OFFICE 365 IS BEING INSTALLED. THIS WINDOW WILL CLOSE WHEN COMPLETE&&prompt $h"`
#echo off
pushd "%~dp0"
powershell.exe -executionpolicy bypass -File Remove-PreviousOfficeInstalls.ps1 -Remove2016Installs
popd
reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set OS=32BIT || set OS=64BIT
if %OS%==32BIT "\\Server\Folder\Folder\setup.exe" /configure "\\Server\Folder\Folder\configuration-Office365-x86.xml"
if %OS%==64BIT "\\Server\Folder\Folder\setup.exe" /configure "\\Server\Folder\Folder\configuration-Office365-x64.xml"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ ----NOTICE----"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ Administrator: ----NOTICE----"
echo %date% %time% Setup ended with error code %errorlevel%. >> %LogLocation%\%computername%.txt
I had to amend the Remove-PreviousOfficeInstalls powershell script to include the switch command:
[Parameter(ValueFromPipelineByPropertyName=$true)]
[switch]$Remove2016Installs = $false,
This then did exactly what I was after, it detected the current version of Office, removed it and installed the correct bit version of Office 365 for that PC\Laptop.
Thanks for all your help

How can I make PowerShell wait until a command is complete before proceeding?

I'm using the following line to uninstall office 2007 based on its Product ID
Start-Process C:\Windows\System32\msiexec.exe -ArgumentList "/uninstall {90120000-0030-0000-0000-0000000FF1CE}"
I'd like to force a reboot after the uninstall is complete however using -Wait or piping the results to Out-Null don't wait until the uninstall is complete before processing the next line which is a restart. I've also tried using cmd to uninstall but with the same result.
cmd /c "msiexec.exe /uninstall {90120000-0030-0000-0000-0000000FF1CE}"
Is there any way to force powershell to wait until the uninstall is complete before processing the Restart-Computer command? I was thinking possibly writing something that detects when the setup.exe process stops before proceeding to the restart?
Start-Process has a wait parameter:
Start-Process C:\Windows\System32\msiexec.exe -ArgumentList "/uninstall {90120000-0030-0000-0000-0000000FF1CE}" -wait
The solution to restart after an misexec.exe uninstallation is to add the /forcerestart parameter to the msiexec call instead of trying to restart in powershell (Credits to Matt):
Start-Process C:\Windows\System32\msiexec.exe -ArgumentList #("/uninstall {90120000-0030-0000-0000-0000000FF1CE}", "/forcerestart")
The simplest workaround: pipe the result. This will force a wait until the process is finished.
No need for start-process or cmd.
You could pipe to out-default, out-file, out-null or out-host, depending on how you want to handle the output. (If you don't care about the output, simply use out-null.)
& msiexec.exe /uninstall "{90120000-0030-0000-0000-0000000FF1CE}" | Out-Null
My suggestion for this is to actually get the Office Removal Tool from Microsoft and extract the VBS script from it. Run that in a Start-Process with the -wait argument, and reboot afterwards. It will not only attempt to gracefully remove Office with msiexec as you are doing, it will also go back and clean up any straggling files or registry entries in case the application is corrupt and will not uninstall nicely.

Advanced installer Setup.exe - command line switch to uninstall package

Using Advanced installer, I've created a package resulting in an EXE (there are deployment features that couldnt be contained within a plain MSI file).
Now, while Advanced Installer allows me to pass through command line parameters to the underlying MSI, I have no idea what parameters to pass to force the package to uninstall.
For example, the following parameters logs the setup events and instructs the underlying MSI to run passively and log its own actions.
"c:\MySetup.exe" /exelog "c:\log.txt" /passive /log "c:\msilog.txt"
The resulting commands that AdvancedInstaller executes is ultimately
msiexec.exe /i [path to extracted msi] /passive /log "c:\msilog.txt"
But try as I might, I cannot figure out how to have AdvancedInstaller launch msiexec with the /uninstall or the /x switch. For example:
"c:\MySetup.exe" /exelog "c:\log.txt" /x /log "c:\msilog.txt"
results in
msiexec.exe /i [path to extracted msi] /x /passive /log "c:\msilog.txt"
which of course fails because the /x is in the wrong place (should be in place of the /i).
What switches/parameters is the Advanced Installer exe needing?
Ok, it was buried a little obscurely in the documentation:
All 'pre-path-to-msi' parameters follow a "[option] // [optional parameters]" pattern
The following will instruct the Advanced Installer EXE bootstrap to fire off the MSI as uninstall.
UPDATED:
"c:\MySetup.exe" /exelog "c:\log.txt" /x // /log "c:\msilog.txt"
You can use the // marker, for example:
"c:\MySetup.exe" /exelog "c:\log.txt" /x // /log "c:\msilog.txt"
This marker is used to replace the msiexec command line. You can read about it in the user guide: http://www.advancedinstaller.com/user-guide/exe-setup-file.html

Windows batch file does not wait for commands to complete

I have a batch file, which exists as soon as start it (run as admin) and does not execute commands that are in it, but if I specify it at the command-line, it runs fine and executes all commands.
Here's what's in it:
start /wait msiexec /x SetupServices.msi /qn /l* "SetupServices.uninstall.log"
start /wait msiexec /i SetupServices.msi /qn /l* "SetupServices.install.log"
(Corrected answer)
First, if you start .exe files in a batch, it is safer, to prefix it with "call".
Sometimes this is necessary to assure, that the batch is waiting to complete.
Using "start" is another possibility, but for this simple usecase not necessary.
You write that the commands are not executed. So, obviously, you have another problem, instead of the "does not wait.. to complete" problem.
Taking a look of your newly provided example, this is the case. In admin mode, you have to provide a full path. With my small trick below ("%~dp0", including already backslash), you can still use the current directory in batchfiles.
Most times, if such a problem occurs with admin rights, this is a problem of the "current directory" path. A batch file with admin rights is not using it in the same way as we were used to, it does not start in it's own directory (but in System32 mostly). Not relying on the CD is an important matter of writing bullet-proof batch files.
A good example batch , combining other answers here, and solving a number of possible problems in your case is:
call msiexec /i "%~dp0MySetup.msi" /qb /L*v "%~dp0MySetup.log"
echo Returncode: %ERRORLEVEL%
pause
It uses the current directory correctly, and assumes an install commandline including a logfile (works only, if you have write access in the current directory, if not specify a path for the logfile with write access like "%TEMP%\MySetup.log".
Attention: Remember to really start the batch file with admin rights (right mouse menu or opening an admin command shell before:)
Coming back to this question I think the "correct way" to do it is via PowerShell
Start-Process -Wait -FilePath msiexec -ArgumentList /i, "setup.msi", /qn, /l*v, "install.log"
Or just prefix it with PowerShell; to invoke directly from CMD
PowerShell; Start-Process -Wait -FilePath msiexec -ArgumentList /i, "setup.msi", /qn, /l*v, "install.log"
No hacks and no tricks :-)
Try taking the start /wait out for the msiexec lines, if that doesn't work create two more bat files one called uninstall.bat the other install.bat and use call to execute them in series.
It goes a little beyond the question, but an extension to my answer concerning handling the current directory: Here is my recommended beginning for every batch file conserving it's own path. The specialty is that it also works for UNC paths. "Pushd" automatically creates a new drive letter, if necessary (assumed, you have one free of 26). Of course you can use "popd" also at the end of the batch file instead immediately, but stable commands do not rely on the current directory as I mentioned, so it is better to always provide full paths.
#echo off
cls
pushd %~dp0
popd
set MYDIR=%CD%
echo Directory of this batch fil: %MYDIR%
You can then add the msi lines from the other answer like this:
call msiexec /i "%MYDIR%\MySetup.msi" /qb /L*v "%MYDIR%\MySetup.log"
echo Returncode: %ERRORLEVEL%
pause
(Remark: For the logfile path of course you are free, it has not to be necessarily in the same directory. But good for testing/debugging. In every case you have to have write access to the directory/file you are giving MSI with.)
While for normal MSI files it is not always necessary to start the batch with admin rights from the beginning, this technique is by far more safe (to start the MSI like already with admin rights) than too rely on the MSI UAC coming later (maybe).
And it works with msiexec ... /qn too, which is important (silent installs).
Add pause statement to the end of the batch, this will prevent the console window from closing and you will be able to see error messages if any. Errors could be the reason why it exits without actually running anything. What kind of error it may be? SetupServices.msi is not found — that's what comes to my mind.
Needs the "Window Title" if you use Parameter
start /wait "Window Title" "MsiExec.exe" /i SetupServices.msi /qn /l* SetupServices.uninstall.log
start /wait "Window Title" "MsiExec.exe" /i SetupServices.msi /qn /l* SetupServices.install.log