Welcome › Forums › General PowerShell Q&A › Invoke-Command with Try/Catch and adding to an external array
- This topic has 22 replies, 5 voices, and was last updated 1 month, 1 week ago by
Participant.
-
AuthorPosts
-
-
November 30, 2020 at 7:31 am #275274
How would I add a computer to an array within an Invoke-Command cmdlet also utilizing try/catch statement?
I have the following which isn’t behaving as I’m expecting:
PowerShell1234567891011121314151617181920212223"*** CONFIGURING IIS ON REMOTE COMPUTERS ***" #| Tee-Object -Append $LogFile# Run config command on array - Results will come back unsorted due to the parallel processing nature of invoke-commandInvoke-Command -ComputerName $IisServers -ErrorAction stop {Try {# Import PowerShell module (Only essential for Win 2k8r2 servers)Import-Module WebAdministration# Remove filename from list if existsIf (Get-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/defaultDocument/files/add' | Where-Object {$_.value -eq $Using:FileName}) {Remove-webconfigurationproperty /system.webServer/defaultDocument -name files -atElement @{value=$Using:Filename}Write-Output "$Env:ComputerName`: '$Using:FileName' removed from Default Document list"}Else {Write-Output "$Env:ComputerName`: '$Using:FileName' doesn't exist in Default Document list"}}Catch {$ErrorMessage = $_.Exception.MessageWrite-Output "$Env:ComputerName`: $ErrorMessage"$ExecutionIssues += $Env:ComputerName}$props = @{ComputerName=$ExecutionIssues}New-Object -Type PSObject -Prop $Props} #| Tee-Object -Append $LogFile- The If/Else loop is being ignored and each time I run it, it just runs the If section regardless.
- The Try/Catch appears to be working for execution of the If command errors but the “ErrorAction Stop” argument appears to be terminating the script on the first WinRM connection error it hits rather than logging and continuing with the rest.
- Nothing is being added to the $ExecutionIssues variable on failure.
Am I missing something obvious?
-
This topic was modified 1 month, 2 weeks ago by
jshizzle14. Reason: Code formatting incorrect
-
This topic was modified 1 month, 2 weeks ago by
jshizzle14. Reason: Removed dodgy formatting
-
This topic was modified 1 month, 2 weeks ago by
jshizzle14. Reason: Corrected formatting
-
This topic was modified 1 month, 2 weeks ago by
jshizzle14.
-
This topic was modified 1 month, 2 weeks ago by
grokkit.
-
November 30, 2020 at 11:05 am #275346
Please fix the code in the post. You should be using PSObjects to return the information you need. There was not an -ErrorAction Stop on the Remove action. In this scenario, you are going to get back the computername and one of three statuses:
PowerShell123456789101112131415161718192021222324try {Import-Module WebAdministrationIf (Get-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/defaultDocument/files/add' | Where-Object {$_.value -eq $Using:FileName}) {Remove-webconfigurationproperty -Filter '/system.webServer/defaultDocument' -name files -atElement @{value=$Using:Filename} -ErrorAction Stop[pscustomobject]@{ComputerName = $env:COMPUTERNAMEStatus = 'Success'}}else {[pscustomobject]@{ComputerName = $env:COMPUTERNAMEStatus = 'Not found: File not in configuration'}}}catch {[pscustomobject]@{ComputerName = $env:COMPUTERNAMEStatus = 'Error: {0}' -f $_}}Edit: Also noticed that your Get and Remove are using different paths. The GET ‘system.webServer/defaultDocument/files/add’ and in the remove you are not using ‘add’ anywhere. Has this been tested on a local IIS server before you run it on other servers?
-
This reply was modified 1 month, 2 weeks ago by
Rob Simmers.
-
December 4, 2020 at 12:52 pm #276630
Hi Rob,
Yes, the Get and Remove are meant to be different as the command to check if a file exists is not the same command used for removing/adding files. These commands have been tested and work as expected.
Just looking at your code and think if I try and simplify my target it may help as still can’t see how I can get the computername added to a variable which can be used externally from the the Invoke-Command script block.
PowerShell123456789101112131415161718$TestArray = @()$Win2k8r2Computers="Server1","Server2"Invoke-Command -ComputerName $Win2k8r2Computers {Try {If (Get-Service | Where-Object {$_.Name -eq "W3SVC"} -ErrorAction Stop ) {Write-Output "IIS is installed on $Env:ComputerName (Win2008r2)"}Else {<strong><add the computername to the array $TestArray></strong>}}catch {[pscustomobject]@{ComputerName = $env:COMPUTERNAMEStatus = 'Error: {0}' -f $_}}}I just need the computer to be added to the array for manipulation later. I know you mention that I need to return the item using pscustomobject but can’t see how this would aid in adding to the array?
-
This reply was modified 1 month, 2 weeks ago by
-
November 30, 2020 at 11:50 am #275364
I’m struggling to read your post. Make sure you are doing code blocks for each code section. Repost and maybe I can follow it.
-
December 1, 2020 at 4:37 am #275550
I’ve tried to update the formatting a number of times but every time I do the thread disappears for most the day and then when I can access it again, the formatting is exactly the same again. I’ll try again but this is painful….
Thanks for the suggestion Rob, looking good. I have found another solution as it goes which simply stores all the errors in the $errmsg variable and outputs them to log at the end. This does actually work well but have no control over the formatting so your suggestion looks much better thanks. I’ll let you know how I get on.
-
December 4, 2020 at 6:31 pm #276705
The site definitely has issues.. BUT if you read the formatting guide and then take your time to format it correctly you’ll have less issues. If your post disappears, please just put a message to the mods in the tech support section, they will release it fairly quickly.
-
December 7, 2020 at 4:58 am #277095
It’s not that I’m unsure how to format, it’s that the code PowerShell formatting option is simply missing from the RTF bar above for me sometimes.
-
December 7, 2020 at 7:13 am #277128
If the button is missing, you can either switch to Text view and use the CRAYON button or refresh the page a few times. Usually after two or three refreshes the <> comes back for me.
-
-
December 9, 2020 at 7:19 am #277944
Ok, I’m now trying to return information from the Invoke-Command using custom objects. I tried using Rob’s method but servers running old versions of PowerShell weren’t outputting the message as expected.
The below seems to give me better results, however, still have a few issues:
PowerShell1234567891011121314151617181920212223$Win2k8r2Computers="Server1","Server2","Server3"$results = Invoke-Command -ComputerName $Win2k8r2Computers { #}$props = @{}Try {If ($PSVersionTable.PSVersion.Major -eq "2") {$props.Add('Message',"Server (Win2008r2) is currently running an incompatible version of PowerShell (v2.1)")}ElseIf (Get-Service | Where-Object {$_.Name -eq "W3SVC"} -ErrorAction Stop ) {$props.Add('Message',"IIS is installed (Win2008r2)")}Else {$props.Add('Message',"IIS is NOT installed (Win2008r2)")}}catch {$props.Add('Message','Error: {0}' -f $_)}New-Object -Type PSObject -Prop $Props} | Sort-Object PSComputerName | Select-Object PSComputerName,Message | FT -AutoSize$results- The catch section doesn’t appear to be returning anything in the $results variable
- How do I go about assigning the computers, depending on result, to variables now I have the information outside of the Invoke-Command block? I’m thinking of using an If/Else statement based on keywords in the Message value string but not sure how to check against this as $results.message doesn’t appear to work.
-
December 9, 2020 at 9:24 am #278001
I would remove your pipe after closing out the Invoke-Command script block. With this in place you are assigning a formatted table to $results instead of the actual objects you want.
PowerShell12345678910111213141516171819202122232425262728293031323334$Win2k8r2Computers="Server1","Server2","Server3"$results = Invoke-Command -ComputerName $Win2k8r2Computers {$props = @{}Try {If ($PSVersionTable.PSVersion.Major -eq "2") {$props.Add('Message',"Server (Win2008r2) is currently running an incompatible version of PowerShell (v2.1)")} #ifElseIf (Get-Service | Where-Object {$_.Name -eq "W3SVC"} -ErrorAction Stop ) {$props.Add('Message',"IIS is installed (Win2008r2)")} #elseifElse {$props.Add('Message',"IIS is NOT installed (Win2008r2)")} #else} #trycatch {$props.Add('Message','Error: {0}' -f $_)} #catchNew-Object -Type PSObject -Prop $Props} #Invoke-Command Script Block#you can now do what you want with $results, if statements etc.foreach ($result in $results) {if ($result.message -eq "IIS is installed (Win2008r2)") {#do stuff} #if}#foreach#to view your formatted table do this:$results |Sort-Object PSComputerName |Select-Object PSComputerName,Message |FT -AutoSize-
This reply was modified 1 month, 1 week ago by
Mike R..
-
This reply was modified 1 month, 1 week ago by
-
December 9, 2020 at 9:47 am #278010
Ah yes that makes sense thanks. I can now call my data using $results.message which helps a great deal. Any ideas on why my catch doesn’t appear to be catching though?
-
December 9, 2020 at 10:13 am #278037
[pscustomobject] type accelerator was available in version 3. If you are running v2, you should seriously consider updating those servers to 5.1 for security reasons alone. As Mike R assisted, you can use a New-Object -TypeName PSObject -Property $myHashTable to support older versions.
-
December 9, 2020 at 10:27 am #278052
What are your indications that it is not working? Are you getting an error message or is the script terminating?
What is the purpose of the try/catch. It appears the only possible terminating error in your try block would be the Get-Service… Are you just trying to determine if that service is installed? If so, you don’t need a try/catch block to do that.
PowerShell12345678910111213141516171819202122232425262728293031$Win2k8r2Computers="Server1","Server2","Server3"$results = Invoke-Command -ComputerName $Win2k8r2Computers {$props = @{}If ($PSVersionTable.PSVersion.Major -gt 2) {If (Get-Service | Where-Object {$_.Name -eq "W3SVC"}) {$props.Add('Message',"IIS is installed (Win2008r2)")} #if IISElse {$props.Add('Message',"IIS is NOT installed (Win2008r2)")} #else no IIS} #if version -gt 2Else {$props.Add('Message',"Server (Win2008r2) is currently running an incompatible version of PowerShell (v2.1)")} #else old PSNew-Object -Type PSObject -Prop $Props} #Invoke-Command Script Block#you can now do what you want with $results, if statements etc.foreach ($result in $results) {if ($result.message -eq "IIS is installed (Win2008r2)") {#do stuff} #if}#foreach#to view your formatted table do this:$results |Sort-Object PSComputerName |Select-Object PSComputerName,Message |FT -AutoSize -
December 9, 2020 at 10:28 am #278055
That’s the problem, I’m not in the position to be able to update PowerShell on these servers so need to filter them out in order to just work on the ones I can.
I can’t see any previous reference to New-Object -TypeName PSObject -Property $myHashTable in any of the replies above. Maybe I’m missing something?
Regardless, I’m good to go other than the Catch simply not catching now and just can’t see why it’s not working.
-
December 9, 2020 at 10:34 am #278064
The accelerator can’t be used, so you need to do it like this:
PowerShell123456789101112131415161718192021222324try {Import-Module WebAdministrationIf (Get-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/defaultDocument/files/add' | Where-Object {$_.value -eq $Using:FileName}) {Remove-webconfigurationproperty -Filter '/system.webServer/defaultDocument' -name files -atElement @{value=$Using:Filename} -ErrorAction StopNew-Object -TypeName PSObject -Property @{ComputerName = $env:COMPUTERNAMEStatus = 'Success'}}else {New-Object -TypeName PSObject -Property @{ComputerName = $env:COMPUTERNAMEStatus = 'Not found: File not in configuration'}}}catch {New-Object -TypeName PSObject -Property @{ComputerName = $env:COMPUTERNAMEStatus = 'Error: {0}' -f $_}} -
December 9, 2020 at 10:43 am #278073
BTW, I really have not paid a lot attention to what you are trying to do, just getting your code working, but now that I look at it, is seems you are just trying to get the PS major version and determine if a service is running on remote servers. If that’s all it is, I would avoid the whole creating a message to return from the remote machine and just collect the data. You can analyze it however you want from the local machine after. Like this:
PowerShell12345678910111213141516171819$Win2k8r2Computers="Server1","Server2","Server3"$results = Invoke-Command -ComputerName $Win2k8r2Computers {$props = @{}$props.Add("services", (Get-Service))$props.Add("PSVersion", $PSVersionTable)New-Object -Type psobject -Property $props}# $results now contains at least 3 properties PSComputerName, Services (which have all the installed services,# and PSVersion which has the remote machines $PSVersionTable hashtable# you can access them with the property derefernce operator . i.e. $results[0].PSversion.PSVersion.Major$results |Sort-Object PSComputerNameSelect-Object PSComputerName,@{n="IIS";e={[bool]($_.services | Where-Object name -eq "W3SVC")}},@{n="PSVers";e={$_.PSVersion.PSVersion.Major}} |Format-Table -AutoSize -
December 9, 2020 at 10:56 am #278079
Thanks Rob. I’m actually working on a different part of the script now but the subject is still relevant. Here’s my actual code with your suggestion so it’s clear what I’m trying to achieve:
PowerShell123456789101112131415161718192021222324252627$Win2k8r2Computers="Server1","Server2","Server3","Server4"$results = Invoke-Command -ComputerName $Win2k8r2Computers { #}Try {If ($PSVersionTable.PSVersion.Major -eq "2") {New-Object -Type PSObject -Prop @{Message = "Server (Win2008r2) is currently running an incompatible version of PowerShell (v2.1)"}}ElseIf (Get-Service | Where-Object {$_.Name -eq "W3SVC"} -ErrorAction Stop) {New-Object -Type PSObject -Prop @{Message = "IIS is installed (Win2008r2)"}}Else {New-Object -Type PSObject -Prop @{Message = "IIS is NOT installed (Win2008r2)"}}}catch {New-Object -Type PSObject -Prop @{Message = 'Error: {0}' -f $_}}}$resultsThis simply shows errors in the console but doesn’t catch them in a controlled way still. I’ve purposely left out the ComputerName property for all the statements as will simply use PSComputerName.
-
This reply was modified 1 month, 1 week ago by
jshizzle14. Reason: Amended code
-
This reply was modified 1 month, 1 week ago by
-
December 9, 2020 at 11:15 am #278106
BTW, I really have not paid a lot attention to what you are trying to do, just getting your code working, but now that I look at it, is seems you are just trying to get the PS major version and determine if a service is running on remote servers. If that’s all it is, I would avoid the whole creating a message to return from the remote machine and just collect the data. You can analyze it however you want from the local machine after. Like this:
<textarea class=”urvanov-syntax-highlighter-plain print-no” style=”tab-size: 4; font-size: 14px !important; line-height: 18px !important; z-index: 0; opacity: 0;” readonly=”readonly” data-settings=”dblclick”></textarea>12345678910111213141516171819$Win2k8r2Computers=“Server1”,“Server2”,“Server3”$results = Invoke-Command -ComputerName $Win2k8r2Computers {$props = @{}$props.Add(“services”, (Get-Service))$props.Add(“PSVersion”, $PSVersionTable)New-Object -Type psobject -Property $props}# $results now contains at least 3 properties PSComputerName, Services (which have all the installed services,# and PSVersion which has the remote machines $PSVersionTable hashtable# you can access them with the property derefernce operator . i.e. $results[0].PSversion.PSVersion.Major$results |Sort-Object PSComputerNameSelect-Object PSComputerName,@{n=“IIS”;e={[bool]($_.services | Where-Object name -eq “W3SVC”)}},@{n=“PSVers”;e={$_.PSVersion.PSVersion.Major}} |Format-Table -AutoSizeThanks Mike but how does this handle connection errors though. I’m trying to assign failed connections to a variable so they can be output to the bottom of a log file to flag as needing manual attention.
Also, I’ve tried your code and the output is not showing information as expected:
services : {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController…}
PSVersion : {PSRemotingProtocolVersion, BuildVersion, PSCompatibleVersions, PSVersion…}
PSComputerName : ComputerName
RunspaceId : fb32f0bb-da71-42e0-8155-507bdb1c5966I’m not sure why the RunspaceId is present either as Select-Object should be excluding this.
-
December 9, 2020 at 11:18 am #278109
What is the error you are receiving? If you only have the one property, there is no need for a custom object, just return the string. PSComputername will still be added as a property.
-
December 9, 2020 at 11:29 am #278121
PSComputerName and RunspaceID properties are created automatically with Invoke-Command. If you want to catch connection issues, it would have to be handled by the Invoke-Command and not inside the remote hosts script block. Here is a simplified example. The variable $connectionissues will capture all the errors. You can call it later and even output the contents to a file if you want.
PowerShell123Invoke-Command -ComputerName localhost, member -ScriptBlock {Get-Process} -ErrorVariable connectionissues -ErrorAction SilentlyContinue$connectionissues$connectionissues will have a targetobject property which will be the computername with the connection issue.
-
December 9, 2020 at 11:42 am #278139
Haha that makes error collection a little easier……! That’s exactly what I was after. Thanks for that.
It seems it didn’t like having the Select-Object properties on separate lines. When I use just the one line the output is as expected:
PowerShell1$results | Select-Object PSComputerName,@{n="IIS";e={[bool]($_.services | Where-Object name -eq "W3SVC")}},@{n="PSVers";e={$_.PSVersion.PSVersion.Major}} | Format-Table -AutoSizeI’m sure there’s a reason but the main thing is that I think this does everything I need now but am sure I’ll be back if it doesn’t 😉
Thanks for bearing with me guys. Much appreciated as always.
-
December 9, 2020 at 11:44 am #278142
Also, I’ve tried your code and the output is not showing information as expected:
services : {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController…}
PSVersion : {PSRemotingProtocolVersion, BuildVersion, PSCompatibleVersions, PSVersion…}
PSComputerName : ComputerName
RunspaceId : fb32f0bb-da71-42e0-8155-507bdb1c5966
I’m not sure why the RunspaceId is present either as Select-Object should be excluding this.
That looks correct for $results. In PowerShell everything is an object. What is displayed on the screen is just a representation of the object. If you use the property dereference operator, you can access the individual properties. i.e. $results.pscomputername or $results.services. The pipe at the end of the I sent should give you usable output to the screen.
PowerShell123456$results |Sort-Object PSComputerNameSelect-Object PSComputerName,@{n="IIS";e={[bool]($_.services | Where-Object name -eq "W3SVC")}},@{n="PSVers";e={$_.PSVersion.PSVersion.Major}} |Format-Table -AutoSize
-
-
AuthorPosts
- You must be logged in to reply to this topic.