272 lines
12 KiB
PowerShell
272 lines
12 KiB
PowerShell
function Get-TerminatedComputersReport {
|
|
<#
|
|
.SYNOPSIS
|
|
Generates a report of computers that have not "logged in" in the last n days.
|
|
|
|
.DESCRIPTION
|
|
Generates a report of computers that have not "logged in" in the last n days.
|
|
If an AWS Profile is provided it will also compare against EC2 instances to attempt
|
|
to validate a confidence level (0 - 4) of termination, 0 indicating it is almost guaranteed
|
|
to be an active system and 4 meaning almost guaranteed to be inactive and "terminated".
|
|
If not compared against AWS termination confidence will be 0 or 1. A 1 indicates that
|
|
the machine password has not been changed in at least 90 days.
|
|
|
|
.PARAMETER Domain
|
|
If providing credentials for an alternate domain, specify the domain.
|
|
|
|
.PARAMETER Credential
|
|
Used when connecting to a non-native domain to generate a report.
|
|
|
|
.PARAMETER OU
|
|
Optional parameter, specify the search base for AD computer objects.
|
|
Example: "OU=FH Computers,DC=FH,DC=local"
|
|
|
|
.PARAMETER NumberOfDays
|
|
The cut off value for the last logon filter, defaults to 120 days.
|
|
|
|
.PARAMETER CheckAWS
|
|
Will compare AD to AWS instances for increased accuracy of termination confidence.
|
|
|
|
.OUTPUTS
|
|
[pscustomobject]
|
|
|
|
.EXAMPLE
|
|
New-TerminatedComputersReport -Domain fh.local -Credential $credential -OU "OU=POD0,OU=FH Computers,DC=fh,DC=local" -NumberOfDays 90
|
|
|
|
This example retrieves all computer objects in the POD0 OU that have not logged on in preceeding 90 days, a [pscredential] was created and passed in as $credential
|
|
.EXAMPLE
|
|
New-TerminatedComputersReport -Domain fh.local -Credential $credential -OU "OU=POD0,OU=FH Computers,DC=fh,DC=local" -NumberOfDays 90 -CheckAWS
|
|
|
|
This example retrieves all computer objects in the POD0 OU that have not logged on in preceeding 90 days, then retrieves EC2 instance information.
|
|
A comparison of Name and IP is done to determine a termination confidence score, which is noted in the output. A [pscredential] was created and passed in as $credential.
|
|
.EXAMPLE
|
|
New-TerminatedComputersReport -Domain fh.local -Credential $credential -OU "OU=POD0,OU=FH Computers,DC=fh,DC=local" -NumberOfDays 90 -CheckAWS | Export-Csv -Path $env:TEMP\TerminatedComputers.csv -NoTypeInformation
|
|
|
|
This example retrieves all computer objects in the POD0 OU that have not logged on in preceeding 90 days, then retrieves EC2 instance information.
|
|
It is then piped to Export-Csv for later review.
|
|
|
|
|
|
#>
|
|
[cmdletbinding(DefaultParameterSetName = "Default")]
|
|
param(
|
|
[parameter(ParameterSetName = "Credentialed", Mandatory = $true)]
|
|
[string]
|
|
[ValidateSet("fh.local", "corp.alkamitech.com")]
|
|
$Domain,
|
|
[parameter(ParameterSetName = "Credentialed", Mandatory = $true)]
|
|
[pscredential]
|
|
$Credential,
|
|
[parameter(ParameterSetName = "Credentialed", Mandatory = $false)]
|
|
[parameter(ParameterSetName = "Default", Mandatory = $false)]
|
|
[ValidateNotNullorEmpty()]
|
|
[string]
|
|
$OU,
|
|
[parameter(ParameterSetName = "Credentialed", Mandatory = $false)]
|
|
[parameter(ParameterSetName = "Default", Mandatory = $false)]
|
|
[uint16]
|
|
[ValidateRange(1, 365)]
|
|
$NumberOfDays = 120,
|
|
[parameter(ParameterSetName = "Credentialed", Mandatory = $false)]
|
|
[parameter(ParameterSetName = "Default", Mandatory = $false)]
|
|
[switch]
|
|
$CheckAWS
|
|
)
|
|
|
|
$outputObjects = [System.Collections.Generic.List[System.Object]]::new()
|
|
$logLead = Get-LogLeadName
|
|
|
|
# Make sure AWS Powershell modul is loaded, if needed
|
|
if ($PSBoundParameters.ContainsKey("CheckAWS")) {
|
|
# Set up some sorting buckets
|
|
$ec2Instances = [System.Collections.Generic.List[System.Object]]::new()
|
|
$ec2ObjectIndexByIP = [System.Collections.Specialized.OrderedDictionary]::new()
|
|
$ec2ObjectIndexByName = [System.Collections.Specialized.OrderedDictionary]::new()
|
|
$ec2IPIndex = 0
|
|
$ec2NameIndex = 0
|
|
# Check if this is running in TeamCity, set accounts as appropriate
|
|
if ((Test-IsTeamCityProcess)) {
|
|
$awsAccounts = "prod", "dev", "qa", "corp"
|
|
}
|
|
else {
|
|
$awsAccounts = "temp-prod", "temp-dev", "temp-qa", "temp-corp"
|
|
}
|
|
# Make sure AWSPowerShell is loaded/can be loaded
|
|
if (!((Get-Module).Name -contains "AWSPowerShell")) {
|
|
try {
|
|
Import-Module AWSPowerShell
|
|
}
|
|
catch {
|
|
Write-Error "$logLead : Unable to load the AWSPowerShell module"
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
# If we are specifying credentials we need to make sure we connect to the correct domain for operations
|
|
if ($PSBoundParameters.ContainsKey("Credential")) {
|
|
try {
|
|
$fastDomainController = ((Resolve-DnsName $Domain -ErrorAction Stop).IPAddress | ForEach-Object { Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue } | Sort-Object ResponseTime)[0].Address
|
|
}
|
|
catch {
|
|
Write-Error "$logLead : Error when resolving DNS for $Domain"
|
|
return
|
|
}
|
|
}
|
|
|
|
$date = (Get-Date).AddDays(-$NumberofDays)
|
|
|
|
# Build a query for AD
|
|
$adComputersSplat = @{}
|
|
$adComputersSplat += @{"Filter" = { lastLogonDate -lt $date } }
|
|
$adComputersSplat += @{"Properties" = @("lastLogonDate", "PasswordLastSet", "Name", "IPv4Address") }
|
|
if ($PSBoundParameters.ContainsKey("Credential")) {
|
|
$adComputersSplat += @{"Credential" = $Credential }
|
|
$adComputersSplat += @{"Server" = $fastDomainController }
|
|
}
|
|
if ($PSBoundParameters.ContainsKey("OU")) { $adComputersSplat += @{"SearchBase" = $OU } }
|
|
# Get AD Computer objects that match our filter
|
|
Write-Progress -Activity "Generating Report" -Status "Retrieving AD Objects" -Id 1 -PercentComplete 25
|
|
try {
|
|
$adComputers = Get-ADComputer @adComputersSplat
|
|
} catch [System.Security.Authentication.AuthenticationException] {
|
|
Write-Error "$logLead : Unable to connect to the target domain, invalid credentials."
|
|
return
|
|
} catch {
|
|
Write-Error "$logLead : Unable to retrieve Computer objects from AD."
|
|
return
|
|
}
|
|
|
|
|
|
|
|
if ($PSBoundParameters.ContainsKey("CheckAWS")) {
|
|
# Retrieve EC2 data
|
|
Write-Progress -Activity "Generating Report" -Status "Retrieving EC2 Data" -Id 1 -PercentComplete 50
|
|
$incrementPercent = 60
|
|
foreach ($awsAccount in $awsAccounts) {
|
|
Write-Progress -Activity "Generating Report" -Status "Retrieving EC2 Data from $($awsAccount)" -Id 1 -PercentComplete $incrementPercent
|
|
try {
|
|
$ec2Instances += (Get-EC2Instance -ProfileName $AWSAccount -Region us-east-1).Instances
|
|
} catch {
|
|
Write-Error "$logLead : An error occurred while retrieving EC2 instances in us-east-1 for account $($awsAccount)."
|
|
}
|
|
|
|
if ($awsAccount -like "*prod*") {
|
|
try {
|
|
$ec2Instances += (Get-EC2Instance -ProfileName $AWSAccount -Region us-west-2).Instances
|
|
} catch {
|
|
Write-Error "$logLead : An error occurred while retrieving EC2 instances in us-west-2 for account $($awsAccount)."
|
|
}
|
|
}
|
|
$incrementPercent += 5
|
|
}
|
|
|
|
# Build some indexes to speed things up later
|
|
foreach ($instance in $ec2Instances) {
|
|
Write-Progress -Activity "Generating Report" -Status "Retrieving EC2 Data" -Id 1 -PercentComplete ($incrementPercent + 5)
|
|
# Index by IPv4 Address
|
|
if ($instance.PrivateIpAddress) {
|
|
$ec2ObjectIndexByIP.Add($instance.PrivateIpAddress, $ec2IPIndex)
|
|
}
|
|
$ec2IPIndex++
|
|
}
|
|
foreach ($instance in $ec2Instances) {
|
|
# Index by the assigned Hostname
|
|
if ($instance.tags.key -contains "alk:hostname") {
|
|
$name = ($instance.tags | Where-Object { $_.Key -eq "alk:hostname" }).Value.ToLower()
|
|
# There are some duplicate hostnames, this will append the index number to avoid throwing an error
|
|
if (!($ec2ObjectIndexByName.Contains($name))) {
|
|
$ec2ObjectIndexByName.Add($name, $ec2NameIndex)
|
|
}
|
|
else {
|
|
$name = $name + $ec2NameIndex
|
|
$ec2ObjectIndexByName.Add($name, $ec2NameIndex)
|
|
}
|
|
}
|
|
$ec2NameIndex++
|
|
}
|
|
# Now that we have indexes let's see if we have matching EC2 Instances
|
|
foreach ($computer in $adComputers) {
|
|
try {
|
|
$nameIndex = $ec2ObjectIndexByName[$computer.Name.ToLower()]
|
|
}
|
|
catch {
|
|
$nameIndex = $null
|
|
}
|
|
try {
|
|
$ipIndex = $ec2ObjectIndexByIP[$computer.IPv4Address]
|
|
}
|
|
catch {
|
|
$ipIndex = $null
|
|
}
|
|
if (!($null -eq $nameIndex)) {
|
|
$instanceByName = $ec2Instances[$nameIndex]
|
|
}
|
|
else {
|
|
$instanceByName = $null
|
|
}
|
|
if (!($null -eq $ipIndex)) {
|
|
$instanceByIPv4 = $ec2Instances[$ipIndex]
|
|
}
|
|
else {
|
|
$instanceByIPv4 = $null
|
|
}
|
|
# Compare results
|
|
if (!($null -eq $ipIndex) -or !($null -eq $nameIndex)) {
|
|
$ec2NameMatchesIP = (($instanceByName.InstanceId) -eq ($instanceByIPv4.InstanceId))
|
|
}
|
|
else {
|
|
$ec2NameMatchesIP = $false
|
|
}
|
|
# If things look to be lining up lets try and get the designation number if it exists
|
|
if ($ec2NameMatchesIP -eq $true) {
|
|
if ($instanceByName.tags.key -contains "alk:designation") {
|
|
$designation = ($instanceByName.tags | Where-Object { $_.Key -eq "alk:designation" }).Value
|
|
}
|
|
}
|
|
else {
|
|
$designation = $null
|
|
}
|
|
# Build a custom object
|
|
$tempObj = New-Object -TypeName PSObject
|
|
# Store values
|
|
$tempObjProps = [ordered]@{
|
|
"ADName" = $computer.Name
|
|
"IsEnabled" = $computer.Enabled
|
|
"ADIPv4Address" = $computer.IPv4Address
|
|
"LastLogon" = $computer.lastLogonDate
|
|
"PasswordLastSet" = $computer.PasswordLastSet
|
|
"SID" = $computer.SID
|
|
"InstanceIdByIP" = ($instanceByIPv4.InstanceId)
|
|
"InstanceIdByName" = ($instanceByName.InstanceId)
|
|
"DoesInstanceMatch" = $ec2NameMatchesIP
|
|
"designation" = $designation
|
|
"TerminationConfidence" = [byte](($null -eq $instanceByIPv4) + ($null -eq $instanceByName) + !($ec2NameMatchesIP) + ($computer.PasswordLastSet -lt (Get-Date).AddDays(-90)))
|
|
}
|
|
# Add custom properties
|
|
$tempObj | Add-Member -NotePropertyMembers $tempObjProps -TypeName ComputerObject
|
|
$outputObjects.Add($tempObj)
|
|
}
|
|
}
|
|
else {
|
|
Write-Progress -Activity "Generating Report" -Id 1 -PercentComplete 95
|
|
foreach ($computer in $adComputers) {
|
|
# Build a custom object
|
|
$tempObj = New-Object -TypeName PSObject
|
|
# Store values
|
|
$tempObjProps = [ordered]@{
|
|
"ADName" = $computer.Name
|
|
"IsEnabled" = $computer.Enabled
|
|
"ADIPv4Address" = $computer.IPv4Address
|
|
"LastLogon" = $computer.lastLogonDate
|
|
"PasswordLastSet" = $computer.PasswordLastSet
|
|
"SID" = $computer.SID
|
|
"TerminationConfidence" = [byte]($computer.PasswordLastSet -lt (Get-Date).AddDays(-90))
|
|
}
|
|
# Add custom properties
|
|
$tempObj | Add-Member -NotePropertyMembers $tempObjProps -TypeName ComputerObject
|
|
$outputObjects.Add($tempObj)
|
|
}
|
|
}
|
|
Write-Progress -Activity "Generating Report" -Status "Finished" -Id 1 -Completed
|
|
return $outputObjects
|
|
} |