PowerShell Install MSI with Property File - powershell

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

Related

Run PowerShell Script within Windows 10 service to convert docx to pdf

I have a powershell script to convert docx to pdf. when running in a user session on windows 10 it runs without problems. However, I need to run it within a windows service.
$docx_filename = 'C:\temp\doc2pdf.docx'
$pdf_filename = 'C:\temp\doc2pdf1.pdf'
$word_app = New-Object -ComObject Word.Application
$document = $word_app.Documents.Open($docx_filename)
$document.SaveAs([ref] $pdf_filename, [ref] 17)
$document.Close()
$word_app.Quit()
The script is called with the following command
powershell.exe -executionpolicy Bypass -NoProfile -NoLogo -NonInteractive -WindowStyle Hidden -File -File "docx2pdf.ps1" -docxFilename "docx2pdf.docx" -pdfFilename "docx2pdf.pdf"
With PSSet-Debug -Trace 2 the following is logged
DEBUG: 14+ >>>> $word_app = New-Object -ComObject Word.Application
DEBUG: ! SET $word_app = 'Microsoft.Office.Interop.Word.ApplicationClass'.
DEBUG: 16+ >>>> $document = $word_app.Documents.Open($docxFilename)
How to get this to work within Windows 10 service?
thanks
You have to create a batch file which can trigger a powershell script like this:
#echo off
Powershell.exe set-executionpolicy remotesigned -File ScriptPath
Save it as "startps.bat"
Then you can use the sc command to create a windows service by mentioning this batch file path like this:
Sc create PSScriptService Displayname= "What you like" binpath= "Startps.bat" start= auto
You must set an account on the service's Log On tab. Be sure to specify an account where you can log in interactively and run the Powershell script successfully (i.e. where you have installed Word).
But even with that change, you may still run into trouble because Office is not entirely suitable for operation in a Windows Service. Some things work, but some things don't. Try it and let us know how you get on.

Run a powershell command with arguments from the Run line

I am still learning PowerShell and the Windows run line seems to make it even harder
Question: How can I do this directly from the run line (if possible an admin powershell) but I can deal with clicking yes after the the download... it just slows down the process
wget 'https://MYSERVER/MYFILE.MSI' -O PROGRAM.msi; start PROGRAM.msi /qn
This works great when powershell is already open as admin, also works when powershell is open as normal user, but I have to wait for the program to be downloaded to click yes instead of clicking yes to the admin powershell and let the rest autoinstall.
I tried
Powershell -Command 'wget...
but not working
Point of note: wget in PoSH is an alias
Get-Alias -Name wget
CommandType Name
Alias wget -> Invoke-WebRequest
... and with the way you are doing this, you are using wget.exe, not the above.
So, you can use wget.exe, but you have to specify the full UNC to wget.exe if it is not in your system path. That .exe is a must.
Or you need to remove the aliases
Remove-Item Alias:WGet
To download files from the web, look at the Invoke-WebRequest examples
Get-Help -Name 'Invoke-WebRequest' -Examples
Or write your own function using .Net, someting like the below and put it in your PoSH user profile
Function New-ToolDownloadInstall ($url)
{
# Set the webclient
$webclient = New-Object System.Net.WebClient
# Extract the filename from the URL and Download
$filename = [System.IO.Path]::GetFileName($url)
$file = "$env:USERPROFILE\Downloads\$filename"
$webclient.DownloadFile($url,$file)
# Remove the web ADS
Unblock-File -Path $file
# Install the file
Start-Process $file -NoNewWindow -wait
}
# Use the function
New-ToolDownloadInstall -url 'https://download.microsoft.com/download/5/0/1/5017D39B-8E29-48C8-91A8-8D0E4968E6D4/en/msoidcli_64.msi'
Here is another example
Download files from websites programatically via powershell
https://gallery.technet.microsoft.com/scriptcenter/files-from-websites-4a181ff3
Also, some DOS level command have to be called this way, if you are in the PowerSHell_ISE.exe.
Start-Process "$PSHOME\powershell.exe" -ArgumentList "-NoExit","-Command &{ wget.exe 'https://MYSERVER/MYFILE.MSI' -O PROGRAM.msi; start PROGRAM.msi /qn }"
See more details here:
https://blogs.technet.microsoft.com/josebda/2012/03/03/using-windows-powershell-to-run-old-command-line-tools-and-their-weirdest-parameters

Loading .exe using PowerShell's Invoke-Command but exe does not read .config file

I am currently running an exe using the command below. The .exe runs fine, however, it doesn't read a config file associate with the program. I used this solution and it works. However, I would like to add something like "-WorkingDirectory" but using invoke-command. My end goal is to be able to run the exe as part of the tfs build using this script.
$file = 'C:\test.exe'
Invoke-Command -ScriptBlock { Param($myarg) & $file }
Pass the directory as an argument and use Set-Location to change the working directory inside the scriptblock:
$file = 'C:\test.exe'
Invoke-Command -ScriptBlock {
Param($exe, $wd)
Set-Location $wd
& $exe
} -ArgumentList $file, 'C:\working\directory'
With that said, there's no point in using Invoke-Command unless you want to run something on a different computer, with different credentials, or as a background job (or any combination of the aforementioned). Otherwise you could just as well change the directory and run the command directly:
Set-Location 'C:\working\directory'
& $file

Powershell file download issue

I'm trying to download the PuTTY executable using PowerShell, but could not get the file on temp path.
My script:
$Url = "https://the.earth.li/~sgtatham/putty/latest/x86/putty.exe"
$Path = "C:%homepath%\AppData\Local\Temp\putty.exe"
$Wc = New-Object System.Net.WebClient
$Wc.DownloadFileAsync($Url,$Path)
I am executing following command via CMD:
powershell.exe "-ExecutionPolicy" "RemoteSigned" "-file" "test.ps1"
You have two problems, both of which need to be corrected for your script to have a chance of working.
The command for executing a Powershell script from within CMD.EXE should not have the arguments quoted:
powershell.exe -ExecutionPolicy RemoteSigned -file test.ps1
To expand a system environment variable from within powershell, you do not surround it with % as you do in CMD. See http://ss64.com/ps/syntax-env.html for more information; assuming that the environment variable HOMEPATH exists, you would reference it in Powershell as $env:homepath, not %homepath%.
The %VAR% form is not used in powershell, this is only used in CMD. In PowerShell you need to use $env:VAR instead.
You can run Get-ChildItem Env: to get a list of all the Environmental Variables you can use.
For your script try this:
$Path = "$env:USERPROFILE\AppData\Local\Temp\putty.exe"
I've used USERPROFILE instead of HOMEPATH as this includes the drive letter so your script will still work if a different letter is used.

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