Azure Functions with PowerShell: A Practical Guide

Published by Sébastien DUCASSE
Category : Azure Functions / Powershell
22/10/2024

Introduction

Azure Functions is a serverless compute service that allows you to run code on demand without the need to explicitly deploy or manage infrastructure. In this article, we will explore how to use PowerShell, a popular and powerful scripting language, in an Azure Function.

Prerequisites for an Azure Functions Project with PowerShell

 

Project Structure

A typical Azure Functions project with PowerShell has a folder structure as follows:

Directory structure in Visual Studio Code for an Azure PowerShell project named "AZUREPOWERSHELLPOC". In this directory, there is a subfolder called "RenewKeyVaultSecrets" containing the following files: function.json: Used to configure an Azure Function. run.ps1: A PowerShell script to execute. host.json: Used to configure host-level settings for Azure Functions. local.settings.json: For local development settings, such as environment variables. profile.ps1: A PowerShell script used to configure or prepare the environment. requirements.psd1: A PowerShell data file (PSD1), listing project dependencies or requirements.
Each function must be in a folder of the same name, which will contain two files: function.json and run.ps1.
The function.json file contains what is needed to trigger the function execution (HTTP trigger, timer trigger, etc.), while run.ps1 contains the function’s body, the code that will be executed.

Dependencies

PowerShell in Azure Functions supports the installation of modules from the PowerShell Gallery repository. To do this, you need to include a `requirements.psd1` file at the root of your project.

Tools

To work with Azure Functions locally, you will need the following tools:

  • Azure Functions Core Tools
  • PowerShell 7.0 or higher
  • A code editor, such as Visual Studio Code with the Azure Functions extension.

 

Function Example

Here is an example of a function that extends KeyVault secrets expiring within less than a month by one year. This function will run once per day thanks to the `TimerTrigger` defined in the `function.json` file of our function. It is important to note that the expiration of a secret in KeyVault is not a “true” expiration date, but more of a reminder. In fact, if the secret itself does not have an expiration date, it will still be usable. However, if you have stored, for example, an OAuth token containing an expiration date, it’s likely that the date indicated on the KeyVault secret corresponds to the expiration date of the token, so the token should be renewed before its expiration.

 

run.ps1 file:

# Define a parameter named $Timer, typically used for a timer-based trigger in PowerShell automation
param($Timer)

# Output the current date and time to the console, indicating when the script is run
Write-Output "PowerShell Timer trigger function executed at: $(Get-Date)"

# Define a function named Renew-ExpiringSecrets to renew secrets that are about to expire in Azure Key Vault
function Renew-ExpiringSecrets {
    param (
        # Define a mandatory parameter $KeyVaultName, which is the name of the Azure Key Vault
        [Parameter(Mandatory=$true)]
        [string]$KeyVaultName,
        # Define an optional parameter $DaysBeforeExpiry with a default value of 30, which specifies how many days before expiry the secret should be renewed
        [Parameter(Mandatory=$false)]
        [int]$DaysBeforeExpiry = 30
    )

    # Authenticate to Azure using Managed Identity
    Connect-AzAccount -Identity -AccountId managedIdentityToken

    # Get the current date and time
    $currentDate = Get-Date

    # Calculate the threshold date by adding the specified number of days to the current date
    $expiryThresholdDate = $currentDate.AddDays($DaysBeforeExpiry)

    # Retrieve all secrets from the specified Azure Key Vault
    $secrets = Get-AzKeyVaultSecret  -VaultName $KeyVaultName

    # Loop through each secret in the Key Vault
    foreach ($secret in $secrets) {
        # Check if the secret has an expiration date
        if ($secret.Expires) {
            # Check if the secret's expiration date is on or before the threshold date (in our case, less than 30 days) 
            if ($secret.Expires -le $expiryThresholdDate) {
                # Renew the secret (this example simply updates the expiry date)
                $newExpiryDate = $secret.Expires.AddYears(1)
                # Get the value of the secret and convert it to a secure string format
                $secretValue = ConvertTo-SecureString (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $secret.Name -AsPlainText) -AsPlainText -Force
                # Update the secret in Azure Key Vault with the new expiry date
                Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $secret.Name -SecretValue $secretValue  -Expires $newExpiryDate
                
                # Output a message indicating that the secret has been renewed and its new expiry date
                Write-Output "Secret '$($secret.Name)' has been renewed and will now expire on $($newExpiryDate)"
            }
        }
    }
}

# Set the name of the Azure Key Vault to be used in the function
$KeyVaultName = "mw-kv-poc"

# Call the function to renew any expiring secrets in the specified Azure Key Vault
Renew-ExpiringSecrets -KeyVaultName $KeyVaultName

And in the function.json file:

{
  "bindings": [
    {
      "name": "Timer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 0 0 * * *"
    }
  ]
}

Here, the function will be automatically triggered by a timerTrigger every day at midnight. For more information on CRON syntax, visit: https://man7.org/linux/man-pages/man5/crontab.5.html.
Dependency management can be specified in the profile.ps1 file, which will be loaded at each cold start. Here’s its content for our example:

profile.ps1

# Check if the environment variable MSI_SECRET is present
# MSI_SECRET is typically set by Azure when using Managed Service Identity (MSI) for authentication
if ($env:MSI_SECRET) {
    # Disable the automatic saving of Azure context (credentials and other session info) to prevent storing this information on disk
    Disable-AzContextAutosave -Scope Process | Out-Null

    # Authenticate to Azure using Managed Identity
    Connect-AzAccount -Identity
}

# Import the Az.Keyvault module, which contains cmdlets to manage Azure Key Vaults and their contents
Import-Module Az.Keyvault

Pour gérer les versions des modules à utiliser, c’est dans requirements.ps1 que ça se passe !

@{
    'Az.Keyvault' = '3.*'
}

 

Conclusion

Azure Functions offers a powerful platform for serverless development, and with PowerShell, you can leverage your existing scripting skills to build robust solutions.