Test-Path in local directory - powershell

Consider this simple path validation:
if ((Test-Path $_) -and ($_ -like "*.msi")) {
$true
}
else {
Throw "Specify correct path to installer."
}
When passing a path e.g. "C:\Scripts\installer.msi" the validation works as expected.
But when executing in same directory as the installer, passing this argument as the path: .\Installer.msi
Validation is still True but breaks the installer.
How can I fix this?

Assuming that you're using msiexec.exe to run this installer, you have to pass the full path to that msi file in order for it to work.
So you're probably calling msiexec like this:
msiexec /i $_
when really you would have to do this:
msiexec /i "$((Get-Item $_).FullName)"
You might also want to look into the MSI PowerShell module. It makes working with msi files a bit nicer within PowerShell.

Related

PowerShell Install MSI with Property File

I looking to replace our old command files with PowerShell file to install msi on a number of servers with a view of automating installations remotely.
What I am trying to do, and this is how can you get PowerShell to install an msi where the installation relies on a details coming from a property file as one argument along with logging out the installation as a log file and setting a new user name and password?
Currently our old cmd file looks a bit like this:
msiexec /I mymsi.msu /quiet /lv mymsi.log USERNAME=AName PASSWORD=APassword CONFIG="C:\Some.properties.env"
What I want to do is recreate this but in PowerShell, but I have not been able to find an example that works.
I'd suggest using Start-Process and Splatting to make it readable and functional:
$startProcessParams = #{
'FilePath' = "$Env:SystemRoot\System32\MSIEXEC.exe"
'ArgumentList' = #(
'/i', 'mymsi.msu'
'/quiet'
'/lv', 'mymsi.log'
'USERNAME=AName', 'PASSWORD=APassword', 'CONFIG="C:\Some.properties.env"'
)
'WorkingDirectory' = $PSScriptRoot
'Wait' = $true
'PassThru' = $true
}
$process = Start-Process #startProcessParams
return $process.ExitCode
Be sure to set WorkingDirectory to where your .msu file is living.
about_Splatting
Start-Process

Run a jar file from powershell

I want to run a jar file from powershell. Till now I have been able to find this:
Start-Process -FilePath java -ArgumentList '-jar Upload_Download.jar FilePickupPath= $pickuppath FileDownloadPath= $download' -PassThru -RedirectStandardError E:\stderr.txt
Some how this is not working. Any suggestions?
Powershell has multiple string quotation characters that behave different ways. The double quote " allows evaluations within the string whilst the single quote ' doesn't.
As a practical example:
$foo=42
write-host "The foo: $foo"
# Prints The foo: 42
write-host 'The foo: $foo'
# Prints The foo: $foo
The command uses single quote like so, (backtick is used to split the code into screen friendly format)
Start-Process -FilePath java `
-ArgumentList '-jar Upload_Download.jar FilePickupPath= $pickuppath fileDownloadPath= $download' `
-PassThru -RedirectStandardError E:\stderr.txt
This will pass literally $pickuppath and $download. The intention is likely to pass $pickuppath and $download's values.
In order to resolve the issue, use double quotes.
Passing the arguments is often a problem so I use an array,
also avoid long lines by using parameter splatting.
if(-not $env:JAVA_HOME)
{
Write-Error "JAVA_HOME not set"
break
}
$params = #{
FilePath = [string]::Format("{0}\bin\java.exe",$env:JAVA_HOME)
WorkingDirectory = "D:\SDL\Web\live\discovery\config\"
ArgumentList = #("-jar", "discovery-registration.jar", "update")
RedirectStandardError = "c:\temp\JavaError.txt"
PassThru = $true
Wait = $true
}
$p = Start-Process #params
if($p.ExitCode -eq 0)
{
Write-Output "Discovery Registration complete"
}
else
{
Write-Output "Discovery Registration failed"
}
My problem was a little different because there are two jre versions and one jdk installed and powershell was always picking up jre based java executable and failing with the message
C:\Program Files\Java\jre1.8.0_73 is not a valid JDK Java Home.
Although jdk was installed and JAVA_HOME variable was set and out of variable displays the jdk path correctly.
> $env:JAVA_HOME
C:\Program Files\Java\jdk1.8.0_73
I stored the jdk java executable path in a variable, navigated to the directory containing jar file for Web logic server and invoked the installation program using following code.
$java="C:\Program Files\Java\jdk1.8.0_73\bin\java"
Start-Process -FilePath $java -ArgumentList '-jar fmw_12.2.1.3.0_infrastructure_generic.jar'
The installation program opened after few seconds. I have omitted -RedirectStandardErro switch to keep the command small and simple although i did use it to catch any error in the first place.
I tried using variable $fmw to store the jar file fmw_12.2.1.3.0_infrastructure_generic.jar but it did not work. Unsuccessful code is as follows
$fmw="fmw_12.2.1.3.0_infrastructure_generic.jar"
Start-Process -FilePath $java -ArgumentList '-jar $fmw'
I hope this would help someone and somebody might provide me a better and clean method to call jar files in powershell.
When you run the command after defining the $java variable, make sure to put the full path of the jar in double quotes inside the single quotes. This is to fix the error that occurs when the path of the file has a space in it.
The other option is to cd into the folder with the jar and run the command that way, as suggested in the above answer. This also works and is helpful with simple paths.
Also when defining the java variable, try putting the path to the executable instead of just the folder. This prevents execution conflicts if paths are similar.

Powershell - Check for updates to script prior to running

I was wondering if anyone knew of a way to have a powershell script check for updates to itself prior to running.
I have a script that I am going to be dispatching out to multiple computers, and don't want to have to redeploy it to each computer each time a change in the script is made. I would like to have it check a certain location to see if there is a newer version of itself (and update itself if needed).
I can't seem to think of a way to do it. Please let me know if anyone can help out. Thanks.
Well, one way maybe to create a simple batch file that runs your actual script and the first line in that batch file may be to check for existence of a ps1 in your update folder. If there is one, it can copy it down first, and then start your powershell script
Eg. whenever there is an update, you put your 'Mypowershellscript.ps1' script in c:\temp\update\ folder
and let's assume your script will be running from
c:\temp\myscriptfolder\
then you can create batch file like this
if NOT exist C:\temp\update\mypowershelscript.ps1 goto :end
copy /Y c:\temp\update\MyPowerShellScript.ps1 c:\temp\MyScriptFolder\
:END
%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe -nologo -noprofile -file "c:\temp\myscriptfolder\mypowershellscript.ps1"
Here's a function I put together. Pass it the path of the file that might hold a newer release. This will update itself and then re-run with any arguments handed to the original script. Do this early in the process, other function results will be lost. I typically check the network is up and I can see the share holding the newer file, then run this:
function Update-Myself
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$SourcePath
)
#Check that the file we're comparing against exists
if (Test-Path $SourcePath)
{
#The path of THIS script
$CurrentScript = $MyInvocation.ScriptName
if (!($SourcePath -eq $CurrentScript ))
{
if ($(Get-Item $SourcePath).LastWriteTimeUtc -gt $(Get-Item $CurrentScript ).LastWriteTimeUtc)
{
write-host "Updating..."
Copy-Item $SourcePath $CurrentScript
#If the script was updated, run it with orginal parameters
&$CurrentScript $script:args
exit
}
}
}
write-host "No update required"
}
Update-Myself "\\path\to\newest\release\of\file.ps1"

Powershell uninstall program with msiexec

I've run into a problem getting msiexec to remove java with Powershell. I've output my resultant command to the screen and pasted it into a batch file and it runs great. But when it's executed via Powershell it fails saying the "package cannot be found". Can anyone spot what I might be doing wrong? I've looked up and down google and tried a few different ways of executing the command w/o success and with the same result.
cls
$java = Get-WmiObject -Class win32_product | where { $_.Name -like "*Java*"}
$msiexec = "c:\windows\system32\msiexec.exe";
#$msiexecargs = '/x:"$app.LocalPackage" /qr'
$msiexecargs = '/uninstall "$app.IdentifyingNumber" /qr /norestart'
if ($java -ne $null)
{
foreach ($app in $java)
{
write-host $app.LocalPackage
write-host $app.IdentifyingNumber
#&cmd /c "msiexec /uninstall $app.IdentifyingNumber /passive"
#Start-Process -FilePath $msiexec -Arg $msiexecargs -Wait -Passthru
[Diagnostics.Process]::Start($msiexec, $msiexecargs);
}
}
else { Write-Host "nothing to see here..." }
Write-Host "check end"
The goal is to use the Windows 7 logon script to remove all versions of Java on end-user systems and then install the latest. I prefer to make it all Powershell, but if I can't get this working I'll just use a batch file hard coded with the uninstall GUID's
The write-host statements are all for the purpose of debugging, I'm just interested in the execution of msiexec in some variation of this format: msiexec /x {GUID} /passive /norestart
The error I get is:
"This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package."
I know it works on its own, just not in this script...so I'm thinking it's a syntax thing.
If you have any questions let me know.
First you have to know the difference between this:
"$app.IdentifyingNumber"
and this
"$($app.IdentifyingNumber)"
So I think you wanted to use the latter (the code is a little bit confusing because of the commented lines):
&cmd /c "msiexec /uninstall $($app.IdentifyingNumber) /passive"

How to reload user profile from script file in PowerShell

I want to reload my user profile from a script file. I thought that dot sourcing it from within the script file would do the trick, but it doesn't work:
# file.ps1
. $PROFILE
However, it does work if I dot source it from PowerShell's interpreter.
Why do I want to do this?
I run this script every time I update my profile and want to test it, so I'd like to avoid having to restart PowerShell to refresh the environment.
If you want to globally refresh your profile from a script, you will have to run that script "dot-sourced".
When you run your script, all the profile script runs in a "script" scope and will not modify your "global" scope.
In order for a script to modify your global scope, it needs to be "dot-source" or preceded with a period.
. ./yourrestartscript.ps1
where you have your profile script "dot-sourced" inside of "yourrestartscript.ps1". What you are actually doing is telling "yourrestartscript" to run in the current scope and inside that script, you are telling the $profile script to run in the script's scope. Since the script's scope is the global scope, any variables set or commands in your profile will happen in the global scope.
That doesn't buy you much advantage over running
. $profile
So, the approach that you marked as the answer may work inside the Powershell command prompt, but it doesn't work inside PowerShell ISE (which, to me, provides a superior PowerShell session) and probably won't work right in other PowerShell environments.
Here's a script that I have been using for a while, and it has worked very well for me in every environment. I simply put this function into my Profile.ps1 at ~\Documents\WindowsPowerShell, and whenever I want to reload my profile, I dot-source the function, i.e.
. Reload-Profile
Here's the function:
function Reload-Profile {
#(
$Profile.AllUsersAllHosts,
$Profile.AllUsersCurrentHost,
$Profile.CurrentUserAllHosts,
$Profile.CurrentUserCurrentHost
) | % {
if(Test-Path $_){
Write-Verbose "Running $_"
. $_
}
}
}
& $profile
works to reload the profile.
If your profile sets aliases or executes imports which fail then you will see errors because they were already set in the previous loading of the profile.
Why are you trying to do this?
Because it is likely to create duplicates (appends to $env:path) and problems with setting constant/readonly objects causing errors.
There was a thread on this topic recently on microsoft.public.windows.powershell.
If you are trying to reset the state of the session there is no way to do this, even using an inner scope ($host.EnterNestedPrompt()) because of the ability to set variables/aliases/... at "all scope".
I found this workaround:
#some-script.ps1
#restart profile (open new powershell session)
cmd.exe /c start powershell.exe -c { Set-Location $PWD } -NoExit
Stop-Process -Id $PID
A more elaborated version:
#publish.ps1
# Copy profile files to PowerShell user profile folder and restart PowerShell
# to reflect changes. Try to start from .lnk in the Start Menu or
# fallback to cmd.exe.
# We try the .lnk first because it can have environmental data attached
# to it like fonts, colors, etc.
[System.Reflection.Assembly]::LoadWithPartialName("System.Diagnostics")
$dest = Split-Path $PROFILE -Parent
Copy-Item "*.ps1" $dest -Confirm -Exclude "publish.ps1"
# 1) Get .lnk to PowerShell
# Locale's Start Menu name?...
$SM = [System.Environment+SpecialFolder]::StartMenu
$CurrentUserStartMenuPath = $([System.Environment]::GetFolderPath($SM))
$StartMenuName = Split-Path $CurrentUserStartMenuPath -Leaf
# Common Start Menu path?...
$CAD = [System.Environment+SpecialFolder]::CommonApplicationData
$allUsersPath = Split-Path $([System.Environment]::GetFolderPath($CAD)) -Parent
$AllUsersStartMenuPath = Join-Path $allUsersPath $StartMenuName
$PSLnkPath = #(Get-ChildItem $AllUsersStartMenuPath, $CurrentUserStartMenuPath `
-Recurse -Include "Windows PowerShell.lnk")
# 2) Restart...
# Is PowerShell available in PATH?
if ( Get-Command "powershell.exe" -ErrorAction SilentlyContinue ) {
if ($PSLnkPath) {
$pi = New-Object "System.Diagnostics.ProcessStartInfo"
$pi.FileName = $PSLnkPath[0]
$pi.UseShellExecute = $true
# See "powershell -help" for info on -Command
$pi.Arguments = "-NoExit -Command Set-Location $PWD"
[System.Diagnostics.Process]::Start($pi)
}
else {
# See "powershell -help" for info on -Command
cmd.exe /c start powershell.exe -Command { Set-Location $PWD } -NoExit
}
}
else {
Write-Host -ForegroundColor RED "Powershell not available in PATH."
}
# Let's clean up after ourselves...
Stop-Process -Id $PID
This is only a refinement of the two line script in guillermooo's answer above, which did not get the new PowerShell window into the correct directory for me. I believe this is because $PWD is evaluated in the new PowerShell window's context, which is not the value we want set-location to process.
function Restart-Ps {
$cline = "`"/c start powershell.exe -noexit -c `"Set-Location '{0}'" -f $PWD.path
cmd $cline
Stop-Process -Id $PID
}
By rights it shouldn't work, as the command line it spits out is malformed, but it seems to do the job and that's good enough for me.
since I stumbled onto this several years later, I thought to add that you can use the invocation operator: & to load your profile with the default variable to your profile: $profile.
so, if your session somehow fails to load your profile (happens to me with cmder/conemu) just type:
& $profile
I used this to troubleshoot what profile was taking forever to load.
Start Run:
powershell_ise -noprofile
Then i ran this:
function Reload-Profile {
#(
$Profile.AllUsersAllHosts,
$Profile.AllUsersCurrentHost,
$Profile.CurrentUserAllHosts,
$Profile.CurrentUserCurrentHost
) | % {
if(Test-Path $_){
Write-Verbose "Running $_"
$measure = Measure-Command {. $_}
"$($measure.TotalSeconds) for $_"
}
}
}
. Reload-Profile
Thank you #Winston Fassett for getting me closer to finding my issue.
Pseudo Alias (simulate keys)
If you just want a function to work like an alias in the console, just simulate the key presses to get around having to use the dot source.
# when "reload" is typed in the terminal, the profile is reloaded
# use sendkeys to send the enter key to the terminal
function reload {
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait(". $")
[System.Windows.Forms.SendKeys]::SendWait("PROFILE")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
}
screenshot of it working