Get-ADComputer and Negative Comparisons

This topic contains 6 replies, has 4 voices, and was last updated by  edwin arlington 3 weeks, 4 days ago.

  • Author
    Posts
  • #89615

    John Zetterman
    Participant

    So, I'm running into an interesting problem with the Get-ADComputer cmdlet. What I am trying to do is get a list of Computers objects that haven't logged in in more than 90 days. That part works fine, however when I attempt to filter out Cluster objects I'm running into an issue.

    There are 219 total objects in the OU that I'm searching. Here is my basic command:
    $Servers = Get-ADComputer -LDAPFilter '(name=*)' -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { $_.servicePrincipalNames -notlike '*MSClusterVirtualServer*' } | Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } | Sort-Object CN

    The portion in bold does not evaluate correctly and all 219 objects are returned, however, if I try using -like it correctly catches only the Cluster objects and only 17 objects are returned. Can anyone explain to me why -like works correctly and -notlike wouldn't?

  • #89621

    Olaf Soyk
    Participant

    Could you please format your code as code here in the forum? Thanks.
    As far as I know the attribute servicePrincipalName is a collection. So you cannot use -notlike as it is. you could try it like this:

    $Servers = Get-ADComputer -LDAPFilter '(name=*)' -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | 
        Where-Object { (($_.servicePrincipalNames) -join '-') -notlike '*MSClusterVirtualServer*' } | 
            Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } | 
                Sort-Object CN
  • #89624

    John Zetterman
    Participant

    Thank you for the reply. Sorry, I didn't see a tag in the editor to format it as code, otherwise I would have. Could you explain to me why you can use LIKE against a collection, but you can't use NOTLIKE?

    Also, is it odd that when I dot select the object it is formatted as String? It is a collection when you use Select-Object, I get that.

    PS C:\Windows\system32> $Servers | Select servicePrincipalNames | gm
    
    
       TypeName: Selected.Microsoft.ActiveDirectory.Management.ADComputer
    
    Name                  MemberType   Definition
    ----                  ----------   ----------
    Equals                Method       bool Equals(System.Object obj)
    GetHashCode           Method       int GetHashCode()
    GetType               Method       type GetType()
    ToString              Method       string ToString()
    servicePrincipalNames NoteProperty ADPropertyValueCollection servicePrincipalNames=Microsoft.ActiveDirectory.Managem...
    
    
    PS C:\Windows\system32> $Servers.servicePrincipalNames | gm
    
    
       TypeName: System.String
    
    Name             MemberType            Definition
    ----             ----------            ----------
    Clone            Method                System.Object Clone(), System.Object ICloneable.Clone()
    CompareTo        Method                int CompareTo(System.Object value), int CompareTo(string strB), int IComparab...
    Contains         Method                bool Contains(string value)
    CopyTo           Method                void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co...
    
  • #89627

    Olaf Soyk
    Participant

    Could you explain to me why you can use LIKE against a collection, but you can't use NOTLIKE?

    Unfortunately not. I just had a similar problem once and solved it this way. I'm used to work solution oriented. If it does the job I don't care why it does the job. 😀 But I'm pretty sure we have some folks here in the forum able to explain this behaviour in detail. Folks? Someone? ....
    BTW: Does it do the job? 😉

  • #89629

    John Zetterman
    Participant

    Not sure, I had already gotten what I needed with the following. I will check your solution later though, as that's a solution I would not have come up with myself, so thank you.

    $Servers = Get-ADComputer -LDAPFilter '(name=*)' -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) }
    $Clusters = Get-ADComputer -LDAPFilter '(name=*)' -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { $_.servicePrincipalNames -like 'MSClusterVirtualServer' }
    $Array = @()
    
    ForEach ($Server in $Servers) {
        If ($Clusters.Name -notcontains $Server.Name) {
            $Array += $Server
        }
    }
  • #89980

    Robert Thomson
    Participant

    Could you explain to me why you can use LIKE against a collection, but you can't use NOTLIKE?

    The short answer is that you can use comparison operators such as like against arrays/collections but they operate differently than with strings. When you use -like or -notlike operators against a collection then an array is returned rather than a boolean. As you performed the test in the where-object construct, which is expecting a boolean result, the array is cast as a boolean. The net result is that the array is not null and will always be cast to $true, and hence all the computer objects are returned.

    The session below demonstrates the return types and how the casting always results in $true being returned.

    PS C:\Users\Rob> $arr = ("Apple", "Orange", "Grapefruit")
    PS C:\Users\Rob> ($arr -like "Apple").GetType()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Object[]                                 System.Array
    
    
    PS C:\Users\Rob> ($arr -notlike "Apple").GetType()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Object[]                                 System.Array
    
    
    PS C:\Users\Rob> ($arr -notlike "Apple") -as [Boolean]
    True
    PS C:\Users\Rob> ($arr -like "Apple") -as [Boolean]
    True
    PS C:\Users\Rob> ($arr -notlike "Pear") -as [Boolean]
    True
    PS C:\Users\Rob>

    If you were to stick with the where-object you could change the test to use -notcontains.

    $Servers = Get-ADComputer -LDAPFilter '(name=*)' -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { $_.servicePrincipalNames -notcontains 'MSClusterVirtualServer' } | Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } | Sort-Object CN

    If the use of the wildcards is important to you then negating the result of the -like is the best approach.

    $Servers = Get-ADComputer -LDAPFilter '(name=*)' -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { -not ($_.servicePrincipalNames -like '*MSClusterVirtualServer*') } | Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } | Sort-Object CN

    And finally to add confusion to the mix, if you "filter left" in the get-ADComputer command you can use the -notlike operator and it will behave as you expect/hoped it would.

    $Servers = Get-ADComputer -filter { (ServicePrincipalName -notlike "*MSClusterVirtualServer*") } -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } | Sort-Object CN

    However there is a corner case I discovered the hard way where some computers don't have a ServicePrincipalName attribute, since it isn't mandatory. The best way to deal with it is to negate the result of the affirmative test.

    $Servers = Get-ADComputer -filter { -not (ServicePrincipalName -like "*MSClusterVirtualServer*") } -SearchBase 'OU=Servers,DC=my,DC=domain,DC=com' -Properties * | Where-Object { $_.LastLogonDate -lt (Get-Date).AddDays(-90) } | Sort-Object CN
  • #90143

    edwin arlington
    Participant

    You can try the following command :

    get-adcomputer -filter * -prop LastLogonDate | ? {$_.LastLogonDate -gt (Get-Date).AddDays(-104)} | select name,lastlogondate

    For moreinformation, please checkout the below article.

    http://www.out-null.eu/2014/04/17/howto-find-all-users-in-active-directory-who-havent-logged-in-longer-than-90-days/

    Here is an article which explain the property and why it lags behind the actual last logon of a device:

    http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx

    You may also automated solution which provides accurate last logon, users that have never logged on, and more

You must be logged in to reply to this topic.