The following seems quite weird to me:
$user1 = Get-ADUser sameuser
$user2 = Get-ADUser sameuser
$user1 -eq $user2 # -> false
# the same for groups:
$group1 = Get-ADGroup samegroup
$group2 = Get-ADGroup samegroup
$group1 -eq $group2 # -> false
Actually it seems that Powershell users can be happy that 1 -eq 1
is true. Also:
"1" -eq 1 # -> true
@("1") -contains 1 # -> true
But:
$h1 = @{bla = 1}
$h2 = @{bla = 1}
$h1 -eq $h2 # -> false
$h1.GetHashCode(), $h2.GetHashCode() # -> 60847006, 5156994
# the above return values of course vary
$a1 = @(1;2;3)
$a2 = @(1;2;3)
$a1.GetHashCode(), $a2.GetHashCode() # -> 52954848, 34157931
# surprise, surprise:
$a1 -eq $a2 # no return value at all? (tested with versions 4.0 and 5.1)
($a1 -eq $a2).GetType() # or an Array?
($a1 -eq $a2).count # -> 0
Aside from these funny behaviors what really feels frustrating is that I cannot simply do it this way:
$ones = Get-ADPrincipalGroupMembership one
$seconds = Get-ADPrincipalGroupMembership second
$excl_ones = $ones | ? { $_ -notin $seconds }
But have to do something like this:
$second_nms = $seconds | % name
$excl_ones = $ones | ? { $_.name -notin $second_nms }
Am I missing something?
To understand some of the oddities your seeing we have to take a step back and consider the bigger picture, namely the framework PowerShell is built on top of: .NET!
$user1 -eq $user2
fails because $user1
and $user2
are two different objects - although they may both represent the same object in Active Directory.
When it comes to object equality in .NET, you'll need to distinguished between value equality and reference equality.
Two variables of a value type, like [int]
for example, are considered equal if their underlying value is the same:
$a = 1
$b = 1
$a.Equals($b) # $true
Two variables of a reference type - anything that's not a value type - are usually only considered equal if they have the same identity - that is, they refer to the same object in memory:
$a = New-Object object
$b = New-Object object
$a.Equals($b) # $false
For all we know, $a
and $b
are exactly the same, but they refer to two distinct [object]
instances in memory.
A type definition can override GetHashCode()
(the function used to determine an object's identity) and Equals()
(the function used to determine equality between two objects), so you may find that some reference types seem to act as value types when comparing them - [string]
being a prime example:
$a = "test"
$b = "test"
$a.Equals($b)
The ADEntity
class (the base type for all output objects in the ActiveDirectory module) doesn't attempt something like this, which is why you see the results you do.
The above doesn't quite explain another weird thing you raised, namely this:
$a1 = @(1;2;3)
$a2 = @(1;2;3)
$a1 -eq $a2 # NOTHING! WHAT'S GOING ON HERE?
To understand what's going on here, you need to study comparison operator behavior in PowerShell itself!
All the comparison operators (-eq
, -ne
, -gt
, -like
, -match
etc.) support two different modes depending on the left-hand side argument: scalar and filtering.
In scalar mode, a comparison operator takes a single object as its left-hand operand, a value expression as its right-hand operand and returns a boolean result: $true
or $false
.
In filtering mode, a comparison operator takes a collection (an array or a list) as its left-hand operand, a value expression as its right-hand operand (just like before) and returns all the individual members of the left-hand collection that satisfy the comparison.
To see this in action, try the following:
$names = "James","Jane","John"
$prefix = "Ja"
$names -like "$prefix*"
You'll see that the -like
operations returns two strings - James
and Jane
.
If we apply this newfound knowledge to your example
@(1;2;3) - eq @(1;2;3)
it becomes obvious why nothing is returned - the left hand operand is clearly an array, and neither of the ensuing comparisons (1 -eq @(1;2;3)
, 2 -eq @(1;2;3)
etc.) will return $true
Now on to the practical part of your problem. Active Directory is designed in a way so that each object in the directory has a unique identifier you can use to figure out the identity of it - the objectGUID
value. A GUID
in .NET happens to be a value type, so you can safely use it as a basis for you comparison:
$ones = Get-ADPrincipalGroupMembership one
$seconds = Get-ADPrincipalGroupMembership second
$excl_ones = $ones | ? { $_.objectGUID -notin $seconds.objectGUID }
For security principals (groups, users, computers etc.), another unique identifier that's safe to use is the objectSID
- security identifiers are always unique.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With