# PSMoveOldComputers.ps1
# PowerShell script to determine when each computer account in the
# domain last had their password changed. If this date is more than a
# specified number of days in the past, the computer object is
# considered inactive and it is moved to a target Organizational Unit.
# The computer account is also disabled. A log file keeps track of which
# computer objects are moved.
#
# ----------------------------------------------------------------------
# Copyright (c) 2011 Richard L. Mueller
# Hilltop Lab web site - http://www.rlmueller.net
# Version 1.0 - April 9, 2011
# Version 1.1 - June 24, 2011 - Escape any "/" characters in DN's.
#
# You have a royalty-free right to use, modify, reproduce, and
# distribute this script file in any way you find useful, provided that
# you agree that the copyright owner above has no warranty, obligations,
# or liability for such use.

Trap {"Error: $_"; Break;}

# Specify log file.
$File = "c:\rlm\PowerShell\OldComputers.log"

# Specify the minimum number of days since the password as last set for
# the computer to considered inactive.
$intDays = 180

# Specify the DN of the OU into which inactive computer objects will be moved.
$TargetOU = "ou=Inactive,dc=MyDomain,dc=com"

# Bind to target OU.
$OU = [ADSI]"LDAP://$TargetOU"

$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"

# Filter on all computers that are not disabled.
$Searcher.Filter = "(&(objectCategory=computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
$Searcher.PropertiesToLoad.Add("pwdLastSet") > $Null
$Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName

# Write information to log file.
$Today = Get-Date
Add-Content -Path $File -Value "Search for inactive computer accounts"
Add-Content -Path $File -Value "Start: $Today"
Add-Content -Path $File -Value "Base of search: $Domain"
Add-Content -Path $File -Value "Log file: $File"
Add-Content -Path $File -Value "Inactive if password not set in days: $intdays"
Add-Content -Path $File -Value "Inactive accounts moved to: $TargetOU"
Add-Content -Path $File -Value "-------------------------------------------"

# Initialize totals.
$Total = 0
$Inactive = 0
$NotMoved = 0
$NotDisabled = 0

$Results = $Searcher.FindAll()
ForEach ($Result In $Results)
{
    $DN = $Result.Properties.Item("distinguishedName")
    $DN = $DN.Replace("/", "\/")
    $PLS = $Result.Properties.Item("pwdLastSet")
    $Total = $Total + 1
    If ($PLS.Count -eq 0)
    {
        $Date = [DateTime]0
    }
    Else
    {
        # Interpret 64-bit integer as a date.
        $Date = [DateTime]$PLS.Item(0)
    }
    # Convert from .NET ticks to Active Directory Integer8 ticks.
    # Also, convert from UTC to local time.
    $PwdLastSet = $Date.AddYears(1600).ToLocalTime()
    If ($PwdLastSet.AddDays($intDays) -lt $Today)
    {
        # Computer inactive.
        $Inactive = $Inactive + 1
        $Computer= [ADSI]"LDAP://$DN"
        Add-Content -Path $File -Value "Inactive: $DN - Password last set $PwdLastSet"
        # Move computer to target OU.
        Try
        {
            $Computer.psbase.MoveTo($OU)
        }
        Catch
        {
            $NotMoved = $NotMoved + 1
            Add-Content -Path $File -Value "Cannot move: $DN"
        }
        # Disable the computer account.
        Try
        {
            $Flag = $Computer.userAccountControl.Value
            $NewFlag = $Flag -bxor 2
            $Computer.userAccountControl = $NewFlag
            $Computer.SetInfo()
        }
        Catch
        {
            $NotDisabled = $NotDisabled + 1
            Add-Content -Path $File -Value "Cannot disable: $DN"
        }
    }
}

Add-Content -Path $File -Value "Finished: $(Get-Date)"
Add-Content -Path $File -Value "Total computer objects found:   $Total"
Add-Content -Path $File -Value "Inactive:                       $Inactive"
Add-Content -Path $File -Value "Inactice accounts not moved:    $NotMoved"
Add-Content -Path $File -Value "Inactive accounts not disabled: $NotDisabled"
Add-Content -Path $File -Value "-------------------------------------------"

"Total computer objects found:   $Total"
"Inactive:                       $Inactive"
"Inactice accounts not moved:    $NotMoved"
"Inactive accounts not disabled: $NotDisabled"
"Done"