AVRI 04

Azure Function - Moi développeur C#, je commence comment ?

Il est l’heure de ce mettre au Serverless, et sur Azure il y en a 2 principaux qui sont Azure Function et Logic Apps. Ce dernier est très orienté Workflow, donc on ne va pas l’aborder ici. Par contre, on va faire un rapide tour des possibilités qu’on a pour Azure Function.

Il existe plusieurs manières de créer ces fonctions dans Azure Functions, qui sont les suivantes :

  • Via le portail Azure
  • Via l’intégration d’un repository Git
  • Via l’intégration d’un zip
  • Via un binaire généré via Visual Studio par exemple

Chacune de ces méthodes a ses avantages et ces inconvénients selon moi.

Génération d’un binaire :

  • Avantages :
    • Utilisation d’un éditeur de code complet comme Visual Studio (oui ce n’est pas toujours simple d’éditer des solutions C# via VSCode)
  • Inconvénients :
    • On risque facilement de dériver vers un binaire trop lourd qui a un Cold Start pas vraiment acceptable pour vos clients
    • On ne voit pas nos fonctions dans le portail, enfin on ne voit pas leurs codes, juste la référence à un binaire. Ce qui avouons le n’est pas l’idéal pour du debug.

Le portail Azure :

  • Avantages :
    • Rapide à dévélopper, et à tester.
    • Pas besoin d’outil, un simple navigateur Web, une connexion internet et de l’immagination suffit.
    • On voit le rendu dans le portail Azure
  • Inconvénients :
    • Il faut bien connaitre son langage, car c’est d’un niveau spartiate l’outillage Web
    • Qu’est-ce que le portail peut être lent dans cette partie là.

Depuis Git ou via un zip :

  • Avantages :
    • Intégration native de déploiement continu dans Azure Function.
    • Utilisation d’un IDE de qualité pro, comme VSCode
    • On voit nos fonctions dans le portail en ReadOnly
  • Inconvénients :
    • Il faut faire des csx si vous faites du C#, et ce n’est clairement pas évident

Pour ma part, j’utilise principalement les deux derniers, car j’utilise Azure Function principalement pour automatiser des actions de management. Par contre, si je devais travailler sur un projet globallement serverless, je me ferais une joie de rouvrir Visual Studio.

De ce fait je vais vous montrer comment faire pour créer vos premières fonctions en csx via VSCode.

Etape 1: Installer la moitié d’Internet sur votre PC :

  • Une ou plusieurs versions de dotnet core.
  • Nodejs

Pour ma part, j’utilise chocolatey pour installer ce genre d’outil sur mon PC.

Etape 2: Lancer VSCode :

Pas besoin de le télécharger normalement, car comme tout bon dev, vous l’avez déjà, mais au cas où, voici le lien: https://code.visualstudio.com/Download

Etape 3: Installer tout pleins d’extensions :

Des extensions, il y en a pleins sur VSCode, notamment celles ci :

Et il y en a bien d’autres très utiles (dont la mienne).

Etape 4: Les outils pour Azure & Azure Function :

Commençons par installer la CLI ou Powershell pour Azure. Où les deux si votre coeur balance

Install-Module Az -AllowClobber -Scope CurrentUser

Les outils Azure Function, il suffit d’aller dans VSCode et faire View > Command Palette (CTRL+SHIFT+P ou F1) et de lancer la tâche Azure Functions : Install or Update Azure Functions Core Tools et de bien choisir la V2.

Etape 5: On code ? :

Ouvrez VSCode dans un dossier vide et lancer les commandes suivantes :

func init --csx
func new --csx

Il faudra choisir un nom pour votre fonction, et surtout un trigger, à ce jour il existe les suivants :

  • Azure Blob Storage trigger
  • Azure Cosmos DB trigger
  • Durable Functions activity
  • Durable Functions HTTP starter
  • Durable Functions orchestrator
  • Azure Event Grid trigger
  • Azure Event Hub trigger
  • HTTP trigger
  • IoT Hub (Event Hub)
  • Outlook message webhook creator
  • Outlook message webhook deleter
  • Outlook message webhook handler
  • Outlook message webhook refresher
  • Microsoft Graph profile photo API
  • Azure Queue Storage trigger
  • SendGrid
  • Azure Service Bus Queue trigger
  • Azure Service Bus Topic trigger
  • Timer trigger

Normalement il y a de quoi trouver votre bonheur avec ces différents triggers.

Après la création de votre fonction, vous devriez avoir un VSCode qui ressemble à ça :

image

Etape 7: on rajoute des paquets NuGet :

Faire du C# sans utiliser NuGet, ça devient vite laborieux. Il est possible de rajouter des paquets Nuget en ajoutant un fichier function.proj dans le dossier de votre fonction, ce fichier contient par exemple le code suivant :

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <AzureFunctionsVersion>v2</AzureFunctionsVersion>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.2.0-preview2" />
  </ItemGroup>
</Project>

Etape 8: On lance en local et on debug si on le souhaite:

Pour lancer en local, il vous suffit de taper la commande suivante :

func host start

Et si vous voulez lancer en mode debug, le plus simple est de configurer votre environnement pour utiliser VSCode via la tâche Azure Functions : Initialize Project for Use with VS Code… puis de faire F5

Etape 9: On publie :

Et maintenant il ne reste plus qu’à publier votre fonction via la commande suivante :

func azure functionapp publish 'nom de votre Azure Function'

Et voilà c’est aussi simple que cela, il ne reste plus qu’à vous lancer.


MARS 22

ARM - Enrichir vos templates avec vos propres fonctions

Les templates ARM vous permettent de déployer vos infrastructures sur Azure ou Azure Stack, et ils vous évitent de nombreux clics dans le portail.

Un des retours que j’ai souvent à leur sujet c’est :

  • Trop verbeux
  • Trop de json
  • Pas assez de méthode built-in

Ce dernier point peut être en parti résolu par une fonctionnalité méconnue des templates ARM.

Pour la plupart des personnes, un template ARM ressemble à ça :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {},
    "resources": [],
    "outputs": {}
}

Et bien sachez qu’il y a une propriété de plus qui n’est pas obligatoire, et qu’on peut voir ici :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {},
    "functions": [],
    "resources": [],
    "outputs": {}
}

Voyons comment créer notre fonction, et oui c’est toujours du json:

{
    "namespace": "woivre",
    "members": {
        "arrayToString": {
            "parameters": [
                {
                    "name": "input",
                    "type": "array"
                }
            ],
            "output": {
                "type": "string",
                "value": "[replace(substring(string(parameters('input')), 1, sub(length(string(parameters('input'))), 2)), '\"', '')]"
            }
        }
    }
}

Ce que l’on peut faire dans les fonctions est assez limité, mais pour de la manipulation de données c’est très efficace.

Au global cela nous donne le template suivant :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {
        "testValues": [
            "One",
            "Two",
            "Three",
            "Four"
        ]
    },
    "functions": [
        {
            "namespace": "woivre",
            "members": {
                "arrayToString": {
                    "parameters": [
                        {
                            "name": "input",
                            "type": "array"
                        }
                    ],
                    "output": {
                        "type": "string",
                        "value": "[replace(substring(string(parameters('input')), 1, sub(length(string(parameters('input'))), 2)), '\"', '')]"
                    }
                }
            }
        }
    ],
    "resources": [],
    "outputs": {
        "test": {
            "type": "string",
            "value": "[woivre.arrayToString(variables('testValues'))]"
        }
    }
}

Pratique si vous ne voulez pas trop vous répéter dans vos templates.


MARS 20

ARM - Provisionnez un Azure Automation avec un RunAsAccount

Lorsque vous voulez utiliser Azure Automation pour gérer vos ressources Azure, vous devez créer un Service Principal qui vous servira de RunAsAccount. Il est possible de faire cela via le portail Azure, et cela vous créera un compte qui ressemble à celui là.

image

Si vous avez une convention de nommage, le nom de l’application : reference-aa_5cz2xH6ut6qwABcMDEIfJNCdFX2GZIDGHNrMbVunQAY= ne vous convient sûrement pas, ça tombe bien moi non plus.

Pour cela, il faut précréer votre Service principal avant de créer votre compte Automation via un template ARM dans mon cas. Le compte doit respecter les éléments suivants :

  • Avoir un joli nom qui répond à vos contraintes de naming : pretty-app
  • Une HomePage Url correspondant à l’url de votre service sur Azure : https://management.azure.com//subscriptions/##SUBSCRIPTION_ID##/resourceGroups/aa/providers/Microsoft.Automation/automationAccounts/sample-aa
  • Un certificat à renseigner en tant que secret de votre application.

Donner des droits à ce compte sur la souscription Azure que vous souhaitez afin de pouvoir valider le bon fonctionnement de votre compte Applicatif.

Maintenant, passons à notre template ARM, qui est le suivant :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {
        "aa-appId": "##_APPLICATION_ID_##",
        "aa-tenantId": "##_TENANT_ID_##",
        "aa-certBase64Value": "##_CERTIFICATE_VALUE_BASE64_##",
        "aa-certThumbprint": "##_CERTIFICATE_THUMBPRINT_##",
        "aa-subId": "##_SUBSCRIPTION_ID_##"
    },
    "resources": [
        {
            "type": "Microsoft.Automation/automationAccounts",
            "apiVersion": "2015-10-31",
            "name": "sample-aa",
            "location": "[resourceGroup().location]",
            "properties": {
                "sku": {
                    "name": "Free"
                }
            },
            "resources": [
                {
                    "name": "AzureRunAsCertificate",
                    "type": "certificates",
                    "apiVersion": "2015-10-31",
                    "dependsOn": [
                        "sample-aa"
                    ],
                    "properties": {
                        "base64Value": "[variables('aa-certBase64Value')]"
                    }
                },
                {
                    "name": "AzureRunAsConnection",
                    "type": "connections",
                    "dependsOn": [
                        "sample-aa",
                        "AzureRunAsCertificate"
                    ],
                    "apiVersion": "2015-10-31",
                    "properties": {
                        "connectionType": {
                            "name": "AzureServicePrincipal"
                        },
                        "fieldDefinitionValues": {
                            "ApplicationId": "[variables('aa-appId')]",
                            "TenantId": "[variables('aa-tenantId')]",
                            "CertificateThumbprint": "[variables('aa-certThumbprint')]",
                            "SubscriptionId": "[variables('aa-subId')]"
                        }
                    }
                }
            ]
        }
    ],
    "outputs": {}
}

Si on détaille un peu on retrouve ici notre account automation :

{
    "type": "Microsoft.Automation/automationAccounts",
    "apiVersion": "2015-10-31",
    "name": "sample-aa",
    "location": "[resourceGroup().location]",
    "properties": {
        "sku": {
            "name": "Free"
        }
    }
}

Maintenant on ajoute le certificat qui sert à nous identifier avec notre service principal

{
    "name": "AzureRunAsCertificate",
    "type": "certificates",
    "apiVersion": "2015-10-31",
    "dependsOn": [
        "sample-aa"
    ],
    "properties": {
        "base64Value": "[variables('aa-certBase64Value')]"
    }
}

Et pour finir, il faut créer le compte dans Azure Automation

{
    "name": "AzureRunAsConnection",
    "type": "connections",
    "dependsOn": [
        "sample-aa",
        "AzureRunAsCertificate"
    ],
    "apiVersion": "2015-10-31",
    "properties": {
        "connectionType": {
            "name": "AzureServicePrincipal"
        },
        "fieldDefinitionValues": {
            "ApplicationId": "[variables('aa-appId')]",
            "TenantId": "[variables('aa-tenantId')]",
            "CertificateThumbprint": "[variables('aa-certThumbprint')]",
            "SubscriptionId": "[variables('aa-subId')]"
        }
    }
}

Et voilà, vous pouvez maintenant utiliser un Azure Automation avec un RunAsAccount qui respecte votre règle de nommage.


MARS 13

Powershell - Astuces avec le module Az pour les Storage Account

Vous n’êtes pas sans savoir que Microsoft a mis à jour son module Powershell AzureRM par Az. Ce module a l’usage a pour principale modification de remplacer vos commandes AzureRM par Az comme on peut le voir ci-dessous :

Connect-AzureRMAccount

Connect-AzAccount

Techniquement pour les migrations de vos scripts c’est assez simple à mettre en oeuvre, donc n’hésitez pas à migrer !

Par ailleurs, le module Az apporte quelques nouveautés plutôt cools notamment pour le Storage. J’avais publié différents articles sur le support de RBAC pour accéder aux data du blob storage :

Et bien maintenant il y a une nouvelle solution, vous pouvez le faire via le module Powershell via les commandes suivantes

Connect-AzAccount # Via un Service Principal ou non

New-AzStorageContext -StorageAccountName $storageDestinationName -UseConnectedAccount

Et voilà comment passer un token AD sans avoir à écrire 30 lignes de powershell. A noter qu’il existe aussi une fonctionnalité similaire pour vos comptes de Storage de dev local

New-AzStorageContext -Local

Et dans une moindre mesure, il est possible de se connecter à un storage de manière anonyme, même si dans ce cas précis, je ne vois pas l’avantage par rapport à des appels REST standards.

New-AzStorageAccount -StorageAccountName $storageDestinationName -Anonymous

Mais bon l’option existe donc si vous voulez l’utiliser, allez-y.


MARS 11

ARM - Nouveautés mars 2019

Un petit article pour vous faire part des nouveautés disponibles sur ARM que je trouve vraiment cool, et surtout que j’attendais particulièrement.

Possibilité d’ajouter un paramètre avec la date courante.

"parameters": {
    "time": {
        "type": "string",
        "defaultValue": "[utcNow()]",
        "metadata": {
            "description": "This returns the current UTC time of deployment. This function may only be used in the defaultValue of a parameter."
        }
    },
    "date": {
        "type": "string",
        "defaultValue": "[utcNow('dd-MM-yy')]",
        "metadata": {
            "description": "Standard dateTime format strings are supported: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings"
        }
    }   
}

A noter que celui-ci ne marche que dans l’objet paramètre et non pas dans la partie variable. Du coup il est maintenant possible d’ajouter la date de création de vos ressources dans vos tags.

Il est maintenant possible d’ajouter un peu d’aléa grâce à la génération de guid dans vos templates

"parameters": {
    "guid": {
        "type": "string",
        "defaultValue": "[newGuid()]",
        "metadata": {
            "description": "This will generate a new GUID each time this template is deployed. This function may only be used in the defaultValue of a parameter."
        }
    },
    "trulyUnique": {
        "type": "string",
        "defaultValue": "[uniqueString(newGuid())]",
        "metadata": {
            "description": "This will generate a new uniqueString() each time this template is deployed. This is not idempotent, use with care."
        }
    },
    "trulyUniqueGuid": {
        "type": "string",
        "defaultValue": "[guid(newGuid())]",
        "metadata": {
            "description": "This will generate a new guid each time this template is deployed. The guid() function can be idempotent, used this way it is not."
        }
    }
}

Voilà il y a aussi d’autres nouveautés liées à ARM, je vous laisse aller voir le blog dédié à celles-ci: https://azure.microsoft.com/en-us/updates/azure-resource-manager-template-language-additions/