I need to get some procs by the owner. My demo script below will first look for procs by owner locally, then it will do the same thing, but it invokes the command on the same box:
cls
write-host 'LOCAL CALL: '
$procs = #(Get-WmiObject win32_process |? {($_.getowner().user -eq 'APP_ACCOUNT') })
write-host $procs.count
$func = {
$procs = #(Get-WmiObject win32_process |? {($_.getowner().user -eq 'APP_ACCOUNT') })
write-host $procs.count
}
write-host 'REMOTE CALL: '
$session = New-PSSession -ComputerName 'SERVER'
$job = Invoke-Command -Session $session -ScriptBlock $func -AsJob
Wait-Job -Job $job
$job | Receive-Job
$job | Remove-Job
Remove-PSSession -Session $session
Most of the time when I run my script it errors with the following output:
LOCAL CALL:
38
REMOTE CALL:
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
26 Job26 RemoteJob Completed True SERVER ...
Exception calling "GetOwner" : "Not found "
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WMIMethodException
+ PSComputerName : SERVER
38
So that first 38 is the number of procs it found for the owner running locally. It finds 38 the second time as well, but errors calling getowner. I don't understand why since it worked the first time. Is it operating in some kind of "bubble" when I invoke the command? In my larger script this is causing me more severe issues as the job state goes to failed and execution halts even though it is throwing the same error. One problem at a time though.
Seems I needed to do a better job of making sure my processes still exist before filtering by owner:
$procs = #()
$allProcs = #(Get-WmiObject win32_process)
foreach($proc in $allProcs)
{
$procActive = get-process -Id $proc.processId -ErrorAction SilentlyContinue
if($procActive)
{
if($proc.getowner().user -eq 'jdholbrook')
{
$procs += $proc
}
}
}
write-host $procs.count
This is probably because the process for which you want to query the owner doesn’t exist anymore.
You can simulate this behaviour on your local PC as follows:
Start some application, like notepad.exe for example. Now run:
$w = (Get-WmiObject win32_process) # Your notepad process will now be the last in the `$w` array.
Close the notepad.exe process.
Now pipe the contents of $w to get the owners:
$w | % {$_.getowner()}
For the last object you will get:
Exception calling "GetOwner" : "Not found "
At line:1 char:20
+ $w | % {$_.getowner <<<< ()}
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WMIMethodException
To make sure this is the notepad.exe you just closed you can double-check:
$w[-1]; # last object
$w[-1].getowner(); # error
So, now you know what is causing, you can start thinking about how to handle it...
Related
I have a script that works to run an executable and wait until done in PS but I need to modify it to use a path defined in a variable earlier in the script.
Working:
$job = Start-Job `
-InitializationScript { Set-Location C:\MyDirectory\ } `
-ScriptBlock { C:\MyDirectory\MyCmdLineExecutable.exe }
Wait-Job $job
Receive-Job $job
Not working:
$Path = "C:\MyDirectory\"
$ExePath = $path+"MyCmdLineExecutable.exe"
$job = Start-Job `
-InitializationScript { Set-Location $Path } `
-ScriptBlock { $ExePath }
Wait-Job $job
Receive-Job $job
Here's the error:
Set-Location : Cannot process argument because the value of argument "path" is null. Change the value of argument "path" to a non-null value.
At line:1 char:2
+ Set-Location $Path
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Location], PSArgumentNullException
+ FullyQualifiedErrorId : ArgumentNull,Microsoft.PowerShell.Commands.SetLocationCommand
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
49 Job49 BackgroundJob Failed False localhost $ExePath
Running startup script threw an error: Cannot process argument because the value of argument "path" is null. Change the value of argument "path" to a non-null value..
+ CategoryInfo : OpenError: (localhost:String) [], RemoteException
+ FullyQualifiedErrorId : PSSessionStateBroken
Combining info from Start-Job docs with About_Scopes article, I am certain of that you need to use -InputObject parameter:
Specifies input to the command. Enter a variable that contains the
objects, or type a command or expression that generates the
objects. In the value of the ScriptBlock parameter, use the
$Input automatic variable to represent the input objects.
$Path = "C:\MyDirectory\"
$ExePath = $path+"MyCmdLineExecutable.exe"
$job = Start-Job -InputObject #( $Path, $ExePath) `
-InitializationScript { <# $Input variable isn't defined here #> } `
-ScriptBlock {
$aux = $Input.GetEnumerator()
Set-Location $aux[0]
& $aux[1] }
Wait-Job $job
Receive-Job $job
BTW, to run commands that are stored in variables and represented by strings, use & Call operator. See the difference:
$ExePath ### output only
& $ExePath ### invocation
I think you want Start-Process with the -Wait parameter. You can also specify the -WorkingDirectory parameter to specify the working directory for the new process. Example:
Start-Process notepad -WorkingDirectory "C:\Program Files" -Wait
Write-Host "Finished"
When you run this script, Notepad will open but the script won't continue until it closes. When you close Notepad, the Write-Host line runs.
Problem
This is semi-related to: PowerShell Start-Service Timeout
I am trying to run through a loop to enforce timeout of the action, but continue to run into a random validation error. The code is setup as such:
$timeout = New-TimeSpan -Seconds $SecondsToWait
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$j = Start-Job -ScriptBlock { Start-Service $ServiceName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue }
$count = 0
Do {
If ($j.State -eq 'Running') {
If ($timer.Elapsed.seconds -ne $count) {
$count ++
Write-Host "Elapsed Time: $($timer.Elapsed.seconds)"
}
$j | Receive-Job
}
} While ( $timer.Elapsed -lt $timeout )
I have tried a view variations in attempts to resolve this, but I keep returning to the same root problem with the program exiting with
Cannot validate argument on parameter 'InputObject'. The Argument is null or empty.
Supply an argument that is not null or empty and then try the command again.
At C:file\path.ps1:543 char:13
$j | Receive-Job
CategoryInfo : InvalidData: (:) [Start-Service], ParameterBindingValidationException
FullyQualifiedErrorId: ParameterArgumentValidatinErrorNullNotAllowed,Microsoft.PowerShell.Commands.StartServiceCommand
I have looked through a number of solutions already on StackOverflow and a few other sites, but most are not similar enough to convert the answer to my problem. Most of the questions that I have seen pertain to the Invoke-Command feature of PowerShell or the issues is with a different command altogether. below is a small list of places I have sought out an answer:
powershell Start-Service fails
Powershell function throwing null exception
"Cannot validate argument on parameter 'Identity'" while changing the description for multiple users
Question
Has anyone run into this situation before and know how to resolve it? Also, before anyone says "oh you are not passing in anything during the Start-Service ", the Do While loop iterates randomly between 15 and 30 times before this error is encountered.
So receive-job gets the results of a thread. Then it deleted those results. The error you are getting is becuase the thread completed and the results are the error of
Start-Service $ServiceName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
which is
Cannot validate argument on parameter 'InputObject'. The argument is
null or empty. Provide an argument that is not null or empty, and then
try the command again.
+ CategoryInfo : InvalidData: (:) [Start-Service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartServiceCommand
+ PSComputerName : localhost
Becuase you didnt specify $ServiceName...
you can verify this by doing the following using -keep tag and remove the -keep tag. The -keep tag will tell the job not to delete the results till next call.
$j = Start-Job -ScriptBlock { Start-Service $ServiceName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue }
$count = 0
Do {
$j.State
$j | Receive-Job -keep
} While ( $timer.Elapsed -lt $timeout )
the following will show running many time and then once completed will show the following
Running
Running
Running
Completed
Cannot validate argument on parameter 'InputObject'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
+ CategoryInfo : InvalidData: (:) [Start-Service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartServiceCommand
+ PSComputerName : localhost
it will keep repeating that error over and over until loop is finished OR until it hits a Receive-Job without a -keep
Sometimes it will show
Running
{Results here}
Running
{Results here}
Complete
{Results here}
This is because Receive-job is giving you faster information then System is updating the Thread.state to Completed.
IF you dont want to see that error then just delete Receive-Job or use a try catch for the error in the scriptblock
We have moved our system alerting over to SCOM 2012 and receive heartbeat alerts when servers go offline. Presently there are approximately 750 servers in the vDC that I am managing. The SCOM 2012 server is in a different untrusted domain.
I have one working script where it puts the servers in Maintenance mode, but its run serially and takes about 40 minutes to put nearly 400 servers in Maintenance Mode. This is a workable solution, but I would like to use the foreach -parallel command to speed it up.
I have my workflow (to use the foreach -parallel command) created and placed in one of the default PowerShell Module locations on the Source and Destination Machines. I've tested the set of commands outside of the workflow on the SCOM server and it runs successfully. When I try to run the command remotely via an Invoke-command, the SCOM commands come back as being unrecognized.
#Get Date for usage in Connection Name
$Date = Get-Date -Format HHmmsss
#combine Name with Date to uniquify
$name = "ScomMM" + $Date
#Collect Servers from WSUS Server
[reflection.assembly]::LoadWithPartialName("Microsoft.Updateservices.Administration") | out-null
$WSUS = [Microsoft.updateservices.administration.adminproxy]::Getupdateserver("ServerName",$false,8530);
$TS = $wsus.getcomputertargetgroups() | ? {($_.name -eq "Group1") -or ($_.Name -eq "Group2")}
$computers = $TS.getcomputertargets() | Select-Object FullDomainName
#Setup Trusted host
invoke-Command -ScriptBlock {(winrm.cmd s winrm/config/client '#{TrustedHosts="*PublicIPAddress*"}')}
Enable-PSRemoting -Force
#Credentials stored in a file
$username = "username"
$password = get-content 'Path' | convertto-securestring
$creds = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,$password
$session = new-PSSession -ComputerName "IPAddress" -Credential $creds -Name $name
#SCOM Commands Module
Import-Module OperationsManager
#Workflow
Import-Module Set-MM
#Run the command remotely
Invoke-Command -Session $session -ScriptBlock {
Import-Module -Name Set-MM
Set-MM -computers $using:computers
}
#Workflow - Stored at C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Set-MM\Set-MM.psm1
Workflow Set-MM
{
Param($computers)
Foreach -Parallel($computer in $computers)
{
Get-SCOMClassInstance -Name $computers.FullDomainName | Start-SCOMMaintenanceMode -EndTime (Get-Date).AddMinutes(6) -Reason PlannedOperatingSystemReconfiguration
}
}
At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Set-MM\Set-MM.psm1:6 char:3
+ Get-SCOMClassInstance -Name $computers.FullDomainName | Start ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cannot find the 'Get-SCOMClassInstance' command. If this command is defined as a workflow, ensure it is
defined before the workflow that calls it. If it is a command intended to run directly within Windows
PowerShell (or is not available on this system), place it in an InlineScript: 'InlineScript {
Get-SCOMClassInstance }'
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : CommandNotFound
+ PSComputerName : IPAddress
The term 'Set-MM' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
+ CategoryInfo : ObjectNotFound: (Set-MM:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : IPAddress
Get-PSSession | Remove-PSSession
If I use an Inlinescript on the script inside the foreach -Parallel
InlineScript{Get-SCOMClassInstance -Name $Using:computers.FullDomainName | Start-SCOMMaintenanceMode -EndTime (Get-Date).AddMinutes(6) -Reason PlannedOperatingSystemReconfiguration}
I get this for each computer that is attempting to get processed in the workflow:
The term 'Get-SCOMClassInstance' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
+ CategoryInfo : ObjectNotFound: (Get-SCOMClassInstance:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : ServerName
Below is a simple script block, the script block works. However, I would like to suppress any errors that the script block would generate.
$Name = 'TEST'
$SB = { param ($DSNName) ;
$conn = new-object system.data.odbc.odbcconnection
$conn.ConnectionString = ('DSN='+ $DSNName)
$conn.open()
$ConState = $conn.State
$conn.Close()
$ConState
}
$test = Start-job -scriptblock $SB -args $Name -RunAs32 -ErrorAction Stop | wait-job | receive-job
What I am trying to get out of this is a simple test for a 32bit ODBC connection. If the connection fails the connection state will remain closed but I also get an exception error I would like to suppress
Exception calling "Open" with "0" argument(s): "ERROR [IM002] [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified"
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : OdbcException
+ PSComputerName : localhost
If I pipe to out-null my $test variable is empty. When I use a valid DSN Name everything works as desired.
You could use try..catch:
try {
$test = Start-job -scriptblock $SB -args $Name -RunAs32 -ErrorAction Stop | wait-job | receive-job
catch [System.Management.Automation.MethodInvocationException] {
# Do nothing here if you want to suppress the exception completely.
# Although redirecting it to a log file may be a better idea, e.g.
# $Error[0] | Out-File -FilePath "script.log"
}
I am trying to individually monitor memory usage of a process (w3wp.exe) that has multiple instances of itself by filtering out a string found in the process' CommandLine property.
It works when I run this script locally:
$proc = (WmiObject Win32_Process -Filter "Name = 'w3wp.exe'" | Where-Object {$_.CommandLine -like "*SomeTextFromCl*"})
$id = $proc.ProcessId
$ws = [math]::round((Get-Process -Id $id).WS/1MB)
Write-Host $ws
However, when I try to run it remotely through Invoke-Command, I get an error telling that the Id property's value is null:
Cannot bind argument to parameter 'Id' because it is null.
+ CategoryInfo : InvalidData: (:) [Get-Process], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : RemoteServerName
My Invoke-Command syntax is:
Invoke-Command -ComputerName RemoteServerName -FilePath script.ps1 -Credential $mycredential
I'm sure it's simple but I'm back to PS after a long absence and I had a look around but couldn't find anything really helpful.
You are writing the answer to the console. You use the ps1 as a function, so you should use:
return $ws
instead of
write-host $ws