Install msi using Start-Process with /q switch? - powershell

I want to install an msi with a /q switch, I look online and the examples don't have the /q switch and I keep getting errors.
I need something like:
$WorkingDirectory = (Split-Path $myinvocation.mycommand.path -Parent)
Start-Process -FilePath msiexec /i "$WorkingDirectory\LAPS.x64.msi" -ArgumentList /q

Don't bother with Start-Process. Use the call operator:
& msiexec.exe /i "$WorkingDirectory\LAPS.x64.msi" /q

put the whole command in the brackets
Example(Python msi):
Start-Process msiexec.exe -Wait -ArgumentList "/I $($LocalPython.FullName) /passive ALLUSERS=1 ADDLOCAL=Extensions"
replace /passive with /q

Not all installers are the same. To find the installer switches for your .msi use:
.\LAPS.x64.msi /?
.\LAPS.x64.msi -?
I would also store the msi path in a variable, and use an ArrayList for the arguments, something like this has worked for me in my scripts:
# Path to .msi
$msiPath= 'C:\LAPS.x64.msi'
# Define arguments
[System.Collections.ArrayList]$arguments =
#("/i `"$msiPath`"",
"/quiet")
# Start installation
Start-Process -FilePath msiexec.exe -ArgumentList "$arguments" -Wait -NoNewWindow

$path="C:\Dat\install.msi"
$parameters="/q"
$packageinstall=(split-path $path -leaf) + ' ' + $parameters
write-host $packageinstall
$computers = get-content c:\com.txt
$computers | where{test-connection $_ -quiet -count 1} | ForEach-Object {
copy-item $path "\\$_\c$\windows\temp" -Force -Recurse
$newProc=([WMICLASS]"\\$_\root\cimv2:win32_Process").Create("C:\windows\temp\$packageinstall")
If ($newProc.ReturnValue -eq 0) {
Write-Host $_ $newProc.ProcessId
} else {
write-host $_ Process create failed with $newProc.ReturnValue
}
}

Related

How to add -OutputPath parameter to Powershell script being called from batch file?

Using a batch file to call a Powershell script (this part is working and saving the output files locally):
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File ""%~dpn0.ps1 -Update""' -Verb RunAs}"
Trying to add in a parameter to redirect the output of the script to a folder (per the code comments it can be to a local or UNC path):
-OutputPath Z:\Current
Have tried inserting it everywhere I can think of in the script, cannot get it to work; am thinking it should go in like this(?):
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File ""%~dpn0.ps1 -OutputPath Z:\Current""' -Verb RunAs}"
Here is where the output path is determined in the code:
# If -OutputPath not specified, set default
If (-not($OutputPath)) {
Switch ($OSPlatform) {
"Windows" {
$OutputPath = "$env:PUBLIC\Documents\ Compliance"
if (!(Test-Path $OutputPath)) {
$null = New-Item -Path $(Split-Path -Path $OutputPath -Parent) -Name $(Split-Path -Path $OutputPath -Leaf) -ItemType Directory
}
}
"Linux" {
$OutputPath = "/opt/STIG_Compliance"
if (!(Test-Path $OutputPath)) {
sudo mkdir $OutputPath
}
}
}
}
From what I can find online, the -OutputPath Z:\Current should come immediately after the .ps1 command, but not sure where it should be inserted amongst the other options being used in the batch file.
The script is not throwing off any errors, but is running and saving the output in the default locatin. I am not that well-versed in Powershell; am I missing something?
Thanks in advance for your help!!
It seems like you're trying to pass the OutputPath parameter to your PowerShell script through the command line argument -File. I believe the correct way to pass parameters to a PowerShell script is to include them in the argument list of the PowerShell command.
You could update your command to include the OutputPath parameter in the argument list, like this:
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File ""%~dpn0.ps1""', '-OutputPath', 'Z:\Current' -Verb RunAs}"
Also, make sure to update your PowerShell script to properly use the OutputPath parameter, like this:
# If -OutputPath not specified, set default
if (-not $OutputPath) {
switch ($OSPlatform) {
"Windows" {
$OutputPath = "$env:PUBLIC\Documents\Compliance"
if (!(Test-Path $OutputPath)) {
$null = New-Item -Path $(Split-Path -Path $OutputPath -Parent) -Name $(Split-Path -Path $OutputPath -Leaf) -ItemType Directory
}
}
"Linux" {
$OutputPath = "/opt/STIG_Compliance"
if (!(Test-Path $OutputPath)) {
sudo mkdir $OutputPath
}
}
}
}
# Redirect output to the specified output path
if ($OutputPath) {
$OutputFile = Join-Path -Path $OutputPath -ChildPath "output.txt"
Write-Output "Writing output to $OutputFile"
# Your code to generate output here
# ...
Out-File -FilePath $OutputFile -InputObject "Output data"
}
else {
# Your code to generate output here
# ...
}
Hopefully this helps ya.

Running a .bat file on several servers

I am currently trying to run a .bat file on around 150 servers. I can get the script to run through as if there's no issues - the .bat copies to the servers, however it does not seem to be executing at all.
Running on windows 2012 servers mainly.
#Variables
$servers = "D:\Apps\Davetest\servers.txt"
$computername = Get-Content $servers
$sourcefile = "D:\Apps\Davetest\test.bat"
#This section will install the software
foreach ($computer in $computername)
{
$destinationFolder = "\\$computer\C$\Temp"
<#
It will copy $sourcefile to the $destinationfolder. If the Folder does
not exist it will create it.
#>
if (!(Test-Path -path $destinationFolder))
{
New-Item $destinationFolder -Type Directory
}
Copy-Item -Path $sourcefile -Destination $destinationFolder
Invoke-Command -ComputerName $computer -ScriptBlock {Start-Process 'c:\Temp\test.bat'}
}
I am looking for it to run the .bat once it hits the servers and currently it only seems to be copying over.
That's because Start-Process immediately returns. Use the -Wait Parameter.
Start-Process -FilePath 'c:\Temp\test.bat' -NoNewWindow -Wait -PassThru
microsoft:
-PassThru
Returns a process object for each process that the cmdlet started. By default, this cmdlet does not generate any output.
-Wait
Indicates that this cmdlet waits for the specified process and its descendants to complete before accepting more input. This parameter suppresses the command prompt or retains the window until the processes finish.
-PassThru returns you a process object, where you can check the ExitCode parameter:
$p = Start-Process -FilePath your_command -ArgumentList "arg1", "arg" -NoNewWindow -Wait -PassThru
if ($p.ExitCode -ne 0) {
throw "Failed to clone $buildItemName from $buildItemUrl to $($tmpDirectory.FullName)"
}
As an alternative to Start-Process you could also use Invoke-Expression which will return you the stdout of the console.
To check if Invoke-Expression was successful you can use:
$output = Invoke-Expression $command
if ((-not $?) -or ($LASTEXITCODE -ne 0)) {
throw "invoke-expression failed for command $command. Command output: $output"
}

Install Chrome on Windows with a .bat file using PowerShell

I was searching around and found a few hints but a few detail pieces are missing.
Here is what I have:
install-chrome.bat
PowerShell -NoProfile -Command "&{Start-Process PowerShell -ArgumentList '-NoProfile -File install-chrome.ps1' -Verb RunAs}"
install-chrome.ps1
$client = New-Object System.Net.WebClient;
$client.DownloadFile("https://dl.google.com/chrome/install/ChromeStandaloneSetup64.exe", ".\ChromeStandaloneSetup64.exe");
.\ChromeStandaloneSetup64.exe /silent /install ;
Two things are not working as expected:
I still get a UAC popup even though the posts I found state that the above should start PowerShell in Admin mode.
I was expecting .\ would download the .exe to the directory the .ps1 and .bat scripts are located.
Any hints on how to solve this?
EDIT:
Thanks to the reply from #TheIncorrigible1 I managed to solve the second part. Both options work more or less (it downloads it, but the installation throws an error locally) when I execute them directly in PowerShell:
< V3
$PSScriptRoot = Split-Path -Parent -Path $script:MyInvocation.MyCommand.Path
$uri = "https://dl.google.com/chrome/install/ChromeStandaloneSetup64.exe"
$path = "$PSScriptRoot\ChromeStandaloneSetup64.exe"
$client = New-Object System.Net.WebClient
$client.DownloadFile($uri, $path)
& $path /install
V3+
$uri = "https://dl.google.com/chrome/install/ChromeStandaloneSetup64.exe"
$path = "$PSScriptRoot\ChromeStandaloneSetup64.exe"
Invoke-WebRequest -Uri $uri -OutFile $path
& $path /install
But the batch still throws errors:
At line:1 char:62
+ ... tart-Process PowerShell -Verb RunAs -ArgumentList -NoProfile, -File, ...
+ ~
Missing argument in parameter list.
At line:1 char:69
+ ... ocess PowerShell -Verb RunAs -ArgumentList -NoProfile, -File, 'C:\Pro ...
+ ~
Missing argument in parameter list.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingArgument
Two things-
You don't need to wrap your batch command to powershell in a scriptblock and -ArgumentList expects an array of string arguments:
powershell.exe -NoProfile -Command "Start-Process -FilePath powershell.exe -ArgumentList #('-NoProfile', '-File', '%~dp0install-chrome.ps1') -Verb RunAs"
There's an automatic variable, $PSScriptRoot, to determine where your root directory is:
$uri = 'https://dl.google.com/chrome/install/ChromeStandaloneSetup64.exe'
if (-not $PSScriptRoot) {
$PSScriptRoot = Split-Path -Parent -Path $script:MyInvocation.MyCommand.Definition
}
$outFile = "$PSScriptRoot\ChromeStandaloneSetup64.exe"
if ($PSVersionTable.PSVersion.Major -lt 3) {
(New-Object -TypeName System.Net.WebClient).DownloadFile($uri, $outFile)
}
else {
Invoke-WebRequest -Uri $uri -OutFile $outFile
}
& $outFile /silent /install
Here you go:
$Path = $env:TEMP; $Installer = "chrome_installer.exe"; Invoke-WebRequest "http://dl.google.com/chrome/install/375.126/chrome_installer.exe" -OutFile $Path\$Installer; Start-Process -FilePath $Path\$Installer -Args "/silent /install" -Verb RunAs -Wait; Remove-Item $Path\$Installer

Start process with path space

I'm having trouble with path spacing. This works:
$mediaPath = 'C:\Scripts'
$installerPath = Join-Path $mediaPath -ChildPath 'test.msi'
Start-Process -FilePath msiexec.exe -ArgumentList "/i $installerPath /quiet" -Wait -NoNewWindow
This does not:
$mediaPath = 'C:\Scripts\Directory with a space\foo'
How to handle spaces in the path when passing arguments to Start-Process?
You're passing the arguments improperly.
$media = 'C:\Scripts\test.msi'
Start-Process -FilePath msiexec -ArgumentList #('/i',"`"$media`"",'/quiet') -Wait -NoNewWindow

How to capture windows command result from Start-Process?

$features = Start-Process powershell -Verb runAs -ArgumentList "Get-WindowsOptionalFeature –Online"
$features
How can I get the result back into my $feature variable?
Quick & dirty workaround: you can use temporary clixml file to store results of Get-WindowsOptionalFeature cmdlet:
$tempFile = [System.IO.Path]::GetTempFileName()
try
{
Start-Process powershell -Wait -Verb runAs -ArgumentList "-Command Get-WindowsOptionalFeature -Online | Export-Clixml -Path $tempFile"
$features = Import-Clixml -Path $tempFile
# Use $features
}
finally
{
if (Test-Path $tempFile)
{
Remove-Item -Path $tempFile -Force -ErrorAction Ignore
}
}