Archive for March, 2017

Build SCOM *Like* SharePoint Dashboards with PowerShell (Utilization, HTTP Response, App Pool Status)

March 2, 2017 Leave a comment

I recently blogged about how to build a SCOM Like dashboard for SharePoint by pulling statistics from our network load balancer application (F5 – Big IP). You can find the blog here –

Now i’m going to show you how to build SCOM *like* dashboards for SharePoint by displaying information regarding Server Utilization (Memory, CPU, Storage), HTTP Responses, and App Pool Status for all servers in the farm. Since SCOM is not fully integrated into our systems yet I had to turn to Powershell to get the results I want. This helps to get a snapshot of current statitics from the SharePoint servers instead of having to visit each server to grab what is needed. These reports are generated into HTM pages that can be viewed in a browser.

Below is the script I generted to get the dashboards and displays results.

Remove-Item c:\scripts\SharePoint-Server-Health.htm


$ServerListFile = "c:\temp\ServerList.txt"
$ServerList = Get-Content $ServerListFile -ErrorAction SilentlyContinue
$ServerListAppsFile = "c:\temp\ServerList_Apps.txt"
$ServerListApps = Get-Content $ServerListAppsFile -ErrorAction SilentlyContinue
$URLListFile = "c:\temp\URLList.txt"
$URLList = Get-Content $URLListFile -ErrorAction SilentlyContinue

$Result = @()
$Result_HTTP = @()
#$Result_AppPools = @()
$Date = Get-Date -format g

ForEach($computername in $ServerList)
	$AVGProc = Get-WmiObject -computername $computername win32_processor | Measure-Object -propert LoadPercentage -Average | Select Average
	$OS = gwmi -Class win32_operatingsystem -computername $computername |
	select-object @{Name = "MemoryUsage"; Expression = {"{0:N2}" -f
	((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/$_.TotalVisibleMemorySize)}}
	$vol = Get-WmiObject -Class win32_Volume -ComputerName $computername -Filter "DriveLetter = 'C:'" |
	select-object @{Name = "C PercentFree"; Expression = {"{0:N2}" -f (($_.FreeSpace / $_.Capacity)*100) }}

	$result += [PSCustomObject] @{
	ServerName = "$computername"
	CPULoad = "$($AVGProc.Average)%"
	MemLoad = "$($OS.MemoryUsage)%"
	CDrive = "$($vol.'C PercentFree')%"

	$Outputreport = "<HTLM><HEAD><META HTTP-EQUIV=""refresh"" content=""240""><TITLE> SharePoint Server Health Reports </TITLE></HEAD>
		<BODY background-color:peachpuff vlink=""black"" link=""black"" alink=""black"">
		<font color =""#99000"" face=""Microsoft Tai le"">
		<center><H2> SharePoint Server Health Report </H2></font>
		*Statistics refresh every 5 mins.  Page automatically refreshes every 90 seconds.
		<U>Last Update:</U> $Date (Server Time)</center></br>
		<font color =""#99000"" face=""Microsoft Tai le"">
		<H2>SharePoint Utilization Status</H2></font>
		<Table border=1 cellpadding=0 cellspacing=0>
		<TR bgcolor=gray align=center>
		<TD><B>Server Name</B></TD>
		<TD><B>CPU Utilization</B></TD>
		<TD><B>Memory Utilization</B></TD>
		<TD><B>C Drive Utilization</B></TD></TR>"

	Foreach($Entry in $Result)
		if((($Entry.CPULoad) -and ($Entry.MemLoad)) -ge "80")
			#$Ouputreport += "<TD bgcolor=red>"
			#$Outputreport += "<TD>$($Entry.Servername)</TD><TD bgcolor=red align=center>$($Entry.CPULoad)</TD><TD bgcolor=red align=center>$($Entry.MemLoad)</TD><TD align=center>$($Entry.CDrive)</TD></TR>"
			#$Ouputreport += "<TR>"
			#$Outputreport += "<TD>$($Entry.Servername)</TD><TD align=center>$($Entry.CPULoad)</TD><TD align=center>$($Entry.MemLoad)</TD><TD align=center>$($Entry.CDrive)</TD></TR>"
		$Outputreport += "<TD>$($Entry.Servername)</TD><TD align=center>$($Entry.CPULoad)</TD><TD align=center>$($Entry.MemLoad)</TD><TD align=center>$($Entry.CDrive)</TD></TR>"
	$Outputreport += "</Table>"

#Build Table for HTTP Status#

ForEach ($url in $URLList)
	$xHTTP = New-Object -com msxml2.xmlhttp;
	$"GET", $url, $false)
	write-host $url
	$HTTPStatus = $xHTTP.status

	$HTTPStatusText = $xHTTP.statusText

	if ($HTTPStatus -eq 400)
		$HTTPStatusText = "Bad Request"
	if ($HTTPStatus -eq 403)
		$HTTPStatusText = "Forbidden"
	if ($HTTPStatus -eq 401)
		$HTTPStatusText = "Unauthorized"
	if ($HTTPStatus -eq 404)
		$HTTPStatusText = "Not Found"
	if ($HTTPStatus -eq 500)
		$HTTPStatusText = "Internal Server Error"

	$Request = New-Object System.Net.WebClient
	$Request.UseDefaultCredentials = $true
	$Start = Get-Date
	$PageRequest = $Request.DownloadString($url)
	$TimeTaken = ((Get-Date) - $Start).TotalSeconds

	$result_http += [PSCustomObject] @{
		URL = "$url"
		Status = "$HTTPStatus"
		StatusText = "$HTTPStatusText"
		LoadTime = "$TimeTaken"

		$Outputreport2 = "<font color =""#99000"" face=""Microsoft Tai le"">
		<H2> SharePoint HTTP Status </H2></font>
		<Table border=1 cellpadding=0 cellspacing=0>
		<TR bgcolor=gray align=center>
		<TD><B>Est. Load Times</B></TD></TR>"

		Foreach($Entry in $Result_HTTP)
			if($Entry.Status -eq 200)
				$Outputreport2 += "<TR><TD><a href=$($Entry.URL) target=""_blank"">$($Entry.URL)</a></TD><TD bgcolor=green align=center>$($Entry.Status)</TD><TD align=center>$($Entry.StatusText)</TD><TD align=center>$($Entry.LoadTime)(s)</TD></TR>"
				$Outputreport2 += "<TR><TD>$($Entry.URL)</TD><TD align=center>$($Entry.Status)</TD><TD align=center>$($Entry.StatusText)</TD><TD align=center>$($Entry.LoadTime)(s)</TD></TR>"
		$Outputreport2 += "</Table>"	

ForEach ($s in $ServerListApps)
	$ServerMgr = [Microsoft.Web.Administration.ServerManager]::OpenRemote($s)
	$AppPoolColl = $ServerMgr.ApplicationPools

	$Result_AppPools = "Result_AppPools_" + $s
	$Result_AppPools = @()

	foreach($AppPool in $AppPoolColl)
		$appPoolState = $AppPool.State
		$appPoolName = $AppPool.Name
		#write-host $AppPool.Name
		#Write-Host $appPoolState
		$result_apppools += [PSCustomObject] @{
		ServerName = "$s"
		AppPoolName = "$appPoolName"
		AppPoolState = "$appPoolState"

		$Outputreport_apppools = "Outputreport_" + $s

		$Outputreport_apppools = "<font color =""#99000"" face=""Microsoft Tai le"">
		<H2> SharePoint Application Pool Status $s </H2></font>
		<Table border=1 cellpadding=0 cellspacing=0>
		<TR bgcolor=gray align=center>
		<TD><B>Server Name</B></TD>

		Foreach($Entry in $Result_AppPools)
			if($Entry.AppPoolState -eq "Stopped")
				if ($Entry.AppPoolName -eq "DefaultAppPool" -or $Entry.AppPoolName -eq "SharePoint Web Services Root")
					$notes = "Please Ignore"
					$notes = "Address Immediately"
				$Outputreport_apppools += "<TR><TD>$($Entry.ServerName)</TD><TD align=center>$($Entry.AppPoolName)</TD><TD bgcolor=red align=center>$($Entry.AppPoolState)</TD><TD align=center>$notes</TD></TR>"
				$notes = ""
				$Outputreport_apppools += "<TR><TD>$($Entry.ServerName)</TD><TD align=center>$($Entry.AppPoolName)</TD><TD align=center>$($Entry.AppPoolState)</TD><TD align=center>$notes</TD></TR>"
		$Outputreport_apppools += "</TABLE>"
	$Outputreport_AppPools_Final += $Outputreport_apppools += "</BODY></HTML>"


$final = $Outputreport += $Outputreport2 += $Outputreport_AppPools_Final
$final | out-file c:\scripts\SharePoint-Server-Health.htm
Categories: Uncategorized

Build SCOM *like* Reports for SharePoint using PowerShell (F5 – BIG IP)

March 1, 2017 1 comment

In my SharePoint 2010 environment we are leveraging two F5 BIG-IP servers as our network load balancer servers that sit between our TMG server, and SharePoint Web Front end servers. I needed to generate some SCOM *Like* dashboards that pulled statistics from the F5 to display to the end user, specifically the helpdesk to give them direct insight to on-the-minute/demand statistical updates for our servers. Since there is “separation of duties” where I work the helpdesk has no insight into our F5 BIG-IP Servers or configurations. This is where I turn to PowerShell to help pull statistics from the F5 and display them to the users.

To get started you first need to register the F5 iControl cmdlet assemblies to the server where you will be execting the PowerShell scripts.

To download the icontrol cmdlets visit here:

Once the icontrol cmdlets are downloaded move the files over to the server where you will be executing the PS Script and unpackage the zip file.

Inside the zip file “f5-icontrol-powershell-snapin-12_1_0” folder you will find the setupSnap.ps1 file. Open up a PowerShell console with Admin rights and browse to the location where the setupSnap.ps1 is.

Execute the setupSnapin.ps1 – .\setupSnapin.ps1
Once the PowerShell script executes and finishes the icontrol .dll assesmblies are applied.

Now you are free to execute F5 BIG-IP iControl cmdlets in PowerShell on that server.

Below is the PowerShell script I executed to help me achieve my goal. I won’t take full credit for this script, the credit goes to Patrik on devcenter for the code snippet. – I took his example and built around it to achieve my goal.


#Initialize Snapin
if ( (Get-PSSnapin | Where-Object { $_.Name -eq "iControlSnapIn"}) -eq $null ){
    Add-PSSnapIn iControlSnapIn

#Setup credentials
$User = "user"
$Password = "password"
$BigIP = "xx.xx.xx.xx"
$Partition = "PARTITION NAME"

#Add a type/class to store the node data in
Add-Type @'
public class Member
    public string Pool;
    public string Address;
    public string Port;
    public string Name;
    public string Availablity;
    public string Enabled;
    public string Status;
    public string Connections;

#Create an empty array to store the node objects in
$Nodes = @()
$NodeDict = @{}

#Connect to the BigIP and get an iControl Handle
$Success = Initialize-F5.iControl -HostName $BigIP -Username $User -Password $Password
$F5 = Get-F5.iControl

#Get the partition list
$f5partitions = $F5.ManagementPartition

#Get Pool Lists, Status, Nodes

$PoolList = $F5.LocalLBPool.get_list()
$PoolMembers = $F5.LocalLBPool.get_member_V2($PoolList)
$PoolMemberStatus = $F5.LocalLBPool.get_member_object_status($PoolList, $PoolMembers)
$NodeList = $F5.LocalLBNodeAddressV2.get_list()
$NodeIPList = $F5.LocalLBNodeAddressV2.get_address($NodeList)

#Save the nodes in a dictionary for convenient access

for ($i=0; $i -lt ($NodeList.Count); $i++)
    $NodeDict.Add($NodeList[$i], $NodeIPList[$i])
for ($i=0; $i -lt ($PoolList.Count); $i++)
    for ($x=0; $x -lt ($PoolMembers[$i].Count); $x++)
         $objTempMember = New-Object Member

         #Populate the object
         $objTempMember.Pool = $PoolList[$i]
         $objTempMember.Name = $PoolMembers[$i][$x].address
         $objTempMember.Address = $NodeDict[$objTempMember.Name]
         $objTempMember.Port = $PoolMember[$i][$x].port
         $objTempMember.Availability = $PoolMemberStatus[$i][$x].availability_status
         $objTempMember.Enabled = $PoolMemberStatus[$i][$x].enabled_status
         #Get Current Connections
         $MemberStatisticsA = $F5.LocalLBNodeAddress.get_statistics($objTempMember.Address);
         $MemberStatisticEntry = $MemberStatisticsA.Statistics;
         $Statistics = $MemberStatisticEntry[0].statistics;

         foreach ($Statistic in $Statistics)
                 $type = $Statistic.type;
                 $value = $Statistic.value;

                 if ($type -eq "STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS" -and $objTempMember.Port -eq "443")
                       $cur_connections = $value.low
                       $objTempMember.Connections = $cur_connections

         $Nodes += $objTempMember
         $Outputreport = "<HTML><HEAD><META HTTP-EQIV=""refresh"" content=""60""><TITLE>SharePoint F5 Node Status</TITLE></HEAD>         
         <BODY background-color:peachpuff>
         <Font color=""#99000"" face=""Microsoft Tai le"">
         <center><H2> SharePoint F5 Node Status </H2></font></center>
         <Table border="1" cellpadding="0" cellspacing="0">
         <TR bgcolor="gray" align="center">

         foreach ($Entry in $Nodes)
                $Outputreport += "<TR><TD>$($Entry.Name)</TD><TD align="center">$($Entry.Address)</TD><TD align="center">$($Entry.Port)</TD><TD align="center">$($Entry.Enabled)</TD><TD align="center">$($Entry.Status)</TD><TD align="center">$($Entry.Connections)</TD></TR>"
         $Outputreport += "</Table></BODY></HTML>"

$Outputreport | out-file

Once the script was created I created a Task Scheduler to execute the script every 5 mintues. ON the report build I had a refresh of 60 seconds to update the dashboard so the end users did not have to do any interaction with the report.

Categories: Uncategorized