Use double quotes.
The -Filter
argument to the Get-AD*
cmdlets takes a string (!), not a script block (even though it looks like it, because braces can also be used instead of quotes).
Get-ADUser -Filter "userPrincipalName -eq '$($ListUsers[100].UserPrincipalName)'"
This way you get proper variable substitution. Note you must end up with a valid filter string, so putting single quotes around the values is necessary when those values are strings.
If you have many objects to fetch from AD, it can be beneficial to fetch them all in one go, instead of one-by-one in a loop. Consider this:
$ListUsers = @(<# ...list of items... #>)
# build LDAP filter string
$upn_filter = $ListUsers.UserPrincipalName -join ')(userPrincipalName='
$upn_filter = "(|(userPrincipalName=$upn_filter))"
$users = Get-ADUser -LDAPFilter $upn_filter
$users.distinguishedName
This builds an LDAP filter in the following form:
(|(userPrincipalName=A)(userPrincipalName=B)(userPrincipalName=C)(userPrincipalName=D))
which would be able to fetch 4 matching objects from AD in one step. Of course you can also build a "PowerShell-style" filter string, but I find the LDAP syntax a lot easier to handle, and it's shorter.
LDAP filter strings can get pretty long without the server complaining, and doing only one round-trip to the domain controller saves time.