AVRI 19

Azure - Résoudre les erreurs liées au module Powershell Az

Ce post peut couvrir une grande partie de vos problèmes en Powershell, et pas uniqument ceux liés aux problèmes avec le module Az.

Vous n’êtes pas sans savoir qu’Azure se base sur des API REST, et que le module Powershell Az se base su celles-ci, comme c’est le cas pour la CLI ou pour les managements librairies.

Maintenant, quand on passe à l’action, il peut arriver qu’on ait des erreurs peu parlantes comme celle-ci :

PS F:\> Add-AzKeyVaultManagedStorageAccount -VaultName $keyvaultName -AccountName $storage.Name -AccountReso
urceId $storage.Id -ActiveKeyName key1 -RegenerationPeriod $period

Add-AzKeyVaultManagedStorageAccount : Operation returned an invalid status code 'BadRequest'
At line:1 char:1
+ Add-AzKeyVaultManagedStorageAccount -VaultName $keyvaultName -Acc ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Add-AzKeyVaultManagedStorageAccount], KeyVaultErrorException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.AddAzureKeyVaultManagedStorageAccount

Alors pas de panique, pour avoir plus d’infos sur l’erreur, vous pouvez mettre l’option -Debug à votre cmdlet comme ci-dessous :

Add-AzKeyVaultManagedStorageAccount -VaultName $keyvaultName -AccountName $storage.Name -AccountReso
urceId $storage.Id -ActiveKeyName key1 -RegenerationPeriod $period -Debug

Je vous passe le résultat qui peut être assez détaillé, et qui peut contenir des informations sensibles, mais à la fin vous avez le plus souvent un joli message d’erreur clair comme celui-ci

Body:
{
  "error": {
    "code": "Forbidden",
    "message": "Key vault service doesn't have proper permissions to access the storage account
  }
}

En règle générale toutes les commandes powershell qui vous renvoient un Bad Request font un appel REST au sein de leur implémentation.


AVRI 15

ARM - DevTestLabs - Spécifier les images de la gallerie autorisées

Lorsque vous mettez en place DevTestLabs en entreprise il est souhaitable d’avoir un moyen d’automatiser la création de celui-ci. Si on regarde du côté des templates ARM fournis par Microsoft : https://github.com/Azure/azure-devtestlab/tree/master/samples/DevTestLabs/QuickStartTemplates Il est possible d’automatiser la création de la plupart des policies.

Maintenant si on regarde le détail pour limiter les images utilisables de la gallerie, nous avons ce template :

{
  "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "newLabName": {
      "type": "string",
      "metadata": {
        "description": "The name of the new lab instance to be created."
      }
    }
  },
  "variables": {
    "allowedImages": "\"{\\\"offer\\\":\\\"CentOS\\\",\\\"publisher\\\":\\\"OpenLogic\\\",\\\"sku\\\":\\\"7.2\\\",\\\"osType\\\":\\\"Linux\\\",\\\"version\\\":\\\"latest\\\"}\",\"{\\\"offer\\\":\\\"Oracle-Linux\\\",\\\"publisher\\\":\\\"Oracle\\\",\\\"sku\\\":\\\"7.2\\\",\\\"osType\\\":\\\"Linux\\\",\\\"version\\\":\\\"latest\\\"}\",\"{\\\"offer\\\":\\\"SQL2016-WS2012R2\\\",\\\"publisher\\\":\\\"MicrosoftSQLServer\\\",\\\"sku\\\":\\\"Enterprise\\\",\\\"osType\\\":\\\"Windows\\\",\\\"version\\\":\\\"latest\\\"}\""
  },
  "resources": [
    {
      "apiVersion": "2018-10-15-preview",
      "name": "[trim(parameters('newLabName'))]",
      "type": "Microsoft.DevTestLab/labs",
      "location": "[resourceGroup().location]",
      "resources": [

Première chose qui choque, c’est que la variable qui liste les images est juste illisible. Quand on creuse un peu, on s’aperçoit que l’API de microsoft attend quelque chose de ce type en entrée:

"allowedImages": "['ImageReference', 'ImageReference']"

Donc une chaine de caractère contenant un tableau de chaine de caractère au format JSON, et cette chaine correspond à l’objet ImageReference des VM dans Azure.

Partant de cela, je décide de créer un fichier paramètre ARM contenant un tableau d’ImageReference comme suit :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "imageReferences": {
            "value": [
                {
                    "offer": "Windows-10",
                    "publisher": "MicrosoftWindowsDesktop",
                    "sku": "rs4-pro",
                    "osType": "Windows",
                    "version": "latest"
                },
                {
                    "offer": "Windows-10",
                    "publisher": "MicrosoftWindowsDesktop",
                    "sku": "rs4-pron",
                    "osType": "Windows",
                    "version": "latest"
                },
                {
                    "offer": "Windows-10",
                    "publisher": "MicrosoftWindowsDesktop",
                    "sku": "rs5-pro",
                    "osType": "Windows",
                    "version": "latest"
                }
            ]
        }
    }
}

Enfin quelque chose de lisible, qui liste 3 machines Windows 10 dans ce cas précis. Maintenant il ne me reste plus qu’à produire ma chaine de caractère, donc dans mon template ARM, j’ai le code suivant:

"variables": {
    "thresholdName": "threshold",
    "copy": [
        {
            "name": "[variables('thresholdName')]",
            "count": "[length(parameters('imageReferences'))]",
            "input": "[string(parameters('imageReferences')[copyIndex(variables('thresholdName'))])]"
        }
    ],
    "thresholdValue": "[string(variables(variables('thresholdName')))]"
}

Dans la propriété input, je transforme chaque ImageReference en chaine de caractère, puis dans la propriété thresholdValue, je transforme mon tableau en chaine de caractère.

Et voilà je peux assigner ce threshold à ma policies comme suit :

{
    "apiVersion": "2018-10-15-preview",
    "name": "[concat('default','/GalleryImage')]",
    "type": "policySets/policies",
    "condition": "[parameters('canDeploy')]",
    "dependsOn": [
        "[resourceId('Microsoft.DevTestLab/labs', parameters('newLabName'))]"
    ],
    "properties": {
        "description": "",
        "factName": "GalleryImage",
        "evaluatorType": "AllowedValuesPolicy",
        "status": "enabled",
        "threshold": "[variables('thresholdValue')]"
    }
}

Et voilà, comme quoi on peut rendre des templates ARM lisibles.


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.