Back to the Well: Putting the "Fun" in Azure Functions

Last week I was reminded I had a blog. I was talking with somebody at work and they mentioned googling me and finding the IoT Button post—I feel silly linking it because it’s literally the next post down—and they wanted to know if the button still worked. I can’t be sure, but I doubt it.

That was posted in May 2016, which now feels like a couple lifetimes ago.  A lot has changed since then—the button itself was lost in a building move, I left Ahead and lost access to the AWS account and Slack instance the button was plumbed to, and a global pandemic broke out, really doing a number on my sense of time and perspective.

Some things, however, have not changed. I still work with Eric Shanks, I still like learning new things, and I still find the best way to do the latter is through light-hearted harassment of the former. It’s time to rebuild.

Ground Rules

I’ve mellowed a bit in my old age—I want to stick to amusement over annoyance. While it seemed like a good idea at the time, sending a message as a Slack DM was a bit much. It made a fairly critical communication medium pretty much unusable by Shanks for a few days.  I’m trying to be less of a jerk these days. We’re all works in progress. 

Also, I don’t have the permissions necessary to configure a Slack integration anywhere anymore.

With the decision to avoid direct delivery, I need to rethink what we’re really doing here, and I’ve landed on allowing anyone to generate custom Eric Shanks facts on demand. This is achievable with only minor tweaks to the original Python code from the last effort.

Last time, I did everything with a text editor, a command line Python interpreter, and the AWS Console. This time, I want to use this as an excuse to play with some things that are new(er) to me—Visual Studio Code and Azure Functions.

Visual Studio Code is feature-rich, super-extensible, and slightly-intimidating cross-platform IDE. I have bounced off it a few times in the past, and have been itching for a reason to dig in and stick it out.

Azure Functions are bits of code that respond to triggers and that Azure will execute without the need for you to provision or understand the underlying infrastructure.  The documentation landing page has a subhead declaring them “More than just event-driven serverless compute,” but that’s all I want out of them today.

In the next few sections, we’re going go through how to set this all up and publish to Azure. If you don’t want to stick around that long, I totally get it—you can just click right here to generate your own Eric Shanks Fact and move on with your day.

Pre-Reqs

We need a few things to get started.

  1. An Azure Account and an active subscription. You can sign up for a free trial here.
  2. Azure Functions Core Tools
  3. Visual Studio Code
  4. Python. 3.8, 3.7, and 3.6 are supported by Azure Functions. Here’s the link to 3.8.

These are straightforward installations, I won’t belabor them here. When you have these installed, launch Visual Studio Code.

Adding Visual Studio Code Extensions

When you first launch Code, you’ll see something like this:

Feel free to poke around there and explore, I have all day. If you don’t want to see this Welcome screen ever again, you can clear the checkbox down in the lower left and close the Welcome tab.

A neat thing about Code is that for almost anything you’d want to do, there’s an extension that can help you do that easier. To build and publish our function, we need two of them: the Python extension and the Azure Functions extension.

You can add these in a couple of ways. You could click the Tetris-block-looking icon on the left tall bar (the Activity Bar) and search for them, or you could follow these direct links: Python, Azure Functions.

Click each link, then click Install. Hit Continue and Open if prompted to launch Code:

Then, from the Code window, click Install:

Close the Extension tab, and repeat for Azure Functions.

Create an Azure Functions project

With our extensions ready, we can create our Functions project. Click the Azure icon in the Activity Bar, then in the Azure Functions area click the Create New Project icon, which looks like a folder with a lightning bolt in its lower right.

You’ll be prompted to select or create a new folder to serve as the project workspace. Pick or create one, and remember where it is.

You’ll then be prompted for additional information:

  1. Language. Select Python.
  2. A Python alias for a virtual environment. Select whichever is appropriate–for me with a fresh installation, it was py 3.8.1
  3. A template for your project’s function. Pick HTTP Trigger.
  4. A function name. Pick something snappy, like “EricShanksFacts”
  5. An Authorization Level. Choose Anonymous, as we want to share this with the world.
  6. How to open your project. Select Add to workspace.

Time will pass while the virtual environment is created. You may get prompted to install a linter, please do so if so.

When it’s done, you’ll have a skeleton function with some template code scattered throughout. We can edit the sample function code and replace it with our own.

If Code didn’t drop you there after the Project was created, click over to the Explorer view. That would be the stacked pieces of paper icon at the top of the Activity Bar, or Ctrl-Shift-E if you’re fancy.

You can see our code skeleton under the ERICSHANKSFACTS heading there. If indentations and IntelliSense formatting frighten you, know that you’re in good company, and we’ll get through this together. All we need to do here is edit two (2) files.

The first is requirements.txt, there at the bottom of the list. Click it.

This file defines any Python modules we might need to include with our function. Our function uses the requests module to scrape a webpage, so we need to list it here. Add requests to line 6, and then hit File->Save.

Next, we’re going to edit __init__.py, which holds the actual Python that gets executed by the HTTP Trigger. Go ahead and click that one.

To make this easier to view, I shrank the Panels pane in the lower right by clicking the upper border and dragging it down. You could also close it by clicking the x, and bring it back later with Ctrl-J.

We’re going to make a couple changes here. First, we’re going to import a couple more things. Requests, mentioned earlier, to scrape a web page, and re for some regular expression stuff. Those are added as lines 4 and 5 in the capture below.

We’re also going to trash and replace everything under line 7 above. That’s all fancy conditional stuff to take an argument and do something with it. That’s more than we need, we just want to do the same thing every time on triggering.

Here’s the screenshot with the new code:

You may or may not get that Unable to import requests error pylint error. We’ll fix that in a minute, first I want to describe what the code is doing.

Just like last time, we’re going out to an existing insult generator and scraping the page (lines 10-11).

Then, we’re extracting just the insult portion and doing some string replacement to fix some character formatting (lines 12-16).

Now, the resulting strings all take the form of “You something-something something or other.” That’s not quite the presentation we’re after. We want these phrased as statements about Eric Shanks. So, in lines 17-20, we’re looking at the first character of the word that follows “You,” and then creating a new string that concatenates “Eric Shanks is a” or “Eric Shanks is an” with everything after “You,” and then returns that string (line 21).

Back to that pylint error. Googling suggests an issue with the path to my virtual environment–the folder was sitting on OneDrive, which may have complicated things. I came back later and tried with a workspace folder somewhere else and it worked just fine. If you get hung up here, you can can side-step the issue by clicking on Python in the status bar to set the interpreter path. You can see it defaults to the .\.venv\ path, which would be preferred, but for now I’m going to select the ~\AppData\ path to which Python was directly installed. That seemed to clear up the error for me.

Code for __init__.py should look like the block below. I will not be taking comments on the quality of the code and my lazy variable names at this time.

import logging

import azure.functions as func
import requests
import re

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    r = requests.get('http://datahamster.com/autoinsult/index.php?style=3')
    scrape = r.text
    x = re.findall(r'<div class="insult" id="insult">(.*?)</div><br>',scrape)
    y = str(x)
    y = y.replace('&#44;', ',')
    y = y.replace('&nbsp;', ' ')
    y = y[2:-2]
    if (y[4] == 'a' or y[4] == 'e' or y[4] == 'i' or y[4] == 'o' or y[4]== 'u'):
        shanks = 'Eric Shanks is an' + y[3:]
    else:
        shanks = 'Eric Shanks is a' + y[3:]
    return func.HttpResponse(shanks)

Go to File->Save, and then we’ll move on to testing the function.

Testing the Function Locally

First off, if you’re on a Windows machine you need to make sure your PowerShell Execution Policy is set to at least RemoteSigned. You can check this by opening a PowerShell console and running Get-ExecutionPolicy, and you can set this by opening a PowerShell console as Administrator and running Set-ExecutionPolicy RemoteSigned

Pop back over to the Azure area by either clicking the icon in the Activity bar or hitting Ctrl-Shift-A.

Hit F5 to start the function app project locally. Allow access if prompted:

You should end up with something like the below image:

Ctrl-Click on that localhost link to access the function locally. If all went well, you should see something like this:

Accurate and functional! Pop back to Code and stop debugging by hitting Shift-F5.

Time to publish to Azure.

Publishing the Function to Azure

Go back to the Azure view by clicking the Icon in the Activity bar or hitting Ctrl-Shift-A. Click Sign in to Azure at the top:

This will launch a window asking you for Azure account credentials. Provide them, and close the page when instructed.

Code should refresh and all subscriptions associated with your account will be viewable at the top of the Azure page. Click the blue arrow icon to deploy the function.

You will be prompted for several pieces of information:

  1. Select Subscription – Pick the subscription you want to use
  2. Select Create a new Function App in Azure
  3. Enter a globally-unique name for the app. Anything will do. I’ll use “EricShanksFactsBlog” for now.
  4. Select a Runtime Stack. Python 3.8 matches what we used locally, so pick that.
  5. Select a location. Pick the Azure region you prefer (North Central US).

Things will happen:

After a few minutes, you should see a message like this pop up in the lower right:

Click View output. That will give you the URL for your function:

Ctrl-Click it to test.

Accurate, functional, and open to the world. Enjoy!