Executing Powershell from Command Line - powershell

I've spent the last week developing a Powershell script. It now works ok when I start it from a Powershell window. However, I need the script to be called from TFS build. I've tried using the syntax Powershell & 'script' from a TFS Build InvokeProcess control but nothing seems to happen. So, I've gone back to basics and created the following script:
Write-Host " Write-Host line"
Write-eventlog -logname "Application" -source "BizTalkDeployment" -eventId "01" -entrytype "Information" -message "test"
I've saved the script as c:\temp\ps_test.ps1. I've opened a command window as admin and tried the following:
powershell & 'c:\temp\ps_test.ps1"
This puts the command line into powershell mode and I get:
PS c:\>
But nothing else happens.
At the end of my TFS build I have an InvokeProcess control that uses "Powershell" for its FileName property and then the following arguments:
String.Format(" ""& '{0}' '{1}' '{2}' '{3}' '{4}' '{5}' '{6}' '{7}' '{8}' '{9}' "" ", DeploymentScriptFileName, IO.Path.GetDirectoryName(DeploymentScriptFileName), "ExecuteBizTalkAppMSI.ps1", "bin\debug\x.Int.MIS-3.0.0.msi", "x.Int.MIS.Deployment.btdfproj", TargetServerPath, "d-vasbiz01", "c:\biztalkdeployment", "C:\Program Files (x86)\x.Int.MIS for BizTalk 2010\3.0", "BTSSvc*MIS*")
The build script runs ok but the InvokeProcess control seems to do nothing - just like my experience with the command line.
Can anyone see where I'm going wrong please?

Either
powershell -file C:\temp\ps_test.ps1
or
powershell "&{ <# code here #> }"

Related

msiexec Powershell silent install

I am searching for a Powershell Script which allows me to silent install a msi file.
We have over 25000 PCs so i have to do that with a script.
Unfortunately at the moment a window popping up (Windows Installer) after the execution which shows the parameter of a msi file. Nothing more, no other "error messages" are popping up.
The first thing the Script should do is to check if the PC is a Desktop or Mobile Device.
If its a Desktop device he should write in a file "Desktop Configuration was used".
In the same time the msi installer should start with some parameter.
If its a Laptop the procedure should be nearly the same.
After the installation is successful the user should be signed out.
I need this script to implement 2FA in our company.
The code at the moment looks like this:
IF ( ((Get-ComputerInfo | select -expand CsPCSystemType) -LIKE "Desktop") )
{
Write-Output "Desktop Configuration was used." >> \\XXX\XXX\XXX\XXX\Log\$env:Computername.txt
msiexec.exe /i "%~dp0setup.msi" /passive /norestart /L*v "%~dp0setup.log"
}
ELSE {
Write-Output "Laptop Configuration was used." >> \\XXX.XXX.XX\X\XX\XXX\XXXX\$env:Computername.txt
msiexec.exe /i "%~dp0setup.msi" /passive /norestart /L*v "%~dp0setup.log"
}
Write-Output "Lock Configuration was used." >> \\XXX\XXX\XXX\XXX\Log\$env:Computername.txt
rundll32.exe user32.dll,LockWorkStation
Any help is really appreciated.
The token %~dp0 (which resolves to the directory where the current script resides in) only works in batch scripts.
In PowerShell, replace $~dp0 by $PSScriptRoot. That should solve the problem of msiexec showing up with the available command-line options.
Another problem of your script is that msiexec runs asynchronously, when called directly as a command. So your script will be finished while the installation is still running, which is propably not what you want. This is caused by msiexec.exe being a GUI application. To run GUI applications synchronously, use Start-Process -Wait.
$Arguments = "/i", "`"$PSScriptRoot\setup.msi`"", "/passive", "/norestart", "/L*v", "`"$PSScriptRoot\setup.log`""
$msiProcess = Start-Process msiexec.exe -Wait -ArgumentList $Arguments -PassThru
# Check if installation was successful
# 0 = ERROR_SUCCESS, 3010 = ERROR_SUCCESS_REBOOT_REQUIRED
if( $msiProcess.ExitCode -in 0, 3010 ) {
Write-Host "Installation succeeded with exit code $($msiProcess.ExitCode)"
}
Try passing silent switches (/qn! or /qb!) instead of /passive flag.

Logfile not being generated while running a powershell script with windows task scheduler

I have created the following powershell script:
stop-service ''3456''
start-sleep -s 60
stop-service "2354"
start-sleep -s 60
Restart-computer QY34 -Force
send -mailmessage -from operating.system#abc.com -To asdf#abc.com -subject test -attachment 'c:\Temp|test.log' -smtp server "127.0.0.1"
I have entered the following information on windows task scheduler
On Actions Tab;
C:\windows\System32\windowspowershell\v1.0\powershell.exe
Arguments Tab:
-file "c:\scripts\test.ps1"*>"c:\Temp\test.log"
Can anyone please help me in getting the log file while running the script via Task Scheduler?
Change the arguments from
-file "c:\scripts\test.ps1"*>"c:\Temp\test.log"
to
"c:\scripts\test.ps1" *> "c:\Temp\test.log"
and it should work as expected. Note the blank spaces before and afer *>.
I think the problem is that Powershell will read everything after -file as the file input. It'll still work, but as it just stops reading after the "file" bit, the > operator is ignored/not seen. Removing -file Powershell will still find the script using positional parameters, but it will also handle the > operator and output to file.
If you run if from a command line it'll work, I think because cmd.exe will handle the output instead of Powershell in that case, but don't quote me on that bit.

How to pause a powershell script until a debugger attaches

I have an application which allows you to run a user provided PowerShell script. I want inject some code at the top of the script to pause the running script on the first line, and wait for a debugger (ie, PowerShell ISE) to attach.
A good example of what I want to achieve is how DSC pauses and waits for you to attach:
PS C:\> Enable-DscDebug -BreakAll
PS C:\> Start-DscConfiguration .\temp\DSCTestClass -wait -force
WARNING: [DEV-14257-44]: [DSCEngine] Warning LCM is in Debug 'ResourceScriptBreakAll' mode. Resource script processing will be stopped to wait for PowerShe1l script debugger to attach.
WARNING: [DEV-14257-44]: [[FileResource]file] Resource is waiting for PowerShell script debugger to attach. Use the following commands to begin debugging this resource script:
Enter-PSSession -ComputerName DEV-14257-44 -Credential <credentials>
Enter-PSHostProcess -Id 596 -AppDomainName DscPsPluginWkr_AppDomain
Debug-Runspace -Id 4
(this example from https://blogs.msdn.microsoft.com/powershell/2016/03/14/debugging-powershell-dsc-class-resources/)
Unfortunately, when I try and setup a similar kind of wait, powershell.exe automatically launches the command line debugger:
Given the file test.ps1:
set-psbreakpoint -script "test.ps1" -line 4
write-host "pid is $pid"
write-host "Pausing for debugger to attach"
write-host "Paused waiting for debugger to attach"
When I run it (even in -noninteractive mode), it launches the command line debugger:
PS C:\temp\PowershellDebugging> powershell -file .\test.ps1 -noninteractive
ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
0 test.ps1 4
pid is 13228
Pausing for debugger to attach
Entering debug mode. Use h or ? for help.
Hit Line breakpoint on 'C:\temp\PowershellDebugging\test.ps1:6'
At C:\temp\PowershellDebugging\test.ps1:6 char:1
+ write-host "Paused waiting for debugger to attach"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[DBG]: PS C:\temp\PowershellDebugging>
How can I get PowerShell to break/pause execution without launching the command line debugger?
EDIT:
The script that is being executed only exists for a short time (it is written to disk, executed, then cleaned up). Also, the powershell.exe instance that runs it only exists for a short time - there is no long running process to manually attach to and set breakpoints on before execution.
After getting some help from the PowerShell team (thanks!), it turns out the key is using New-RunSpace. Normally, if you just execute a script, it will run in the default RunSpace, and that will have the built-in PowerShell debugger already attached.
The following demonstrates how to use it:
# create a fake script that simulates the user provided scrip
#'
Write-Output "Starting Test"
1..20 | foreach {
Write-Output "I Love PowerShell!"
}
Write-Output "Test Complete"
'# | Set-Content -Path c:\temp\Test.ps1
# create a launcher script
#'
Write-Output "Process Id: $pid"
$ps = [powershell]::Create([System.Management.Automation.RunspaceMode]::NewRunspace)
Write-Output "Runspace Id: $($ps.Runspace.Id)"
$ps.AddScript(‘Wait-Debugger; C:\temp\Test.ps1’).Invoke()
'# | Set-Content -Path c:\temp\LaunchTestInNewRunspace.ps1
# run the launch script
PS C:\> powershell -File c:\temp\LaunchTestInNewRunspace.ps1
Process Id: 14288
Runspace Id: 2
In another window:
# in another window
PS C:\> Enter-PSHostProcess -Id 14288
[Process:14288]: PS C:\> Debug-RunSpace -Id 2
Debugging Runspace: Runspace2
To end the debugging session type the 'Detach' command at the debugger
prompt, or type 'Ctrl+C' otherwise.
Entering debug mode. Use h or ? for help.
At line:1 char:16
+ Wait-Debugger; C:\temp\Test.ps1
+ ~~~~~~~~~~~~~~~~
This breaks into the debugger, ready to step into Test.ps1.

Running script on selected folder/drive using right click on windows explorer

Environment: Windows 7
With the help of straight forward article from Scott, I am able to have "PowerShell Here" as the right click item of windows explorer.
Right clicking "PowerShell Here" opens a powershell command prompt with selected folder as the current working directory.
But I want little bit different which I am not able to do - inplace of opening the powershell prompt, I want to run a powershell script taking argument as the selected drive/folder/filename!
So, I updated the following line of "powershellhere.inf" file,
;existing one
;HKCR,Drive\Shell\PowerShellHere\command,,,"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -NoExit ""cd '%1'"""
;updated one, added -Command <ScriptFile>
HKCR,Drive\Shell\PowerShellHere\command,,,"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -Command d:\temp.ps1 -NoExit ""cd '%1'"""
But when I right click and select the "PowerShell Here", it's not running the script in the selected drive/folder/file, it's running in C:\Windows\System32 folder.
The string I believe you are looking for:
"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -NoExit -File ""C:\temp\test.ps1"" ""'%1'"""
-NoExit had to be before the -File else it was being picked up as an argument to the ps1 file. Also you need to put a full file path. I'm sure you could use environment variables. In my script i just had Write-Host $args[0] which output the path.
I was having an issue with the path being passed at first. I think the single quotes were not the ones I expected them to be inside the script. My script now changes directory successfully. Contents of test.ps1
$location = $args[0].Trim("'")
Write-Host "Path is valid = $(Test-Path $location)"
Set-Location $location
Use -File instead of -Command and get rid of the cd stuff:
HKCR,Drive\Shell\PowerShellHere\command,,,"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -File d:\temp.ps1 -NoExit
-Command is for literal commands on the command line. You may also want to set the -ExecutionPolicy if you have an issue with that.
If you don't want the prompt to stick around after executing the script, get rid of -NoExit.

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"