I am trying to filter out users that are in a specific group.
I got the following output in a variable:
Group1
Group2
etc...
One group for each line saved in an array. Im trying to filter out only one specific group. But when I use -contains
it always says $false
, even tho the group is there.
My Code:
$group = get-aduser -identity name -properties memberof |
select-object -expandproperty memberof | %{ (get-adgroup $_).name }
$contains = $group -contains "string"
$contains
is $false
even if the array has elements that contain the string...
What am I missing?
It looks like your misconception was that you expected PowerShell's -contains
operator to perform substring matching against the elements of the LHS array.
Instead, it performs equality tests - as -eq
would - against the array's elements - see this answer for details.
In order to perform literal substring matching against the elements of an array, use:
# With non-literal search strings:
[bool] $contains = $group -match ([regex]::Escape($someString))
# With a string literal that doesn't contain regex metachars.,
# escaping isn't needed.
[bool] $contains = $group -match 'foo'
# With a string literal with metachars., you must individually \-escape them.
[bool] $contains = $group -match 'foo\.bar'
Note:
The above shows a robust, generic way of ensuring that your search string is treated as a literal value using [regex]::Escape()
, which is necessary because -match
expects a regex (regular expression) as its RHS (the search pattern).
Escaping isn't always necessary; specifically, only the presence of so-called metacharacters (those with special meaning in a regex, such as .
) requires it, and when you're using a string literal, you can opt to directly \
-escape them; e.g., to search for literal substring a.b
, you can pass 'a\.b'
.
- Chances are that AD group names do not require escaping, but it's important to be aware of the need for it in general.
As with all operators in PowerShell, by default the matching is case-insensitive; use the -cmatch
variant for case-sensitive matching.
The [bool]
type constrained above is used to ensure that the result of the -match
operation is converted to a Boolean:
- While
-match
directly returns a Boolean with a scalar (non-array) LHS, with an array LHS it acts as a filter, and returns the matching array elements instead; interpreted in a Boolean context, such as in an if
conditional, that usually still gives the expected result, because a non-empty array is interpreted as $true
, whereas an empty one as $false
; again, however it's important to know the difference.
This will rarely be a performance concern in practice, but it is worth noting that -match
, due to acting as a filter with arrays, always matches against all array elements - it doesn't stop once the first match is found, the way that the -contains
and -in
operators do.
- On the plus side, you can use
-match
to obtain the matching elements themselves.
The mistaken expectation of -contains
performing substring matching may have arisen from confusion with the similarly named, but unrelated String.Contains()
method, which indeed performs literal substring matching; e.g., 'foo'.Contains('o')
yields $true
. Also note that .Contains()
is case-sensitive by default.
PowerShell has no operator for literal substring matching.
However, you could combine PowerShell's generic array-filtering features with the .Contains()
string method - but note that this will typically perform (potentially much) worse than the -match
approach.
A reasonably performant alternative is to use the PSv4+ .Where()
array method as follows:
# Note: Substring search is case-sensitive here.
[bool] $contains = $group.Where({ $_.Contains("string") }, 'First')
On the plus side, this approach stops matching once the first match is found.
The answer was -match
insted of contains
. Now the output is true.