Pass array of CimInstance to CimMethod - powershell

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

Related

ConvertTo-EnhancedHTML : Parameter set cannot be resolved using the specified named parameters

The code:
$Bios = Get-WmiObject -class Win32_BIOS | Select-Object -Property PSComputerName,Manufacturer,BIOSVersion | ConvertTo-EnhancedHTMLFragment -As List
$Bios2 = Get-WmiObject -class Win32_BIOS | Select-Object -Property PSComputerName,Manufacturer,BIOSVersion | ConvertTo-EnhancedHTMLFragment -As Table
ConvertTo-EnhancedHTML -HTMLFragments $Bios,$Bios2 | Out-File AsExample.html
The error:
ConvertTo-EnhancedHTML : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:1
+ ConvertTo-EnhancedHTML -HTMLFragments $Bios,$Bios2 | Out-File AsExamp ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ConvertTo-EnhancedHTML], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,ConvertTo-EnhancedHTML
I don't understand why I am receiving the error. I've examined the help, and what I'm doing seems like it ought to be fair game:
-HTMLFragments <String[]>
One or more HTML fragments, as produced by ConvertTo-EnhancedHTMLFragment.
-HTMLFragments $part1,$part2,$part3
Required? true
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
I have resolved the error by specifying an additional parameter:
ConvertTo-EnhancedHTML -HTMLFragments $Bios,$Bios2 -CSSStyleSheet syles.css | Out-File AsExample.html
While reviewing the 2 available parameter sets for ConvertTo-EnhancedHTML, I noticed that there was only 1 parameter required in each one -- -HTMLFragments. The error was telling me it didn't know which parameter set I wanted to use. So what I ended up having to do was specify the -CSSStylesheet parameter, as that parameter and its opposing -CSSUri parameter are the only parameters unique to their respective sets. In other words, the command wouldn't execute because I had to specify one of the CSS parameters so powershell knew which parameter set to use DESPITE THE FACT THAT neither of the CSS parameters are actually required parameters. Wow!

Cannot convert System Object to String Powershell

Hi I am trying to use the below powershell script
$get_AD_Server = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration).DnsDomain | Out-String
$get_Nearest_DC = (Get-ADDomainController -DomainName $get_AD_Server -Discover -NextClosestSite).Name
The output of $get_AD_Server contains contoso.com, however when i pass the variable $get_AD_Server in the next variable it always errors out, any idea on what am i doing wrong?
Get-ADDomainController : The format of the specified domain name is invalid
At line:2 char:20
+ ... arest_DC = (Get-ADDomainController -DomainName $get_AD_Server.ToStrin ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-ADDomainController], ADException
+ FullyQualifiedErrorId : GetADDomainController:BeginProcessingOverride:DiscoverDC:1212,Microsoft.ActiveDirectory.Management.Commands.GetADDomainController
Automatic variable unrolling will return a collection.
DNSDomain property may not be populated. In my case it isn't. Assuming you've got that covered I think you might have better luck if you isolate the NIC configuration you care about. If you narrow the return to 1 object .DNSDomain will be a string.
In my case this looks like:
(Get-WmiObject win32_NetworkAdapterCOnfiguration -Filter "IPEnabled = 'True'").DnsDomain
If needed just work on the filter until you find something that reliably only returns the NIC you care about.
Note: I may have misread something, but I worry you'll have an issue with the next step. You may have trouble querying the AD domain when you aren't authenticated. If you do hit something like that you may consider using the -Credential parameter on Get-ADDomainController. Of course it'd be interactive at that point.
Instead of fetching the DNS domain associated with the NIC, pull the computers domain from the Win32_ComputerSystem class:
$domain = (Get-WmiObject -Class Win32_ComputerSystem).Domain
$nearestDC = (Get-ADDomainController -DomainName $domain -Discover -NextClosestSite).Name

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

How to get WMI object from a WMI object reference?

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'