Cannot seem to identify type - powershell

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

Related

Powershell - how to replace OS Version number with String

I am querying remote servers for their operating system. I know that I can return the Version, but I want to replace these values with the friendly name. The code I have so far is:
$Computer = (gc c:\servers.txt)
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $Computer -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
If ({$BuildVersion.Version -match "5.2.3790"})
{$Build="2003"}
Elseif ({$BuildVersion.Version -match "6.1.7601"})
{$Build="2008"}
Elseif ({$BuildVersion.Version -like "6.3.9600"})
{$Build="2012"}
But this doesn't seem to work and only returns "2003" regardless. Please help, I'm fairly new to PS and coding.
thanks
The problem is your if statements. Putting the Boolean expression inside squiggly brackets makes it a script block, and that's going to get cast as a string before being cast as a Boolean. Strings cast to Booleans always evaluate to true unless they're empty.
PS C:\> {$BuildVersion.Version -match "5.2.3790"}
$BuildVersion.Version -match "5.2.3790"
PS C:\> ({$BuildVersion.Version -match "5.2.3790"}) -as [bool]
True
PS C:\> $BuildVersion.Version -match "5.2.3790"
False
PS C:\> ($BuildVersion.Version -match "5.2.3790") -as [bool]
False
So what you're running is essentially:
if ([bool]'$BuildVersion.Version -match "5.2.3790"') [...]
And that's always going to be true.
Try:
$Computer = (gc c:\servers.txt)
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $Computer -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
If ($BuildVersion.Version -match "5.2.3790")
{
$Build = "2003"
}
Elseif ($BuildVersion.Version -match "6.1.7601")
{
$Build = "2008"
}
Elseif ($BuildVersion.Version -like "6.3.9600")
{
$Build = "2012"
}
Bottom line is that squiggly brackets are not parentheses and you can't use them like they are.
However, there's also a major logic error here. You're potentially fetching an array for $BuildVersion because you're reading from a file, but then you treat it like a single value. You never loop through $BuildVersion. However, I do not have enough information about what you're actually trying to do with your script (like what you do with $Build) to be able to fix that.
I originally said this, but I've since changed my mind
The reason this is only returning 2003 is that you're only running your If code on a single entry in the list.
Wrong
As TessellatingHeckler says, the reason your if wasn't working is that you had too many curly braces, so PowerShell wasn't actually evaluating your logic.
However, you still need to step through each of the computers to do what you're trying to do. We'll do that by adding in a ForEach loop. I also went ahead and replaced your If {} logic with a Switch statement, which I think is easier to understand for a scenario like this with multiple clauses. If's just get way too verbose.
Finally, I'm assuming you want to output the results too, so I added a custom object here, which is just a way of choosing which properties we want to display.
$Computer = (gc c:\servers.txt)
ForEach ($system in $computer){
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $system -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
switch ($build){
"5.2.3790" {$Build="2003"}
"6.1.7601" {$Build="2008"}
"6.3.9600" {$Build="2012"}
}
#output results
[pscustomobject]#{Server=$system;OSVersion=$build;CSName=$buildVersion.CSname}
}#EndOfForEach
Output
>Server OSVersion CSName
------ --------- ------
dc2012 2012 DC2012
sccm1511 2012 SCCM1511
You can use this:
Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
Additionally you can see everything this WMI object holds like this:
Get-WmiObject -Class Win32_OperatingSystem | fl *
Edit: if you want to remove some text from the string, you can use -replace:
(Get-WmiObject -Class Win32_OperatingSystem |
Select-Object -ExpandProperty Caption) -replace "Microsoft Windows Server ",""

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

Array.Find on powershell array

How can I use the Array.Find method in powershell?
For example:
$a = 1,2,3,4,5
[Array]::Find($a, { args[0] -eq 3 })
gives
Cannot find an overload for "Find" and the argument count: "2".
At line:3 char:1
+ [Array]::Find($a, { $args[0] -eq 3 })
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
The array class has the methods I expect, as given by:
PS> [Array] | Get-Member -Static
TypeName: System.Array
Name MemberType Definition
---- ---------- ----------
Find Method static T Find[T](T[] array, System.Predicate[T] match)
Should the array be casted to something else to match the T[] type? I know there are other ways to achieve the find functionality, but I was wondering why this doesn't work.
There is no need to use Array.Find, a regular where clause would work fine:
$a = #(1,2,3,4,5)
$a | where { $_ -eq 3 }
Or this (as suggested by #mjolinor):
$a -eq 3
Or this (returns $true or $false):
$a -contains 3
Where clause supports any type of objects, not just basic types, like this:
$a | where { $_.SomeProperty -eq 3 }
You need to cast the ScriptBlock as a Predicate[T]. Consider the following example:
[Array]::Find(#(1,2,3), [Predicate[int]]{ $args[0] -eq 1 })
# Result: 1
The reason that you received the error, is because there was no matching method overload, in the case where you're passing in a PowerShell ScriptBlock. As you noted in your Get-Member output, there is no Find() method overload that accepts a ScriptBlock as its second parameter.
[Array]::Find(#(1,2,3), { $args[0] -eq 1 })
Cannot find an overload for "Find" and the argument count: "2".
At line:1 char:17
+ [Array]::Find(#(1,2,3), { $_ -eq 1 })
+ ~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Another option would be using an ArrayList, which provides a Contains method:
PS C:\> [Collections.ArrayList]$a = 'a', 'b', 'c'
PS C:\> $a.Contains('b')
True
PS C:\> $a.Contains('d')
False
Or, as #Neolisk mentioned in the comments, you could use PowerShell's -contains operator:
PS C:\> $a = 'a', 'b', 'c'
PS C:\> $a -contains 'b'
True
PS C:\> $a -contains 'd'
False
Trevor Sullivan's answer is the right one, not only for Find() static method, but for FindIndex() as well.
When you've got several NIC cards with both ipv4 & ipv6 active on your servers and want to check the ipv4 IP/netmask pairs, something like this is good :
$NetworkAdapters = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = True' | Select-Object -Property Description, IPAddress, IPSubnet, DefaultIPGateway, DNSServerSearchOrder, DNSDomain
$NetworkAdapters | % {
"Adapter {0} :" -f $_.Description
# array'ing to avoid failure against single homed netcards
$idx = [System.Array]::FindIndex(#($_.IPAddress), [Predicate[string]]{ $args[0] -match "\d+.\d+.\d+.\d+" })
" IP {0} has netmask {1}" -f #($_.IPAddress[$idx]), #($_.IPSubnet)[$idx]
}
My point is it works like a charm on 2012 WinPE, and fails on a production Win7 wks. Anyone got an idea ?
This was run across ~6 million items in a system.array using both methods
$s=get-date
$([array]::FindALL($OPTArray,[Predicate[string]]{ $args[0] -match '^004400702(_\d{5})?' })).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds
20 items
33.2223219 seconds
$s=get-date
$($OPTArray | where { $_ -match '^004400702(_\d{5})?'}).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds
20 items
102.1832173 seconds

Why don't errors get returned when calling properties that don't exist?

Given the following snippet
$drives = Get-PSDrive
foreach($drive in $drives)
{
Write-Host $drive.Name "`t" $drive.Root
Write-Host " - " $drive.Free "`t" $drive.PropertyDoesntExist
}
The drive.PropertyDoesntExist property doesn't... erm... exist so I would expect an error to be thrown but instead it returns a null.
How can I get errors or exceptions?
EDIT - Me bad - I asked 2 questions in one so I moved one into a separate question.
The NextHop Blog provides a good solution to this problem. It doesn't give you an error, but instead a boolean. You can use Get-Member to get a collection of all of the real properties of the object's type and then match for your desired property.
Here's an example for strings:
PS C:\> $test = "I'm a string."
PS C:\> ($test | Get-Member | Select-Object -ExpandProperty Name) -contains "Trim"
True
PS C:\> ($test | Get-Member | Select-Object -ExpandProperty Name) -contains "Pigs"
False
If you explicitly want an error, you may want to look into Set-Strictmode as Set-StrictMode -version 2 to trap non-existent properties. You can easily turn it off when you're done with it, too:
PS C:\> Set-StrictMode -version 2
PS C:\> "test".Pigs
Property 'Pigs' cannot be found on this object. Make sure that it exists.
At line:1 char:8
+ "test". <<<< Pigs
+ CategoryInfo : InvalidOperation: (.:OperatorToken) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
PS C:\> Set-StrictMode -off

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'