I have this Powershell:
Try
{
#Start the installer remotely
$process = ([WMICLASS]"\\$comp\ROOT\CIMV2:Win32_Process").Create($InstallString)
if ( $process.ReturnValue -eq 0 )
{
$logstr = $comp + ": spawned process " + $process.ProcessId
Write-Host -ForegroundColor GREEN $logstr
}
else
{
Write-Host -ForegroundColor RED "${comp}: failed to create process ${InstallString}"
Continue
}
}
Catch
{
Write-Host -ForegroundColor RED "${comp}: error: $_.Exception.Message"
Continue
}
Where $comp is a valid PC name and InstallString is \\SERVERNAME\ShareFolder\setup.exe.
Within that ShareFolder is an installer and its files. I expect I can run the setup like this to remote install, but it is not working. It's falling into the else:
COMPUTERNAME: failed to create process \\SERVERNAME\ShareFolder\setup.exe
What am I missing? I can access this path outside Powershell.
This works on a local path. Do I need some special syntax in Powershell to point to this share?
This code works if the exe (well, a different installer) is located on COMPUTERNAME and the code is still executed remotely.
It could be a permission problem. According to this link: Creating Processes Remotely
A process created remotely can run under any account if the account has the Execute Method and Remote Enable permissions for root\cimv2. The Execute Method and Remote Enable permissions are set in WMI Control in the Control Panel.
Related
Is there any sane, reliable contract that dictates whether Write-Host is supported in a given PowerShell host implementation, in a script that could be run against any reasonable host implementation?
(Assume that I understand the difference between Write-Host and Write-Output/Write-Verbose and that I definitely do want Write-Host semantics, if supported, for this specific human-readable text.)
I thought about trying to interrogate the $Host variable, or $Host.UI/$Host.UI.RawUI but the only pertinent differences I am spotting are:
in $Host.Name:
The Windows powershell.exe commandline has $Host.Name = 'ConsoleHost'
ISE has $Host.Name = 'Windows PowerShell ISE Host'
SQL Server Agent job steps have $Host.Name = 'Default Host'
I have none of the non-Windows versions installed, but I expect they are different
in $Host.UI.RawUI:
The Windows powershell.exe commandline returns values for all properties of $Host.UI.RawUI
ISE returns no value (or $null) for some properties of $Host.UI.RawUI, e.g. $Host.UI.RawUI.CursorSize
SQL Server Agent job steps return no values for all of $Host.UI.RawUI
Again, I can't check in any of the other platforms
Maintaining a list of $Host.Name values that support Write-Host seems like it would be bit of a burden, especially with PowerShell being cross-platform now. I would reasonably want the script to be able to be called from any host and just do the right thing.
Background
I have written a script that can be reasonably run from within the PowerShell command prompt, from within the ISE or from within a SQL Server Agent job. The output of this script is entirely textual, for human reading. When run from the command prompt or ISE, the output is colorized using Write-Host.
SQL Server jobs can be set up in two different ways, and both support capturing the output into the SQL Server Agent log viewer:
via a CmdExec step, which is simple command-line execution, where the Job Step command text is an executable and its arguments, so you invoke the powershell.exe executable. Captured output is the stdout/sterr of the process:
powershell.exe -Command x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
via a PowerShell step, where the Job Step command text is raw PS script interpreted by its own embedded PowerShell host implementation. Captured output is whatever is written via Write-Output or Write-Error:
#whatever
Do-WhateverPowershellCommandYouWant
x:\pathto\script.ps1 -Arg1 -Arg2 -Etc
Due to some other foibles of the SQL Server host implementation, I find that you can emit output using either Write-Output or Write-Error, but not both. If the job step fails (i.e. if you throw or Write-Error 'foo' -EA 'Stop'), you only get the error stream in the log and, if it succeeds, you only get the output stream in the log.
Additionally, the embedded PS implementation does not support Write-Host. Up to at least SQL Server 2016, Write-Host throws a System.Management.Automation.Host.HostException with the message A command that prompts the user failed because the host program or the command type does not support user interaction.
To support all of my use-cases, so far, I took to using a custom function Write-Message which was essentially set up like (simplified):
$script:can_write_host = $true
$script:has_errors = $false
$script:message_stream = New-Object Text.StringBuilder
function Write-Message {
Param($message, [Switch]$iserror)
if ($script:can_write_host) {
$private:color = if ($iserror) { 'Red' } else { 'White' }
try { Write-Host $message -ForegroundColor $private:color }
catch [Management.Automation.Host.HostException] { $script:can_write_host = $false }
}
if (-not $script:can_write_host) {
$script:message_stream.AppendLine($message) | Out-Null
}
if ($iserror) { $script:has_errors = $true }
}
try {
<# MAIN SCRIPT BODY RUNS HERE #>
}
catch {
Write-Message -Message ("Unhandled error: " + ($_ | Format-List | Out-String)) -IsError
}
finally {
if (-not $script:can_write_host) {
if ($script:has_errors) { Write-Error ($script:message_stream.ToString()) -EA 'Stop' }
else { Write-Output ($script:message_stream.ToString()) }
}
}
As of SQL Server 2019 (perhaps earlier), it appears Write-Host no longer throws an exception in the embedded SQL Server Agent PS host, but is instead a no-op that emits nothing to either output or error streams. Since there is no exception, my script's Write-Message function can no longer reliably detect whether it should use Write-Host or StringBuilder.AppendLine.
The basic workaround for SQL Server Agent jobs is to use the more-mature CmdExec step type (where Write-Output and Write-Host both get captured as stdout), but I do prefer the PowerShell step type for (among other reasons) its ability to split the command reliably across multiple lines, so I am keen to see if there is a more-holistic, PowerShell-based approach to solve the problem of whether Write-Host does anything useful for the host I am in.
Just check if your host is UserInteractive or an service type environment.
$script:can_write_host = [Environment]::UserInteractive
Another way to track the output of a script in real time is to push that output to a log file and then monitor it in real time using trace32. This is just a workaround, but it might work out for you.
Add-Content -Path "C:\Users\username\Documents\PS_log.log" -Value $variablewithvalue
I'm trying to Start a service using Powershell and using the code below
function CheckServiceStatus {
param($winupdate)
$getservice = Get-Service -Name $winupdate
if($getservice.Status -ne $running){
stop-service $winupdate
Start-Service $winupdate
Write-output "Starting" $winupdate "service"|out-file "C:\Users\Mani\Desktop\abc.txt"
Add-Content C:\Users\Mani\Desktop\abc.txt $getservice.Status
}
}
Read-Host -Prompt "Press Enter to exit"
#Variables
$winupdate = 'vsoagent.192.Shalem'
$running = 'Running'
CheckServiceStatus $winupdate
I got the following error:
Service 'ABC' cannot be stopped due to the following error: Cannot
open ABC service on computer '.'
I found some link here on our forum but couldn't resolve. Please suggest
If you want to start/stop/etc services, you need elevated privileges (run as Admin). You can still get information about the service, but that's it. If you want to include a more verbose error, include this:
$isadmin = [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")
if($isadmin){
Write-Error "You need elevated privileges to run this script"
exit(1)
}
...
Rest of your code
Or even better, if you're running Powershell 4.0 or higher (You can get it by checking $PSVersionTable), you can include
#Requires -RunAsAdministrator
At the top of your file, and you won't be able to run it without admin privilages.
I had this issue while running the command on PowerShell ISE. All I did was start the PowerShell ISE as an administrator.
Look into Event Viewer and find more details. In my case, I have found relevant info in Administrative Events, then Service Control Manager. The error was related to insufficient privileges given for the account. The service was creating a new file and this task failed. Of course, your error's details are probabably different, but that is the tip.
Update - the original question claimed that I was able to successfully perform an Invoke-Command and then shortly after was unable to; I thought it was due to processes going on during login after a windows upgrade.
It turns out the PC was actually starting, running a quick batch/cmd file, and then restarting. This is what was leading to being able to do PS Remoting and then suddenly not. The restart was quick enough after first boot that I didn't realize it was happening. Sorry for the bad question.
For the curious, the machine was restarting because of a remnant of the Microsoft Deployment Toolkit in-place upgrade process. The way MDT completes its task-sequence post-upgrade is problematic for many reasons, and now I've got another to count.
Old details (no longer relevant, with incorrect assumption that machine was not restarting after first successful Invoke-Command):
I'm automating various things with VMs in Hyper-V using powershell and powershell remoting. I'll start up a VM and then want to run some commands on it via powershell.
I'm struggling with determining when I can safely start running the remote commands via things like Invoke-Command. I can't start immediately as I need to let the machine start up.
Right now I poll the VM with a one second sleep between calls until the following function returns $true:
function VMIsReady {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)][object]$VM
)
$heartbeat = $vm.Heartbeat
Write-Host "vm heartbeat is $heartbeat"
if (($heartbeat -eq 'OkApplicationsHealthy') -or ($heartbeat -eq 'OkApplicationsUnknown'))
{
try
{
Invoke-Command -VMName $vm.Name -Credential $(GetVMCredentials) {$env:computername} | out-null
}
catch [System.Management.Automation.RuntimeException]
{
Write-Host 'Caught expected automation runtime exception'
return $false
}
Write-Host 'remoting ready'
return $true
}
}
This usually works well; however, after a windows upgrade has happened, there are issues. I'll get Hyper-V remoting errors of various sorts even after VMIsReady returns $true.
These errors are happening while the VM is in the process of first user login after upgrade (Windows going through "Hi;We've got some updates for your PC;This might take several minutes-Don't turn off your PC). VMIsReady returns true right as this sequence starts - I imagine I probably should be waiting until the sequence is done, but I've no idea how to know when that is.
Is there a better way of determining when the machine is in a state where I can expect remoting to work without issue? Perhaps a way to tell when a user is fully logged on?
You can use Test-WSMan.
Of run a script on the invoke that will receive a response from the server.
[bool]$Response | Out-Null
try{
$Response = Invoke-Command -ComputerName Test-Computer -ScriptBlock {return $true}
}catch{
return $false
}
if ($Response -ne $true){
return $false
}else{
return $true
}
I've written some scripts to perform a set of remote deployments. I do this by executing a script locally, which does an Invoke-Command to trigger a script to run on the remote server. This deploys a Windows Service with supplied credentials.
This all works great for the most part, and saves us (me) a whole load of time. However, I occasionally hit an issue which manifests itself with a Prompt (alert/dialog box etc) that I cannot seem to get around.
If I log onto the remote machine and run the script, I can see the prompt appear. It says something along the lines of "This action cannot be completed because the file is open in another process". The actual issue itself is probably race condition related, as I can get around it just by dismissing the prompt, but the problem is that I cannot "get the alert" remotely - the script at my end just hangs.
What I'd really like to do is handle the Alert, regardless of what it says and what the options are, I'd like to either "bring it back" to the remote end so to speak, or at least have some sort of notification that it's blocking the operation with some way to dismiss it.
Here's an example of it:
EDIT:
We have two machines (A), and (B). (A) is my machine, (B) is the remote server.
On (A), I have a script [let's call it RemoteExecute.ps1], which contains something like this:
Invoke-Command -Session $session -Scriptblock { c:\updates\Deploy.ps1 }
C:\updates is a location on (B), and Deploy.ps1 is a script in that location. Deploy.ps1 is the script which triggers the Alert on (B). If I log onto (B) via Remote Desktop and execute Deploy.ps1 locally on that machine, it gives me the Alert on screen.
However, when I run this script remotely from (A), via the RemoteExecute.ps1 script, I don't see the Alert that's generated on (B) and I have no way of even knowing that an Alert is blocking things. My my end on (A) all I see is the RemoteExecute script hanging indefinitely.
How can I detect that there's an Alert waiting to be dismissed on (B)?
EDIT 2:
#Extract from Deploy script:
Write-Host 'Deploying files ...'
if (!(Test-Path $targetpath)) {
mkdir $targetpath
}
Expand-ZIPFile "$releasepath\$ExeName.zip" $targetpath
Write-Host 'Files deployed'
...
#Function from another file:
function Expand-ZIPFile($file, $destination)
{
$shell = new-object -com shell.application
$zip = $shell.NameSpace($file)
$destFolder = $shell.Namespace($destination)
if (!(test-path $file)) { throw "$file does not exist" }
foreach($item in $zip.items())
{
$shell.Namespace($destination).copyhere($item, 0x14)
}
}
I have this script to check if there is mapped disk on remote server. When I run it in command line, it works perfectly, from PowerGUI and ISE it works perfectly, but when I shedule it in Windows task scheduler, I get a mail message (mail sending part of script is not included), that disk is not mapped - the "else" is executed in spite of disk is mapped.
if(Invoke-Command -ComputerName sdebt -ScriptBlock { Get-WmiObject win32_logicaldisk -ComputerName sdebt -Filter "DeviceID = 'L:'"}) {
Write-Host -ForegroundColor Green "L: is OK"} else {
Write-Host -ForegroundColor Magenta "L: is NOT OK"
$subject = "CHYBA: Disk L is not mapped"
$body += "Disk L is not mapped `r" }
Thank you.
Probably is related with permissions.
When you run it from your commandline/ISE/PowerGUI you are using your credentials
When you run it from schedule task by default you run it with system credentials
If I were you I would:
Try running it with your credentials to see if this make a difference.
Try running it with highest privileges
If after 1) and 2) still fails at least you know for sure that is not a permission issue :-)