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
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 started with this, on a recommendation from a friend
Get-WmiObject win32_product | ft name, version
But then I found this, which gives me pause.
A little research led me to this
wmic product where "Name='Revit 2018'" get name,version
Which works as far as the data gathered. And yes, I am looking for a different program in this example. in any case, once I had good info using WMIC I tried to get the data into a variable so I could get just the version number, but the data formatting is something I have never seen before. I was hoping for a simple solution, like
$object = wmic product where "Name='Revit 2018'" get name,version
$object.version
But only the result is an array with 6 items, and only one seems to be the actual data line, and that's a single line, not two properties. And, I really wonder if an old command line utility is the right answer here. If it really is the best way to do this, is there a trick to converting the raw data to something more, PowerShelly? And if it's not the best way to get this info, what is? Is that scary link real, or is Get-WmiObject win32_product actually safe? And if so, is there a way to filter on a specific name, to speed things up? And indeed, Get-WmiObject doesn't work as I was expecting, as
$object = Get-WmiObject win32_product | ft name, version
foreach ($item in $object) {
Write-Host "$($item.version)"
}
Doesn't work as expected at all.
EDIT: This seems to be working as expected, which is progress.
$version = (Get-WmiObject win32_product -filter:"Name = 'Revit 2018'" | Select-Object -property:*).version
Write-Host "$version!"
I guess the question is really, is this a safe and consistent approach, or is there a better one?
Why not use the registry?
Set-Location HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall
$app = Get-ChildItem | Where-Object { $_.GetValue("DisplayName") -match 'YourSoftware' }
$app.GetValue("DisplayVersion")
Or
Set-Location HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall
$apps = Get-ChildItem
foreach ($app in $apps) {
$app.GetValue("DisplayName","DisplayVersion")
}
Note: You'll also need to check the SysWow64 registry location as well
HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
Note: Not all items will have a display version in which case you always have the option of looking in the installation directory for the executable itself, which should have a version on it.
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
Okay, sorry for the probably noobish question.
I've been studying PowerShell for a while now, and have run into something I can't quite figure out how to word correctly for google.
In the most basic sense, here is what I'm trying to do.
Get-Process -id 76*
Now I understand that -id will not handle wildcard * characters.
If I wanted to in theory use
Get-Process -id
and create a wildcard script for this purpose, how would I do this? Do i need to create my own function?
I'd like to add as well that PS says specifically the * is not a usable character for the -Name Parameter, yet I can use this. Is this an error with MS?
Thank you for any advice in advance!
Use a (Where-Object) filter over the Get-Process output.
In this case:
Get-Process | where { $_.Id -like '76*' }
(where is an alias for Where-Object cmdlet.)
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