Problem
I want to know if there is a easy way to search multiple objects in a WMI object or CIM instance.
I'm aware it's possible with commands like
Get-CimInstance Win32_BaseBoard | Select-Object Manufacturer,Product
But I want a command with a dot notation where you can set multiple objects for a search like (Get-CimInstance Win32_BaseBoard).Manufacturer with more than one object.
Something like (Get-CimInstance Win32_BaseBoard).Object1.Obejct2.Object3
Ben's solution from the comments will work but note that it calls Get-CimInstance once for each property you want, even though that's unnecessary (could take a while depending on the call you're making).
Let's look at it a few other ways. We'll start by storing the wanted property names in an array.
$properties = 'Manufacturer', 'Product'
Now we can do something similar to what Ben did:
$allValues = $properties |
ForEach-Object -Begin {
$bb = Get-CimInstance Win32_Baseboard
} -Process {
$bb.$_
}
That keeps his approach but does the CIM call once.
If you want to do with dot notation purely, you can use the .ForEach() method and the .PSObject hidden property to get at the properties:
(Get-CimInstance Win32Baseboard).ForEach({$_.PSObject.Properties.Where({$_.Name -in $properties}).Value})
As noted, DotNotation (Intellisense) is 1:1 / single node / collection thing. You can validate this, by pushing the result of the call to XML and walking the nodes.
(Get-CimInstance Win32_BaseBoard | ConvertTo-Xml).Objects.Object.Property
(Get-CimInstance Win32_BaseBoard | ConvertTo-Xml).Objects.Object.Property.name
(Get-CimInstance Win32_BaseBoard | ConvertTo-Xml).Objects.Object.Property.'#text'
The only other option off the top of my head is constructing a proxy that uses enums and switches for all possible properties for the namespace you are using.
That's way more unneeded effort and code to just do what BenH has pointed out, specifically because you'd have to do that for every class.
Now, if you just wanted to shorthand this, maybe do this
$Base = Get-CimInstance Win32_BaseBoard
$Base.Manufacturer;$base.Model;$base.Name;$Base.PartNumber
But that is just unwieldy, especially, since all this really doing is single commands set on a single line with the command break separator, the semi-colon. Which is a wrong thing to do. IMHO.
---small rant ---
If you need the separator, then just put the next thing on a new line and avoid the semi-colon. I mean, I can see that semi-colon use as the PoSH console host, but in a real script, function, module, well, just, no. Again, IMHO
--- small rant ---
Lastly, depending on what your target PoSH version is, DotNotation had issues in v2 - v3 days
Related
Started in recent weeks in a Junior infrastructure role, and begun playing around with powershell to help save some time here and there.
I am trying to do the following:
1- I'm port a CSV file with a single column names asset
2- Perform a "ForEach" check on each line to find the device's serial number
3- Output results to a CSV with two column "asset" and "serialnumber"
I have dabbled in a few areas, and am currently sitting at something like this:
$file1 = Import-Csv -path "c:\temp\assets.csv" | ForEach-Object {
$asset = $_.asset
}
wmic /node:$asset bios get serialnumber
Export-Csv -Path "c:\temp\assetandserial.csv" -NoTypeInformation
As you may or may not see, I tried to set the column labelled "asset" as the variable, however, not sure if I placed it correctly.
I have tried a few other things, but honestly it's all new to me, so I haven't the foggiest idea where to go from here.
wmic is deprecated, and, for rich type support (OO processing), using PowerShell-native cmdlets is preferable in general.
wmic's immediate PowerShell counterpart is Get-WmiObject, which, however, is equally deprecated, in favor of Get-CimInstance.
Important: The command below uses Get-CimInstance, but note that the CIM cmdlets use a different remoting protocol than the obsolete WMI cmdlets. In short: To use the CIM cmdlets with remote computers, those computers must be set up in the same way that PowerShell remoting requires - see this answer for details.
Get-CimInstance Win32_BIOS -ComputerName (Import-Csv c:\temp\assets.csv).asset |
Select-Object #{ n='Asset'; e='PSComputerName' }, SerialNumber |
Sort-Object Asset |
Export-Csv c:\temp\assetandserial.csv -NoTypeInformation
Note the use of member-access enumeration to extract all .asset values directly from the collection of objects returned from Import-Csv.
All computer (asset) names are passed at once to Get-CimInstance, which queries them in parallel. Since the ordering of the responses from the targeted remote machines isn't guaranteed, Sort-Object is used to sort the results.
A calculated property is used with Select-Object to rename the automatically added .PSComputerName property to Asset.
I'm just wondering, what is the optimal, fastest way of retrieving a property in powershell?
I'm using the (). right now to identify a remote computer's architecture (x86 or x64):
$arch = (Get-WmiObject –ComputerName XXXXX –Class Win32_OperatingSystem).osarchitecture
sometimes on a very very slow link, the command takes a while to resolve. For this reason, is there a faster way of retrieving a property than the (). method? I know there are different methods, for example:
... | Select-Object -expandproperty osarchitecture
Any suggestion as to which is better amongst all the possibilites? Thank you
Comment from TheIncorrigible1 chosen as answer:
No, there is not a "faster" way to retrieve a powershell object's
property. What is slow is your RPC connection to the PC. As far as the
most supported option? Select-Object -ExpandProperty since the ().
syntax doesn't work on collections before v3 – TheIncorrigible1
I have an example code snippet that suggests using
(Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}).Count
to return the count of all processess using > 20Mb.
It works, but when typing, neither Intellisense or the "Tab" key shows this property, rather they show the properties of an individual process - which I find misleading.
I understand, that specifying an item property will give me the list of that property only, but is there a way to easily see, in general, what ALL the valid propeties are, including list aggregates etc?
Even assigning to a variable
$processes = Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}
does not show me "Count" as a valid property of $processes until AFTER the assignment has been actually run and the value assigned - when writing the script it still shows the properties for an individual item.
For me, Intellisense / Tab help that does not cover all the options kind of defeats the purpose ... (not having to remember hundreds objects/functions and their properties / parameters).
Is there any way to improve this situation? Some syntax trick have I missed?
The correct way to find out all of the properties of an object is to pipe the output to Get-Member:
Get-Process | Get-Member
Sometimes there are hidden properties and methods that can only be seen if you add the -force switch:
Get-Process | Get-Member -Force
The count property is an automatic property that is always usable on any collection object but that isn't explicitly listed as a property. Another example of an automatic property is length.
Using #() to force an array type is handy when that is what is wanted.
e.g. $processes = #(Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}). will show you "Count" and the other array properties.
Other than that, let's say the Intellisense has various limitations / shortcomings that I will just have to learn... sigh.
I'm having a problem getting Powershell to behave the way I'm expecting.
I'm trying to use get-wmiobject win32_networkconnection to list the mapped drives for the current user, so I can loop through the drives.
When I run $var = get-wmiobject win32_networkconnection | select -expand localname I get exactly what I expect: a list of the drive letters for the mapped network connections.
However, when I run $var = (get-wmiobject win32_networkconnection).localname I get nothing. It doesn't seem to be selecting the property correctly.
This is problematic, because, ideally, I'd like to loop over all the drives, and then select the various properties for each drive. Instead, it seems like I'll be forced to kludge together an iterator, and then iterate over all the variables one at a time (not very elegant, in my opinion).
I'm not super experienced with Powershell, so there may be something I'm missing. However, from what I've read, this should be working. Is this a limitation of get-wmiobject?
What you're trying to do only works in PowerShell 3.0 and newer versions. The official documentation is very vague:
What's New in Windows PowerShell 3.0
Windows PowerShell Language Enhancements
Windows PowerShell 3.0
includes many features [...] The improvements include
property enumeration, count and length properties on scalar objects,
new redirection operators [...]
This blog post goes a bit more into depth: New V3 Language Features
Yes, this is a limitation of PowerShell 2.0.
Your call to Get-WmiObject is returning an array. In PS2, you would need to pipe the array into something like Select-Object or otherwise iterate over it and reference each individual item.
In PS3+, you can use $array.PropertyName and it does that for you, returning an array of properties.
intead of select propertyName, you can use select -exp propertyName
In PoweShell 2 we did:
Get-ChildItem | ForEach-Object {$_.LastWriteTime} | Sort-Object
In Powershell 3 we do:
(Get-ChildItem).LastWriteTime | Sort-Object
But how does it work, i read this blog post on MSDN and they say that its faster because the foreach loop isnt running? So how does it enumerate the properties then ?
PowerShell is doing the hard work for us and it loops over the collection internally. I like to call this "implicit foreach". Assuming the member you specified is present on each object, if the member you specified is a property, you get back its value. If it's a method, it invokes the method on the each object.
In v2, to get all process names you had to take care of looping yourself:
Get-Process | Foreach-Object {$_.Name}
In v3, the equivalent would be:
(Get-Process).Name
Same applies to methods. To kill all processes with name starting with note*:
(Get-Process note*).Kill()
The blog says foreach-object cmdlet is not running. Now it is taken care of by the language engine and not a cmdlet, making it faster. How it EXACTLY works is internal implementation detail and I think that is not what you really want to know.