Filtering in Powershell - powershell

I have an array of objects called $listy.
I am looking for object for which property $_.WorkflowAssociations.Count is greater than 0.
When I select that property I can see that several objects fulfill my criteria:
$listy | select title, workflowassociations.count
However when I use where: $listy | where {$_.WorkflowAssociations.Count -gt 0} none of the objects are listed:
I have the same issue with $_.Views.Count property. Other numeric properties seem to filter without issues. Is it because of the point (.)? Why? The property is called exactly Views.Count:

As #EtanReisner already pointed out in the comments to your question: if you have a property name containing a dot (like WorkflowAssociations.Count) you must put the name in quotes when trying to access it via dot notation:
$listy | Where-Object { $_.'WorkflowAssociations.Count' -gt 0 }
If you don't, the term $_.WorkflowAssociations.Count will be interpreted as the property Count of a property WorkflowAssociation of the current object ($_). Which, of course, doesn't exist.

Related

All parameters of Get-MsolUser into array

Want to throw the names of all parameters supported by Get-MsolUser into an array to be able to dynamically get different information from a user.
Get-Command Get-MsolUser
The command above doesn't show me the parameters, only the command itself.
You can run the following, which simply chains the member-access operator .
$array = (Get-Command Get-MSolUser).Parameters.Values.Name
Parameters is a Dictionary object with a built-in Values property. That property is a collection that contains attributes about each parameter. Since you only want the name, you can simply access the Name property.
If you want to exclude certain parameters, I'd just introduce an exclusion list.
$exclude = 'Debug','ErrorAction','ErrorVariable','InformationAction','InformationVariable','OutVariable','OutBuffer','PipelineVariable','Verbose','WarningAction','WarningVariable'
((Get-Command Get-MSolUser).Parameters.Values | Where Name -notin $exclude).Name

ADSI Search for DistinguishedName of the primary group based on primarygroupid

Because we don't have the active directory module available on all our systems we're using ADSI instead. The following code retrieves a user object from AD by using the AdsiSearcher:
$ADUser = ([AdsiSearcher]"(samaccountname=$SamAccountName)").FindOne()
This results in finding the property primarygroupid which represents the domain primary group for user, usually number 513. When we have this number we would like to find the distinguishedName of the group. However, the code below does that just fine I was wondering if there is a better filter that can be used instead of filtering after the FindAll() method?
$searcher = [adsisearcher]'objectclass=group'
$searcher.PropertiesToLoad.Add('primarygrouptoken')
$searcher.PropertiesToLoad.Add('distinguishedName')
$searcher.FindAll() |
Where-Object { $_.Properties.primarygrouptoken -eq 513}
Something like this would be great but it's not possible:
([adsisearcher]”(&(objectCategory=group)(primaryGroupid=513))”).FindOne()
The primaryGroupToken is a constructed attribute, meaning that it's not actually materialized in the database, and can't be filtered using LDAP.
In order to build an equivalent filter we'll need to look at how it is constructed - and the primary group token in Active Directory is always the same as the group's RID part (the relative identifier) of the objectSid attribute.
So, if we want to search by it, we can simply filter by objectSid instead:
# Obtain domain SID
$dncDN = ([adsi]"LDAP://RootDSE").defaultNamingContext
$dnc = [adsi]"LDAP://$dncDN"
$domainSID = [System.Security.Principal.SecurityIdentifier]::new($dnc.objectSid.Value, 0)
# Set the group ID we're looking for
$RID = 513
# Search for group by objectSid value:
([adsisearcher]"(&(objectCategory=group)(objectSid=${domainSID}-${RID}))").FindOne()

Powershell enumerate properties of properties

I have a commandlet that gathers information from a device register:
PS C:\windows\system32> Get-PSDevice serverA
HostName: ServerA
OOB:
Criticality: Normal
IsVirtual: True
etc
Some of these have an array of 'sub properties' inside, for example:
Cluster : #{Url=https://ps-apps.com/DeviceRegister/api/Clusters/62; VCenterUrl=https://ps-apps.com/DeviceRegister/api/VCenters/2; ClusterId=62; VCenterId=2; Name=Vcenter 1 ABC Prod;
DataCenterUrl=https://ps-apps.com/DeviceRegister/api/DataCenters/3; DataCenter=; IsValidated=True; IsExceptionCluster=False; SupportsProdWorkloads=False; SupportsNonProdWorkloads=False; SupportsSqlWorkloads=False;
ManagedByabc=False}
I can get whatever property within the aray I want using something like:
(Get-PSDevice ServerA).cluster.name
I'm trying to determine a way to enumerate all of the sub properties using a foreach type statement to populate a value.
What would be the best way to achieve this?
Every object in PowerShell has a hidden .PSObject property which tells you things about the object. One of its properties is a .Properties property (as PetSerAl points out, it's not a property but in fact a MemberSet, though you access it with property semantics).
(Get-PSDevice ServerA).cluster.PSObject.Properties
That would return [PSProperty] objects that show you the information about the properties (the name, value, type, whether it's gettable and settable, etc.).

Powershell - Display value of an object's properties, where the property names are like '*quota*'

As per the subject, I'm trying to get the name of a property and the value assocaited with that property, for a specific mailbox.
So, the line below gets me a nice list of the available object properties, and a default column displayed in the output has the heading 'Name'
Get-Mailbox -Identity "Person Name" | gm
I then want to say something like:
For the object: "Mailbox of Person Name"
Where the property of "Mailbox of Person Name" has a name like 'quota'
List both the actual property name and it's value for "Mailbox of Person Name"
I've tried a number of things using -ExpandProperty/Select-Object/Where-Object but they're all failing. I'm sure this is pretty basic, but Powershell is definitely not my strength. Can anyone show me how to structure this pipeline correctly?
You do not need to use Where-Object, only Select-Object:
Get-Mailbox -Identity "Person Name" | Select-Object -Property *quota*
You seem to have used the correct commandlets. Where-Object filters. Select-Object selects specific properties.
From my experience, sometimes what you see on the console doesn't match the actual property name because there is a formatter that can even change the column name. If you you drive the Where-Object and Select-Object with that virtual property name then they do fail. Also sometimes, the output is not really a recordset that works well with these cmdlets.
My advice is to always check the type of an object when things go strange. Starting from $items=Get-Mailbox -Identity "Person Name".
Then $items.GetType() reveals the actual .net type.
Then $items.Count reveals if it is actually an array or a single object.
Then $items|ForEach-Object {$_.GetType()} reveals the type of each object.
Also the $items|Get-Member is very helpful to figure out the property names. If necessary use it also within your loop.
That is how I troubleshoot strange behaviors and if you can post your findings and the code you tried with Where-Object and Select-Object that would be a great help.

How can I summarize an object the same way Format-List does?

For example, looking at a processes threads shows something like this:
PS C:\> (Get-Process)[0] | Format-List -Property Threads
Threads : {1548, 1600, 15940, 13996}
But if you actually grab that property directly, it looks like this:
PS C:\> (Get-Process)[0].Threads
BasePriority : 8
CurrentPriority : 9
Id : 1548
IdealProcessor :
PriorityBoostEnabled :
PriorityLevel :
PrivilegedProcessorTime :
StartAddress : 8790537024736
StartTime :
ThreadState : Wait
TotalProcessorTime :
UserProcessorTime :
WaitReason : UserRequest
ProcessorAffinity :
Site :
Container :
BasePriority : 8
... etc
Format list obviously has a method to summarize objects intelligently. It took a list of objects, pulled out a representative property from each one, and displayed it as a short array. I cannot find a method or cmdlet that allows me to summarize an collection of objects in the same manner.
I want to be able to pass an arbitrary collection of objects to a method and have it summarize. This is used when listing email addresses in Exchange objects, listing groups in AD objects, and many other places... I doubt these are all special cases.
To expand (after learning more from #JoelSmith's comments):
.NET Objects have formatting definitions that are used by Powershell when formatting output. Additional details are available using help about_Format.ps1xml[1]. These definitions are generic and can be accessed by any command, but by default there are no functions in Powershell to directly retrieve the output of an object property directly as it would be displayed in Format-List.
One hackish workaround is to split and strip the output like so:
(Get-Mailbox user | Format-List -Property Languages | Out-String).Split(':')[1].Trim()
# => {en-US,fr-CA}
However this method is extremely fragile, and will fail when the output spans multiple lines or contains a colon in the output:
(Get-Mailbox user | Format-List -Property EmailAddresses | Out-String).Split(':')[1].Trim()
# => {smtp
What is needed is a method that reads the formatting definition defined for the object and retrieves it directly, then use it to output the desired string. I have failed to find any example online.
You can use the
PSStandardMembers.DefaultDisplayProperty
and
PSStandardMembers.DefaultDisplayPropertySet
properties of your objects to determine the default properties that should be displayed for each type. You can read more about it here. We've run into a similar problem recently in our like PowerShell project. You can find this discussion we've had helpful. There are some subtle differences between PS v2 and v3 which we debate on that thread.
Usually .ToString() works but sometimes they forget to implement that method.
(Get-Process)[0] | %{$_.Threads.Id}
EDIT: to answer your comment below
(Get-Process)[0] | Format-List -Property Threads | Out-String
Unfortunately not all cmdlets are the same.
Are you looking for something like this?
(Get-Process)[0].Threads | Format-Table -Property ID -AutoSize
Id
--
13060
13064
13068
13072
13076
13080
13084
This needs to be customized for each cmdlet depending on what the output is and what fields you need. The reason it doesn't work with just (Get-Process)[0] | Format-Table -Property Threads -AutoSize is because Threads returns thread-objects, and an array of objects are displayed like your first sample (string-presentation of your objects in a collection { .. }) .
Here's what I can tell so far:
The Id property is the default display property for a thread object (System.Diagnostics.ProcessThread).
I couldn't find any tracks of this in any of PowerShell's type files but I can change the way Format-* display threads (requires PowerShell 3.0).
By default the format cmdlets prints the Id value of each thread object:
Threads : {1548, 1600, 15940, 13996}
Formatting cmdlets checks the value of the $FormatEnumerationLimit variable (default is 4) to decide how to format the object.
If the result is one object (scalar) only it will print as:
Threads : 1548
If it's a collection of items and the collection count is up to the value of $FormatEnumerationLimit (4) it will display as:
Threads : {1548, 1600, 15940, 13996}
A count greater than $FormatEnumerationLimit will look like (... indicates that there are more objects):
Threads : {1548, 1600, 15940, 13996...}
I can tell Id is the default property in use because I can change it to another property and see its value reflecting in the output.
For example, Here I'm setting the ThreadState as the default display property:
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty ThreadState -Force
PS> (Get-Process)[0] | Format-List -Property Threads
Threads : {Wait, Wait, Wait, Wait...}
# revert back
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty Id -Force
Hope that helps