Azure Functions for PowerShell

Azure Functions are an inexpensive way to run lightweight utility code. Some of my favorite use cases for Functions are:

  • stashing log data from APIs into log analytics
  • custom custodian scripts (a la cron jobs)
  • supplementing low code workflows (ADF, Logic Apps, etc.) with custom code

In this lab, we'll create an Azure Function, enable a Timer Trigger, enable a Web Trigger, enable support for the AZ module, add input from Application variables, and provide input via HTTP request body, query and headers.

Create an Azure Function Resource

First, we need to create an Azure function in the Azure Portal.

  1. Click + Create a resource
    01-CreateAResource

  2. Type Function in the marketplace search field. Click Function App
    02-FunctionApp

  3. In the Function App Marketplace item, keep the default plan and click Create
    03-FunctionAppCreate

  4. In Create Function App, set the following fields. For this lab, you can leave the rest of the settings at defaults.

    • Function App Name: HelloWorld-# (Set # to a random value)
    • Runtime stack: PowerShell Core
    • Region: A region closest to you.
  5. Click Review + create
    04-CreateFunctionApp01

  6. Confirm the settings and create the resource by clicking Create
    05-CreateFunctionApp02-1

  7. When the resource is finished creating, click Go to resource
    06-GoToResource

Create a Function with Timer Trigger

Now we will create a Function in the Function App. We will use a timer trigger for this function.

  1. Click Functions to go to the functions in the Function App
    07-Functions

  2. Click Create to create a new Function
    08-Functions-Create

  3. For Template select Timer trigger. You can leave the rest of the fields at defaults.

  4. If you want to use a custom Schedule, note that Azure Functions use the NCronTab library. The fields are: {second} {minute} {hour} {day} {month} {day-of-week}

  5. Click Create
    09-TimerTrigger

Add and Run Code

Now we will add simple custom code.

  1. Click Code+Test
    10-CodeTest

  2. Add the following custom code to the end of the template. Don't change the template lines. The currentUTCtime is expected by the framework and we need the param section.

    Write-Host "Hello World!"
    

    11-CodeTestSave

  3. Click Test/Run and then click Run
    12-TestRun

  4. Wait for the code to execute. You should see your output in the log.

    2023-01-15T00:11:15Z   [Information]   INFORMATION: Hello World!
    

    13-TestRunResults

Enable Az Module Support (optional)

Azure Functions have Out of Box support for the Az PowerShell Module. There are many scenareos where you will need to interact with Azure and the Az PowerShell Module is the easiest way to do that. To enable support for the Az PowerShell Module, we will need to update a configuration file in the Kudu management interface. Kudu can also be used for advanced diagnostics of an Azure Function or App Configuration.

Navigate to the Function App in another tab. We will be returning to the code window later.

  1. Control-Click Overview
    14-Overview

  2. Click the Function app name e.g. HelloWorld-223478
    15-FunctionApp

  3. Click Advanced Tools and click Go
    16-AdvancedTools

  4. Select the Debug console -> PowerShell pulldown
    17-DebugConsole

  5. In the folder navigation, click site. Note: The shell also accepts command input.
    18-site

  6. Click wwwroot
    19-wwwroot

  7. Click the pencil icon next to requirements.psd1
    20-requirements

  8. Uncomment the last line to enable the Az module. You can adjust the version restriction if needed, but keep the default setting for now. The value 9.* signifies to use the latest module with major version 9.

    'Az' = '9.*'
    

    21-AzModule

  9. Since we've just enabled the module, it hasn't been downloaded and installed yet.

  10. Click Test/Run and then click Run. The module will be downloaded and installed. Review the log to see the progress. You will see code monitoring errors because downloading and installing the Az module takes longer than the function timeout. The module should install successfully, but the code will not actually execute this iteration. It will take a few more minutes for the Az module to finish installing.
    26-Dependency

  11. Update the code by adding a Function from the Az Module. To keep it simple, we will use a function that does not require an Azure Connection. Add the following to the function.

Get-AzConfig
  1. Click Run. You should see output similar to the following
    27-Get-AzConfig

Azure Connections

If Identity is enabled in the Azure Function, the Azure Function implicitly connects to the System Managed Account or User Managed Account before your code is run. If you need to connect to Azure with other credentials, you will need to explicitly connect to Azure with Connect-AzAccount.

Set and Read Application Settings

Now we will retrieve Application Settings from the Function Configuration. The Application settings in the Function App are automatically configured as environment variables when the Function App starts. These environment variables are available to your script at runtime.

Navigate to Function App in another tab. We will be returning to the code window later.

  1. Control-Click Overview
    14-Overview

  2. Click the Function app name e.g. HelloWorld-223478
    15-FunctionApp

  3. Under Settings, click Configuration

  4. Click + New application setting
    28a-Configuration

  5. Enter the name/value pair and click OK.

    • Name: hello
    • Value: world
      28-NewApplicationSetting

    As a matter of code style, you may consider using a prefix for all of your variable names. Since the Application settings are sorted by Name in the UI, a prefix will cause them to neatly group by name in Application settings. Example: customVar1, customVar2, ..

  6. Click Save to apply the change and then click Continue to apply the change with a restart of the Function App.
    29-SaveConfiguration

    30-Continue

  7. Return to the code editor

  8. Add a line of code that references the custom variable. Click Save and click Run. You should get output similar to that below.

    Write-Host "Hello $env:hello!"
    

    31-env-2

Variables and Source Control

You can optionally use different settings for dev/qa/prod by using different settings for your dev/qa/prod instances. To manage separate dev/qa/prod instances, either create separate Azure Function Resources for each, or create specific slots for dev/qa in your instance. You can then create slot-specific variables. A detailed review of slots is beyond the scope of this article.

Add a Web Trigger

We will now add a Web Triggered function to show how we can run code via HTTP(S) request.

  1. Click Overview
    14-Overview

  2. Click the Function app name e.g. HelloWorld-223478
    15-FunctionApp

  3. Click Functions and click Create
    32-Functions

  4. Click HTTP trigger, set Authorization level to Anonymous and click Create. For secure APIs, you would set the Authorization level to Function or Admin.
    33-CreateFunction

  5. Click Code+Test
    34a-CodeTest

  6. Examine to variable flow:
    a. The name is already set as Azure in the HTTP Body JSON.
    b. The HTTP Body will be passed in via the $Request parameter.
    c. The value of $Request.Body.Name is set as Azure
    d. The value of $name is then set as Azure
    e. The value of $name is used to construct $body
    f. Finally, $body is returned in the HTTP response body.

  7. Click Test/Run and Run
    34-TestRun-1

  8. Confirm that the HTTP response has the value Azure for the name
    35-Response

  9. Set a Query parameter to name/AzureQuery and click Run. Note that on this execution, $name will be set from $Request.Query.Name
    36-AzureQuery

  10. Confirm that the HTTP response has the value AzureQuery
    36-AzureQueryResponse

  11. The default code does not have a handler for Headers, so we'll add one. Insert the following code between "$name" and "if (-not $name)". The insert point is line 11 as of this writing. Click Save to apply the code change.

    if (-not $name) {
        $name = $Request.Headers.name
    }
    
  12. Remove the Query parameter, set a Header parameter to name/AzureHeaders and click Run. On this execution, $name will be set from $Request.Headers.Name
    38-AzureHeaders

  13. Confirm that the HTTP response has the value AzureHeaders
    39-AzureHeadersResponse

Congratulations! You are now ready to begin your journey coding Azure Functions.