tl;dr
Don't store the AD user objects themselves in your arrays, use their .SamAccountName
property value instead (generally, pick a property that uniquely identifies the objects):
# ...
$SPSecUKUsers += $SPSecUser.SamAccountName
# ...
if ($SPSecUKUsers -notcontains $UKUser.SamAccountName) { # ...
See the next section, if you want to know why storing the objects themselves doesn't work.
Alternatively - for faster lookups - use a hashtable:
$SPSecUKUsers = @{} # initialize hashtabe
# ...
# Create an entry for the object at hand, using its .SamAccountName
# as the entry *key*; you can store the object itself as the entry *value*.
# If all you need are lookups by SAM account name, however, you can just
# use a fixed value such as $true.
$SPSecUKUsers[$SPSecUser.SamAccountName] = $SPSecUser
# ...
if ($SPSecUKUsers.ContainsKey($UKUser.SamAccountName)) { # ...
About PowerShell's containment (collection-membership) operators:
As Olaf and Lee_Dailey imply in the comments, PowerShell's containment operators (-contains
/ notcontains
and -in
/ -notin
) check the comparison operand for reference equality (identity) with the elements of the input array, if those elements are instance of .NET reference types, with the exception of [string]
instances. [string]
instances and instance of value types are tested with
value equality (equivalence) - see Equality Comparisons.
You can think of these operators as an implicit loop over the input array's elements, testing each against the comparison operand with the -eq
operator (or, if you use the case-sensitive variant such as -ccontains
, with -ceq
), using reference equality or value equality, depending on the element type.
Important: Due to PowerShell's flexible automatic type-conversion rules, which operand is the LHS in an -eq
operation matters. Using -in
or -contains
means that the LHS of the implied -eq
operation is the array element being tested against, as the following examples show:
# `, 10` creates a single-element array
'0xa' -in , 10 # equivalent of: 10 -eq '0xa' => $true
, 10 -contains '0xa' # ditto
#
10 -in , '0xa' # equivalent of: '0xa' -eq 10 => $false
, '0xa' -contains 10 # ditto
In the first 2 operations, the LHS being a number ([int]
) forces the string RHS ([string]
) to a number ([int]
) too, and the hex "number string" '0xa'
converts to an [int]
with decimal value 10
too.
In the latter 2, the LHS being a string ([string]
) forces the number 10
to become a string too, and '10'
obviously doesn't match '0xa'
.
Value equality (equivalence) means that two objects have the same content, even though, with distinct value-type objects, that content is by definition stored in different memory locations.
Numeric types such as [int]
and [double]
are value types, for instance. As a rough rule of thumb, objects that have properties are often reference types.
You can check a given type's .IsValueType
property; e.g., [int].IsValueType
returns $true
.
Reference equality (identity) means that two values are only considered equal if they point to the very same object in memory, i.e., the same instance of a reference type.
Otherwise, they're considered not equal, even if they represent what is conceptually the same entity, which is what happened in your case: two separate calls to Get-ADUser
return distinct objects, even if you (in part) ask for the same users in both cases (Get-ADUser
returns instance of type Microsoft.ActiveDirectory.Management.ADUser
, which is a reference type).
Examples:
# Create a custom object...
$customObject = [pscustomobject] @{ one = 1; two = 2 }
# which is an instance of a reference type.
$customObject.GetType().IsValueType # -> $false
# Create an array comprising a value-type instance (1)
# and a reference-type instance (the custom object).
$arr = 1, $customObject
# Look for the value-type instance.
$objectToLookFor = 1
$arr -contains $objectToLookFor # value equality -> $true
# Create another custom object, with the same properties as above.
$objectToLookFor = [pscustomobject] @{ one = 1; two = 2 }
# This lookup *fails*, because $objectToLookFor, despite having the same
# properties as the custom object stored in the array, is a *different object*
$arr -contains $objectToLookFor # reference equality -> $false(!)
# If we look for the very same object stored in the array, the lookup
# succeeds.
$arr -contains $customObject # -> $true