FinalizeUnusedUserAccounts.ps1
<#PSScriptInfo
.VERSION 1.0.1 .GUID b347c6d7-2af6-4cf3-ae62-89ceece60f7e .AUTHOR Jack Olsson .COMPANYNAME .COPYRIGHT .TAGS .LICENSEURI .PROJECTURI https://github.com/jaols/PSJumpStart .ICONURI .EXTERNALMODULEDEPENDENCIES ActiveDirectory .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES #> #Requires -Module PSJumpStart <# .SYNOPSIS Finalize removal of unused user accounts .DESCRIPTION This script will search for users disabled by DisableUnusedUserAccounts and remove them after they have been disabled for the given period. .PARAMETER searchRootOU Root OU-path for search. .PARAMETER ADserver Default AD server to use for operations .PARAMETER monthsDisabled The number of months a user account has not been disabled. .PARAMETER exceptionGroup Do NOT touch members of this group. .PARAMETER writeReport Write formatted info to CSV-file. NO ACTION WILL TAKE PLACE. .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]$monthsDisabled, [string]$exceptionGroup, [switch]$writeReport, [switch]$useEventLog ) #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 KillUser($userObject) { Msg "Kill user: $($userObject.SamAccountName)" if ($pscmdlet.ShouldProcess("$($userObject.SamAccountName)", "Kill user")) { if (Test-Path $($userObject.HomeDirectory)) { #Remove home folder Write-Verbose "KillUser:Remove folder $($userObject.HomeDirectory)" Remove-Item -Path $($userObject.HomeDirectory) -Force } if (Test-Path $($userObject.ProfilePath)) { #Remove profile folder Write-Verbose "KillUser:Remove folder $($userObject.ProfilePath)" Remove-Item -Path $($userObject.ProfilePath) -Force } #Kill AD-object Write-Verbose "KillUser:Remove user $($userObject.distinguishedName)" Remove-ADUser -Identity $userObject } } function GetDateDisabled($inputString) { if ($inputstring -match "(Disabled \[)(.*)(\].*)") { $result = Get-Date($Matches[2]) } else { $result = Get-Date } Write-Verbose "GetDateDisabled:$result <- $inputString" $result } 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","Comment","HomeDirectory","ProfilePath") $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($monthsDisabled) -or $monthsDisabled -eq 0) { Msg "Please create a dfp file for standard values." $monthsDisabled = 200 } #Get ADserver to run ALL operations if ([string]::IsNullOrEmpty($ADserver)) { $ADserver = (Get-ADDomainController ).Name } [datetime]$unusedTime = (Get-Date).AddMonths(-$monthsDisabled) Write-Verbose "Get users disabled since $unusedTime" if (![string]::IsNullOrEmpty($exceptionGroup)) { $untouchables = Get-ADGroupMember -Identity $exceptionGroup -Recursive | Select -ExpandProperty samAccountName } $filter = {Enabled -eq $false -and comment -like "Disabled [*" -and comment -like "*due to last logon*"} #Prepare report header if ($writeReport) { Msg "Write report to $reportFile" $row="" ForEach($prop in $PropertiesToGet) { $row += $prop + ";" } $row | Out-File -FilePath $reportFile -Force } Msg "User filter $filter" if ([string]::IsNullOrEmpty($searchRootOU)) { #Get-ADUser -LDAPFilter $filter -Properties $PropertiesToGet | % { Get-ADUser -Filter $filter -Properties $PropertiesToGet | % { if ($untouchables -notcontains $($_.SamAccountName)) { $disabledDate = GetDateDisabled $($_.Comment) if ($disabledDate -lt $unusedTime) { if ($writeReport) { ReportData $_ $reportFile } else { KillUser $_ } } } } } else { Get-ADUser -Filter $filter -Properties $PropertiesToGet -SearchBase $searchRootOU | % { if ($untouchables -notcontains $($_.SamAccountName)) { if ($writeReport) { ReportData $_ $reportFile } else { $_.SamAccountName $_.comment KillUser $_ } } } } Msg "End Execution" |