Thursday, March 1, 2018

Building an Edge Server Port Monitor with Azure Function Apps – Part 1

For this blog series I thought I would branch out a bit and take my Powershell scripting to the next level using Azure Function Apps. What on earth is an Azure Function App, I hear you asking? Well, come in closer around the camp fire and I’ll explain. An Azure Function App is a Serverless application platform. I know that sounds like an oxymoron… An application platform that is serverless! Well, yes, you are correct, there are in fact servers that are running these Function Apps in a nameless data centre somewhere. However, the reason that they are referred to as being “serverless” is that you never have to administer any part of the server hardware or software yourself. You simply write your code and paste it into the Azure Portal and then click the run button….  aannnndd bam! You end up with a very useful application that runs all day, every day, on hardware in a distant land that you never have think about. You just kick back and once a month pay the very reasonable rates offered by Azure. Brilliant!

As a means to teach myself about the Azure Functions platform I decided to give myself a challenge to design a useful application that could run using only Powershell as the programming language. The application I decided to design was an Skype for Business Edge Server port monitoring service. The idea of this application is that it would monitor all the important ports on any number of remote Edge servers and report to me if any of them went down. Due to the number of steps involved in doing this I will be breaking this into 3 separate blog posts.

For part 1 the requirements of the application are as follows:
  • Allow for multiple far end server ports to be monitored.
  • Allow for multiple far end servers to be monitored.
  • Allow for tests to be run on an ongoing basis at configurable intervals.
  • If a port is found to be inaccessible, then the application must email me with details of what is down.
In part 2 of the series I will expand on the application to do the following:
  • Allow for coalescing of emails so that multiple errors result in only one email being sent instead of many emails.
  • Allow for the application to have a memory so it can only send me an email after seeing a configurable number of errors. This is to guard against flapping type scenarios and only to update in the case of a fair dinkum outage.
  • In addition to sending an email when ports are down, it will also send an email to inform me that the ports have come back up - so I can rest easy in the knowledge that the server recovered without logging in to check. 
  • Use Azure Storage Tables to store state about the Edge servers current status.
In part 3 (Coming Soon) of the series I will expand the application to do even more:
  • Save all port test results to Azure Storage tables for later analysis.
  • Use Power Bi to connect to Azure Storage Tables and create nice graphs showing information about the Edge servers over a period of time.

By the end of the series there will be a feature-rich service that allows for the monitoring of remote servers and analyses results in a relatively comprehensive way. This should highlight how powerful Azure Function Apps can be!

Limitations of Powershell Functions


Upfront I should say that Azure Function Apps using Powershell is still considered an “Experimental” language. Whilst I was writing this application I found there are some limitations when coding in Powershell using Function Apps. This list is by no means complete - it’s just some interesting things I noticed:
  • I couldn’t call any commands that requested access to the networking stack. By this I mean commands to query the IP Address of the machine or ports in use, etc. This seems fair given that Function Apps run on shared machines and there is some level of abstraction between the code and the machine it’s running on.
  • I noticed that Invoke-WebRequest would give a warning that requested the use of Basic Parsing due to “The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.” This is not a limitation for the functionality in this application but may be something to keep in mind for your future apps.
  • I had a weird issue where function (ie. “function ConnectTCP”) returns would include all of the text from Write-Output commands that executed in the function. I’m not sure if this was a bug or a limitation... Something to look out for though.
  • You need to manually upload additional external powershell modules into the Azure backend. There is no basic “Install-Module” command that can grab code from the Powershell Gallery yet. This can also be problematic if the module attempts to access something that’s not supported by Azure Functions (for example access the networking stack like mentioned earlier).
  • “Write-Host” is not a supported output method like it is in server based Powershell. In order to get output from your script you need to use “Write-Output” which will be written to the Function App log.
  • When sending out port queries I tried to also specify the source port being used, however, it appears that the source port gets NATed on the way out of Azure. So the source port will be random in this case.

Creating the Function App


 Step 1
Open up the Azure Portal and Select “App Services” from the left menu. Then select the “Add” button:



Step 2
Select “Function App” as the type of service app that you would like to create:



Step 3
Click the create button:




Step 4
Fill in the details of the function application. The important parts here are to give your app a unique name, select Windows as the OS and select a Location nearest the servers you would like to be testing.




Step 5
You will need to wait a few minutes for the Function App to be provisioned for you. During this time you will see a “Deploying Function App” icon on the dashboard:



Step 6
Once the Function App has finished deploying, open the app to the Overview tab. Then select “Application settings”:



Step 7
In order for your application to output the correct time information in emails, you need to set the timezone within the “Application settings” to your local timezone. To do this add a row in the “Application settings” called “WEBSITE_TIME_ZONE” and then enter the name of the timezone (eg. “AUS Eastern Standard Time”). You can get a full list of timezones from here: https://msdn.microsoft.com/en-us/library/gg154758.aspx



Don’t forget to click the save button on the Application settings page after making the change:



Step 8
In the main page of the Function App select the “Functions” tab on the left-hand tree. Then click the “New function” button:



Step 9
You will now be presented with a screen that allows you to select the type of Function App that you would like to create. In order to be able to create a Powershell based Function App you need to click the “Experimental Language Support” switch in the top right corner of the screen:



Step 10
Once “Experimental Language Support” has been selected you can choose to create a “Timer trigger” app using Powershell as the language:



Step 11
The timer trigger type of application uses a CRON type format for representing when the application will execute. You will be presented with a dialog that allows you to enter the “Name” and “Schedule” for your application to run. The schedule expression is a CRON expression that includes 6 fields. These are:

{second} {minute} {hour} {day} {month} {day of the week}

Note: This is slightly different to CRON that is used on Linux that doesn’t have the seconds value. You need to be careful when borrowing CRON syntax off the Internet. For more information on the CRON format refer to this page: https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer



Note: The important thing to know about this CRON Schedule is that the “*/5” means to run once every 5 minutes.

Step 12
Now that you have created your Trigger Timer, it will appear under the Functions tree in the left side of the Function App. When you select the trigger timer it will open up a code window that is pre-filled with a single “Write-Output” statement. This is the window in which you will be pasting in the Powershell code. You will also see on the right hand side the actual Powershell file called “run.ps1” and a function.json manifest file. At the bottom of the screen there is a Logs window where all the “Write-Output” logging will be sent to. Note that Function Apps use Write-Output instead of Write-Host like you see in most Powershell script running on servers.




Step 13 - Sign up for Mailjet
The script uses a web service called Mailjet in order to send emails. In order for the Function Application to send the email it needs to send a REST call out to Mailjet with the correct Account Keys in it. The free level of Mailjet lets you send up to 6000 emails per month, which is plenty for this kind of scenario.

After signing up for the Mailjet web site go to the following location to get your API username and password:

My Account > REST API > Master API Key & Sub API key management

In your Mailjet account you will also need to set up the mail account that you wish to be allowed to send emails: 

Accounts > Sender Addresses



Step 14
Insert the following Powershell script into your Function App code window (run.ps1) and make the edits required in Step 15:




Like this:




Step 15
Edit the script as necessary for your Mailjet account and Edge servers.

Enter your Mailjet API Key (Username) and Secret Key (Password) and paste them into the following section of the script. Also update the sender email address to your email account that is configured in the "Sender Addresses" in Mailjet and the recipent email address you want to send to:


#MAIL JET USERNAME/PASSWORD#######
$emailUsername = "kjh3k23h4kjhkj37573f8f020879dff7"     
$emailPassword = "9898f98fhdjkkdjh46cd418100075a3b"
#EMAIL ADDRESS TO SEND ERRORS FROM
$SENDEREMAIL = "YourRealEmailAddress@domain.com"
#EMAIL ADDRESS TO SEND ERRORS TO
$RECIPIENTEMAIL = "YourRealEmailAddress@domamin.com"
################################## 

Edit the Skype for Business Edge server details as required. These are entered as an array of hash tables. The sections highlighted in yellow can be changed. In this case the application is monitoring 2 Edge servers, one in Melbourne and one in Sydney.

Location
ServerName
ServerRole
DestinationPort
Protocol
Melbourne
147.70.50.10
Federation
5061
TCP
Melbourne
147.70.50.10
Access Edge
443
TCP
Melbourne
147.70.50.11
Web Conferencing
443
TCP
Melbourne
147.70.50.12
AV Edge
443
TCP
Sydney
147.70.60.20
Federation
5061
TCP
Sydney
147.70.60.20
Access Edge
443
TCP
Sydney
147.70.60.21
Web Conferencing
443
TCP
Sydney
147.70.60.22
AV Edge
443
TCP
Note: The script only supports testing TCP ports at this time.

$Records= @(@{"Location"= "Melbourne"; "ServerName" ="147.70.50.10"; "ServerRole"= "Federation"; "DestinationPort" = "5061"; "Protocol" ="TCP"})
$Records+= @(@{"Location"= "Melbourne"; "ServerName" ="147.70.50.10"; "ServerRole"= "Access Edge"; "DestinationPort" = "443"; "Protocol" ="TCP"})
$Records+= @(@{"Location"= "Melbourne"; "ServerName" ="147.70.50.11"; "ServerRole"= "Web Conferencing"; "DestinationPort" = "443"; "Protocol" ="TCP"})
$Records+= @(@{"Location"= "Melbourne"; "ServerName" ="147.70.50.12"; "ServerRole"= "AV Edge"; "DestinationPort" = "443"; "Protocol" ="TCP"})

$Records+= @(@{"Location"= "Sydney"; "ServerName" ="147.70.60.20"; "ServerRole"= "Federation"; "DestinationPort" = "5061"; "Protocol" ="TCP"})
$Records+= @(@{"Location"= "Sydney"; "ServerName" ="147.70.60.20"; "ServerRole"= "Access Edge"; "DestinationPort" = "443"; "Protocol" ="TCP"})
$Records+= @(@{"Location"= "Sydney"; "ServerName" ="147.70.60.21"; "ServerRole"= "Web Conferencing"; "DestinationPort" = "443"; "Protocol" ="TCP"})
$Records+= @(@{"Location"= "Sydney"; "ServerName" ="147.70.60.22"; "ServerRole"= "AV Edge"; "DestinationPort" = "443"; "Protocol" ="TCP"})



Step 16
By default your Function App will be running at the CRON trigger interval. However, you can stop your Function App from running by switching off the trigger function in under the “Functions” tree item on the left hand side:



Step 17
Start receiving emails when your Edge servers are offline!




The Wrap Up


Well, this has been fun! Hasn’t it?! I hope that in addition to helping you monitor your edge servers, this has been informative and taught you some new skills that might help in the future when making your own Function Apps. Enjoy!




0 comments to “Building an Edge Server Port Monitor with Azure Function Apps – Part 1”

Post a Comment