<#
.NOTES
################################################################################
# script to search unused AD objects  v2.o                                     #
# purpose: maintenance                                                         #
#                                                nexorek@gmail.com             #
# o8.o5.2o15                                                                   #
#  - whole code rewritten                                                      #
#                                                                              #
################################################################################
.PARAMETER scope
    defines the query. possible are queries for: 
        inactive computers ("Computers"), 
        inactive users ("Users"), 
        users with nonExpiring password ("nonExpiring") 
        and do all the queries ("all")
.PARAMETER unusedTime
    defines the period of maximum inactivity based on lastLogon attribute
.PARAMETER toScreen
    data are saved to files by defaults. use toScreen to output to screen    
#>
#Requires –Version 3
[cmdletBinding()]
param(
    [validateSet("Computers","Users","nonExpiring","All")][string]$scope="All",
    [validateRange(1,1096)][int]$unusedTime=96,
    [string[]]$OUsTOSKIP=(gc .\find-UnusedADObjects.OUsToSkip),
    [switch]$toScreen,
    [switch]$splitFiles
    #additional option to split/extract files with 'skipped' objects in seperate files 
    #for easier reviewing. better reporting.
)

#maxium number of days the user/computer may be inactive
$MAXUSERIDLE=$unusedTime
$MAXCOMPUTERIDLE=$unusedTime

#output file for further processing
$fdate=get-date -Format ("yyyyMMddhhmm")
$FILE_NON_EXPIRING=".\fado-nonexpiring-$fdate.csv"
$FILE_INACTIVE_USERS=".\fado-inactiveUsers-all-$fdate.csv"
$FILE_INACTIVE_COMPUTERS=".\fado-inactiveComputers-$fdate.csv"
$usedFiles=""

#list of OU names, that should be skipped during object scan. 
[regex] $ouRX = ‘(‘ + (($OUsTOSKIP |foreach {[regex]::escape($_)}) –join “|”) + ‘)’

#prepare LDAP filters for queries
$incativeFlag="(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(userAccountControl:1.2.840.113556.1.4.803:=65536))"
$loggedBeforeFlag="(&(objectCategory=[OBJCATEGORY])(objectClass=user)(|(lastLogonTimeStamp<=[INTERVAL])(!lastLogonTimeStamp=*)))"
$searchProperties="sAMAccountName,displayname,distinguishedname,lastLogonTimeStamp,passwordlastset,objectclass"

# Convert the local time to UTC format because all dates are expressed in UTC (GMT) format in Active Directory
$currentDateUtc = (get-date).ToUniversalTime()
$lastLogonIntervalLimit = $currentDateUtc.AddDays(-$MAXUSERIDLE).ToFileTime()

function prepareADObject {
param(
    [parameter(mandatory=$true)]$ADobj  
)
    # Convert the date and time to the local time zone
     if($ADobj.lastLogonTimestamp) {
       $lastLogon = [System.DateTime]::FromFileTime($($ADobj.lastLogonTimestamp))
    } else {
       $lastLogon=-1
    }     
     
    $customObj=new-object -TypeName psObject
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name distinguishedname -value $($ADobj.distinguishedname) -Force
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name displayName -value $($ADobj.displayname) -force
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name samAccountName -value $($ADobj.samaccountname) -Force
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name lastLogon -value $($lastLogon) -force
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name pwdLastSet -value $($ADobj.passwordLastSet) -force
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name type -value $($ADobj.ObjectClass) -force
    Add-Member -InputObject $customObj -MemberType NoteProperty -Name skip -value $false -force
 
    if($customObj.distinguishedname -match $ouRX) {$customObj.skip=$true}
    return $customObj
}

function doTheSearch {
param( 
    [string]$type,
    [string]$filter,
    [string]$fileName
)
    $script:usedFiles+=$fileName+"`n"
    $queryCommand="get-ad$type -LDAPFilter `"$filter`" -properties $searchProperties"
    Write-Verbose $queryCommand
    $resultList=@()
    Invoke-Expression $queryCommand | %{
            $resultList+=prepareADObject($_)
    }
    if($toScreen) {$resultList} else {
        $resultList|Export-Csv -Delimiter ';' -NoTypeInformation -Encoding UTF8 -Path $fileName
        if($splitFiles -and $scope -eq 'Users') {
            foreach($skipped in $OUsTOSKIP) {
                $fname="fado-inactiveUsers-$skipped-$fdate.csv"
                $resultList|Export-Csv -Delimiter ';' -NoTypeInformation -Encoding UTF8 -Path $fname
                $script:usedFiles+=$fName+"`n"
            }
        }
    }
}

Switch -regex($scope) {
    'nonExpiring|all' { 
        doTheSearch -type "user" -filter $incativeFlag -fileName $FILE_NON_EXPIRING 
    }
    'Users|all' { 
        $LDAPFilter=$loggedBeforeFlag.Replace("[OBJCATEGORY]","person").Replace("[INTERVAL]",$lastLogonIntervalLimit)
        doTheSearch -type "user" -filter $LDAPFilter -fileName $FILE_INACTIVE_USERS 
    }
    'Computers|all' {
        $LDAPFilter=$loggedBeforeFlag.Replace("[OBJCATEGORY]","computer").Replace("[INTERVAL]",$lastLogonIntervalLimit)
        doTheSearch -type "computer" -filter $LDAPFilter -fileName $FILE_INACTIVE_COMPUTERS 
    }

}


#FINISH
write-host -ForegroundColor Magenta "done."
if(!$toScreen) { Write-Host "files written:`n$usedFiles"}
