Azure Functions avec PowerShell : Un guide pratique

Publié par Sébastien DUCASSE
Catégorie : Azure Functions / Powershell
22/10/2024

Introduction

Azure Functions est un service de calcul sans serveur qui vous permet d’exécuter du code à la demande sans avoir à déployer explicitement ou gérer l’infrastructure. Dans cet article, nous allons explorer comment utiliser PowerShell, un langage de script populaire et puissant, dans une azure function.

Prérequis d’un projet Azure Functions avec PowerShell

 

Arborescence du projet

Un projet typique d’Azure Functions avec PowerShell a une structure de dossier comme suit :

Structure de répertoire dans Visual Studio Code pour un projet Azure PowerShell nommé "AZUREPOWERSHELLPOC". Dans ce répertoire, il y a un sous-dossier appelé "RenewKeyVaultSecrets" contenant les fichiers suivants : function.json : Utilisé pour configurer une Azure Function. run.ps1 : Un script PowerShell à exécuter. host.json : Utilisé pour configurer les paramètres au niveau de l'hôte pour les Azure Functions. local.settings.json : Pour les paramètres de développement local, tels que les variables d'environnement. profile.ps1 : Un script PowerShell utilisé pour configurer ou préparer l'environnement. requirements.psd1 : Un fichier de données PowerShell (PSD1), listant les dépendances ou les exigences du projet.
Chaque function doit être dans un dossier du même nom, qui contiendra deux fichiers : function.json et run.ps1.
Le fichier function.json contient le nécessaire pour déclencher l’execution de la function (http trigger, timer trigger …), tandis que run.psi contient le corps de la function, le code qui sera executé.

Dépendances

PowerShell dans Azure Functions prend en charge l’installation de modules à partir du référentiel PowerShell Gallery. Pour cela, vous devez inclure un fichier requirements.psd1 à la racine de votre projet.

Outils

Pour travailler avec Azure Functions en local, vous aurez besoin des outils suivants :

  • Azure Functions Core Tools
  • PowerShell 7.0 ou supérieur
  • Un éditeur de code, comme Visual Studio Code avec l’extension Azure Functions.

 

Exemple de fonction

Voici un exemple de fonction qui prolonge d’un an les secrets d’un keyvault qui expirent dans moins d’un mois. Cette fonction sera exécutée une fois par jour grâce au TimerTrigger défini dans le fichier function.json de notre fonction.
Il est à noter que l’expiration à proprement parler d’un secret dans un keyvault n’est pas une « vraie » date d’expiration, mais plutôt un aide mémoire. En effet, si le secret en lui même n’a pas de date d’expiration, cela n’aura aucune incidence, il pourra tout de même continuer à être utilisé. En revanche, si vous avez stocké, par exemple, un token OAuth contenant une date d’expiration, il y a fort à parier que la date indiquée sur le secret du keyvault correspond à la date d’expiration du token, il convient donc de renouveler le token avant son expiration.

 

Fichier run.ps1 :

# 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

Et dans le fichier function.json :

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

Ici, la fonction sera déclenchée automatiquement par un timerTrigger tous les jours à minuit. Pour en savoir plus sur la syntaxe CRON : https://man7.org/linux/man-pages/man5/crontab.5.html
La gestion des dépendances peut être précisée dans le fichier profile.ps1, qui sera chargé à chaque cold start. Voici son contenu pour notre exemple :

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 offre une plateforme puissante pour le développement sans serveur, et avec PowerShell, vous pouvez tirer parti de vos compétences existantes en scripting pour créer des solutions robustes.