I've come across a problem which I can't seem to figure out. I have this piece of code which does exactly what I want. It searches for Windows Updates which are installed and have an specific UpdateID.
param(
$updateId = $false,
$hostName = $false
)
if(($updateId -eq $false) -or ($hostName -eq $false))
{
Write-Host "checkUpdateInstalled.ps1 -updateId <updateIdValue> -hostName <Remote Host Name>"
exit
}
Invoke-Command -ComputerName $hostName -ScriptBlock {
$searcher = New-Object -ComObject Microsoft.Update.Searcher
$searcher.Search("IsInstalled=1 AND UpdateID='$Using:updateId'")
$tmp.Updates| ForEach-Object {
$i
Write-Host "UpdateInfo Update No. $i"
Write-Host "Title: `t`t" $_.Title
Write-Host "Description: `t`t " $_.Description
Write-Host "UpdateID: `t`t " $_.Identity.UpdateID
Write-Host "RevisionNumber: `t`t " $_.Identity.RevisionNumber
Write-Host "KBArticleIDs: `t`t " $_.KBArticleIDs
Write-Host "==============================================="
}
}
With this solution I can't use $tmp.Updates
outside of the Invoke-Command
but the information I try to gather with the ForEach-Object
Loop works fine. Printing $tmp.Updates
in this case gives me information about the specific update.
So I tried the following to have access to $tmp
:
## Same top part
$tmp = Invoke-Command -ComputerName $hostName -ScriptBlock {
$searcher = New-Object -ComObject Microsoft.Update.Searcher
$searcher.Search("IsInstalled=1 AND UpdateID='$Using:updateId'")
}
$tmp.Updates| ForEach-Object {
$i
Write-Host "UpdateInfo Update No. $i"
Write-Host "Title: `t`t" $_.Title
Write-Host "Description: `t`t " $_.Description
Write-Host "UpdateID: `t`t " $_.Identity.UpdateID
Write-Host "RevisionNumber: `t`t " $_.Identity.RevisionNumber
Write-Host "KBArticleIDs: `t`t " $_.KBArticleIDs
Write-Host "==============================================="
}
With this attempt the Loop does not print information. If I try to print $tmp.Updates
I just get System.__ComObject
.
Can anyone relate?
CodePudding user response:
That is the kind of behavior you will get with Invoke-Command
by design.
Invoke-Command
do not return the objects from the remote session. Rather, it return a representation of the object that did go through several processes.
First, it is serialized on the remote environment, then deserialized back on the local environment.
That is for everything that get transmitted. There are some primitive types, serialization-wise, that get deserialized into a "live" object directly, such as:
- Byte, SByte, Byte[]
- Int16, Int32, Int64, UInt16, UInt32, Uint64
- Decimal, Single, Double
- TimeSpan, DateTime, ProgressRecord
- Char, String, XmlDocument, SecureString
- Boolean,Guid, Uri, Version
Then you have types that are not deserialized with full fidelity, but behave as primitive types for most practical purposes.
This include Enums, which are deserialized into an underlying integer. Similarly, deserializer will preserve contents of lists, but might change the actual type of the container. (eg: List deserialized to ArrayList, Dictionaries deserialized into Hashtables, etc...)
Finally, you also have some objects that get rehydrated into their live counterpart. For instance, IP Address object get serialized, then deserialized into a Deserialized.System.Net.IPAddress
and converted again to its original type through "rehydration", which is the process that dictate how the deserialized type should be converted again.
There is some built-in rehydration for some of PowerShell types… :
- PSPrimitiveDictionary
- SwitchParameter
- PSListModifier
- PSCredential
as well as for some types from base class libraries:
- IPAddress, MailAddress
- CultureInfo
- X509Certificate2, X500DistinguishedName
- DirectorySecurity, FileSecurity, RegistrySecurity
So, to do what you seek, you will need to return objects that are serializable. You will need to dig into the COM object and return the properties values that you want. You could use Get-Member
to determine the available properties and from there, return what you want.
You could also use ConvertTo-Json
on the remote object to return a json representation of it and convert it back to a PSObject locally. You won't get an accurate representation either, type-wise, but you might get a better view of the properties / values. Don't forget to set the -Depth
parameter to an higher number if needed since the default is 4 layers deep.
Reference
Microsoft Dev-blogs - How objects are sent to and from remote address.