0

I'm trying to compare 2 arrays for duplicate entries. If an entry exists in both arrays I want to save that to a variable. Also I'd like for the variable with the information to contain the existing structure I am pulling working with from the spreadsheet. (You can see in my code I'm pulling info such as patch window, ASA, test, dmz, server name).

Here is a bit more information of what I am trying to accomplish.

First I pull a list of servers from Active Directory using [adsisearcher]. The only variable that really interests me is the name so that's all it stores. Let's name this variable $A.

Next I have a spreadsheet with a bunch of servers and other information such as whether it's a test server, prod, etc etc... I use the "Import-Excel" powershell module to import the data from my worksheet, but the difference here is I pull a few different things. servername, whether it's DMZ, test server, the patch window and the sys admin of the server.

I use LINQ to check values that intersect between the 2 arrays, but the output is empty.

The end result is this... I'd like a fast way to spot check the servers with what's on the spreadsheet to make sure they're still active and\or it's not an appliance.

I have tried doing a foreach loop to check each server using:

if(($($ServerList.DMZServer) -eq $true) -OR ($NULL -ne (([adsisearcher]"(&(objectClass=computer)(objectCategory=computer)(samaccountname=$($ServerList.ServerName)$))").FindOne())))    
{
blah
}

However this takes longer than I'd like so I was hoping for something a bit faster. Since I have all the data available and I can compare 2 arrays I thought that might be the best option, but I'm not getting the results I'm hoping for. I'm open to any recommendations or assistance.

Here is my script:

CLS

$ErrorActionPreference = 'SilentlyContinue'
$ErrorFile = (Split-Path $script:MyInvocation.MyCommand.Path) + "\ERROR.txt"

#Install the module that will let us perform certain tasks in Excel
#Install PSExcel Module for powershell
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

#Check if Module is imported
if(Get-Module -ListAvailable -Name "ImportExcel")
{
        Import-Module ImportExcel
}
else
{
    #Install NuGet (Prerequisite) first
    Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
    
    Install-Module -Name ImportExcel -Scope CurrentUser -Force -Confirm:$False
    Import-Module ImportExcel
}

#Clear screen again
CLS

#Start Timestamp
Get-Date

Write-Host " "

#------------------------------------ Global Variables

$Path = (Split-Path $script:MyInvocation.MyCommand.Path)

$ListA = $ListB = $ServerList = $ServersList = $FilteredServers = @()
    
#------------------------------------  Setup Excel Variables

#The file we will be reading from
$ExcelFile = (Get-ChildItem -Path "$Path\*.xlsx").FullName
    
#Worksheet we are working on (by default this is the 1st tab)
$worksheet = (((New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList (New-Object -TypeName System.IO.FileStream -ArgumentList $ExcelFile,'Open','Read','ReadWrite')).Workbook).Worksheets[1]).Name

$Servers = Import-Excel -Path $ExcelFile -WorkSheetname $worksheet -StartRow 1

#------------------------------------ Populate our variable with data from spreadsheet

foreach($Server in $Servers)
{
    $ServersList += [pscustomobject]@{ 'ServerName'   = $Server."Child"
                                       'TestServer'   = $Server."Test server"
                                       'DMZServer'    = $Server."DMZ"
                                       'PatchWindow'  = $Server."Patch Window"
                                       'ASA'          = $Server."Primary" }
}

#------------------------------------ Remove Duplicate entries

$ServersList = ($ServersList | Sort-Object -Property ServerName -Unique)

#------------------------------------ Seperate Servers from DMZ Servers

foreach($ServerList in $ServersList)
{
    #Check if server is on DMZ
    if($($ServerList.DMZServer) -eq $true)
    {
        $ServerList.ServerName += ".dmz.com"
    }

    $FilteredServers += $ServerList
}

#------------------------------------ Filter out Appliances

#                First Grab all servers from AD

#------------------------------------

#Get Servers
$searcher = [adsisearcher]::new()

$searcher.Sort.PropertyName = "name"

#Search root is the OU level we want to search at
$searcher.SearchRoot = [adsi]"LDAP://OU=Servers,DC=blah,DC=com"

#Make this any non zero value to expand the default result size.
$searcher.PageSize = 100

#Filter Computers only and enabled
$searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

#List the properties we are interested in
$searcher.PropertiesToLoad.AddRange(('name','distinguishedname'))

#Now output the results with the exception of filtering out any machines in the citrix farms (we're not interested in those)
$Servers = Foreach ($Computer in $searcher.FindAll()){ ($Computer.Properties).name }

#------------------------------------

## Compare the 2 arays and pull out the servers that we want.

[String[]]$ListA = $Servers
[String[]]$ListB = $FilteredServers.ServerName

[System.Linq.Enumerable]::Intersect([object[]]$ListA, [object[]]$ListB)
Koobah84
  • 185
  • 2
  • 12
  • Use a hash table as in: [In Powershell, what's the best way to join two tables into one?](https://stackoverflow.com/a/45483110/1701026)). Using this [`Join-Object script`](https://www.powershellgallery.com/packages/Join)/[`Join-Object Module`](https://www.powershellgallery.com/packages/JoinModule), I quess you want to do something like: `$Computer |Join $FilteredServers -on Name -eq ServerName` As an aside, if you are concerned with performance, you should [try to avoid using the increase assignment operator (`+=`) to create a collection](https://stackoverflow.com/a/60708579/1701026). – iRon Mar 31 '23 at 12:14
  • 1
    Thank you for your response and suggestion. I will look into that module and into those few optimizations to improve the performance of the script. I'm always eager to learn new tricks and seeing how I can improve something. – Koobah84 Mar 31 '23 at 14:10
  • @iRon I used some of your recommendations with avoiding using the += operator in powershell. I'm not sure if you see any other ways of optimizing the below code. the code concatentation I'm not sure if there is a better way of doing it. I could figure out how to get the join to work properly that's why I used concat. I also tried using stringbuilder, but not sure if it can be applied in my code below. – Koobah84 Apr 03 '23 at 01:10

1 Answers1

0

UPDATE 4-11-23: Ended up using PoshRSJob module instead of my runspacepool. Now my script runs in under 10 seconds. I don't think it can be any more optimized than that. I'll update the main runspace bit below.

UPDATE 4-5-23: I updated and optimized my code even further. I am able to run this in under a minute. There were 2 major changes from my previous code.

  1. When I pull the list of servers from AD, I was using ADSISearcher. This is generally pretty fast, however I have come across even a faster method and it required less code. The new method I use is DSQUERY.
  2. The 2nd big update is how I calculate uptime on each server. I was trying to find a clever way to get the value. I tried several ways using get-wmiobject, ciminstance, reading from event viewer logs, trying various commands and sorting through the output, and the tick function. All of these were "acceptable", but still made my script take anywhere from 1+ min(s) to run. Then I thought of a different way of calculating this. My theory was to grab a file or directory that we know changes at every boot time and no sooner. This wasn't an easy task because I came across a lot of false positives/negatives and couldn't find a file or directory that worked... until I did. What I noticed is the file located at "C:\Windows\Tasks\SA.DAT" seems to be exactly what I was looking for. So I calculated the time from when it was last written to to get the amount of days the server has been online and it's been working really well for me. It's also significantly faster. I cut off at least 20 seconds from my previous code. From all the troubleshooting and searching I have done, I haven't come across a strategy like this even though if you think about it, it's pretty trivial. Anyway, I wanted to share with the community and hopefully you find it helpful!

I got my code working. I'm still open to any suggestions for optimization and tweaks, but the final code works for me. I can query 1000 servers and my script takes just over 1 minute to run.

I ended up using this code to do the comparison between my spreadsheet and what's in AD.

$FilteredServers = ($FilteredServers | Where-Object {($_.servername -in $Servers.name) -or ($_.DMZ -eq $True)})

Ironically the runspace to calculate the server uptime takes the longest amount of time, otherwise my script runs in 3-4 seconds.

If there is a more optimized way to check uptime, I'm all ears. By the way, I know cimInstance is better than using wmi-object, but I was having trouble using ciminstance on some servers and get-wmiobject seems to work without a hitch.

([timespan]::FromMilliseconds([Math]::Abs([Environment]::TickCount))).days

The code above runs fastest locally, but there isn't a good way to run it remotely on a bunch of servers.

Here is the whole script for anyone interested.

CLS

$ErrorActionPreference = 'SilentlyContinue'
$ErrorFile = (Split-Path $script:MyInvocation.MyCommand.Path) + "\ERROR.txt"

#Install the module that will let us perform certain tasks in Excel
#Install PSExcel Module for powershell

#Check if Module is imported
if(Get-Module -ListAvailable -Name "ImportExcel")
{
        Import-Module ImportExcel
}
else
{
    #Install NuGet (Prerequisite) first
    Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
    
    Install-Module -Name ImportExcel -Scope CurrentUser -Force -Confirm:$False
    Import-Module ImportExcel
}

#----------------------------------

#Clear screen again
CLS

#Start Timestamp
Get-Date

Write-Host " "

#------------------------------------ Global Variables
$Results = @()
$Output = @()
$Path = (Split-Path $script:MyInvocation.MyCommand.Path)
    
#------------------------------------  Setup Excel Variables

#The file we will be reading from
$ExcelFile = (Get-ChildItem -Path "$Path\*.xlsx").FullName
    
#Worksheet we are working on (by default this is the 1st tab)
$worksheet = (((New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList (New-Object -TypeName System.IO.FileStream -ArgumentList $ExcelFile,'Open','Read','ReadWrite')).Workbook).Worksheets[1]).Name

$Servers = Import-Excel -Path $ExcelFile -WorkSheetname $worksheet -StartRow 1

#------------------------------------ Populate our variable with data from spreadsheet

$ServersList = foreach($Server in $Servers) {
    $Server | Select-Object @{Name="ServerName";Expression={$_.Child}}, "Primary", @{Name="PatchWindow";Expression={$_."Patch Window"}}, @{Name="TestServer";Expression={$_."Test Server"}}, "DMZ" 
}

#------------------------------------ Remove Duplicate entries

$ServersList = ($ServersList | Sort-Object -Property ServerName -Unique)

#------------------------------------ Seperate Servers from DMZ Servers

$FilteredServers = $ServersList | ForEach-Object -Process {

    if($($_.DMZ) -eq $true)
    {
        $_.ServerName = [System.String]::Concat("$($_.ServerName)",".DMZ.com")
    }

    if($($_.PatchWindow) -like "*PVS*")
    {
        $_.ServerName = [System.String]::Concat("$($_.ServerName)","- PVS *SKIP*")
    }

    $_
}

#------------------------------------ Grab all servers from AD so we can use to compare against our list

#Get Servers
$searcher = [adsisearcher]::new()

$searcher.Sort.PropertyName = "name"

#Search root is the OU level we want to search at
$searcher.SearchRoot = [adsi]"LDAP://DC=blah,DC=com"

#Make this any non zero value to expand the default result size.
$searcher.PageSize = 100

#Filter Computers only and enabled
$searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"

#List the properties we are interested in
$searcher.PropertiesToLoad.AddRange(('name','operatingSystem'))

$Servers = Foreach ($Computer in $searcher.FindAll() | where { ($_.properties["operatingSystem"] -like "*Server*") }){ ($Computer.Properties) }

#------------------------------------ Compare our list to servers in AD and filter out appliances

$FilteredServers = ($FilteredServers | Where-Object {($_.servername -in $Servers.name) -or ($_.DMZ -eq $True)})

#------------------------------------ Script Block

[System.Management.Automation.ScriptBlock]$ScriptBlock = {

    Param ( $FilteredServer)

    $RebootNeeded = $Check = $NULL

    #Ping servers to make sure they're responsive
    if([bool](Get-CimInstance -ClassName Win32_PingStatus -Filter "Address='$($FilteredServer.ServerName)' AND Timeout=100") -eq $True)
    {
        $Check = ((Get-Date) - (Get-WmiObject win32_operatingsystem -ComputerName $FilteredServer.ServerName | select csname, @{LABEL='LastBootUpTime';EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}}).LastBootUpTime).days

        #Check if server hasn't been rebooted in greater than or equal to 20 days.
        if($Check -ge 20)
        {
           $RebootNeeded = $FilteredServer | Add-Member -NotePropertyMembers @{"Day's Online" = $Check} -PassThru
        }
    }

    return $RebootNeeded
}

function Invoke-AsyncJob
{
    $AllJobs = New-Object System.Collections.ArrayList

    $HostRunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(20,100,$Host)

    $HostRunspacePool.Open()

    foreach($FilteredServer in $FilteredServers)
    {
        $asyncJob = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddArgument($FilteredServer)
        
        $asyncJob.RunspacePool = $HostRunspacePool

        $asyncJobObj = @{ JobHandle   = $asyncJob
                          AsyncHandle = $asyncJob.BeginInvoke()    }

        $AllJobs.Add($asyncJobObj) > $NULL
    }
    $ProcessingJobs = $true

    Do 
    {
        $CompletedJobs = $AllJobs | Where-Object { $_.AsyncHandle.IsCompleted }

        if($null -ne $CompletedJobs)
        {
            foreach($job in $CompletedJobs)
            {
                
                $RebootNeeded = $job.JobHandle.EndInvoke($job.AsyncHandle)

                if($null -ne $RebootNeeded)
                {
                    $Results += $RebootNeeded
                }

                $job.JobHandle.Dispose()

                $AllJobs.Remove($job)
            } 
        } 
        else 
        {
            if($AllJobs.Count -eq 0)
            {
                $ProcessingJobs = $false
            } 
            else 
            {
                Start-Sleep -Milliseconds 1000
            }
        }
    } 
    While ($ProcessingJobs)

    $HostRunspacePool.Close()
    $HostRunspacePool.Dispose()

    $Output = ($Results | Sort-Object -Property ServerName)

    #Output to File
    $Output |  Export-Csv -Path "$Path\Results.csv" -NoTypeInformation -Force

    #Output to screen
    return $Output

    Write-Host "`n`n"
} 

Invoke-AsyncJob


Write-Host " "

Get-Date

UPDATED CODE 4-5-23

CLS

#Install the module that will let us perform certain tasks in Excel
#Install PSExcel Module for powershell

#Check if Module is imported
if(Get-Module -ListAvailable -Name "ImportExcel")
{
        Import-Module ImportExcel
}
else
{
    #Install NuGet (Prerequisite) first
    Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
    
    Install-Module -Name ImportExcel -Scope CurrentUser -Force -Confirm:$False
    Import-Module ImportExcel
}

#----------------------------------

#Clear screen again
CLS

#Start Timestamp
$Start = Get-Date

#------------------------------------ Global Variables
#Global Variables
$Path = (Split-Path $script:MyInvocation.MyCommand.Path)
$ErrorFile = (Split-Path $script:MyInvocation.MyCommand.Path) + "\ERROR.txt"
$Results = @()
    
#------------------------------------  Setup Excel Variables

#The file we will be reading from
$ExcelFile = (Get-ChildItem -Path "$Path\*.xlsx").FullName
    
#Worksheet we are working on (by default this is the 1st tab)
$worksheet = (((New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList (New-Object -TypeName System.IO.FileStream -ArgumentList $ExcelFile,'Open','Read','ReadWrite')).Workbook).Worksheets[1]).Name

$ExcelServers = Import-Excel -Path $ExcelFile -WorkSheetname $worksheet -StartRow 1

#------------------------------------ Populate our variable with data from spreadsheet

$ExcelServersList = foreach($ExcelServer in $ExcelServers) {
    $ExcelServer | Select-Object @{Name="ServerName";Expression={$_.Child}}, "Primary", @{Name="PatchWindow";Expression={$_."Patch Window"}}, @{Name="TestServer";Expression={$_."Test Server"}}, "DMZ" 
}

#------------------------------------ Remove Duplicate entries

$SortedExcelServersList = ($ExcelServersList | Sort-Object -Property ServerName -Unique)

#------------------------------------ Seperate Servers from DMZ Servers

$FilteredServers = ForEach($SortedExcelServerList in $SortedExcelServersList) {

    if($($SortedExcelServerList.DMZ) -eq $true)
    {
        $SortedExcelServerList.ServerName = [System.String]::Concat("$($SortedExcelServerList.ServerName)",".DMZ.com")
    }

    $SortedExcelServerList
}

#------------------------------------ Grab all servers from AD so we can use to compare against our list - also trim any whitespaces from output

$Servers = (dsquery * -filter "(&(objectClass=Computer)(objectCategory=Computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(operatingSystem=*Server*))" -limit 0 -attr Name | sort).trim()

#------------------------------------ Compare our list to servers in AD and filter out appliances

$FilteredServersResult = $Null

$FilteredServersResult = ForEach ($Item in $FilteredServers) 
{
    If (($item.servername -in $Servers) -or ($item.DMZ -eq $True))
    {
        $Item
    }
}

#------------------------------------ Script Block

[System.Management.Automation.ScriptBlock]$ScriptBlock = {

    Param ( $FilteredServer)

    $RebootNeeded = $Check = $NULL
    $ServerPath = "\\$($FilteredServer.ServerName)\c$\Windows\Tasks\SA.DAT"

    #Ping servers to make sure they're responsive
    if($NULL -ne (Get-CimInstance -ClassName Win32_PingStatus -Filter "Address='$($FilteredServer.ServerName)' AND Timeout=100").ResponseTime)
    { 
        #Instead of calculating server uptime, we check by getting the lastwrittenTime for a file that generally updates @ every boot. You can calculate the difference to get the # of days server is up
        $Check = ((Get-Date) - (Get-ChildItem $ServerPath -Force).LastWriteTime).Days

        #Check if server hasn't been rebooted in greater than or equal to 25 days.
        if($Check -ge 20)
        {
           $RebootNeeded = $FilteredServer | Add-Member -NotePropertyMembers @{"Day's Online" = $Check} -PassThru
        }
    } 

    return $RebootNeeded
}

function Invoke-AsyncJob
{
    $AllJobs = New-Object System.Collections.ArrayList

    $HostRunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(20,100,$Host)

    $HostRunspacePool.Open()

    foreach($FilteredServer in $FilteredServersResult)
    {
        $asyncJob = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddArgument($FilteredServer)
        
        $asyncJob.RunspacePool = $HostRunspacePool

        $asyncJobObj = @{ JobHandle   = $asyncJob
                          AsyncHandle = $asyncJob.BeginInvoke()    }

        $AllJobs.Add($asyncJobObj) > $NULL
    }
    $ProcessingJobs = $true

    Do 
    {
        $CompletedJobs = $AllJobs | Where-Object { $_.AsyncHandle.IsCompleted }

        if($null -ne $CompletedJobs)
        {
            foreach($job in $CompletedJobs)
            {
                $RebootNeeded = $job.JobHandle.EndInvoke($job.AsyncHandle)

                if($null -ne $RebootNeeded)
                {
                    $Results += $RebootNeeded
                }
                
                $job.JobHandle.Dispose()

                $AllJobs.Remove($job)
            } 
        } 
        else 
        {
            if($AllJobs.Count -eq 0)
            {
                $ProcessingJobs = $false
            } 
            else 
            {
                Start-Sleep -Milliseconds 1000
            }
        }
    } 
    While ($ProcessingJobs)

    $HostRunspacePool.Close()
    $HostRunspacePool.Dispose()

    ($Results | Sort-Object -Property ServerName) | Export-Csv -Path "$Path\Results.csv" -NoTypeInformation -Force

    #Output to screen
    return $Results

    Write-Host "`n`n"
} 

Invoke-AsyncJob

#End Timestamp
$End = Get-Date

$End - $Start

UPDATED CODE 4-11-23

CLS

#Install the module that will let us perform certain tasks in Excel
#Install PSExcel Module for powershell

#Check if Module is imported
#------------- PoshRSJob (multitasking)

if((Get-Module -ListAvailable -Name "PoshRSJob") -or (Get-Module -Name "PoshRSJob"))
{
        Import-Module PoshRSJob
}
else
{   
    Install-Module -Name PoshRSJob -Scope CurrentUser -Force -Confirm:$False
    Import-Module PoshRSJob
}

#------------- ImportExcel Module 

if((Get-Module -ListAvailable -Name "ImportExcel") -or (Get-Module -Name "ImportExcel"))
{
        Import-Module ImportExcel
}
else
{
    #Install NuGet (Prerequisite) first
    Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
    
    Install-Module -Name ImportExcel -Scope CurrentUser -Force -Confirm:$False
    Import-Module ImportExcel
}

#----------------------------------

#Clear screen again
CLS

#Start Timestamp
$Start = Get-Date

#------------------------------------ Global Variables
#Global Variables
$Path = (Split-Path $script:MyInvocation.MyCommand.Path)
$ErrorFile = (Split-Path $script:MyInvocation.MyCommand.Path) + "\ERROR.txt"
$Results = @()

#------------------------------------  Setup Excel Variables

#The file we will be reading from
$ExcelFile = (Get-ChildItem -Path "$Path\*.xlsx").FullName

#Worksheet we are working on (by default this is the 1st tab)
$worksheet = (((New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList (New-Object -TypeName System.IO.FileStream -ArgumentList $ExcelFile,'Open','Read','ReadWrite')).Workbook).Worksheets[1]).Name

$ExcelServers = Import-Excel -Path $ExcelFile -WorkSheetname $worksheet -StartRow 1

#------------------------------------ Populate our variable with data from spreadsheet

$ExcelServersList = foreach($ExcelServer in $ExcelServers) {
    $ExcelServer | Select-Object @{Name="ServerName";Expression={$_.Child}}, "Primary", @{Name="PatchWindow";Expression={$_."Patch Window"}}, @{Name="TestServer";Expression={$_."Test Server"}}, "DMZ" 
}

#------------------------------------ Remove Duplicate entries

$SortedExcelServersList = ($ExcelServersList | Sort-Object -Property ServerName -Unique)

#------------------------------------ Seperate Servers from DMZ Servers

$FilteredServers = ForEach($SortedExcelServerList in $SortedExcelServersList) {

    if($($SortedExcelServerList.DMZ) -eq $true)
    {
        $SortedExcelServerList.ServerName = [System.String]::Concat("$($SortedExcelServerList.ServerName)",".DMZ.com")
    }

    $SortedExcelServerList
}

#------------------------------------ Grab all servers from AD so we can use to compare against our list - also trim any whitespaces from output

$Servers = (dsquery * -filter "(&(objectClass=Computer)(objectCategory=Computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(operatingSystem=*Server*))" -limit 0 -attr Name | sort).trim()

#------------------------------------ Compare our list to servers in AD and filter out appliances

$FilteredServersResult = $Null

$FilteredServersResult = ForEach ($Item in $FilteredServers) 
{
    If (($item.servername -in $Servers) -or ($item.DMZ -eq $True))
    {
        $Item
    }
}

#------------------------------------ Multithreading Magic using Posh-RSJob

$FilteredServersResult | Start-RSJob -Throttle 25 -ScriptBlock {
    Param($Server)

    #Ping servers to make sure they're responsive
    if($NULL -ne (Get-CimInstance -ClassName Win32_PingStatus -Filter "Address='$($Server.servername)' AND Timeout=100").ResponseTime)
    { 
        Try
        {
            $ServerPath = "\\$($Server.ServerName)\c$\Windows\ServiceProfiles\LocalService\NTUSER.DAT"

            #If you're checking file date
            $Check = ((Get-Date) - (dir $ServerPath -force -ErrorAction Stop).LastWriteTime).days
        }
        Catch
        {
            Try
            {
                $Check = ((Get-Date) - (Get-WmiObject win32_operatingsystem -ComputerName $($Server.ServerName) -ErrorAction Stop | select csname, @{LABEL='LastBootUpTime';EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}}).LastBootUpTime).days
            }
            Catch
            {                
                if($Error.exception -like "*")
                {
                    ($Server | Add-Member -NotePropertyMembers @{"Error" = [string]$Error} -PassThru) | Export-Csv -Path $using:ErrorFile -NoTypeInformation -Force -Append
                }

                #$Error | Select –Property *
            }
        }
        
        #Check if server hasn't been rebooted in greater than or equal to 20 days.
        if($Check -ge 20)
        {
            $Server | Add-Member -NotePropertyMembers @{"Day's Online" = $Check} -PassThru
        }
    }

} | Wait-RSJob -ShowProgress | Receive-RSJob | Export-Csv -Path "$Path\Results.csv" -NoTypeInformation -Force

#End Timestamp
$End = Get-Date

$End - $Start
Koobah84
  • 185
  • 2
  • 12