How to get WMI object from a WMI object reference? - powershell

Similar to this question except that no answer was given with regards to the main question of getting an object from reference.
For example:
PS C:\Users\admin> Get-WmiObject -Namespace $namespace -Class $class
...
IsActive : 1
oA: \\.\ROOT\abc\abc\ABC:abc.xyz="tst2"
oB : \\.\ROOT\abc\abc\ABC:abc.xyz="tst3"
PSComputerName : admin-test2
oA and oB are references and therefore come up as strings in powershell. Is there a way I can get the object they represent using WMI query in powershell?

Assuming that oA and oB actually are strings you should be able to resolve these WMI paths to WMI objects like this:
Get-WmiObject -Namespace $namespace -Class $class | ForEach-Object {
$oA = [wmi]$_.oA
$oB = [wmi]$_.oB
}
Example:
PS C:\> $namespace = 'root/cimv2'
PS C:\> $class = 'Win32_OperatingSystem'
PS C:\> $obj1 = Get-WmiObject -Namespace $namespace -Class $class
PS C:\> $obj1
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 7601
RegisteredUser : foo
SerialNumber : 00371-OEM-8310595-XXXXX
Version : 6.1.7601
PS C:\> $obj1.GetType().FullName
System.Management.ManagementObject
PS C:\> $obj1.Path.Path
\\FOO\root\cimv2:Win32_OperatingSystem=#
PS C:\> ($obj1.Path.Path).GetType().FullName
System.String
PS C:\> $obj2 = [wmi]$obj1.Path.Path
PS C:\> $obj2
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 7601
RegisteredUser : foo
SerialNumber : 00371-OEM-8310595-XXXXX
Version : 6.1.7601
PS C:\> $obj2.GetType().FullName
System.Management.ManagementObject
Your question is rather vague, though, so I'm not sure if this answer actually covers what you've been asking.

As OP mentioned that all he wants is a generic answer (which is again tough given the nature of Object Paths and dependency on key), I am giving another example of using Associators Of WMI query.
$query = "ASSOCIATORS OF {Win32_Account.Name='DemoGroup2',Domain='DomainName'} WHERE Role=GroupComponent ResultClass=Win32_Account"
Get-WMIObject -Query $query | Select Name
If you need to use the example above, you need to first find out what is the key property and use that in the object path.
-----Original answer -----
What namespace? What class? You need to use associations and/or references to retrieve that. It is hard to give a generic answer unless we know the exact object path. For example,
$query = "REFERENCES OF {Win32_Service.Name='Netlogon'} WHERE ClassDefsOnly"
Get-WMIObject -Query $query
The above query will give all references of Win32_Service with an object path ServiceName='NetLogon'

Related

Cannot seem to identify type

Modeled on the example in Get-Help about_Type_Operators:
PS C:\> (get-culture) -is [System.Globalization.CultureInfo]
True
I am trying to do just about the same thing with a different type. Why does this fail? I copied the type name from the output of Get-TypeData.
(My apologies for the original question using is instead of -is.)
This suggestion did not work.
PS C:\> (Get-WMIObject -Class Win32_BIOS) -is [System.Management.ManagementObject#root\cimv2\Win32_BIOS]
Unable to find type [System.Management.ManagementObject#root\cimv2\Win32_BIOS].
At line:1 char:1
+ (Get-WMIObject -Class Win32_BIOS) -is [System.Management.ManagementOb ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Manageme...imv2\Win32_BIOS:TypeName)
[], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
On a related note, what is the purpose of each of these?
PS C:\> Get-TypeData | Where-Object {$_.TypeName -like '*Win32_BIOS' }
TypeName Members
-------- -------
System.Management.ManagementObject#root\cimv2\Win32_BIOS {}
Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_BIOS {}
Assuming...
PS> $bios = Get-WmiObject -Class Win32_BIOS
...you can use the __CLASS system property to test the specific WMI class of an object like this...
PS> $bios.__CLASS -eq 'Win32_BIOS'
True
...or this...
PS> $bios.SystemProperties['__CLASS'].Value -eq 'Win32_BIOS'
True
You might also test the namespace just to be really sure you've got the right class:
PS> $bios.__NAMESPACE -eq 'root\cimv2' -and $bios.__CLASS -eq 'Win32_BIOS'
True
Note that the comparisons above don't work exactly like -is because you are testing for the exact class, whereas -is takes the class hierarchy into account. That is, the following fails even though Win32_BIOS inherits from CIM_BIOSElement:
PS> $bios.__CLASS -eq 'CIM_BIOSElement'
False
The reason why $bios | Get-Member shows System.Management.ManagementObject#root\cimv2\Win32_BIOS as the type name is because Win32_BIOS and its inheritance chain have been added to the TypeNames property...
PS> $bios.PSObject.TypeNames
System.Management.ManagementObject#root\cimv2\Win32_BIOS
System.Management.ManagementObject#root\cimv2\CIM_BIOSElement
System.Management.ManagementObject#root\cimv2\CIM_SoftwareElement
System.Management.ManagementObject#root\cimv2\CIM_LogicalElement
System.Management.ManagementObject#root\cimv2\CIM_ManagedSystemElement
System.Management.ManagementObject#Win32_BIOS
System.Management.ManagementObject#CIM_BIOSElement
System.Management.ManagementObject#CIM_SoftwareElement
System.Management.ManagementObject#CIM_LogicalElement
System.Management.ManagementObject#CIM_ManagedSystemElement
System.Management.ManagementObject
System.Management.ManagementBaseObject
System.ComponentModel.Component
System.MarshalByRefObject
System.Object
The actual type is still ManagementObject...
PS> $bios.GetType().FullName
System.Management.ManagementObject
You are using the string is as the comparison operator; however, all comparison operators begin with the hyphen, so you should be using -is: (Get-WMIObject -Class Win32_BIOS) -is [System.Management.ManagementObject...]

Powershell, WMI Methods

using WMI with PowerShell I found something I don't understand:
# Syntax 1
gwmi -Class Win32_Share | gm -MemberType Method
Output: Delete, GetAccessMask, SetShareInfo
# Syntax 2
$a = New-Object "System.Management.ManagementClass" "Win32_Share" :
$a | gm -MemberType Method
Output: Create.....
So: why I don't get the "Create" Method using the syntax "1"?
Because they return two different types of objects.
(gwmi -Class Win32_Share).GetType()
returns a System.Array instance while
(New-Object "System.Management.ManagementClass" "Win32_Share").GetType()
returns a System.Management.ManagementObject instance
Note that it doesn't make sense to call Create on an already instantiated object anyway, or in other words: why do you think you need it?
Edit
Your comment actually made me rethink (finally) and your conondrum is that you should use -query instead of -class. I have yet to figure out what the actual difference between both methods of calling is but I assume it's the same class/instance distinction.
Get-WmiObject -query "SELECT * FROM meta_class WHERE __class = 'Win32_Share'"

Powershell how to get the ParentProcessID by the ProcessID

I've problems to get the ParentProcessID from a Process where I have the ProcessID. I tried it like this, this is how it works with the ProcessID:
$p = Get-Process firefox
$p.Id
But if I try it with the ParentProcessID, it doesn't work:
$p.ParentProcessId
Is there a way to get the ParentProcessID by the ProcessID?
As mentioned in the comments, the objects returned from Get-Process (System.Diagnostics.Process) doesn't contain the parent process ID.
To get that, you'll need to retrieve an instance of the Win32_Process class:
PS C:\> $ParentProcessIds = Get-CimInstance -Class Win32_Process -Filter "Name = 'firefox.exe'"
PS C:\> $ParentProcessIds[0].ParentProcessId
3816
This worked for me:
$p = Get-Process firefox
$parent = (gwmi win32_process | ? processid -eq $p.Id).parentprocessid
$parent
The output is the following:
1596
And 1596 is the matching ParentProcessID I've checked it with the ProcessExplorer.
In PowerShell Core, the Process object returned by Get-Process cmdlet contains a Parent property which gives you the corresponding Process object for the parent process.
Example:
> $p = Get-Process firefox
> $p.Parent.Id
I wanted to get the PPID of the current running PS process, rather than for another process looked up by name. The following worked for me going back to PS v2. (I didn't test v1...)
$PPID = (gwmi win32_process -Filter "processid='$PID'").ParentProcessId
Write-Host "PID: $PID"
Write-Host "PPID: $PPID"

Pass array of CimInstance to CimMethod

I am working with WMI API through Cim cmdlets. The problem is that I can't figure out how to pass wmi object to wmi method that accepts array of wmi objects.
Here are method parameters definition:
Name CimType Qualifiers
---- ------- ----------
Path String {ID, in}
Permissions InstanceArray {EmbeddedInstance, ID, in}
ResetChildren Boolean {ID, in}
Path and ResetChildren are simple parameters. They accepts simple values like "/path" and $true respectively. But I have troubles with Permissions parameter.
Here is my code
#Acquiring object that I want to pass to method
$group = Get-CimInstance -Namespace "root\VisualSVN" -ClassName VisualSVN_Group -Filter "Name='Readers'"
#Acquiring object which method will be called
$repositories = Get-CimInstance -Namespace "root\VisualSVN" -ClassName VisualSVN_Repository
#Preparing method arguments
$args = #{
Path = "/";
Permissions = #($group[0]); #Trouble here
ResetChildren = $true
}
#Invoking method with arguments
Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Arguments $args
Execution of this code will result in error:
Invoke-CimMethod : Unable to cast object of type 'Microsoft.Management.Infrastructure.CimInstance' to type 'M
icrosoft.Management.Infrastructure.Native.InstanceHandle'.
Parameter name: value
At C:\somepath\script1.ps1:11 char:1
+ Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Argume ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-CimMethod], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.Management.Infrastructure.CimCmdlets.Invoke
CimMethodCommand
If you change code
Permissions = #($group[0]); #Trouble here
To code
Permissions = $group; #Trouble here
Then error message will also change:
Invoke-CimMethod : Unable to cast object of type 'Microsoft.Management.Infrastructure.Native.InstanceHandle'
to type 'System.Collections.IList'.
Parameter name: value
At C:\somepath\script1.ps1:11 char:1
+ Invoke-CimMethod -InputObject ($repositories[0]) -MethodName SetSecurity -Argume ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-CimMethod], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.Management.Infrastructure.CimCmdlets.Invoke
CimMethodCommand
Any ideas how to pass $group to method properly?
I had exactly same problem with VisualSVN_Repository::SetSecurity method.
When working with CIM method arguments, you must cast ANY array arguments to [CimInstance[]].
For example, this worked for me:
$Everyone = Get-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_Everyone
# Grant Everyone a Read/Write access:
$AccessRule = New-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_PermissionEntry -ClientOnly -Property #{ Account = $Everyone; AccessLevel = [UInt32]2 }
$SvnRepo = Get-CimInstance -Namespace root/VisualSVN -ClassName VisualSVN_Repository -Filter "Name='MY_REPOSITORY_NAME'"
Invoke-CimMethod -InputObject $SvnRepo -MethodName SetSecurity -Arguments #{
Path = '/';
Permissions = [CimInstance[]]$AccessRule;
ResetChildren = $true
} | Out-Null
You must cast array argument to [CimInstance[]] even when it is just a single item.
P.S.: Be careful with Ref array arguments too: you must first cast it to [CimInstance[]] and then to [ref[]]. For example, when calling VisualSVN_Group::Create method:
[CimInstance[]] $SvnUsers = [CimInstance[]]($ArrayOf_VisualSVN_User_Objects)
Invoke-CimMethod -ClassName VisualSVN_Group -Namespace root/VisualSVN -MethodName Create -Arguments #{
Members = [ref[]]$SvnUsers;
Name = 'MY_NEW_GROUP_NAME'
} | Out-Null
See also: Tip#5: Passing ref and embedded instances on PowerShell Blog.
If you had a repro that did not rely on software I do not have installed, I could answer definitively, so I will make my best attempt based on what I can infer from your question.
To make what you are assigning to Permissions an array, and you are only assigning one value, use a comma in front.
Permissions = ,$group
Here is some sample script and the output to demonstrate.
Script
$var1 = "any value"
$var2 = ,$var1
$var1.GetType()
$var2.GetType()
$var2.Count
$var2[0]
Output
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True Object[] System.Array
1
any value
A possible solution (it worked for me with a similar issue that led me here) based on the 'hint' that CIM are inert objects. In my case it was installing SCCM updates in a scripted manner while allowing for reboots if the updates required them, and stopping if the entire process took more than a certain amount of time (to not go past the available maintenance window).
I was passing a CIMUpdate object through to a powershell function to perform the update to allow for rebooting if necessary between updates (Perform a single update at a time, verify if reboot is needed, then try the next update).
The CIM object was giving a Type Mismatch error when forcing it to cast as [CIMInstance[]], and if not casting at all I would get the same Unable to cast object of type 'Microsoft.Management.Infrastructure.CimInstance' to type 'Microsoft.Management.Infrastructure.Native.InstanceHandle' error message.
Solution was to use that CIMUpdate and re-query to get a live version of the object within the function and then use that new object as the argument to the CIMMethod.
as of now, it works for me , the case I am working on is passing New-ScheduledTaskTrigger instance to a function.
I am passing a CimInstance to a function and receiving it as PSCustomObject type.
Send CimInstance receive as [PSCustomObject] -> at target it remains CimInstance object type
Send CimInstance receive as [CimInstance] -> at target it remains CimInstance object type
Send CimInstance receive as [CimInstance[]] -> at target it turn to CimInstance array type

PowerShell's Write-Debug won't output arrays, but Write-Output does. Is this on purpose?

Shown below, an array works fine as input for Write-Output but not for Write-Debug (I expected them to be more similar than that).
PS C:\> [string[]]$test = #("test1", "test2", "test3")
PS C:\> Write-Output $test
test1
test2
test3
PS C:\> $DebugPreference = "Inquire"
PS C:\> Write-Debug $test
Write-Debug : Cannot convert 'System.String[]' to the type 'System.String' required by parameter 'Message'. Specified method is not supported.
At line:1 char:12
+ Write-Debug <<<< $test
+ CategoryInfo : InvalidArgument: (:) [Write-Debug], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.WriteDebugCommand
PS C:\>
I'm thinking this is just an unfortunate design, but hoping for a sensible explanation. Am I using Write-Debug correctly? If so, anyone have a favorite simple workaround?
I kept having the same problem, and none of the solutions I found above or anywhere else would work in the general case.
For example, the first answer above works only because the array is an array of strings. If it's an array of anything else, that solution breaks, and Write-Debug will output the object type, and not its value as one would expect.
Finally I found a general solution: The key point is to first convert the input object to a string using the Out-String command. Once everything is a string, the above solution works. Using "Out-String -stream" improves the output alignment.
Example:
PS C:\> gwmi win32_bios
SMBIOSBIOSVersion : 786F3 v01.34
Manufacturer : Hewlett-Packard
Name : Default System BIOS
SerialNumber : CZC8196Q8S
Version : HPQOEM - 20120709
PS C:\> gwmi win32_bios | ft -auto
SMBIOSBIOSVersion Manufacturer Name SerialNumber Version
----------------- ------------ ---- ------------ -------
786F3 v01.34 Hewlett-Packard Default System BIOS CZC8196Q8S HPQOEM - ...
PS C:\> $DebugPreference = "Continue"
PS C:\> gwmi win32_bios | ft -auto | Write-Debug
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.GroupEndData
PS C:\> gwmi win32_bios | ft -auto | Out-String | Write-Debug
DEBUG: SMBIOSBIOSVersion Manufacturer Name SerialNumber Version
----------------- ------------ ---- ------------ -------
786F3 v01.34 Hewlett-Packard Default System BIOS CZC8196Q8S HPQOEM - ...
PS C:\> gwmi win32_bios | ft | Out-String -stream | Write-Debug
DEBUG:
DEBUG: SMBIOSBIOSVersi Manufacturer Name SerialNumber Version
DEBUG: on
DEBUG: --------------- ------------ ---- ------------ -------
DEBUG: 786F3 v01.34 Hewlett-Packard Default Syst... CZC8196Q8S HPQOEM - 201...
DEBUG:
DEBUG:PS C:\>
If you want write-debug to handle each one separately:
[string[]]$test = #("test1", "test2", "test3")
Write-Output $test
test1
test2
test3
$DebugPreference = "Inquire"
$test | Write-Debug
DEBUG: test1
DEBUG: test2
DEBUG: test3
Write-Debug is designed for outputting simple messages when debug preferences are set in a particular way. It takes only a string, not just anything like Write-Host does (and magically formats). You will have to format your output yourself into a single string.
You could combine Write-Host and Write-Debug if you have extra info to output before prompting the user:
if ($DebugPreference -ne 'SilentlyContinue') {
Write-Host 'such-and-such array:' $array
}
Write-Debug 'such-and-such array dumped'
Write-Host is used because it will always write to the console host, rather than to the script's output, as Write-Output does. If you where redirecting standard output of the script to a file, Write-Output would end up in the file, while Write-Host would still be seen in the console.
You could also try doing something like this if your array is of simply enough types that an automatic call to ToString() on them (if they're not strings already) gets you what you want:
$array = 'Alice','Bob','Charlie'
Write-Debug ([String]::Join("`n", $array))
Write-Debug:
Write-Debug [-Message] <string> [<CommonParameters>]
It expects a string. It is unable to convert a string array to a string as the error says. The reason why it expects a string is because it writes debug messages to the console from a script or command.
Note that Write-Output and Write-Host take an object:
Write-Output [-InputObject] <PSObject[]> [<CommonParameters>]
and
Write-Host [[-Object] <Object>] ...