ES1_HealthChecks.psm1

<#
    .NOTES
    ===========================================================================
 
    Copyright � 2018 Dell Inc. or its subsidiaries. All Rights Reserved.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
           http://www.apache.org/licenses/LICENSE-2.0
    ===========================================================================
    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
    WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
    IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
    CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.

    .DESCRIPTION
        Functions for inspecting SourceOne operations and getting health and status information
        Functions adapted from Mike Tramonts Health Check scripts

#>


#requires -Version 3

<#
.SYNOPSIS
    Gets Status and health information about all servers that make up the SourceOne implmentation
.DESCRIPTION
  Gets Status and health information about all servers that make up the SourceOne implmentation.
  This includes the results of a "ping" and whether all the SourceOne services installed on a machine are
  in the running state.

.OUTPUTS

.EXAMPLE

#>

Function Get-ES1ServersStatus
{
    [CmdletBinding()]
    PARAM ()
    BEGIN {}
    #
    # TODO - Add parameter to pass in additional servers that are not discoverable...
    #

PROCESS {
        $MachineProps=[ordered]@{'ComputerName'='';'Ping'='N/A';'AllServicesRunning'='N/A';'Uptime'='N/A';'Services'=@()}
        $MachineAccess =@()

        try {
        
        $servers=@()
        # Get all es1 servers from DB, includes workers and archivers
        $servers=@(Get-ES1Servers)

        $dbServers=@()
        $dbServers = @(Get-ES1ArchiveDatabases | Select-Object DBServer -Unique )
        $activityDBInfo = Get-ES1ActivityDatabase | Select-Object DBServer
        $dbServers +=  $activityDBInfo

        $dbServers = $dbServers| Select-Object -unique
        
        # Add database servers to the list, parse the instance name if there is one
        foreach($DBServer in $dbservers)
        {
            if ($DBServer.DBServer.Contains('\'))
            {
                $names=$DBServer.DBServer.Split('\\')
                $svrName= $names[0]
            }
            else
            {
                $svrName = $DBServer.DBServer
            }
            $servers += $svrName
        }
        

        $Available = Invoke-Ping -Computer $servers -Quiet

        # Build Unavailable/Unreachable
        if ($Available.Count -ne $servers.Count)
        {
            $unavailable = Compare-Object $servers $Available | where { $_.SideIndicator -eq '<=' }
            foreach ($noping in $unavailable)
            {
                $machineobject = New-Object -TypeName psobject -prop $MachineProps
                $machineobject.ComputerName = $noping.InputObject
                $machineobject.Ping = 'Fail'

                $MachineAccess += $machineobject
            }

  
        }
  
        $local = hostname

        foreach ($pingOK in $Available)
        {
            $machineobject = New-Object -TypeName psobject -prop $MachineProps
            $machineobject.ComputerName = $pingOK
            $machineobject.Ping="Pass"
        
            $uptime = Get-Uptime -ComputerName  $machineobject.ComputerName
            $machineobject.Uptime=$uptime.Uptime           # TODO - Drop the milliseconds
            

            $services = Get-ES1Services -ComputerName $pingOK
            
            if ($services.Count -gt 0)
            {
               $machineobject.AllServicesRunning='Pass'
            }

            $installPath = S1Dir $pingOK

            # Append * so the -Include filter works...
            $installPath += '\*'

              if ($pingOK -eq $local)
                {
                    $exeFiles = Get-ChildItem -recurse -path $installpath -Include @("*.exe") | select-object -ExpandProperty VersionInfo 
                    $AllBinaries = $exeFiles
                }
                else
                {

                    if (Test-PsRemoting $pingOK)
                    {

                        $files = Invoke-Command -Cn $pingOK  -ScriptBlock{
                            $exeFiles = Get-ChildItem -recurse -path $Args[0] -Include @("*.exe") | select-object -ExpandProperty VersionInfo 
                            $exeFiles
                        } -Args $installPath -ErrorVariable remoteErr -ErrorAction SilentlyContinue

                        # $remoteErr contains any error message from the remote system
                        if ($remoteErr)
                        {
                            write-warning $remoteErr
                            $props = [ordered]@{'PSComputerName'=$pingOK ;'FileName'=$remoteErr ;'FileVersion'="ERROR"}
                            $AllBinaries = New-object -TypeName PSObject -Prop $props
                        }
                        else
                        {
                            $AllBinaries = $files
                        }
                    }
                    else
                    {
                        write-warning "**** Ps Remoting is not enabled on remote machine: $pingOK ******"
                        $props = [ordered]@{'PSComputerName'=$server;'FileName'='NA' ;'FileVersion'="Unreachable - Powershell remoting not enabled"}
                        $AllBinaries += New-object -TypeName PSObject -Prop $props

                    }
                }

                
                $services | Add-Member NoteProperty -Name "FileVersion" -Value ""
                foreach ($service in $services)
                {
                    #
                    # Would be good to check the startType but it isn't exposed until .Net 4.6
                    if (($service.Status -eq 'Stopped') -or ($service.Status -eq 'Stop_Pending'))
                    {
                        $machineobject.AllServicesRunning='Fail'
                    }
                    $wmiService = get-wmiobject -ComputerName $pingOK -query "select * from win32_service where name='$($service.Name)'"
           
                    $exePath=$wmiService.PathName.Trim('"')

                    foreach ($binary in $AllBinaries)
                    {
                        if ($exePath -eq $binary.FileName)
                        {

                            $service.FileVersion = $binary.FileVersion
                        }
                    }

                }
                 $machineobject.Services = $services
  
                $MachineAccess += $machineobject
        
            }

     }
     catch
     {
        throw $_
     }

     $MachineAccess

    }

    END{}
}


<#
.SYNOPSIS
  
.DESCRIPTION
  
.OUTPUTS

.EXAMPLE

#>

Function Get-ArchiveFolderStatus
{
    [CmdletBinding()]
    PARAM ()
    BEGIN {}

PROCESS {
    $folderStatus=@()

    $folderStatus = Get-ArchiveFolderSummary

    foreach($folder in $folderSummary)
    {
        $folder | Add-Member NoteProperty -Name "Status" -Value "Pass"

        if (($folder."Volume Items" -ne $folder."Index Items") -or ($folder.Errors -gt 0))
        {
            $folder.Status = "Fail"
        }
        
    }

    $folderStatus
}
END {}



}



## Temporary to help porting Mike T's stuff
function Add-Log
{
   Param
    (
        [Parameter(Position=0)]
        [string] $Msg = '',

        [Parameter(Position=1)]
        [string] $Level = 'Info'
    )

    Write-Host $($Msg)
}


<#
.SYNOPSIS

    Adapted from S1emServerStorageMon.ps1
.DESCRIPTION
  
.OUTPUTS

.EXAMPLE

#>

Function Get-ES1ServerStorageStatus
{
    [CmdletBinding()]
    PARAM (
    [Parameter(Position=0,Mandatory=$false)]
    $threshold_perc = 10.0,
    [Parameter(Position=1,Mandatory=$false)]
     $threshold_drive_free = 100.0
    )
BEGIN {}

PROCESS {

Try
{
    $returnCode = 0
    
    #
    # Get Storage Percent Free Threshold
    #
   
        
    if ($threshold_perc -gt 100)
    {
        $threshold_perc = 100.0
    }
    elseif ($threshold_perc -lt 0)
    {
        $threshold_perc = 0.0
    }
   
    #
    # Get Drive Free Threshold
    #
    # from commandline

    #
    # Initialize Server Collection
    #

    # TODO - Load servers we might not be able to discover, like master, exchange, search , etc

    $cServers = @()
    
    $aServers = @()
    
    # Get all es1 servers from DB, includes workers and archivers
    $aServers=@(Get-ES1Servers)

    $dbServers=@()
    $dbServers = @(Get-ES1ArchiveDatabases | Select-Object DBServer -Unique )
    $activityDBInfo = Get-ES1ActivityDatabase | Select-Object DBServer
    $dbServers +=  $activityDBInfo

    $dbServers = $dbServers| Select-Object -unique
    # Add database servers to the list, parse the instance name if there is one
    foreach($DBServer in $dbServers)
    {
        if ($DBServer.DBServer.Contains('\'))
        {
            $names=$DBServer.DBServer.Split('\\')
            $svrName= $names[0]
        }
        else
        {
            $svrName = $DBServer.DBServer
        }
        $aSservers += $svrName
    }
    
    # Get SourceOne Master Server
    #if ($hConfig.ContainsKey('MASTER'))
    #{
    # $aServers += $hConfig.MASTER
    #}
    #else
    #{
    # $aServers += $scriptComputerName
    #}
    
    
    # Remove Duplicates from SourceOne Server List
    $aServers = @($aServers | Sort-Object | Select-Object -uniq)

    # Add SourceOne Servers
    if ($aServers)
    {
        $cServers += $aServers
    }

    #
    # Process Each Server
    #
    $warning = $false
    $cDrives = @()

    foreach ($server in $cServers)
    {
    
        Add-Log "Processing Server: $server"
        $wmiDrives = Get-DriveInfo $server
        if (!($wmiDrives))
        {
            $warning = $true
            $status = 'Failed'
            Add-Log "Cannot access server: $server" Warn
            $props = [ordered] @{'ComputerName'=$server;'Drive'='N/A'; `
                                   'Volume_Name'='N/A'; 'Free'= 'N/A'; `
                                   'Size_GB'= 'N/A'; 'Free_GB'='N/A';`
                                   'Used_GB' = 'N/A';'Status'=$status}

            $oDrive = New-object -TypeName PSObject -Prop $props    
            $cDrives += $oDrive  


        }
        else
        {
           
            foreach ($drive in $wmiDrives)
            {        
                $size = [long]$drive.Size
                $free = [long]$drive.FreeSpace
                [long]$used = $size - $free
                [double]$free_percent = $free/$size
                
                [double]$size_gb = $size/(1024.0*1024.0*1024.0)
                [double]$free_gb = $free/(1024.0*1024.0*1024.0)
                [double]$used_gb = $used/(1024.0*1024.0*1024.0)
                
                if (($free_percent * 100 -le $threshold_perc) -and ($free_gb -lt $threshold_drive_free))
                {
                    $status = 'Warning'
                    $warning = $true
                }
                else
                {
                    $status = 'Pass'
                }            
            
                $props = [ordered] @{'ComputerName'=$server; 'Drive'=$drive.Name; `
                                   'Volume_Name'=$drive.VolumeName; 'Free'= $("{0:P1}" -f $free_percent); `
                                   'Size_GB'= $("{0:N1}" -f $size_gb); 'Free_GB'=$("{0:N1}" -f $free_gb);`
                                   'Used_GB' = $("{0:N1}" -f $used_gb); 'Status'=$status}

                $oDrive = New-object -TypeName PSObject -Prop $props       
                $cDrives += $oDrive                              
            }
   
           $cDrives

        }     
    }
    }
    Catch
    {
        throw $_
    }
}
END {}
}

<#
.SYNOPSIS
    Gets the disk space characteristics for the various CIFS shares used by an archive.

.DESCRIPTION
    Gets the disk space characteristics for the various CIFS shares used by an archive.
    
    The location type is returned in shorthand as follows:
    'MCL' = Message Center location
    'AFL' = Archive Container location
    'FTL' = Full Text Index location (for ISYS indexes)

  Adapted from Mike Tramont's S1emCIFSShareMon.ps1
    
.OUTPUTS

.PARAMETER threshold
    Percent Free Threshold to use for issuing a warning on the Message Center location.

.EXAMPLE
Get-ES1CIFSShareStatus | ft -AutoSize

DATABASE ES1Archive on sql2008-j1: Querying CIFS Shares
DATABASE IPMArchive on sql2008-j1: Querying CIFS Shares
DATABASE SecondIPMArchive on sql2008-j1: Querying CIFS Shares

DateTime Share Type Free Size_GB Free_GB Used_GB Status
-------- ----- ---- ---- ------- ------- ------- ------
2018-05-15 13:36:43 \\S1MASTER64\PBA AFL 98.7 % 30.0 29.6 0.4 Ok
2018-05-15 13:36:43 \\S1MASTER64\MSGCENTER MCL 45.4 % 30.0 13.6 16.4 Ok
2018-05-15 13:36:43 \\S1MASTER64\INDEXSHARE FTL 45.4 % 30.0 13.6 16.4 Ok
2018-05-15 13:36:43 \\S1NASServer\ARCHIVES\ARCHIVE1 AFL 64.1 % 931.5 597.3 334.2 Ok

.EXAMPLE
Get-ES1CIFSShareStatus -threshold 50.0 | where {$_.Status -eq 'Failed' -or $_.Status -eq 'Warning'} | select Share, Type,Status | ft -AutoSize

DATABASE ES1Archive on sql2008-j1: Querying CIFS Shares
DATABASE IPMArchive on sql2008-j1: Querying CIFS Shares
DATABASE SecondIPMArchive on sql2008-j1: Querying CIFS Shares
Cannot access CIFS Share: \\ESQLEXT4-J\EX-INDEXES
Cannot access CIFS Share: \\ESQLEX4-J\EX_INDEXES
Cannot access CIFS Share: \\ESQLEXT4-J\EXINDEXMIRROR
Cannot access CIFS Share: \\S1MASTER7-J1\MSGCENTER
Cannot access CIFS Share: \\ESQLEXT4-J\ESQLEXT4-EMAILXTENDER
Cannot access CIFS Share: \\ESQLEX4-J\EX_CONTAINERS
Cannot access CIFS Share: \\ESQLEXT4-J\ESQLEXT4-CONTAINERS

Share Type Status
----- ---- ------
\\ESQLEXT4-J\EX-INDEXES FTL Failed
\\ESQLEX4-J\EX_INDEXES FTL Failed
\\ESQLEXT4-J\EXINDEXMIRROR FTL Failed
\\S1MASTER7-J1\MSGCENTER MCL Failed
\\S1MASTER64\MSGCENTER MCL Warning
\\ESQLEXT4-J\ESQLEXT4-EMAILXTENDER FTL Failed
\\ESQLEX4-J\EX_CONTAINERS AFL Failed
\\ESQLEXT4-J\ESQLEXT4-CONTAINERS AFL Failed

#>

Function Get-ES1CIFSShareStatus
{
    [CmdletBinding()]
    PARAM (
        [Parameter(Position=0,Mandatory=$false)]
        [double] $threshold= 10.0
    )
BEGIN {
    $scriptStart = Get-Date
    
    # Change to script location
       $scriptDirectory  = Split-Path -parent $PSCommandPath
    Set-Location $scriptDirectory

}

PROCESS {
Try
{
    $returnCode = 0
 
    #
    # Get Storage Percent Free Threshold
    #
        
    if ($threshold -gt 100)
    {
        $threshold = 100.0
    }
    elseif ($threshold -lt 0)
    {
        $threshold = 0.0
    }
    
    #
    # Get Cifs Shares from the Archive Databases
    #
    $hCifsShares = @{} 
    $Archives = Get-ES1ArchiveDatabases

    foreach ($archive in $Archives)
    {
       
            $dbName = $archive.DBName
            $dbServer = $archive.DBServer
     
            Add-Log "DATABASE $dbName on $dbServer`: Querying CIFS Shares"

            # For expediancy I kept this as SQL queries, but the shares are accessible in the COM
            # archive folder object... too
            $hTemp = Get-S1CifsShares -DBServer $dbServer -DBName $dbName
            foreach ($key in $hTemp.Keys)
            {
                if ($hCifsShares.ContainsKey($key) -eq $false)
                {
                    $hCifsShares[$key] = $hTemp[$key]
                } 
            }                    
      }
    
    # TODO
    # Get Additional Cifs Shares from Input file
    #
  # $infile = 'CIFSshares-SourceOne.txt'
  # if (Test-Path $infile)
  # {
  # $aLines = @((Get-Content $infile) | ? {$_.trim() -ne "" } | Sort-Object)
        
  # if ($aLines)
  # {
  # foreach ($line in $aLines)
  # {
  # $lineupper = $line.Trim().ToUpper()
        #if ($lineupper.Contains('FTL'))
        #{
  # $hCifsShares[$lineupper] = 'FTL'
        #}
        #elseif ($lineupper.Contains('AFL'))
        #{
  # $hCifsShares[$lineupper] = 'AFL'
        #}
        #else
        #{
  # $hCifsShares[$lineupper] = 'OTHER'
        #}
  # }
  # }
  # }

     
    #
    # Process each CIFS Share
    #
    $warning = $false
    $oFSO = New-Object -com  Scripting.FileSystemObject
    $cShares = @()
  
    foreach ($share in $hCifsShares.Keys)
    {   
        $shareType = $hCifsShares[$share].ToString()
        $shareprops = [ordered] @{'DateTime'=$(Get-Date -Date $scriptStart -Format �yyyy-MM-dd HH:mm:ss�); `
                        'Share'=$share; 'Type'=$shareType; 'Free'=''; 'Size_GB'=''; 'Free_GB'=''; `
                        'Used_GB'=''; 'Status'=''}

        $oShare = New-Object -TypeName PSObject -Property $shareprops

        
       try
       {
            #
            # Size of Folder in MB:
            #
            # "{0:N2}" -f (($oFSO.GetFolder("\\SOURCEONE1B\C$\S1CIFS\S1FTL001").Size) / 1MB) + " MB"

            #
            # Count of Files in Folder:
            #
            # (Get-ChildItem "\\SOURCEONE1B\C$\S1CIFS\S1FTL001" -Recurse).Count
            
            #$oDrive = $oFSO.GetDrive($share)
            $oDrive = $oFSO.GetFolder($share).Drive
            $size = [long]$oDrive.TotalSize
            $free = [long]$oDrive.FreeSpace
            [long]$used = $size - $free
            [double]$free_percent = $free/$size
            
            [double]$size_gb = $size/(1024.0*1024.0*1024.0)
            [double]$free_gb = $free/(1024.0*1024.0*1024.0)
            [double]$used_gb = $used/(1024.0*1024.0*1024.0)
            
            $status = 'Ok'
            if ($shareType -eq 'FTL')
            {
                if ($free_percent * 100 -lt 4)
                {
                    $status = 'Warning'
                    $warning = $true
                }
            }
            elseif ($shareType -eq 'AFL')
            {
                if ($free_percent * 100 -lt 4)
                {
                    $status = 'Warning'
                    $warning = $true
                }
            }            
            else
            {
                if ($free_percent * 100 -le $threshold)
                {
                    $status = 'Warning'
                    $warning = $true
                }            
            }
            
            $oShare.Status = $status
            $oShare.Free = $("{0:P1}" -f $free_percent)
            $oShare.Size_GB = $("{0:N1}" -f $size_gb)
            $oShare.Free_GB = $("{0:N1}" -f $free_gb)
            $oShare.Used_GB = $("{0:N1}" -f $used_gb)
            
            $cShares += $oShare  
     
        }
        catch
        {
            $warning = $true        
        
            Add-Log "Cannot access CIFS Share: $share" Info
            
            $oShare.Status = 'Failed'

            $cShares += $oShare        
        
        }
      }

    }        
  
    Catch
    {
        throw $_
 
    }

    $cShares 

}
END {}
}


<#
.SYNOPSIS
    Gets the ContainerLocations,IndexLocations, and MsgCenterPaths for archive folders defined in the given archive database.
    NOTE: uses direct SQL queries for legacy reasons.
    Returns a hashmap of the UNC path and short name for the location type.

.DESCRIPTION
    Gets the ContainerLocations,IndexLocations, and MsgCenterPaths for archive folders defined in the given archive database.
    NOTE: uses direct SQL queries for legacy reasons.
    Returns a HashMap of the UNC path and short name for the location type.

    'MCL' = Message Center location
    'AFL' = Archive Container location
    'FTL' = Full Text Index location (for ISYS indexes)

.OUTPUTS

.EXAMPLE

#>

Function Get-S1CifsShares
{
[CmdletBinding()]
Param
    (
        [Parameter(Position=0,Mandatory=$true,
                    ValueFromPipeLine=$true, 
                    ValueFromPipeLineByPropertyName=$true)]
        [string] $dbServer,
        [Parameter(Position=1,Mandatory=$true,
                ValueFromPipeLine=$true, 
        ValueFromPipeLineByPropertyName=$true)]
        [string] $dbName
    )

BEGIN {}

PROCESS {

    #
    # Define SQL Queries
    #
    $sqlQueryContainer = @'
SELECT DISTINCT NodeProps.value('(/fpprops/ContainerLocation)[1]','nvarchar(max)') AS ContainerLocation
FROM FolderPlan (NOLOCK)
WHERE Type = 1
'@

    $sqlQueryIndex = @'
SELECT DISTINCT NodeProps.value('(/fpprops/IndexLocation)[1]','nvarchar(max)') AS IndexLocation
FROM FolderPlan (NOLOCK)
WHERE Type = 1
'@

    $sqlQueryMsgCenter = @'
SELECT DISTINCT REPLACE(ConfigXML.value('(/ExAsSrvCfg/ArchiveService/MsgCenterPath)[1]','nvarchar(max)'), '\Message_Center', '') AS MsgCenter
FROM ServerInfo (NOLOCK)
WHERE LEN(MacAddress) > 0
'@


    #
    # Hashtable to collect and dedup the Cifs Shares
    #
    $hShares = @{}

    #
    # Process Message Center Shares
    #
    $dtResults = Invoke-ES1SQLQuery $dbServer $dbName $sqlQueryMsgCenter
    foreach ($row in $dtResults)
    {
        if ($row.MsgCenter.StartsWith('\\'))
        {
            $hShares[$row.MsgCenter.ToUpper()] = 'MCL'
        }
    }

    #
    # Process Container Shares
    #
    $dtResults = Invoke-ES1SQLQuery $dbServer $dbName $sqlQueryContainer
    foreach ($row in $dtResults)
    {
        if ($row.ContainerLocation.StartsWith('\\'))
        {
            $hShares[$row.ContainerLocation.ToUpper()] = 'AFL'
        }
    }

    #
    # Process Index Shares
    #
    $dtResults = Invoke-ES1SQLQuery $dbServer $dbName $sqlQueryIndex
    foreach ($row in $dtResults)
    {      
        if ($row.IndexLocation.Contains(';'))
        {
            $aValues = $row.IndexLocation.Split(';')
            foreach ($val in $aValues)
            {
                if ($val -match '\\\\.+\\.+')
                {
                    $hShares[$matches[0].ToUpper()] = 'FTL'
                }
            }
        }
        elseif ($row.IndexLocation -match '\\\\.+\\.+')
        {
            $hShares[$matches[0].ToUpper()] = 'FTL'
        }
    }

    #
    # Return Hash Table
    #
    , $hShares
 }
 
 END{}
    
}




#-------------------------------------------------------------------------------
# Function: Get-DriveInfo
#-------------------------------------------------------------------------------
function Get-DriveInfo {

    Param
    (
        [Parameter(Position=0)]
        [string] $Server
    )

    $connect = $false
    
    if (test-connection -computername $server -quiet  -count 1)
    { 
        try
        {
            $wmiDrives = Get-WmiObject -ComputerName $Server -Class Win32_LogicalDisk -Filter "DriveType = 3" -ErrorAction SilentlyContinue
            $connect = $true
        }
        catch
        {
            Write-Error $_
        }
    }
    
    if ($connect)
    {
        $wmiDrives
    }
    else
    {
        , @()  # Comma Operator forces the return of an empty array -- otherwise only $null is returned
    }
}


#-------------------------------------------------------------------------------
# Function: Get-SpaceMB
#-------------------------------------------------------------------------------
function Get-SpaceMB {

    Param
    (
        [Parameter(Position=0)]
        [string] $InputPath
    )

    $results = @()

    if (Test-Path $InputPath)
    {
        try
        {
            if ($InputPath.StartsWith('\\'))
            {
                $InputPath = $InputPath.TrimEnd('\')           
                $aValues = $InputPath.Split('\')
                if ($aValues.Length -ge 4)
                {
                    $drive = '\\' + $aValues[2] + '\' + $aValues[3]
                }
                
                $oFSO = New-Object -com  Scripting.FileSystemObject
                $oDrive = $oFSO.GetDrive($drive)

                $size = [long]$oDrive.TotalSize
                $free = [long]$oDrive.FreeSpace
            
                $size_mb = $size/(1024.0*1024.0)
                $free_mb = $free/(1024.0*1024.0)
                $used_mb = $size_mb - $free_mb               
                  
            }
            else
            {
                $drive = (Split-Path $InputPath -qualifier).TrimEnd(':')
                $free = (Get-PSDrive $drive).Free
                $used = (Get-PSDrive $drive).Used
                $size = $free + $used
                
                $size_mb = $size/(1024.0*1024.0)
                $free_mb = $free/(1024.0*1024.0)
                $used_mb = $used/(1024.0*1024.0)
            }
           
            $size_mb_formatted = $("{0:N1}" -f $size_mb)
            $free_mb_formatted = $("{0:N1}" -f $free_mb)
            $used_mb_formatted = $("{0:N1}" -f $used_mb)
            
            $results = ($size_mb, $free_mb, $used_mb, [double]$size_mb_formatted, [double]$free_mb_formatted, [double]$used_mb_formatted)
        
        }
        catch
        {    
            Write-Error $_
        }
    }

    if ($results.Count -gt 0)
    {
        $results
    }
    else
    {
        , @()  # Comma Operator forces the return of an empty array -- otherwise only $null is returned
    }
}







<#
.SYNOPSIS
  
.DESCRIPTION
  
.OUTPUTS

.EXAMPLE

#>

Function FunctionTemplate
{
    [CmdletBinding()]
    PARAM ()
    BEGIN {}
    #
    # TODO - Add parameter to pass in additional servers that are not discoverable...
    #

PROCESS {
}
END {}
}

Export-ModuleMember -Function * -Alias *