Lync UCWA App – E-mail Alert Based Upon Presence

My organization is completely Lync for phones (including security guards) and we recently hit a bug with Polycom VVX phones where once the 180 day certificate expired, VVX phones would not re-new their certificate.  When this happens the phones would become “Offline”.  Given that our security phones need to be online 24/7 this wasn’t good.  We engaged Microsoft and asked them “Hey, Lync is a presence based product, how can we alert when presence of KEY phones (such as our security guard phones) become offline?”

Their response was as of today this is not a feature that is currently in Lync (would be nice to have a cmdlet like get-csuserpresence) and recommended we look at using UCWA to pull this data.  Myself not being a developer I had a hard time getting it to work.  Lucky we have very talented individuals on our team (Thanks Justin/Matt/Anju!) who could do this.
In all it’s glory…here it is!
We had some issues with Version 1 of this APP re-authenticating to Lync.  It’s corrected in Version 2._________________________________________________________________________________

$authData = $null

function Log($msg, $init)
{
$log = $true
$logFile = “D:\Lync\Scripts\LyncCheck.log”
if($log -eq $true)
{
if(($init -eq $true) -or ((Test-Path $logFile) -eq $false))
{
“$(Get-Date) – $msg” | Out-File $logFile
}
else
{
“$(Get-Date) – $msg” | Out-File $logFile -Append
}
$log = ((Test-Path $logFile) -eq $true)
}

if($log -eq $false)
{
Write-Host “$(Get-Date) – $msg”
}
}

#function GetDuration($start, $end) { return (($end – $start) / [TimeSpan]::TicksPerMillisecond) / 1000 }

function Authenticate($domain, $guid, $authType)
{
$lyncdiscovers = “LyncDiscoverInternal”, “LyncDiscover”
$lyncprotocols = “http”, “https”
$userURI = $null
$xframeURI = $null
$rootURI = $null
foreach($lyncdiscover in $lyncdiscovers)
{
foreach($lyncprotocol in $lyncprotocols)
{
$autoURI = $lyncprotocol + “://” + $lyncdiscover + “.” + $domain
$autoResponse = Invoke-WebRequest -Uri $autoURI -Method Get -ContentType “application/json”
if($autoResponse.StatusCode -eq 200)
{
$userURI = [System.Uri]((ConvertFrom-Json $autoResponse)._links.user.href)
$xframeURI = [System.Uri]((ConvertFrom-Json $autoResponse)._links.xframe.href)
$rootURI = $userURI.Scheme + “://” + $userURI.Host
break
}
}
if($userURI -ne $null)
{
break
}
}

if($userURI -ne $null)
{
$authURL = $rootURI + “/WebTicket/oauthtoken”
$authBody = @{grant_type=$authType}
$authEstablished = Get-Date
$authResponse = Invoke-WebRequest -Uri $authURL -Method Post -Body $authBody -ContentType “application/x-www-form-urlencoded;charset=UTF-8” -UseDefaultCredentials
if($authResponse.StatusCode -eq 200)
{
$authContent = (ConvertFrom-Json $authResponse)
$authToken = $authContent.access_token
$authTokenType = $authContent.token_type
$authExpiresIn = [Int32]$authContent.expires_in
$authExpires = $authEstablished.AddSeconds($authExpiresIn)
$authHeader = @{“Authorization”=$authTokenType + ” ” + $authToken}
$userResponse = Invoke-WebRequest -Uri $userURI -Method Get -Headers $authHeader -ContentType “application/json”
if($userResponse.StatusCode -eq 200)
{
$applicationsURI = (ConvertFrom-Json $userResponse)._links.applications.href
$newApplicationGUID = @{$true=$guid;$false=[guid]::NewGuid();}[$guid -ne $null]
$newApplicationBody = ConvertTo-Json @{“UserAgent”=”LyncRESTMonitor”;”EndpointId”=$newApplicationGUID;”Culture”=”en-US”}
$newApplicationResponse = Invoke-WebRequest -URI $applicationsURI -Method Post -Headers $authHeader -Body $newApplicationBody -ContentType “application/json”
if(($newApplicationResponse.StatusCode -eq 201) -or ($newApplicationResponse.StatusCode -eq 200))
{
Log “Authentication expires $authExpires”
$newApplication = ConvertFrom-Json $newApplicationResponse
$data = @{}
$data.Add(“Root”, $rootURI)
$data.Add(“User”,$userURI)
$data.Add(“Xframe”,$xframeURI)
$data.Add(“Auth”,$authURL)
$data.Add(“Token”,$authToken)
$data.Add(“Type”,$authTokenType)
$data.Add(“Expires”,$authExpires)
$data.Add(“Application”,$newApplication)
$data.Add(“Base”,$rootURI + $newApplication._links.self.href)
return $data
}
}

}
}

return $null
get-service > D:\lync\Scripts\test3.txt
}

function GetUserPresence($data, $sip)
{
$sip = ($sip -replace “SIP:”, “”)
$presenceURL = $data.Base + “/people/” + $sip + “/presence”
$authHeader = @{“Authorization”=$data.type + ” ” + $data.token}
$presenceResponse = Invoke-WebRequest -URI $presenceURL -Method Get -Headers $authHeader -ContentType “application/json”
if($presenceResponse.StatusCode -eq 200)
{
return (ConvertFrom-Json $presenceResponse)
}
return $null
}

Log “Starting”
$guid = “fad289b4-14fc-4b6a-9a33-a03a6423700e”
#Enter Sip Domain
$authData = Authenticate “EnterSipDomain” $guid “urn:microsoft.rtc:windows”
#Make sure that we successfully authenticated

if($authData -ne $null)
{
$status = @{}
$notifyStatuses = “OFFLINE”, “DONOTDISTURB”, “ONLINE”, “NONE”
$pauseHours = 0
$pauseMinutes = 5
$pauseSeconds = 0
$pauseDuration = ((((($pauseHours * 60) + $pauseMinutes) * 60) + $pauseSeconds) * 1000)

while($true)
{
#If the session has reached its max duration then re-authenticate
if($authData.Expires.Ticks -lt (Get-Date).Ticks)
{
Log “Session has expired, attempting to re-authenticate”
$authData = $null
while($authData -eq $null)
{
#Enter Sip Domain
$authData = Authenticate “EnterSIPDomain” $guid “urn:microsoft.rtc:windows”
}
“Successfully re-authenticated”
}

#Make sure that a usable session exists
if($authData -ne $null)
{
##Enter some Ldap query
Get-ADObject -Server somedomain -SearchBase “someOU” -LDAPFilter “(someLdapFilter)” -Properties “cn”,”msRTCSIP-PrimaryUserAddress”,”msrtcsip-line” | Select-Object $_.”msRTCSIP-PrimaryUserAddress” | Sort $_.”cn” | `
foreach {
$cn = $_.”cn”
$sip = $_.’msRTCSIP-PrimaryUserAddress’
$presence = GetUserPresence $authData $sip

#Make sure that the Lync server returned some data
if($presence -ne $null)
{
$availability = ($presence.availability).ToUpper()
$mailState = @{$true=$presence.availability;$false=”Offline”;}[$availability -ne “NONE”]
$mailSubject = $null
if(!$status.ContainsKey($cn))
{
Log “The initial status of $cn is $($presence.availability)”
$status.Add($cn, $availability)
if($notifyStatuses.Contains($availability) -and ($availability -ne “ONLINE”))
{
$mailSubject = “The status of $cn is $mailState”
}
}
else
{
if(($status[$cn] -ne $availability) -and $notifyStatuses.Contains($status[$cn]) -and $notifyStatuses.Contains($availability))
{
$mailSubject = “The status of $cn changed from $($status[$cn]) to $mailState”
}
}

if($mailSubject -ne $null)
{
Log “Sending Email With Subject: $mailSubject”
##Enter some SMTP info
Send-MailMessage -Subject $mailSubject -SmtpServer someSMTPServer -From SomeFromAddress -to someToAddress
}

$status[$cn] = $availability
}
}
}

#Since this is running in a single thread, use the following loop instead of [System.Threading.Thread]::Sleep($pauseDuration), to allow for stopping the script during the pause
$pausedMilliseconds = 0
$ms = 100
while($pausedMilliseconds -lt $pauseDuration)
{
[System.Threading.Thread]::Sleep($ms)
$pausedMilliseconds += $ms
}
}
}
else
{
Log “Authentication Failed. Exiting the script.”
}

$authData = $null

function Log($msg, $init)
{
$log = $true
$logFile = “D:\Lync\Scripts\LyncCheck.log”
if($log -eq $true)
{
if(($init -eq $true) -or ((Test-Path $logFile) -eq $false))
{
“$(Get-Date) – $msg” | Out-File $logFile
}
else
{
“$(Get-Date) – $msg” | Out-File $logFile -Append
}
$log = ((Test-Path $logFile) -eq $true)
}

if($log -eq $false)
{
Write-Host “$(Get-Date) – $msg”
}
}

#function GetDuration($start, $end) { return (($end – $start) / [TimeSpan]::TicksPerMillisecond) / 1000 }

function Authenticate($domain, $guid, $authType)
{
$lyncdiscovers = “LyncDiscoverInternal”, “LyncDiscover”
$lyncprotocols = “http”, “https”
$userURI = $null
$xframeURI = $null
$rootURI = $null
foreach($lyncdiscover in $lyncdiscovers)
{
foreach($lyncprotocol in $lyncprotocols)
{
$autoURI = $lyncprotocol + “://” + $lyncdiscover + “.” + $domain
$autoResponse = Invoke-WebRequest -Uri $autoURI -Method Get -ContentType “application/json”
if($autoResponse.StatusCode -eq 200)
{
$userURI = [System.Uri]((ConvertFrom-Json $autoResponse)._links.user.href)
$xframeURI = [System.Uri]((ConvertFrom-Json $autoResponse)._links.xframe.href)
$rootURI = $userURI.Scheme + “://” + $userURI.Host
break
}
}
if($userURI -ne $null)
{
break
}
}

if($userURI -ne $null)
{
$authURL = $rootURI + “/WebTicket/oauthtoken”
$authBody = @{grant_type=$authType}
$authEstablished = Get-Date
$authResponse = Invoke-WebRequest -Uri $authURL -Method Post -Body $authBody -ContentType “application/x-www-form-urlencoded;charset=UTF-8” -UseDefaultCredentials
if($authResponse.StatusCode -eq 200)
{
$authContent = (ConvertFrom-Json $authResponse)
$authToken = $authContent.access_token
$authTokenType = $authContent.token_type
$authExpiresIn = [Int32]$authContent.expires_in
$authExpires = $authEstablished.AddSeconds($authExpiresIn)
$authHeader = @{“Authorization”=$authTokenType + ” ” + $authToken}
$userResponse = Invoke-WebRequest -Uri $userURI -Method Get -Headers $authHeader -ContentType “application/json”
if($userResponse.StatusCode -eq 200)
{
$applicationsURI = (ConvertFrom-Json $userResponse)._links.applications.href
$newApplicationGUID = @{$true=$guid;$false=[guid]::NewGuid();}[$guid -ne $null]
$newApplicationBody = ConvertTo-Json @{“UserAgent”=”LyncRESTMonitor”;”EndpointId”=$newApplicationGUID;”Culture”=”en-US”}
$newApplicationResponse = Invoke-WebRequest -URI $applicationsURI -Method Post -Headers $authHeader -Body $newApplicationBody -ContentType “application/json”
if(($newApplicationResponse.StatusCode -eq 201) -or ($newApplicationResponse.StatusCode -eq 200))
{
Log “Authentication expires $authExpires”
$newApplication = ConvertFrom-Json $newApplicationResponse
$data = @{}
$data.Add(“Root”, $rootURI)
$data.Add(“User”,$userURI)
$data.Add(“Xframe”,$xframeURI)
$data.Add(“Auth”,$authURL)
$data.Add(“Token”,$authToken)
$data.Add(“Type”,$authTokenType)
$data.Add(“Expires”,$authExpires)
$data.Add(“Application”,$newApplication)
$data.Add(“Base”,$rootURI + $newApplication._links.self.href)
return $data
}
}

}
}

return $null
get-service > D:\lync\Scripts\test3.txt
}

function GetUserPresence($data, $sip)
{
$sip = ($sip -replace “SIP:”, “”)
$presenceURL = $data.Base + “/people/” + $sip + “/presence”
$authHeader = @{“Authorization”=$data.type + ” ” + $data.token}
$presenceResponse = Invoke-WebRequest -URI $presenceURL -Method Get -Headers $authHeader -ContentType “application/json”
if($presenceResponse.StatusCode -eq 200)
{
return (ConvertFrom-Json $presenceResponse)
}
return $null
}

Log “Starting”
$guid = “fad289b4-14fc-4b6a-9a33-a03a6423700e”
#Enter Sip Domain
$authData = Authenticate “EnterSipDomain” $guid “urn:microsoft.rtc:windows”
#Make sure that we successfully authenticated

if($authData -ne $null)
{
$status = @{}
$notifyStatuses = “OFFLINE”, “DONOTDISTURB”, “ONLINE”, “NONE”
$pauseHours = 0
$pauseMinutes = 5
$pauseSeconds = 0
$pauseDuration = ((((($pauseHours * 60) + $pauseMinutes) * 60) + $pauseSeconds) * 1000)

while($true)
{
#If the session has reached its max duration then re-authenticate
if($authData.Expires.Ticks -lt (Get-Date).Ticks)
{
Log “Session has expired, attempting to re-authenticate”
$authData = $null
while($authData -eq $null)
{
#Enter Sip Domain
$authData = Authenticate “EnterSIPDomain” $guid “urn:microsoft.rtc:windows”
}
“Successfully re-authenticated”
}

#Make sure that a usable session exists
if($authData -ne $null)
{
##Enter some Ldap query
Get-ADObject -Server somedomain -SearchBase “someOU” -LDAPFilter “(someLdapFilter)” -Properties “cn”,”msRTCSIP-PrimaryUserAddress”,”msrtcsip-line” | Select-Object $_.”msRTCSIP-PrimaryUserAddress” | Sort $_.”cn” | `
foreach {
$cn = $_.”cn”
$sip = $_.’msRTCSIP-PrimaryUserAddress’
$presence = GetUserPresence $authData $sip

#Make sure that the Lync server returned some data
if($presence -ne $null)
{
$availability = ($presence.availability).ToUpper()
$mailState = @{$true=$presence.availability;$false=”Offline”;}[$availability -ne “NONE”]
$mailSubject = $null
if(!$status.ContainsKey($cn))
{
Log “The initial status of $cn is $($presence.availability)”
$status.Add($cn, $availability)
if($notifyStatuses.Contains($availability) -and ($availability -ne “ONLINE”))
{
$mailSubject = “The status of $cn is $mailState”
}
}
else
{
if(($status[$cn] -ne $availability) -and $notifyStatuses.Contains($status[$cn]) -and $notifyStatuses.Contains($availability))
{
$mailSubject = “The status of $cn changed from $($status[$cn]) to $mailState”
}
}

if($mailSubject -ne $null)
{
Log “Sending Email With Subject: $mailSubject”
##Enter some SMTP info
Send-MailMessage -Subject $mailSubject -SmtpServer someSMTPServer -From SomeFromAddress -to someToAddress
}

$status[$cn] = $availability
}
}
}

#Since this is running in a single thread, use the following loop instead of [System.Threading.Thread]::Sleep($pauseDuration), to allow for stopping the script during the pause
$pausedMilliseconds = 0
$ms = 100
while($pausedMilliseconds -lt $pauseDuration)
{
[System.Threading.Thread]::Sleep($ms)
$pausedMilliseconds += $ms
}
}
}
else
{
Log “Authentication Failed. Exiting the script.”
}

_________________________________________________________________________________

How to tweak it for your environment (Use at your own discretion…I take no responsibility).

  1. Copy the above and save it as a .ps1
  2. Open in your editor of choice.  I use Windows PowershellISE
  3. Line 117 – Need to enter a sip domain
  4. Line 138 – need to enter a sip domain
  5. Line 147 – Need to enter some domain controller, someDN, and whatever  your LDAPFilter is going to be
  6. Line 181 – Enter SMTP server, From Address, To Address
  7. Run the task under a Lync Enabled account.
We have our scheduled task running at start-up.  The script will run every 5 minutes because of line 125.  You can adjust as you need for your environment.  Should it find a phone that is offline, you will get an e-mail only once.  Should that phone come back online you will get an alert getting the phone is now back online.  Also as we know, you are given an 8hr token from Lync, the script will automatically renew it’s token after 8hrs.
Please let me know how you are using it.  This script can continue to evolve.  Hope this is useful for others.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s