Diagnostics/Simple/OVF.SharePoint.EnterpriseSearch.Tests.ps1




function New-HostTemplate{
    
    # Template object for the host array:
    $hostTemplate = New-Object -TypeName PSObject
    $hostTemplate | Add-Member -MemberType NoteProperty -Name hostName -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name components -Value 0
    $hostTemplate | Add-Member -MemberType NoteProperty -Name cpc -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name qpc -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name pAdmin -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name sAdmin -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name apc -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name crawler -Value $null
    $hostTemplate | Add-Member -MemberType NoteProperty -Name index -Value $null

    return $hostTemplate

}

function New-HATemplate{
    
    # Template object for the HA group array:
    $haTemplate = New-Object -TypeName PSObject
    $haTemplate | Add-Member -MemberType NoteProperty -Name entity -Value $null
    $haTemplate | Add-Member -MemberType NoteProperty -Name partition -Value -1
    $haTemplate | Add-Member -MemberType NoteProperty -Name primary -Value $null
    $haTemplate | Add-Member -MemberType NoteProperty -Name docs -Value 0
    $haTemplate | Add-Member -MemberType NoteProperty -Name components -Value 0
    $haTemplate | Add-Member -MemberType NoteProperty -Name componentsOk -Value 0
    
    return $haTemplate
    
}

function New-ComponentTemplate{
    
    # Template object for the component/server table:
    $compTemplate = New-Object -TypeName PSObject
    $compTemplate | Add-Member -MemberType NoteProperty -Name Component -Value $null
    $compTemplate | Add-Member -MemberType NoteProperty -Name Server -Value $null
    $compTemplate | Add-Member -MemberType NoteProperty -Name Partition -Value $null
    $compTemplate | Add-Member -MemberType NoteProperty -Name State -Value $null
    
    return $compTemplate

}

function New-ComponentHAList{
    
    param(
        $searchComponent
    )
    
    if ($searchComponent.ServerName)
    {
        $hostName = $searchComponent.ServerName
    }
    else
    {
        $hostName = "Unknown server"
    }
    $partition = $searchComponent.IndexPartitionOrdinal
    $newHostFound = $true
    $newHaFound = $true
    $entity = $null

    foreach ($searchHost in ($script:hostArray))
    {
        if ($searchHost.hostName -eq $hostName)
        {
            $newHostFound = $false
        }
    }
    if ($newHostFound)
    {
        # Add the host to $script:hostArray
        $hostTemp = New-HostTemplate
        $hostTemp.hostName = $hostName
        $script:hostArray += $hostTemp
        $script:searchHosts += 1
    }

    # Fill in component specific data in $script:hostArray
    foreach ($searchHost in ($script:hostArray))
    {
        if ($searchHost.hostName -eq $hostName)
        {
            $partition = -1
            if ($searchComponent.Name -match "Query") 
            { 
                $entity = "QueryProcessingComponent" 
                $searchHost.qpc = "QueryProcessing "
                $searchHost.components += 1
            }
            elseif ($searchComponent.Name -match "Content") 
            { 
                $entity = "ContentProcessingComponent" 
                $searchHost.cpc = "ContentProcessing "
                $searchHost.components += 1
            }
            elseif ($searchComponent.Name -match "Analytics") 
            { 
                $entity = "AnalyticsProcessingComponent" 
                $searchHost.apc = "AnalyticsProcessing "
                $searchHost.components += 1
            }
            elseif ($searchComponent.Name -match "Admin") 
            { 
                $entity = "AdminComponent" 
                if ($searchComponent.Name -eq $script:primaryAdmin)
                {
                    $searchHost.pAdmin = "Admin(Primary) "
                }
                else
                {
                    $searchHost.sAdmin = "Admin "
                }
                $searchHost.components += 1
            }
            elseif ($searchComponent.Name -match "Crawl") 
            { 
                $entity = "CrawlComponent" 
                $searchHost.crawler = "Crawler "
                $searchHost.components += 1
            }
            elseif ($searchComponent.Name -match "Index") 
            { 
                $entity = "IndexComponent"
                $partition = $searchComponent.IndexPartitionOrdinal
                $searchHost.index = "IndexPartition($partition) "
                $searchHost.components += 1
            }
        }
    }

    # Fill in component specific data in $script:haArray
    foreach ($haEntity in ($script:haArray))
    {
        if ($haEntity.entity -eq $entity)
        {
            if ($entity -eq "IndexComponent")
            {
                if ($haEntity.partition -eq $partition)
                {
                    $newHaFound = $false
                }
            }
            else 
            { 
                $newHaFound = $false
            }
        }
    }
    if ($newHaFound)
    {
        # Add the HA entities to $script:haArray
        $haTemp = New-HATemplate
        $haTemp.entity = $entity
        $haTemp.components = 1
        if ($partition -ne -1) 
        { 
            $haTemp.partition = $partition 
        }
        $script:haArray += $haTemp
    }
    else
    {
        foreach ($haEntity in ($script:haArray))
        {
            if ($haEntity.entity -eq $entity) 
            {
                if (($entity -eq "IndexComponent") )
                {
                    if ($haEntity.partition -eq $partition)
                    {
                        $haEntity.components += 1
                    }
                }
                else
                {
                    $haEntity.components += 1
                    if (($haEntity.entity -eq "AdminComponent") -and ($searchComponent.Name -eq $script:primaryAdmin))
                    {
                        $haEntity.primary = $script:primaryAdmin
                    }
                }
            }
        }
    }
}

function Get-IndexerEvents{

    $indexerComps = $script:escss | Where-Object{$_.Name -match "Index" -or $_.Name -match "Content" -or $_.Name -match "Admin" -and $_.Name -notmatch "Cell" -and $_.State -notmatch "Unknown" -and $_.State -notmatch "Registering"}

    foreach ($component in $indexerComps)
    {

        [array]$events += Get-SPEnterpriseSearchStatus -SearchApplication $script:essa -HealthReport -Component $component.Name
        
        return $events
    
    } 

}

function Test-AnalyticsStatus{
    
    $analyticsStatus = Get-SPEnterpriseSearchStatus -SearchApplication $script:essa -JobStatus

    foreach ($analyticsEntry in $analyticsStatus)
    {
        
        # Output additional diagnostics from the dictionary
        foreach ($de in ($analyticsEntry.Details))
        {
            # Skip entries that is listed as Not Available
            if ( ($de.Value -ne "Not available") -and ($de.Key -ne "Activity") -and ($de.Key -ne "Status") )
            {
                if ($de.Key -match "Last successful start time")
                {
                    $dLast = Get-Date $de.Value
                    $dNow = Get-Date
                    $daysSinceLastSuccess = $dNow.DayOfYear - $dLast.DayOfYear
                    if ($daysSinceLastSuccess -gt 3)
                    {
                        [array]$analytcsJobNotRunCount += 1
                    }
                }
            }
        }
    }
    
    return $analytcsJobNotRunCount
}


Add-PSSnapin Microsoft.SharePoint.Powershell
    
$script:essa = Get-SPEnterpriseSearchServiceApplication
$script:esccs = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $script:essa
$script:est = Get-SPEnterpriseSearchTopology -SearchApplication $script:essa -Active
$script:esc = Get-SPEnterpriseSearchComponent -SearchTopology $script:est
$script:ess = Get-SPEnterpriseSearchStatus -SearchApplication $script:essa -JobStatus
$script:eshc = Get-SPEnterpriseSearchHostController
$script:escss = Get-SPEnterpriseSearchStatus -SearchApplication $script:essa


$script:hostArray = @()

$script:haArray = @()

$script:compArray = @()

$script:topologyCompList = Get-SPEnterpriseSearchComponent -SearchTopology $script:est

foreach ($component in ($script:esccs)){
    if ( ($component.Name -match "Admin") -and ($component.State -ne "Unknown") ){
        if (Get-SPEnterpriseSearchStatus -SearchApplication $script:essa -Primary -Component $($component.Name)){
            $script:primaryAdmin = $component.Name
        }
    }
}   

foreach ($searchComponent in ($script:topologyCompList))
{
    New-ComponentHAList -searchComponent $searchComponent
}


$componentEvents = Get-IndexerEvents


Describe "Operational Validation of SharePoint 2013 Search Topology"{

    
    
    It "Enterprise Search Service Application should be Online"{
    
        $script:essa.Status | Should be "Online"

    }
    
    It "All Enterprise Search Components should be Online"{
        
        $offlineComponents = ($script:escss | Where-Object{$_.State -ne "Active"} | Measure-Object).Count
        
        $offlineComponents | Should be 0

    }    

    It "Indexer, Content Processor and Admin Component should not have Errors"{
        $errorEventCount = ($componentEvents | Where-Object{$_.Level -eq "Error"} | Measure-Object).Count
        
        $errorEventCount | Should be 0
    }
    
    It "Indexer, Content Processor and Admin Component should not have Warnings"{
        $warningEventCount = ($componentEvents | Where-Object{$_.Level -eq "Warning"} | Measure-Object).Count
        
        $warningEventCount | Should be 0
    }

    It "No Component should be on a High Document Count"{
        $docsHighCount = ($script:haArray | Where-Object{$_.docs -gt 9000000} | Measure-Object).Count 
    
        $docsHighCount | Should be 0
    }
    
    It "No Component should exceed the Healthy Document Count"{
    
        $docsExceededCount = ($script:haArray | Where-Object{$_.docs -gt 10000000}| Measure-Object).Count
        
        $docsExceededCount | Should be 0
    }

    It "All Host Controllers should have the same Repository Version"{
        
        $hostControllerCount = ($script:eshc | Measure-Object).Count
        if($hostControllerCount -eq 1){
            $sameVersion = $true
        }
        elseif($hostControllerCount -gt 1){
            
            $highestVersion = 0
            
            foreach($hostController in $script:eshc){
                if($hostController.Version -gt $highestVersion){
                    $highestVersion = $hostController.Version
                }
            }
            
            $hostControllerWithLowerVersionCount = ($script:eshc | Where-Object{$_.Version -lt $highestVersion} | Measure-Object).Count
            
            if($hostControllerWithLowerVersionCount = 0){
                $sameVersion = $true
            }
            else{
                $sameVersion = $false
            }
            
        }
        
        $sameVersion | Should be true

    }

    It "All Analytics Jobs should have Succesfully Run in the last three Days"{
        $jobsNotRunCount = Test-AnalyticsStatus
        
        $jobsNotRunCount | Should be 0
    }

    It "Enterprise Search Service Application should not be Paused"{
        
        $essaStatus = $script:essa.Ispaused()
        
        $essaStatus | Should be 0
        
    }
    
    It "All Content Sources should have been successfully crawled in the last 3 days"{
        
        $contentSources = Get-SPEnterpriseSearchCrawlContentSource -SearchApplication $global:essa
        
        $crawlNotCompletedCount = 0
        
        foreach($contentSource in $contentSources){
            
            $crawlLastCompleted = $contentSource.CrawlCompleted
            $timeDifference = (Get-Date) - $crawlLastCompleted
            
            if($timeDifference.Days -ge 3){
                $crawlNotCompletedCount++
            }
            
        }
        
        $crawlNotCompletedCount | Should be 0
        
    }


}