Using Where-Object with an object in PowerShell - powershell

I can't seem to get my brain around something and I am hoping someone can help
I have an array of objects called $SnapVMsAll that looks like this:
VMName Name SnapshotType CreationTime ParentSnapshotName
------ ---- ---------- ------------ ------------------
SHARED-server.host.com SHARED-server.host.com - (02/10/2017 - 13:02:44) Standard 02/10/2017 13:05:58
I need to display all the records in this array of objects that have string "Veeam" in the name column, but I think I am having problems isolating a specific attribute of the object to compare.
My attempts have been as follows:
echo $SnapVMsAll | Where-Object (Foreach-Object -MemberName name) -Like "Veeam Replica"
This returned the error:
Where-Object : Cannot validate argument on parameter 'Property'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
I have also tried
echo $SnapVMsAll | Where-Object $SnapVMsAll.name -Like "Veeam Replica"
But again I get the same error.

$SnapVMsAll does not exist inside the pipeline, it is the object you pass to the pipeline. Within a pipeline, objects can be accessed using the automatic variable $_.
Also note that -Like is usually used with wildcards to match a string. If the name is Veeam Replica you should use -eq, if the name contains Veeam Replica, you should change to -Like "*Veeam Replica*"
So changing your code to the following should work:
$SnapVMsAll | Where-Object { $_.name -Like "*Veeam Replica*" }

Related

How to call multiple properties in powershell

$loadbalancer = Get-AzureRmLoadBalancer | select Name,ResourceGroupName
$loadbalancer has two value ILB name and its resource group.
I want call both value in single PS command, Like when I call ILB name its respective resource group should be called.
The problem isn't entirely clear, can you please give an example of the function being used in the next step. I used "get-command" for the examples rather than get-azurermloadbalancer to avoid being package dependent.
To get the propety from $loadbalancer you use a dot and the name.
To get the current row from pipeline you can use $_
$loadbalancer = Get-Command get-* | Select-Object -Property CommandType,Name
#get a particular property
$loadbalancer.CommandType
#Use both properties in one function
$loadbalancer | Where-Object{($_.CommandType -eq "function") -and ($_.Name -match "Token")}

Return full value of a property

I'm trying to get the Program IDs of DCOM applications but when returning the value of the property, it doesn't give the full contents.
$a = Get-ChildItem 'registry::HKEY_CLASSES_ROOT\WOW6432Node\CLSID\' -Recurse | Where-Object Name -match 'ProgID'
This returns all applications that contain a ProgID
Hive: HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{000C1090-0000-0000-C000-000000000046}
Name Property
---- --------
ProgId (default) : WindowsInstaller.Installer
When trying to get the property value in the example, "WindowsInstaller.Installer"
via $a.Property
returns
(default)
How do I return the full property contents?
What Get-ChildItem returns for registry locations are Microsoft.Win32.RegistryKey instances representing registry keys.
To get the data for the unnamed default value of each such key - which contains the ProgID of interest - you can use the .GetValue() method with the empty string ('').
Note that PowerShell represents the unnamed default value differently, as '(default)', as shown in your question.
Get-ChildItem registry::HKEY_CLASSES_ROOT\WOW6432Node\CLSID -Recurse |
Where-Object Name -match ProgID |
ForEach GetValue ''
As an aside: the Name property of registry [Microsoft.Win32.RegistryKey] instances contains the full key path, so a better way to limit matching to just a key's name is to use
Where-Object Name -match '\\ProgID$ in your case.

Passing subset of objects down pipeline, based on count of properties?

I need to script up some things in PowerCLI (VMWare's bolt on to PowerShell). Basically we have a server cluster with three hosts. Each host has multiple virtual switches. Each virtual switch has multiple vlans ('port groups' in VMWare speak). I need to audit the fact that the same port groups exist on each host (so things keep working if the VM is moved).
Step 1 to achieving this is would be to know that the port group name exists on each of the three host machines.
I'm falling over with how to filter some objects out of all the ones returned by a cmdlet, based on number of results returned from a property of those objects. I then need to perform further operations with original object type that passes the filter test to go on down the pipeline.
To give some specifics, this an example showing 'Some PortGroup Name' and the three hosts it exists on (and as a bonus, the vSwitch):
Get-VirtualPortGroup -Name 'Some PortGroup Name' |
Select-Object Name, VMHostID, VirtualSwitchId
produces the output
Name VMHostId VirtualSwitchId
---- -------- ---------------
Some PortGroup Name HostSystem-host-29459 key-vim.host.VirtualSwitch-vSwitch6
Some PortGroup Name HostSystem-host-29463 key-vim.host.VirtualSwitch-vSwitch6
Some PortGroup Name HostSystem-host-29471 key-vim.host.VirtualSwitch-vSwitch6
Instead of 3, I'm starting with the 1849 port group names that are being returned by Get-VirtualPortGroup. I need a pipeline to whittle the number of VirtualPortGroup objects down to a collection consisting of only those objects where a count of the 'VMHostId' property is less than 3, and pass the remaining VirtualPortGroup objects down the pipeline for further processing.
This seems simple enough to do. I'm still failing though.
The following almost works. Piping it to measure shows a count of 229, instead of the original 1849 (so it's definitely filtered a lot out, and is possibly correctly returning the subset I'm after...?). The problem is, the object type is now a 'Group' or something at this point in the pipeline, and doesn't have all the properties and methods of the original Get-VirtualPortGroup objects.
Get-VirtualPortGroup |
Group-Object -Property Name |
Where-Object $_.Count -lt 3
Bolting a | Select-Object -ExpandProperty Group to the end of the above seemed promising, except it then seems to return the entire collection of Get-VirtualPortGroup objects as though I had done no filtering in there at all....
Am I doing something fundamentally wrong?
How can I filter out objects based on the count of the number of results returned by a specific property of an object, but still pass the original object type down the pipe?
Your approach is correct, but you got the Where-Object syntax wrong. The abbreviated syntax is:
Where-Object <property> <op> <value>
without the current object variable ($_). In your case that would be:
Where-Object Count -lt 3
Otherwise you must use scriptblock notation:
Where-Object { $_.Count -lt 3 }
This should do what you want:
Get-VirtualPortGroup |
Group-Object -Property Name |
Where-Object { $_.Count -lt 3 } |
Select-Object -Expand Group

How are parenthesis around a powershell command different than without?

I am using Get-NetIPAddress. ALL I want is the actual address without ANY formatting or tables or anything.
If I use
(Get-NetIPAddress).IPAddres
I get a list of addresses for all ports. I want only a single port so I tried to pipe it but gave nothing in return:
(Get-NetIPAddress).IPv4Address | Where-Object InterfaceAlias -eq "MyPortName"
But if I do this:
Get-NetIPAddress.IPv4Address | Where-Object InterfaceAlias -eq "MyPortName"
It says that this is an invalid command.
If I use this:
Get-NetIPAddress | Where-Object InterfaceAlias -eq "MyPortName" | Where-Object AddressFamily -eq IPv4 | Select-Object IPAddress
I get:
IPAddress
---------
10.45.22.100
So what is going on when I put parenthesis around Get-NetIPAddress and how can I get JUST the darn address number for a particular port?
By enclosing a command with () you create an order of precedence, so everything inside of () executes first and after that your other commands are evaluated.
also, there is no such construct as cmdlet.property
as for the second part, after you do (Get-NetIPAddress).IPv4Address you are probably getting another object, just compare output from:
Get-NetIPAddress | Get-Member
and
(Get-NetIPAddress).IPv4Address | Get-Member
Get-NetIpAddress is a cmdlet, PowerShell runs it. It outputs one or more objects, and by that I mean one many names and numbers and features all grouped together into one blob.
Get-NetIpAddress | Select-Object IPv4Address takes the object(s) and turns them into simpler, smaller objects with just one property, the IPv4Address.
That's what this is:
IPAddress
---------
10.45.22.100
it's a rendering of an object with one property.
Get-NetIpAddress | Select-Object -ExpandProperty IPv4Address takes the objects, takes just the IPv4Address, and throws away the grouping and leaves just the IPv4Address as a string.
That would output:
10.45.22.100
10.45.22.101
These have been expanded to strings, there's no column title. They've been unwrapped.
This is something you want to do a lot, but it's a lot of typing. $x = Get-NetIpAddress; $x.IPv4Address is a shortcut way of getting just the Ipv4Address unwrapped from the object and without anything else.
So you want to do
Get-NetIpAddress.IPv4Address but that falls over, because it looks like the name of a different cmdlet and PowerShell tries to run it and cannot. This is just a clash of syntax - it could be a name, it could be a property lookup, and powershell chooses differently. you need something (parens) to make the distinction.
(Get-NetIpAddress).IPv4Address makes it clear to the shell that there is a cmdlet, it runs that, then gets the result into the parentheses, then gets the property from them. That clears up the name clash. But at this point you have thrown away the InterfaceName, so pushing this output to the pipeline can't let you choose based on interface name.
Follow that through and you can see
If you do (Get-NetIPAddress).IPv4Address the output is strings and you have thrown away all the other data that came out of the cmdlet.
If you want to choose one string based on the interface name, you have to do that before you unwrap the IP and throw the interface name away.
So
Get-NetIpAddress |
Where-Object { pick the one I want } |
Select-Object -ExpandProperty 'the thing I want'
Brackets work in the same way as they do in mathematics, they set an order of precedence, so anything within brackets will execute first.
To get just the IP Address value from your final command, do this instead:
(Get-NetIPAddress | Where-Object InterfaceAlias -eq "MyPortName" | Where-Object AddressFamily -eq IPv4).IPAddress
This command:
(Get-NetIPAddress).IPv4Address
Returns the value of the IPv4Address property (probably as a string), which is why this command then doesn't work:
(Get-NetIPAddress).IPv4Address | Where-Object InterfaceAlias -eq "MyPortName"
As you are piping a string object that contains an IP Address into the Where-Object cmdlet and then are looking to filter on the InterfaceAlias property that does not exist in that object.
When you access a property via the . notation you get it's value. When you access a property via the Select-Object cmdlet you return an object (of the same type as the source object) that is filtered down to just containing that single property (unless you use the -ExpandProperty switch, which causes Select-Object to return the value of the defined property instead of an object in the same way as using . notation).
Long story short, do any filtering you need to do first, then access the property/properties you want as a result last.
Using this syntax you provided:
Get-NetIPAddress | Where-Object InterfaceAlias -eq "MyPortName" | Where-Object AddressFamily -eq IPv4 | Select-Object IPAddress
...getting just the IP address (without the object notation), can be done in two ways:
Get-NetIPAddress | Where-Object InterfaceAlias -eq "MyPortName" | Where-Object AddressFamily -eq IPv4 | Select-Object -Expand IPAddress
notice the "ExpandProperty" argument, abbreviated "Expand" on the above line ...or:
(Get-NetIPAddress | Where-Object InterfaceAlias -eq "MyPortName" | Where-Object AddressFamily -eq IPv4 | Select-Object IPAddress).IPAddress
...where we return the object and then reference the IPAddress property on the object.

Referencing variables that are not strings

I want to reference properties selected from a cmdlet and then use them later on in my script as $mailboxarray.totalitemsize and $mailboxarray.totalitemsize
This is breaking later on in my script as I assume it doesn't like the type of object it is. How can I make anything I pull into the references a string? At the moment I am getting "No mapping exists from object type System.Management.Automation.PsObject to a known managed provider type". My code at the moment: the first command works fine but I can't use expandproperty when there are multiple entries like line two?
$recipienttype = Get-mailbox -identity $recordset.PrimarySmtpAddress | Select -expandproperty RecipientTypeDetails
$mailboxarray = Get-Mailbox -identity $recordset.PrimarySmtpAddress | Get-MailboxStatistics | Select-object totalitemsize, lastlogontime