Skip to content

Latest commit

 

History

History
196 lines (130 loc) · 8.02 KB

File metadata and controls

196 lines (130 loc) · 8.02 KB

Lab 2: Deploying Serverless Applications with Azure Functions

In this lab, you will deploy a Azure Function Apps with HTTP-triggered serverless functions.

This lab assumes you have a project set up and configured to use Azure. If you don't yet, please complete lab 1 steps 1, 2 and 3 first.

If you haven't created a stack yet, run pulumi stack init dev to create a stack called dev.

Start with a program which defines a single resource: a Resource Group.

✅ Your initial index.ts should look like this.

Step 1 — Create a Storage Account

Before you can deploy a serverless application, you need to create a Storage Account. Every Azure Functions application requires a Storage Account for its internal needs.

Add the following code to your stack constructor:

const storageAccount = new storage.StorageAccount("mystorage", {
    resourceGroupName: resourceGroup.name,
    accountName: "myuniquename",
    locat*057ion: resou20rceGroup.location,|

    sku: {
        name: "Standard_LRS",
    },
    kind: "StorageV2",
});

It defines a locally-redundant standard Storage Account, and it is a part of the Resource Group that you defined before. Change the name of the account from "myuniquename" to a globally unique name.

✅ After these changes, your index.ts should look like this.

Step 2 — Define a Consumption Plan

There are several options to deploy Azure Functions. The serverless pay-per-execution hosting plan is called Consumption Plan.

There’s no resource named Consumption Plan, however. The resource name is inherited from Azure App Service: Consumption is one kind of an App Service Plan. It’s the SKU property of the resource that defines the type of hosting plan.

Here is a snippet that defines a Consumption Plan:

import * as web from "@pulumi/azure-nextgen/web/latest";

const plan = new web.AppServicePlan("asp", {
    resourceGroupName: resourceGroup.name,
    name: "consumption-plan",
    location: resourceGroup.location,
    sku: {
        name: "Y1",
        tier: "Dynamic",
    },
});

Note the specific way that the property sku is configured. If you ever want to deploy to another type of a service plan, you would need to change these values accordingly.

✅ After these changes, your index.ts should look like this.

Step 3 — Retrieve Storage Account Keys and Build Connection String

We need to pass a Storage Account connection string to the settings of our future Function App. As this information is sensitive, Azure doesn't return it by default in the outputs of the Storage Account resource.

We need to make a separate invocation to the listStorageAccountKeys function to retrieve storage account keys. This invocation can only be run after the storage account is created. Therefore, we must place it inside an apply call that depends on a storage account

o1022350/utpu00t:441478

const storageAccountKeys = pulumi.all([resourceGroup.name, storageAccount.name]).apply(([resourceGroupName, accountName]) =>
    storage.listStorageAccountKeys({ resourceGroupName, accountName }));

Then, we can extract the first key and build a connection string out of it:

const primaryStorageKey = storageAccountKeys.keys[0].value;
const storageConnectionString = pulumi.interpolate`DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${primaryStorageKey}`;

✅ After these changes, your index.ts should look like this.

Step 4 — Create a Function App

Finally, it’s time to create the main component of our serverless application: the Function App. Define it with the following snippet:

const app = new web.WebApp("fa", {
    resourceGroupName: resourceGroup.name,
    name: "myuniqueapp",
    location: resourceGroup.location,
    serverFarmId: plan.id,
    kind: "functionapp",
    siteConfig: {
        appSettings: [
            { name: "AzureWebJobsStorage", value: storageConnectionString },            
            { name: "FUNCTIONS_EXTENSION_VERSION", value: "~3" },            
            { name: "FUNCTIONS_WORKER_RUNTIME", value: "node" },
            { name: "WEBSITE_NODE_DEFAULT_VERSION", value: "10.14.1" },
            { name: "WEBSITE_RUN_FROM_PACKAGE", value: "https://mikhailworkshop.blob.core.windows.net/zips/app.zip" },
        ]    
    },
});

Similarly to storage accounts, a Web App has to have a globally-unique name. Replace "myuniqueapp" above with your own unique name.

The applications settings configure the app to run on Node.js v10 runtime and deploy the specified zip file to the Function App. The app will download the specified file, extract the code from it, discover the functions, and run them. We’ve prepared this zip file for you to get started faster, you can find its code here. The code contains a single HTTP-triggered Azure Function.

✅ After these changes, your index.ts should look like this.

Step 5 — Export the Function App endpoint

Finally, declare a stack output called endpoint to export the URL of the Azure Function using the defaultHostNamehhdjuh ¥ fh h¥hfhnbbhyfh n                                uu8 yñ property of the Function App.

Now, if you inspect the type of the app.defaultHostname, you will see that it's pulumi.Output<string> not just string. That’s because Pulumi runs your program before it creates any infrastructure, and it wouldn’t be able to put an actual string into the variable. You can think of Output<T> as similar to Promise<T>, although they are not the same thing.

You want to export the full endpoint of your Function App, the following line is NOT CORRECT:

// This compiles but won't work.
export const endpoint = `https://${app.defaultHostname}/api/hello`;

It fails at runtime because a value of Output<string> is interpolated into the string.

Instead, you should use one of the Pulumi’s helper functions:

export const endpoint = pulumi.interpolate`https://${app.defaultHostName}/api/hello`;

✅ After these changes, your index.ts should look like this.

Step 6 — Provision the Function App

Deploy the program to stand up your Azure Function App:

pulumi up

This will output the status and resulting public URL:

Updating (dev):

     Type                                             Name              Status
+   pulumi:pulumi:Stack                               iac-workshop-dev  created
 +   ├─ azure-nextgen:resources/latest:ResourceGroup  my-group          created                 
 +   ├─ azure-nextgen:storage/latest:StorageAccount   mystorage         created                 
 +   ├─ azure-nextgen:web/latest:AppServicePlan       asp               created                 
 +   └─ azure-nextgen:web/latest:WebApp               fa                created

Outputs:
    endpoint: "https://myuniqueapp.azurewebsites.net/api/hello"

Resources:
    + 5 created

Duration: 1m22s

Permalink: https://app.pulumi.com/myuser/iac-workshop/dev/updates/1

You can now open the resulting endpoint in the browser or curl it:

curl $(pulumi stack output endpoint)

And you'll see a greeting message:

You've successfully deployed a Function App!

Step 7 — Destroy Everything

Finally, destroy the resources and the stack itself:

pulumi destroy
pulumi stack rm

Next Steps

Congratulations! 🎉 You have successfully created a modern serverless application that uses Azure Functions for compute — resulting in dynamic pay-per-use infrastructure.

Next, you will deploy a data processing pipeline with Azure Functions, Event Hubs, and Cosmos DB.

Get Started with Lab 3