Monitoring Your Lync 2013 Peak Call Capacity

Recently, my company wanted to analyze our SIP trunk usage with our vendor and determine if we had unnecessary capacity. When the vendor was only able to provide call detail records, I went looking for solutions on the web.

I found several, but the problem was that they relied on the LcsCDR database to determine what sessions were open. The behavior has changed with Lync 2013 and session detail is no longer stored until the end of the call. This means that you cannot query for active/open sessions to determine usage.

I found this script by Rune Stokes in his article Monitoring your Lync Peak Call Capacity. It wasn’t viable as written for Lync 2013 because the counter names changed.

I updated it for the 2013 counter names as well as making a few other adjustments to the loop in order to stabilize the script on our W2k12 Mediation Server.

Download here: Get-CallCounters

<# ---------------------------------------------------------------------
    .SYNOPSIS
    Get-CallCounters.ps1
    
    Script to retrieve the performance counters of ongoing inbound and
    outbound calls, and also calculate the combined usage.
    
    It will output the average and peak values for each hour for as long 
    as it runs.

    .NOTES
    Must be run on Mediation server.
    
    Made by Rune Stoknes - https://stoknes.wordpress.com/
	Adaptations by Duncan Bachen - http://eureka.greenhead.com

    v0.5 - Apr 15 2014 - first simple version, CSV output only
    v0.8 - Apr 16 2014 - added console output for current counters
    v0.9 - Feb 16 2015 - Altered the outbound and inbound call paths to match what the counter references were in Lync 2013 [Duncan Bachen]
         - Feb 17 2015 - Changed clear screen not use console class so it can be tested in ISE and other shells [Duncan Bachen] 
					   - Script kept hanging during long running without explanation. Changed the wait for keypress logic slightly.
					   - Added loop output time and counter to the display so that it's easier to monitor and make sure it's progressing.  
					   - Altered loop time to 10 secs to have finer granularity of counters [Duncan Bachen] 
		 
   
   -------------------------------------------------------------------- #>

   # Lets define the actual counter paths we will be fetching.
   # Changed to 2013 paths
<#
   $outboundCallPath = "\LS:MediationServer - 00 - Outbound Calls(_total)\- 000 - Current"
   $inboundCallPath = "\LS:MediationServer - 01 - Inbound Calls(_total)\- 000 - Current" #>
   
   
   $outboundCallPath = "\LS:MediationServer - Outbound Calls(_Total)\- Current"
   $inboundCallPath = "\LS:MediationServer - Inbound Calls(_Total)\- Current"

  
   # Create CSV file for output. It will be named \Users\<user>\Documents\Call counters DDMMYYY.csv
   
   $outputPath = $env:HOMEPATH + "\Documents\Call counters " + (Get-Date -UFormat "%d%m%Y") + ".csv"
   
   try
   {
        "Date;Hour;Avg inbound;Peak inbound;Avg outbound;Peak outbound;Avg concurrent;Peak concurrent" | Out-File -FilePath $outputPath -Encoding default
   }
   catch
   {
        "Unable to create file " + $outputPath
        exit
   }

   # Create empty arrays to store counters. These will be reset every hour.

   [int[]]$inboundCalls = $null
   [int[]]$outboundCalls = $null
   [int[]]$concurrentCalls = $null
   [int]$counter = 0
   
   # Get the time
   $now = Get-Date

   # Now, for every 15 seconds we will repeat this same procedyre:
   # -> Fetch current call counter for inbound and outbound
   # -> Store data in array
   # -> if the hour changes, calculate average and peak counters and store to file

   $Stopped = $False

   do {

        # That was then, this is now
        $then = $now
        $counter += 1

        # Get the counters we want, and add them to the arrays
        [int]$currentInbound = (Get-Counter $inboundCallPath).CounterSamples[0].CookedValue.ToString()
        [int]$currentOutbound = (Get-Counter $outboundCallPath).CounterSamples[0].CookedValue.ToString()
                
        $inboundCalls += $currentInbound
        $outboundCalls += $currentOutbound
        $concurrentCalls += ($currentInbound + $currentOutbound)

        
        # Now let's get the time
        $now = Get-Date
        
        # Output to console
        # Console class is not available is ISE and other shells, so don't call it directly
        # [System.Console]::Clear()
        Clear-Host
        "Current number of inbound calls: " + $currentInbound
        "Current number of outbound calls: " + $currentOutbound
        "Current number of concurrent calls: " + ($currentInbound + $currentOutbound)
	    "Current Hour Counter: " + $counter
        "Current Loop Start: " +  $Now

        
        # Has the hour changed since then?
        if ($now.Hour -inotlike $then.Hour)
        {
 
            # The peak and average value can be calculated and derived using Measure-Object
            $inbound = ($inboundCalls | Measure-Object -Maximum -Average)
            $outbound = ($outboundCalls | Measure-Object -Maximum -Average)
            $concurrent = ($concurrentCalls | Measure-Object -Maximum -Average)

            # Let's append this to the CSV file we created
            (Get-Date -Date $then -UFormat "%d %b %Y;") + $then.Hour + ";" + $inbound.Average + ";" + $inbound.Maximum +  ";" + $outbound.Average + ";" + $outbound.Maximum + ";" + $concurrent.Average + ";" + $concurrent.Maximum | Out-File -FilePath $outputPath -Encoding default -Append

            # Let's not forget to reset our arrays now that the hour has changed
            [int[]]$inboundCalls = $null
            [int[]]$outboundCalls = $null
            [int[]]$concurrentCalls = $null
            [int]$counter = 0
                        
        }

        # Let's hear if the user wants to end the script
	    Write-Host "`n `nPress ESC to end the script on next 10s loop." -ForegroundColor Red

                
        if ($Host.UI.RawUI.KeyAvailable -and ($Host.UI.RawUI.ReadKey("IncludeKeyUp,NoEcho").VirtualKeyCode -eq 27)) 
         {
           Write-Host "`nExiting shortly...`nCheck output file for any historical data:`n" $outputPath "`n" -Background DarkRed
              # Clean up and exit while pointing to the results file
           $Stopped = $True
        }

    Start-Sleep -Seconds 10

   }
   Until ($Stopped)

Responsive ISA login form for TMG

My company uses Microsoft Forefront TMG in order to authenticate our external users trying to reach our internal applications.

Designed in a era before the proliferation of mobile devices, TMG’s default login pages are unwieldy. Users have to zoom in if they want to try and input into the username and password fields.

Additionally, my company uses Safenet Authentication Service to provide 2 factor authentication for our external users. This provides one more field that the users struggle to enter data into, and a time sensitive one at that.

Scott Glew developed a set of TMG Responsive Auth Forms that partially solve this problem. While they are ideal with a company only using Name/Password, they don’t work if a company utilizes RADIUS OTP.

I’ve forked Scott’s project and submitted a revision to the file which supports the use of a 2 factor passcode.

Here’s the text submitted as part of that patch which summarizes what I did:

The original ISA responsive form is only for the page asking for username and password (usr_pwd.htm). If you elect to ‘Collected additional delegation credentials in the form” on the listener for use with RADIUS OTP, TMG instead uses usr_pwd_pcode,htm.

This version merges the code from Microsoft’s original non-responsive page into Scott’s responsive form.

We use this version with CryptoCard/BlackShield.

Unlike the original Microsoft form, I have switched the order of the fields to be Name/Password/Passcode instead of Name/Passcode/Password, as the natural flow for users is to enter their regular credentials first and then pause to add the 2-factor passcode.

In the original format, since Passcode is time sensitive, users  often had their passcode rejected as they fumbled with data entry and switching between fields.