Filter cim instance class by property name - powershell

I'm trying to filter results by property Name. I cannot use piping in my script.
Get-CimInstance -ClassName Win32_Product -Property Name -Filter "Microsoft*"
Returns the error: Get-CimInstance : Invalid query.
I'm trying to get output similar to this command:
Get-CimInstance -ClassName Win32_Product | ? {$_.Name -like 'Microsoft*'}
But without piping to Where-Object.
What am I doing wrong?

If you look at Get-Help Get-CimInstance -Full, you will find the following -
-Filter [<String>]
Specifies a where clause to use as a filter. Specify the clause in either the WQL or the CQL query language.
Note: Do not include the where keyword in the value of the parameter.
Required? false
Position? named
Default value none
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
You don't have to include the Where-Object here, and you need to write your code as a query. The -filter parameter will take the Property(Name in this case) in form of the Windows Query Language. You don't need to define explicitly mention the -Property parameter while making use of the -filter parameter. Furthermore, since you are using WQL, your wildcard search would change from * to %, much like that in SQL. Keeping these points in mind, you can use the below query -
Get-CimInstance -ClassName Win32_Product -Filter 'Name like "Microsoft%"'

Related

How to find UPN that contains digit?

i would like to know how to find UPN that constain digit with -filter?
Get-ADUser -filter {(UserPrincipalName -contains "I dont know what i should add here #contoso.com")} -properties userprincipalname | select userprincipalname
The -Filter argument of AD cmdlets, which accepts a string, uses PowerShell-like syntax, but with only a limited subset of supported operators, some of which work in subtly different ways than in PowerShell.
The filter language is not sophisticated enough to do the matching you want: the only pattern matching supported is via wildcards, which are limited to use of *, using the -like operator.[1]
Therefore, use -Filter for pre-filtering with -like, then use a Where-Object call to let PowerShell filter the results down, using its regex capabilities:
Get-ADUser -Filter 'UserPrincipalName -like "*#contoso.com"' -Properties UserPrincipalName |
Where-Object UserPrincipalName -match '\d'
Select-Object UserPrincipalName
Note:
-match '\d' matches if at least one digit (\d) is present in the input.
I've used a string rather than a script block ({ ... }) to specify the -Filter argument, because that's what -Filter expects. While seductively convenient, the use of script blocks is conceptually problematic and can lead to misconceptions - see this answer.
[1] By contrast, PowerShell's -like operator supports PowerShell's more fully-featured wildcard expressions. Also, the AD -Filter's language at least situationally interprets * to mean: at least one character, whereas PowerShell's wildcard expression interpret it as zero or more.

Filtering Get-ADGroups with question mark wildcard doesn't seem to work

I'm trying to get a list of AD groups that have a name that starts with "Users-####-" (# is a number 0-9).
I've tried using Get-ADGroup -Filter {name -like "Users-[0-9][0-9][0-9][0-9]-*"} and Get-ADGroup -Filter {name -like "Users-????-*"}, but got no results.
I can of course use Get-ADGroup -Filter {name -like "Users-*"}, but this will also include all the groups that have something else than four characters after Users-.
I then decided to try using Where-Object and the this code returned the expected groups
Get-ADGroup -Filter * | Where-Object {$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"}
According to Microsoft documentation about wildcards, both ways I tried should work, but they actually don't.
Anybody have an idea what I'm doing wrong or is this just a bug in how ADGroup filtering works?
According to Microsoft documentation about wildcards, both ways I tried should work, but they actually don't.
That's a reasonable assumption, but the -Filter parameter exposed by some cmdlets in the ActiveDirectory module is a deceptive construct - it's designed to look like PowerShell's native operator syntax, but "underneath the hood" the cmdlet translates the filter expression to a valid LDAP query filter:
name -like "Users-*"
# is translated to
(name=Users-*)
$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"
# is translated to
(Name=Users-[0-9][0-9][0-9][0-9]-*)
Since LDAP doesn't recognize the wildcard range construct [0-9], it ends up querying the directory store for objects where the name literally starts with Users-[0-9][0-9][0-9][0-9]- - same goes for ?.
Since * is the only wildcard accepted by LDAP, the closest you can get is:
Get-ADGroup -Filter {name -like "Users-*-*"}
And then filter the results further on the client with Where-Object (in which case we're back to PowerShell performing the comparison and we can use all the wildcards again):
Get-ADGroup -Filter {name -like "Users-*-*"} | Where-Object Name -like 'Users-[0-9][0-9][0-9][0-9]-*'
As stated in about_ActiveDirectory_Filter:
Note: PowerShell wildcards, other than "*", such as "?" are not
supported by the -Filter parameter syntax.
In this case, you can combine -LDAPFilter with Where-Object to keep your query compatible and efficient:
Get-ADGroup -LDAPFilter "(name=Users-*-*)" | Where-Object {
$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"
}
You can use -Filter in this case as pre-filter, so at least you will get only groups with names starting with Users-.
Then in a further Where-Object clause you can specify further and in this case I would use regex -match there like:
Get-ADGroup -Filter "Name -like 'Users-*'" | Where-Object { $_.Name -match '^Users-\d{4}-.*' }
P.S. -Filter should be a string, not a scriptblock
The filters in the Powershell Active Directory module have odd behaviors.
Filter or Where Clause
There are two ways to restrict the output of an AD cmdlet like
Get-ADUser. First, you can use the -LDAPFilter or -Filter parameters
to filter the output. Second, you can pipe the results to the
Where-Object cmdlet. Where possible, the first method is more
efficient for two reasons.
Filtering is done on the domain controller instead of the local
client. The domain controller is more likely to be a server class
computer optimized for queries. Filtering results in a smaller
resultset sent over the network from the domain controller to the
client. In contrast, the Where-Object cmdlet only filters on the local
client after the resultset has been sent from the remote computer. For
example, you could retrieve all users with a department that starts
with "IT" using the Where-Object cmdlet as follows:
Get-ADUser -Filter * -Properties department | Where-Object {$_.department -Like "it*"} | Select sAMAccountName, department The
resultset from the Get-ADUser statement includes all users in the
domain. A more efficient method to get the same results would use a
filter, similar to below:
Get-ADUser -Filter {department -Like "it*"} -Properties department | Select sAMAccountName, department Now only the users needed are
included in the resultset from Get-ADUser. In a test domain with 2,150
users (7 of which have "IT" departments) the first command above took
4 times as long as the second (average of 10 trials each with 16
minutes between trials). The difference could be substantial in a
domain with ten's of thousands of users.
Also, note that the statements above use the -Properties parameter to
specify only the properties needed. The default properties exposed by
the cmdlet are always included, like sAMAccountName in this case. If
you request all properties, with -Properties *, the resultset will
include many properties for each user. The resultset will be much
smaller if you only specify the extended properties needed, like
department in this case. Repeating the last command above in the test
domain with 2,150 users, but requesting all properties (with
-Properties *) required 75% more time on average to complete. The default and extended properties exposed by the Get-ADUser cmdlet are
documented in Active Directory: Get-ADUser Default and Extended
Properties.
PowerShell Filter Syntax
The PowerShell Active Directory module cmdlets support an extended
form of the PowerShell Expression Language. PowerShell documentation
indicates that PowerShell syntax filters should be enclosed in braces.
But there are many examples where single quotes or double quotes are
used instead. As you might expect, this affects how the filter is
interpreted.
Using String Attributes The following table shows some example
PowerShell syntax filters using string properties, like Department.
Some filters result in error, others do not raise an error but never
produce results. The variable $Dept is defined as previously.
Filter Result
-Filter {department -eq "IT Department"} Works
-Filter {department -eq $Dept} Works
-Filter {department -eq "$Dept"} No Results
-Filter {department -eq '$Dept'} No Results
-Filter "department -eq $Dept" Error
-Filter 'department -eq $Dept' Works
-Filter {department -eq "it*"} No Results
-Filter {department -Like "it*"} Works
-Filter "department -Like ""it*""" Works
-Filter "department -Like 'it*'" Works
-Filter 'department -Like "it*"' Works
-Filter 'department -Like ''it*''' Works
-Filter {department -ge "IT"} Works
Some of these results may not be expected.
For example, you might expect enclosing a variable in a quoted string
to work. The best policy might be to always enclose PowerShell syntax
filters in braces, and to refrain from quoting variables.
The last example using the "-ge" operator is only useful in rare
situations. The filter will result in any departments that are
lexicographically greater than or equal to "IT". For example, it will
return "Test Department", because "T" is greater than "I".

Replacing a string with a variable in Get-ADGroup

I'm trying to use PowerShell to search AD for Group Names.
Why don't either of these work, the param or the Read-Host? Both are passing strings, but the results are empty. However, if I replace the variable $ADGroup in the command with an actual Group Name (a string) and run the command Get-ADGroup... results are provided as expected. I tried to replace the double quotes with single quotes and I get the same results, the command works alone but neither Read-Host or param provide information. I can't figure out why the string isn't being passed when it's a variable ($ADGroup). Thanks.
param(
[Parameter(Mandatory=$true)]
[string]$ADGroup
)
# One or the other param or Read-Host
$ADGroup = Read-Host "Enter Group Name"
PS \> Get-ADGroup -Filter {name -like "*$ADGroup*"} -Properties * | Select-Object -Property Name
Get-ADGroup -Filter {name -like '*GroupName*'} -Properties * | Select-Object -Property Name
Name
----
Results
Results
Results
Results
Results
This is one of the reasons why using a script block based filter (-Filter {...}) on the cmdlets of the ActiveDirectory Module is not recommended.
The -Filter on the Parameter section of the Get-* cmdlets from ActiveDirectory Module states the following:
-Filter
Specifies a query string that retrieves Active Directory objects. This string uses the PowerShell Expression Language syntax. The PowerShell Expression Language syntax provides rich type-conversion support for value types received by the Filter parameter. The syntax uses an in-order representation, which means that the operator is placed between the operand and the value.
Query String:
Get-ADGroup -Filter "name -like '*$ADGroup*'"
LDAP Query String:
Get-ADGroup -LDAPFilter "(name=*$ADGroup*)"
Recommended Documentations for efficient Filtering:
about_ActiveDirectory_Filter
Active Directory: LDAP Syntax Filters
Note: Worth mentioning, when querying Active Directory you will want to retrieve only the needed attributes from the AD Objects, specially when querying big Domains / Forests. Using -Properties * is a bad practice and also very inefficient, this will slow down your query as it is retrieving all available attributes of the objects being queried.
maybe it doesn't recognize it as a string or the filter is not correct.
param(
[Parameter(Mandatory=$true)]
[string]$ADGroup
)
#one or the other param or read-host
$ADGroup = Read-Host "enter group name"
$ADGroup = $ADGroup.ToString()
Get-ADGroup -Filter {name -like "*$ADGroup*"} -Properties * | select -Property Name
or this should do it..
$ADGroup = $ADGroup.ToString()
Get-ADGroup -Filter {name -like "*$ADGroup*"} -Properties * | Select-Object -expandProperty Name

Negate a -switch (all but the switch parameter)

How to obtain all the items except the one indicated by a switch?
I use:
Get-Disk -UniqueId 4875E7EB064AA60
to get information only a specific disk drive.
I want to use the same command, but get all drives except this one.
Something like this (in pseudo code):
Get-Disk -not( -UniqueId 4875E7EB064AA60 )
PowerShell as a language does not allow for "inverting" a parameter value. You need to filter the returned results with Where-Object after the fact.
Get-Disk | Where-Object { $_.UniqueId -ne '4875E7EB064AA60' }
There are several cmdlets that do allow expressing "everything but this" semantics, but it's up to the individual cmdlet if and how they implement that. For example:
The Get-ChildItem and Select-Object cmdlets have a parameter -Exclude that allows to exclude particular results.
The Select-String cmdlet has a switch -NotMatch to invert what is selected by the parameter -Pattern.
All cmdlets with filter parameters that allow expressing a not condition (like Get-WmiObject -Filter, Get-ADUser -Filter, or Get-ADUser -LDAPFilter) obviously also allow expressing a "not this" semantic.

Powershell - How to use result of one WMI query in another WMI query?

Trying to list the user permissions of shares on a server, where the share's path has a common file path element in it.
I have a script that successfully uses the Win32_LogicalShareSecuritySetting WMI class to enumerate the share permissions of all the shares on the server, but unfortunately that class does not have the file path of the share as an attribute... I can use the Win32_Share class and do something like:
$FinShares = Get-WmiObject -Class Win32_Share -Filter "Path LIKE '%Finance%'" -ComputerName $computername
and I do get a list of the desired shares. But how to pass that list into the next Get-WmiObject statement? I've tried something like:
$FinShares = (Get-WmiObject -Class Win32_Share -Filter "Path LIKE '%Finance%'" -ComputerName $computername | Select-Object Name)
foreach ($ShareInst in $FinShares)
{
$FinShareSS = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name = '$ShareInst'" -ComputerName $computername
$SecurityDescriptor = $FinShareSS.GetSecurityDescriptor()
(...)
When I try that, the variable $FinShareSS remains null... Can someone give me a pointer (or some kind of better way altogether) as to how I can do this?
The problem is your filter using $ShareInst; it's not working, because it is not returning the Name like you expect. Try just putting "$ShareInst" inside your foreach loop; you should see things like:
\COMPUTERNAME\root\cimv2:Win32_Share.Name="ADMIN$"
Which is the WMI object's PATH, not it's name. What you have in $ShareInst is an object of type System.Management.ManagementObject#root\cimv2\Win32_Share, not a string. When you put that variable inside double quotes, PowerShell expands the variable into a string using the objects .ToString() method. Which in the case of this Win32_Share object, returns the PATH of the object, not the name.
So basically you just need to get the actual name string in the -Filter string, so that it will actually return the share security object you are looking for. There are several ways to do this:
Embed the Property name in the string, like so:
-Filter "Name = '$($ShareInst.Name)'"
The $() wrapper tells PowerShell to evaluate the .Name property before expanding the variable into that long PATH value, so you get just the short name you're looking for.
If you just need the Win32_Share objects for the Name, then you can just change the foreach line to look like this:
foreach ($ShareInst in ($FinShares | Select-Object -ExpandProperty Name))
The -ExpandProperty parameter of the Select-Object tells PowerShell to get the Name property of each object and just return that, instead of the full object. Now $ShareInst will be just the name of the Win32_Share, so you can leave your filter as-is.
There are any number of other ways to resolve this issue, but those two seem the most straight-forward to me.