Hey, Scripting Guy! How Can I Find Old Computer Accounts?

Hey, Scripting Guy! How Can I Find Old Computer Accounts?

  • Comments 5
  • Likes

Hey, Scripting Guy! Question

Hey, Scripting Guy! We have had a number of changes at our company recently. As a result, our Active Directory Domain Services (AD DS) installation is somewhat shabby. Okay, a lot shabby. When I go into AD DS Users and Computers and look through it, I cannot tell which computers are supposed to be there or not. How can I get rid of all the old computer accounts that still reside in AD DS?
- MS

SpacerHey, Scripting Guy! Answer

Hi MS,

That is the problem with computer accounts. They get created and are often associated with a specific user. Then the user goes to somewhere like the Cayman Islands to go scuba diving. While the user is down there, he is having all kinds of fun, is relaxed, and begins to think, “You know, if I watched my money, I could live down here and make a living as a scuba instructor. I could get paid to go scuba diving”:

Image of an underwater world

 

Okay, I’m back. I was reliving my dive when I took that picture. The water was 80 degrees Fahrenheit (26.67 Celsius), visibility was over 100 feet (30.48 meters), and I was gliding along like a seal. Sigh.

Because I came back from the Cayman Islands and did not decide to become a full-time scuba instructor, here is a script you can use to find those users who “forgot” to return:

$maxOldLogonDays = 60
$adsiSearcher = new-object DirectoryServices.DirectorySearcher("LDAP://rootdse")
$adsiSearcher.filter = "objectCategory=Computer"
$adsiSearcher.findall() | 
Foreach-Object `
{
 "Processing $($_.path)"
 $rawLogon = $_.properties.item("lastlogon")
 $convertedLogOn = [datetime]::FromFileTime([int64]::Parse($rawLogon))
 If( ((get-date) - $convertedLogOn).days  -ge $maxOldLogonDays )
  {
    "$($_.properties.item('distinguishedName')) 
     has not logged on for more than  $maxOldLogonDays days" 
  } #end if
} #end foreach

We begin the script with creating a variable $maxOldLogonDays and setting it equal to 60. (Also see an example of working with the lastlogon attribute in VBScript.) We will use this to filter out the computer objects that are returned based upon our query:

$maxOldLogonDays = 60

Now we need to search AD DS. (Also see an example of searching for computer accounts using VBScript.) To do this, we have several options. We can use ADO as the VBScript example does, or we can use the System.DirectoryServices.DirectorySearcher .NET Framework class.

In Windows PowerShell 2.0 this class has an alias of [adsiSearcher], and you do not need to use the new-object cmdlet to create an instance. The code we are using here will work on both Windows PowerShell 2.0 and on Windows PowerShell 1.0.

When we use the New-Object cmdlet to create an instance of the DirectorySearcher class, we give it “LDAP://rootdse” for the constructor. (The DirectorySearcher class is documented here.) A constructor is used when we create an instance of a class. What this is saying is that we want to search the rootdse using the LDAP protocol:

Image of using the LDAP protocol to search rootdse

 

To translate the lastlogon attribute, we can use the FromFileTime static method from the system.datetime class. We also use the static method parse from the system.int64 class and give it the value we stored in the $rawLogon variable. We save the converted datetime object into the $convertedLogOn variable. This is seen here:

$convertedLogOn = [datetime]::FromFileTime([int64]::Parse($rawLogon))

Once we have done this, we need to evaluate our date to see if it is less than the maximum old logon days value that we stored in the $maxOldLogonDays variable. The cool thing is that we can easily subtract datetime values. Here is an article that talks about working with dates in VBScript. If our lastlogon time is less than a specific number of days old, nothing happens:

If( ((get-date) - $convertedLogOn).days -ge $maxOldLogonDays )
  {

If, however, the lastlogon time is greater than the number of days, we print out the distinguished name as seen here:

"$($_.properties.item('distinguishedName')) 
     has not logged on for more than  $maxOldLogonDays days" 
  } #end if
} #end foreach

MS, I hope you can use this script to help you find all your missing users. If you do contact them, tell them that I am a certified assistant scuba instructor and would be glad to help them with their dives. See you tomorrow.

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Really? Were you underwater when you wrote this or is it a test?

    incorrect syntax, missing Writes, datemath issues...?

    I am trying to learn Powershell and you actually did me a favor while I tried to figure out if I am stupid or the snippet was full of holes.

    $maxOldLogonDays = 60

    $query = New-Object system.directoryservices.directorysearcher

    $root = [adsi]"LDAP://-your path-,DC=COM"

    $query.SearchRoot = $root

    $query.filter = "(objectCategory=computer)"

    $query.SearchScope = "subtree"

    $result = $query.findAll() |

    ForEach-Object -process `

    {

    if ($_.properties.item("lastlogon") -gt 0)

    #I get alot of lastlogons that are not null but not empty either

    #-gt 0 seems to work best as test for valid datestamp

    {

    $rawLogon = $_.properties.item("lastlogon")

    $convertedLogOn = [datetime]::FromFileTime([int64]::Parse($rawLogon))

    Write-Host $convertedLogOn

    $passwordage = ((get-date) - $convertedLogOn)

    Write-Host $passwordage.Days

    If($passwordage.Days -gt $maxOldLogonDays)

    {

    Write-Host "$($_.properties.item('distinguishedName'))

    has not logged on for more than  $maxOldLogonDays days"

    }

    }

    }

  • Thanks for the comment, I have updated the code.

  • Hi, I want a VBA function which will get PC's lastlogon data
    like =lastlogon(A1) in A1 there will be PC name and it will give the value of last logon

  • This take wrong value of computer name

    {
    Function SachinComp1(ComputerName) As String

    Set objConnection = CreateObject("ADODB.Connection")
    objConnection.Open "Provider=ADsDSOObject;"

    Set objCommand = CreateObject("ADODB.Command")
    objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000

    objCommand.CommandText = _
    ";" & "(objectCategory=computer);" & _
    "adspath;subtree"
    Set objRecordSet = objCommand.Execute

    'objRecordSet.EOF
    Set objuser = GetObject(objRecordSet.Fields("adspath").Value)
    Set objLastLogon = objuser.get("lastLogonTimestamp")
    intLastLogonTime = objLastLogon.HighPart * (2 ^ 32) + objLastLogon.LowPart
    intLastLogonTime = intLastLogonTime / (60 * 10000000)
    intLastLogonTime = intLastLogonTime / 1440

    SachinComp1 = objuser.get("CN") & " " _
    & intLastLogonTime + #1/1/1601#

    objRecordSet.Close

    objConnection.Close

    End Function
    }

  • thank you