PowerShell Start-Service - Unable to validate 'InputObject' issue - powershell

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

Related

PowerShell module not dot-sourcing / importing functions as expected

Update 1:
Originally, I posted this with the title: "Scripts ignoring error handling in PowerShell module" as that is the current issue, however it seems more of a module issue, so I have renamed the title.
Update 2:
After a comment that made me question Azure cmdlets, I've tested with the most basic of scripts (added to the module) and the findings are the same, in that the error is not passed to the calling script, however, adding -errorVariable to Get-Service does return something (other than WriteErrorException) that I could probably harness in the handling of the error:
function Test-MyError($Variable)
{
Try
{
Get-Service -Name $variable -ErrorAction Stop -ErrorVariable bar
#Get-AzureRmSubscription -SubscriptionName $variable -ErrorAction Stop
}
Catch
{
Write-Error $error[0]
$bar
}
}
returns:
Test-MyError "Foo"
Test-MyError : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:3 char:1
+ Test-MyError "Foo"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-MyError
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find any service with service name 'foo'.
However, if I run "Test-MyError" in ISE, then call the function, I get:
Test-MyError "Foo"
Test-MyError : Cannot find any service with service name 'Foo'.
At line:3 char:1
+ Test-MyError "Foo"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-MyError
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find any service with service name 'Foo'.
So I am not sure what is happening when running "Test-MyError" in ISE and calling it, against it being dot-sourced in the PSM1 file and then calling it?
Do I now have to use -ErrorVariable and handle on that?
Original Question:
I have two functions in a module: Get-Subscription and Get-AllSubscriptions. Each function sits in its own PS1 file and the PSM1 file dot-sources them. The module seems fine as the module scripts are accessible using intelisense and the module loads without issue. I've used this structure in many modules and I haven't come across this problem before. (Although I wonder if MS have changed the way modules work in PS 5.1 as I have noticed using FunctionsToExport='x','y','Z' and Export-ModuleMember don't seem to behave the same way as they used to.)
Get-AllSubscriptions calls Get-Subscription.
If I am not logged into Azure, Get-Subscription should throw an error which is handled, prompting me to log in. This works as expected, if I call Get-Subscription from the Get-Subscription.ps1.
However, when I call Get-Subscription from the a new PS1 file, Get-AllSubscriptions or from the powershell console, it doesn't work. It iterates all the way through the do..until loop, without "handling" the errors as I would expect. On each iteration, it seems to throw a generic error:
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
However, I do see the last error, Get-Subscription : Unable to find requested subscription after 3 login attempts.
If I execute Get-Subscription in ISE, then call Get-Subscription in a new PS1 file or from Get-AllSubscriptions, it works as expected, however, once I re-import the module (Import-Module AzureVnetTools -Force -Verbose), it goes back to the incorrect behaviour.
If I dot-source Get-Subscription, inside the caller script, it works, but why? This is what should happen with the module's PSM1.
Can anyone help me work out what I am doing wrong here?
(PS 5.1, Windows 7)
Get-Subscription:
function Get-Subscription
{
[cmdletbinding()]
Param
(
[string]$SubscriptionName,
[string]$UserName,
[string]$code
)
$c=1
Write-Verbose "Checking access to '$SubscriptionName' with user '$UserName'..."
Do
{
Write-Verbose "Attempt $c"
Try
{
$oSubscription = Get-AzureRmSubscription -SubscriptionName $SubscriptionName -ErrorAction Stop -WarningAction SilentlyContinue
Write-Verbose "Subscription found: $($oSubscription.SubscriptionName)."
}
Catch
{
if($error[0].Exception.Message -like "*Please verify that the subscription exists in this tenant*")
{
Write-Verbose "Cannot find subscription '$SubscriptionName' with provided credentials."
$account = Login-AzureRmAccount -Credential (Get-Credential -UserName $Username -Message "Subscription '$SubscriptionName' user' password:")
}
elseif($error[0].Exception.Message -like "*Run Login-AzureRmAccount to login*")
{
Write-Verbose "No logged in session found. Please log in."
$account = Login-AzureRmAccount -Credential (Get-Credential -UserName $Username -Message "Subscription '$SubscriptionName' user' password:")
}
else
{
Write-Error $error[0]
}
}
$c++
}
until(($oSubscription) -or ($c -eq 4))
if($c -eq 4)
{
Write-Error "Unable to find requested subscription after $($c-1) login attempts."
break
}
$oSubscription | Add-Member -MemberType NoteProperty -Name Code -Value $code
$oSubscription
}
Get-AllSubscriptions:
function Get-AllSubscriptions
{
[cmdletbinding()]
param
(
[string]$MasterSubscription,
[string]$MasterSubscriptionCode,
[string]$MasterSubscriptionUsername,
[string]$ChildSubscription,
[string]$ChildSubscriptionCode,
[string]$ChildSubscriptionUsername
)
Write-Verbose "Getting all subscriptions..."
$oAllSubscriptions = #()
$oMasterSubscription = Get-Subscription -SubscriptionName $MasterSubscription -UserName $MasterSubscriptionUsername -code $MasterSubscriptionCode -Verbose
$oChildSubscription = Get-Subscription -SubscriptionName $ChildSubscription -UserName $ChildSubscriptionUsername -code $ChildSubscriptionCode -Verbose
$oAllSubscriptions = ($oMasterSubscription,$oChildSubscription)
$oAllSubscriptions
}
Test:
$splat2 = #{
SubscriptionName = "SomeSubscription"
Code = "S02"
Username = "some.user#somewhere.com"
}
#Write-Output "Dot-source:"
#. "D:\Temp\PS.Modules\AzureVnetTools\functions\public\Get-Subscription.ps1"
Get-Subscription #splat2 -verbose
Output:
Get-Subscription #splat2 -verbose
VERBOSE: Checking access to 'SomeSubscription' with user 'some.user#somewhere.com'...
VERBOSE: Attempt 1
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
VERBOSE: Attempt 2
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
VERBOSE: Attempt 3
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
Get-Subscription : Unable to find requested subscription after 3 login attempts.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
AzureVnetTools.psm1
#Get public and private function definition files.
$Public = #( Get-ChildItem -Path $PSScriptRoot\Functions\Public\*.ps1 -ErrorAction SilentlyContinue )
$Private = #( Get-ChildItem -Path $PSScriptRoot\Functions\Private\*.ps1 -ErrorAction SilentlyContinue )
#Dot source the files
Foreach($import in #($Public + $Private))
{
#write-error $import.fullname
Try
{
#Write-Host "Dot-sourcing file: $($import.fullname)."
. $import.fullname
}
Catch
{
Write-Error -Message "Failed to import function $($import.fullname): $_"
}
}
Export-ModuleMember -Function $Public.Basename
AzureVnetTools.psd1 (Relevant section):
FunctionsToExport = '*'
-ErrorAction Stop -WarningAction SilentlyContinue
Is it a warning that's being thrown instead of an error?
So my specific problem was that I was relying on handling the error and doing something based on that. The problem was caused by the way PowerShell's Write-Error works (or not) as I learned from the reply here, given by #Alek.
It simply wasn't passing the actual error back to the calling script. As #Alex suggested, I replaced Write-Error with $PSCmdlet.WriteError(). Although this didn't totally work.
In the Catch{} block, I then changed $error[0] to $_ and the full error was returned to the calling script / function.
I went one further and wrote a reusable function, added to my module:
function Write-PsError
{
[cmdletbinding()]
Param
(
[Exception]$Message,
[Management.Automation.ErrorCategory]$ErrorCategory = "NotSpecified"
)
$arguments = #(
$Message
$null #errorid
[Management.Automation.ErrorCategory]::$ErrorCategory
$null
)
$ErrorRecord = New-Object -TypeName "Management.Automation.ErrorRecord" -ArgumentList $arguments
$PSCmdlet.WriteError($ErrorRecord)
}
Which seems to be working well at the moment. I especially like the way intellisense picks up all the ErrorCategories. Not sure what or how ISE (PS 5.1 / Win 7) does that. I thought I was going to have to add my own dynamic parameter.
HTH.

How do I suppress PowerShell script block errors?

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"
}

Property is empty when run through Invoke-Command

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

Start-job resulting in null parameter

I'm trying to execute the following two lines of code in PowerShell v2.0 as a job, and am having trouble. I think I have the syntax right, but I can't get it to do what I think it should do, so I clearly am doing something wrong...
$report = <command line thing>
invoke-expression $report
These two lines work in PowerShell. But when I try to put it into a start-job command:
start-job -scriptblock {invoke-expression $report} -argumentlist $report | wait-job | receive-job
I get the following error:
Cannot bind argument to parameter 'Command' because it is null.
+ CategoryInfo : InvalidData: (:) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.InvokeExpre
ssionCommand
+ PSComputerName : localhost
I understand that error as saying that the argument I'm passing the invoke-expression is null, but, I'd thought that by passing $report in the -argumentlist param, it'd get through?
You cannot access $report directly unless you do:
-scriptblock {param($report) invoke-expression $report}
The param($report) part captures the $report variable passed in via -ArgumentList and makes it available for use inside the scriptblock.

Powershell: Exception calling "GetOwner" : "Not found " when invoked as job

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...