DisableUnusedUserAccounts.ps1

<#PSScriptInfo
 
.VERSION 1.0.1
 
.GUID c054c9d5-d69d-4354-bb5f-2543f855eb75
 
.AUTHOR Jack Olsson
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI https://github.com/jaols/PSJumpStart
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES ActiveDirectory
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


#Requires -Module PSJumpStart

<#
.SYNOPSIS
    Disables unused user accounts
.DESCRIPTION
    This script will search for User accounts not used in -monthsUnused months.
    Found users will be disabled and moved to the -disabledOU (if parameter is present)
.PARAMETER searchRootOU
    Root OU-path for search.
.PARAMETER ADserver
    Default AD server to use for operations
.PARAMETER monthsUnused
    The number of months a user account has not been used.
.PARAMETER disabledOU
    OU-path to to the OU receiving the disabled accounts.
.PARAMETER exceptionGroup
    Do NOT touch members of this group.
.PARAMETER writeReport
    Write formatted info to CSV-file. NO ACTION WILL TAKE PLACE.
.PARAMETER UseEventLog
    Standard parameter. Write info to EvenLog as well as std out.
.NOTES
    Author: Jack Olsson
    Date: 2018-07-20
 
    Changelog:
    2018-08-01 Added support for retreiving ADserver if not set
#>

[CmdletBinding(SupportsShouldProcess = $True)]
param (       
   [string]$searchRootOU,
   [string]$ADserver,
   [int]$monthsUnused,
   [string]$disabledOU,
   [string]$exceptionGroup,
   [switch]$writeReport
)

#region local functions
function GetLocalDefaultsFromDfpFiles($CallerInvocation) {        
    #Load script default settings
    foreach($settingsFile in (Get-SettingsFiles $CallerInvocation ".dfp")) {
        Write-Verbose "GetLocalDefaultsFromDfpFiles: [$settingsFile]"
        if (Test-Path $settingsFile) {        
            $settings = Get-Content $settingsFile
            #Enumerate settingsfile rows
            foreach($row in $settings) {
                #Remarked lines are not processed
                if (($row -match "=") -and ($row.Trim().SubString(0,1) -ne "#")) {
                    $key = $row.Split('=')[0]                            
                    $var = Get-Variable $key -ErrorAction SilentlyContinue
                    if ($var -and !($var.Value))
                    {
                        try {                                            
                            $var.Value = Invoke-Expression $row.SubString($key.Length+1)
                            Write-Verbose "GetLocalDefaultsFromDfpFiles: $key = $($var.Value)" 
                        } Catch {
                            $ex = $PSItem
                            $ex.ErrorDetails = "Err adding $key from $settingsFile. " + $PSItem.Exception.Message
                            throw $ex
                        }
                    }                   
                }
            }
        }
    }
}
function DisableUser($userObject) {
    $logonTime = ""
    if ($_.lastLogonTimeStamp) {
        $logonTime = TimeFromInteger $_.lastLogonTimeStamp
    }
    Msg "Disable user $($userObject.SamAccountName) last logged on $logonTime"
    
    if ($pscmdlet.ShouldProcess("$($userObject.SamAccountName)", "Disable user")) {        
        #Set "Notes" info
        Set-ADUser -Identity $userObject.SamAccountName -Replace @{comment="Disabled [$((Get-Date).ToString())] due to last logon $logonTime"} -Server $ADserver
        
        #Disable user
        Disable-ADAccount -Identity $userObject.SamAccountName -Server $ADserver
        
        #Move user to DisableOU (if specified)
        if (![string]::IsNullOrEmpty($disabledOU)) {
            Move-ADObject -Identity $userObject -TargetPath $disabledOU -Server $ADserver
        }
    } 
}
function ReportData($userObject, $csvFile, $separator) {
    Write-Verbose "Export user $($userObject.SamAccountName) last logged on $logonTime"
    $row = ""
    ForEach($prop in $PropertiesToGet) {
        #Write-Host $prop
        if ($userObject.$prop) {
            if ($userObject.$prop.GetType().Name -eq "Int64") {
                $time = TimeFromInteger $($userObject.$prop)
                $row += $time.ToString() + $separator
            } else {
                $row += $($userObject.$prop).ToString() + $separator
            }
        } else {
            $row += $separator
        }
    }
    
    $row | Out-File -Append -FilePath $csvFile    
}
function TimeFromInteger {
    Param(
     [parameter(mandatory=$true)]
     $TimeStamp
    )

    [datetime]::FromFileTime($TimeStamp)
}

function TimeToInteger {
    Param(
     [parameter(mandatory=$true)]
     [DateTime]$TimeStamp
    )

    $TimeStamp.ToFileTime()
}
#endregion

#region Init
$CSVseparator = ";"
$PropertiesToGet = @("samAccountName","DisplayName","Description","WhenCreated","lastLogonTimeStamp")
 
$reportFile = "$_scriptPath\$_scriptName - " + (Get-Date -Format 'yyyyMMdd HHmmss') + ".csv"

if (-not (Get-Module ActiveDirectory)) {
    Import-Module ActiveDirectory
}
if (-not (Get-Module PSJumpStart)) {
    Import-Module PSJumpStart
}

#Get Local variable default values from external DFP-files
GetLocalDefaultsFromDfpFiles($MyInvocation)

#Get global deafult settings when calling modules
$PSDefaultParameterValues = Get-GlobalDefaultsFromDfpFiles $MyInvocation -Verbose:$VerbosePreference

#endregion

Msg "Start Execution"
#Prevent disaster if dfp-file is missing
if ([string]::IsNullOrEmpty($monthsUnused) -or $monthsUnused -eq 0) {
    Msg "Please create a dfp file for standard values."
    $monthsUnused = 200
}
#Get ADserver to run ALL operations
if ([string]::IsNullOrEmpty($ADserver)) {
    $ADserver = (Get-ADDomainController ).Name
}


if (![string]::IsNullOrEmpty($exceptionGroup)) {
    $untouchables = Get-ADGroupMember -Identity $exceptionGroup -Recursive | Select -ExpandProperty samAccountName
}

[datetime]$unusedTime = (Get-Date).AddMonths(-$monthsUnused)
Write-Verbose "Get users not logon since $unusedTime"
#$filter = "(lastLogonTimeStamp >= $($unusedTime.ToFileTime()))"
#$filter = {LastLogonTimeStamp -lt $unusedTime -and Enabled -eq $true}
$filter = {Enabled -eq $true -and (LastLogonTimeStamp -lt $unusedTime -or (LastLogonTimeStamp -notlike "*" -and WhenCreated -lt $unusedTime))}

#Prepare report header
if ($writeReport) {
    Msg "Write report to $reportFile"
    $row=""
    ForEach($prop in $PropertiesToGet) {
        $row += $prop + $CSVseparator
    }
    $row | Out-File -FilePath $reportFile -Force    
}

Msg "Filter users last used $unusedTime"
if ([string]::IsNullOrEmpty($searchRootOU)) {
    #Get-ADUser -LDAPFilter $filter -Properties $PropertiesToGet | % {
    Get-ADUser -Filter $filter -Properties $PropertiesToGet | % {        
        if ($untouchables -notcontains $($_.SamAccountName)) {
            if ($writeReport) {
                ReportData $_ $reportFile $CSVseparator
            } else {
                DisableUser $_        
            }
        }
    }
} else {
    Get-ADUser -Filter $filter -Properties $PropertiesToGet -SearchBase $searchRootOU | % {
        if ($untouchables -notcontains $($_.SamAccountName)) {
            if ($writeReport) {
                ReportData $_ $reportFile $CSVseparator
            } else {
                DisableUser $_
            }
        }
    }
}

Msg "End Execution"