Enfin les jobs dans le module Azure Powershell

Voilà un truc que j’attendais depuis longtemps. Même si la recommandation est d’utiliser les templates ARM, on a tous une bibliothèque de scripts PowerShell qui créé des machines virtuelles aux petits oignons (charte de nommage, tags, activation de la sauvegarde, supervision avec un beau DSC ou Custom script pour finir la personnalisation de la machines virtuelle). Un truc propre quoi.

Pourtant on a tous pestés sur les commandes les plus connues du module AzureRm.Compute :

  • New-AzurermVM
  • Update-AzurermVM
  • Start-AzurermVM
  • Stop-AzurermVM

 

Pour mieux comprendre une illustration toute simple

$startdate = Get-Date

Write-host $startdate -ForeGroundColor Green -BackgroundColor Black

Stop-azurermvm -Name $vm.name -ResourceGroupName $vm.ResourceGroupName -Force

$enddate = Get-Date

Write-Host $enddate -ForeGroundColor Green -BackgroundColor Black

Write-Host (New-TimeSpan -Start $startdate -End $enddate).Totalseconds -ForeGroundColor Red

clip_image001

 

Le problème, c’est de devoir attendre plus de deux minutes pour que l’API Azure nous rende la main. Multipliez-le par le nombre de machines virtuelles à déployer en même temps en une seule journée, à la fin, ça commence à faire long. Par le passé, j’avais tenté les jobs PowerShell mais, ces commandes PowerShell n’étaient pas éligibles. C’était vrai jusqu’à la version 5.1.1 du module PowerShell pour Azure introduite début décembre 2017.

Depuis la version 5.1.1, les commandes PowerShell citées en début de ce billet introduisent l’argument AsJob

clip_image002

 

Reprenons le même exemple en ajoutant juste « -AsJob »

clip_image003

 

Tout de suite plus intéressant d’un point de vue performance. Si c’est un job, lors on doit pouvoir le suivre avec la commande Get-Job :

clip_image004

 

Ce qui est intéressant, c’est que même si la commande échoue, on peut quand même récupérer le pourquoi en creusant en peu dans les propriétés du job.

clip_image005

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure Scheduled Maintenance

Azure Scheduled Maintenance est une nouvelle approche de la gestion de la maintenance des infrastructures Azure. Celle-ci a été introduite progressivement mais ce n’est qu’en cette fin d’année 2017 que l’on peut constater l’arrivée de cette fonctionnalité dans les régions North Europe & West Europe.

Pour rappel, la fonctionnalité doit permettre aux clients Azure :

  • D’être notifié des périodes de maintenances pour le service Azure Virtual machines.
  • De pouvoir influer sur les périodes de maintenance proposées en les anticipant

 

Dans le portail Azure, cela se présente comme illustré ci-dessous avec la mention « Scheduled maintenance will start soon ».

clip_image002

 

Si on regarde plus en détail, nous n’avons que peu d’information. C’est normal car pour cette machine virtuelle, Azure ne nous annonce que la période de maintenance planifiée, pas encore la période de « pré-maintenance ».

clip_image004

 

Donc à ce stade, cliquer sur le bouton Redeploy ne permettra pas d’anticiper la maintenance. Par contre, quand on regarde une machine virtuelle dont la planification de maintenance est un peu plus avancée, l’interface est un peu différente :

clip_image006

 

Nous sommes informés que la future possibilité d’anticiper la phase de maintenance. C’est important car une fois entrée en phase de Scheduled Maintenance, le processus est 100% automatisé, nous ne pourrons plus intervenir.

clip_image008

 

Lorsque la fenêtre de pré-maintenance sera ouverte, nous recevons un mail comme celui-ci, tout du moins, un par souscription Azure dans laquelle nous avons des machines virtuelles.

clip_image009

 

A noter que la même information est disponible maintenant dans le Azure Service Health dans la rubrique « Planned Maintenance ».

clip_image011

 

Lorsqu’on regarde l’onglet « Affected ressources », on a une vue synthétique. Dans l’illustration ci-dessous, j’ai deux machines virtuelles pour lesquelles je peux dès maintenant anticiper la période de maintenance proposée par Azure

clip_image013

 

Maintenant qu’on est dans la fenêtre de pré-maintenance, on peut lancer l’opération avec le bouton « Start Maintenance ».

clip_image015

 

L’opération va consister tout simplement en un « Redeploy ». La machine virtuelle va donc s’arrêter, quitter l’hôte Hyper-V et être redéployé sur un autre hôte Hyper-V qui lui a déjà été mis à niveau par Microsoft.

clip_image016

 

Avantage d’anticiper la maintenance, c’est nous qui contrôlons la durée de l’indisponibilité. Ce n’est plus le cas au terme de la période de pré-maintenance.

Pour avoir une vue d’ensemble, le plus simple reste de personnaliser la vue « Virtual machines » pour inclure les colonnes suivantes :

  • Maintenance – Auto-Scheduled Window
  • Maintenance – Proactive Windows

clip_image018

 

Après, on peut aussi jouer en PowerShell :

get-azurermvm -status | select Name, MaintenanceRedeployStatus -ExpandProperty MaintenanceRedeployStatus | select Name, IsCustomerInitiatedMaintenanceAllowed, PreMaintenanceWindowStartTime, PreMaintenanceWindowEndTime, MaintenanceWindowStartTime, MaintenanceWindowEndTime | Out-GridView

clip_image020

 

Les informations sont identiques. Ce qui est bien, c’est qu’on n’est pas obligé d’interroger chaque machine virtuelle pour collecter l’information.

clip_image022

Bonus

Le Bonus, c’est d’être informé de ces opérations dès que possible. Comme dans Azure, il y a une fonctionnalité pour tout, en creusant dans Azure Service Health, vous découvrirez la possibilité de créer une alerte sur ce sujet. La seule critique, c’est que l’alerte est liée à un groupe de ressources. Il faudra donc autant d’alertes que de groupes de ressources contenant des machines virtuelles.

clip_image024

 

Pour conclure, un peu de lecture sur le sujet :

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure API Management 5/5 – PowerShell Time

C’est la dernière ligne droite et aussi le temps du PowerShell. Pour Parler PowerShell, tout comme pour Azure Function, cela reposera sur un Invoke-WebRequest. C’est donc presque pareil, à quelques exceptions. La première chose qui change, c’est l’URL. Pour rappel, voilà l’URL de notre Azure Function exposée via notre instance du service Azure API Management.

clip_image001

 

La seconde chose qui change, c’est la clé d’authentification. Nous ne passons plus la clé d’authentification de notre Azure Function (c’est géré au niveau des policies d’Azure API Management) mais une des deux clés dont nous disposons dans la souscription au produit qui présente notre Azure Function. En tant que consommateur, nous pouvons retrouver cette information dans le portail développeur mis à disposition par l’instance du service Azure API Management. Cette clé, nous allons la référencer sous le nom « Ocp-Apim-Subscription-Key »

clip_image003

 

Nous avons toutes les informations, ne nous reste plus qu’à construire notre requête.

$url = « https://tpapimanagement.azure-api.net/AzuretableValetKeyAPI/api/AzuretableValetKeyAPI »

Invoke-RestMethod -Uri $Url -Method POST -ContentType « application/json » -Headers @{« Ocp-Apim-Subscription-Key »= »2fab81140f42490683XXXXXXXXa26ac0 »}

clip_image005

 

Nous y sommes. Nous avons réussi à consommer notre Azure Function exposée par Azure API Management.

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure API Management 4/5 – C’est l’heure de consommer

Nous avons une API exposée avec des nouvelles fonctionnalités :

  • Point d’accès unique pour toutes mes futures API
  • Une méthode d’authentification unique pour toutes mes API
  • Une journalisation centralisée et non plus gérée au niveau de chaque API
  • Une politique de limitation d’usage

 

La prochaine étape, c’est donc nécessairement de consommer. Azure API Management est capable de s’interfacer avec bon nombre de systèmes d’authentification. Dans le contexte de cette série de billets, nous allons utiliser le fournisseur d’identité par défaut. Chaque utilisateur doit disposer d’un compte et d’un mot de passe pour accéder au service. On peut soit créer des utilisateurs ou les inviter. L’avantage de l’invitation, c’est que c’est l’invité qui sera chargé de configurer son mot de passe. J’ai donc retenu de lui envoyer une invitation.

clip_image002

 

Le client de ma licorne reçoit un mail qui l’invite à se connecter au portail. A noter que ces éléments de communication (template de mails) sont configurables dans Azure API Management.

clip_image004

 

Point d’attention, la politique de mot de passe d’Azure API Management est assez pointue. N’essayez pas d’utiliser des séquences continues dans vos mots de passe.

clip_image006

 

Une fois authentifié, on arrive sur le portail développeurs de notre instance du service Azure API Management. Dans la rubrique « Products », on constate qu’il nous est proposé la souscription d’un service. La notion de souscription à un produit est importante car pour chaque souscription de produit, une paire de clés d’accès est générée.

clip_image008

 

Au sens Azure API Management, un produit permet aux futurs clients de ma licorne de souscrire à nos API. Dans l’exemple ci-dessous, Azure API Management va imposer une Policy qui limite le nombre d’appels par minutes / semaines. Dans mon Business plan de la mort qui tue, j’ai un mode gratuit mais faut pas non plus qu’il me coute les yeux de la tête. Pour l’instant, il n’y a qu’une seule API.

clip_image009

 

Il est possible de personnaliser le nom de chaque souscription réalisée. C’est intéressant si on souscrit plusieurs fois au même produit.

clip_image011

 

C’est souscrit, pour accéder à mon API, je peux constater que j’ai deux clés.

clip_image013

 

En tant que client consommateur de l’API, je peux aussi référencer ma propre application qui va changer la face du monde.

clip_image015

 

Je peux la soumettre à publication, reste à voir si l’administrateur de l’Azure API Management service est prêt à m’aider à démarrer et qui sait devenir demain le nouveau Facebook 3.0.

clip_image017

 

Nous approchons de la fin de cette série de billets. Ne nous reste plus qu’à nous placer en tant que client pour consommer notre API. Ce sera le sujet du dernier billet.

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure Reserved Instances – La fonctionnalité la plus simple à expliquer

Généralement, les lecteurs de mon blog s’attendent à ce que je disserte sur les fonctionnalités les plus technique d’Azure. La, on va faire juste expliquer la fonctionnalité la plus simple d’Azure. Pour la démontrer, même pas besoin du portail Azure, la calculatrice Azure suffira. L’idée de base, c’est plus on s’engage dans le temps pour consommer plus on obtient de remise. Pour illustrer mon propos, voilà le prix du service Azure compute pour la SKU D1 déployée en standard avec l’OS Windows, le tout hébergé dans la région West Europe. C’est notre prix de référence pour du « Pay as you go ».

clip_image001

Maintenant, si on s’engage à ce que notre machine virtuelle soit provisionnée dans Azure pendant un an, le prix baisse de 33%. C’est bien mais pas top.

clip_image002

Pour être top, il faut s’engager sur trois ans. La, le prix baisse de 44%.

clip_image003

Voilà, c’est tout ce qu’il y à savoir sur Azure Reserved Instance, enfin presque. Il y a quand même quelques limitations. A ce jour, cela ne concerne que les souscriptions de type Pay as you go ou Enterprise Agreement, pas encore les CSP.

Pour plus d’informations, lisez la FAQ.

 

Désolé, j’ai pas plus simple. Même le DAF il va liker!

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure API Management 3/5 – Exposition de notre première API

A partir de maintenant, tout va se passer dans Azure API Management. Notre Azure Function est prête à être référencée comme API. Dans l’interface de gestion du service, en cliquant sur « API », on arrive à l’interface ci-dessous :

clip_image001

Azure API Management prend en charge plusieurs langages de description pour nos API. Par chance, l’intégration avec Azure Function est nativement supportée ou presque. L’interface nous propose bien de sélectionner notre Function App (ce qui porte notre fonction) mais pas la fonction en elle-même. C’est pour cela que je spécifie le paramètre API Url suffixe correspondant le nom de la fonction.

clip_image002

Autre sujet important, l’accès authentifié à notre Azure Function. Pour rappel, chaque fonction que nous avons développée dispose de sa propre URL et clé d’accès :

clip_image003

Si je ne fais rien, aucune authentification sera présentée à mon Azure Function. Il faut donc pouvoir intégrer ce code d’authentification dans l’appel depuis Azure API Management. Vu que c’est un secret, on va commencer par le stocker dans une Named Value. Au moment de l’écriture de cette série de billets, il n’est pas encore possible de stocker ses secrets dans un Azure KeyVault. A ce jour, c’est un feedback qui a été remonté à Microsoft : Integration with Azure KeyVault. En attendant KeyVault, nous allons utiliser les Named Values.

clip_image004

Azure API Management permet d’injecter des paramètres additionnels dans le Header. C’est une des nombreuses fonctionnalités offertes par les Azure Policies d’Azure API Management. Nous allons travailler au niveau « Inbound Processing »

clip_image005

Notre opération consistera à ajouter un nouveau paramètre à notre requête HTTP. Pour éditer ma policy, je suis passé en mode « Code-View » pour intégrer une règle de transformation nommée « Set query request parameter ». De cette manière, on va injecter le paramètre « Code » avec la valeur contenue dans la Named Value {{AzureFunctionAPICode}}. Reste à ne pas oublier de cliquer sur le bouton Save.

clip_image006

De retour dans la view « From View », c’est plus simple à comprendre.

clip_image007

Attention, l’utilisation du Named Parameters, dans la vue form view ne conservera pas le named parameters mais va juste remplacer le contenu. D’ou le passage en mode « Form View ».

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure API Management 2/5 – Préparer l’exposition de notre Azure Function

Notre service Azure API Management est maintenant posé. Nous allons y revenir. Pour commencer, nous allons rendre notre Azure Function consommable par mon service API Management. Il faut un peu de préparation. A ce stade, il manque une description contenant :

  • Ce que produit notre API
  • Les paramètres qu’elle attend en entrée
  • Les paramètres qu’elle retourne
  • La ou ses différentes versions

Première étape, on va commencer par limiter ce que va accepter notre Azure Function en termes de verbes. Au niveau du nœud « Integrate » de notre fonction. Nous allons affiner la listes méthodes HTTP pour ne retenir que POST sans oublier de sauvegarder notre modification.

clip_image001

 

Prochaine étape, documenter notre API. Dans le contexte Azure Function, c’est le Framework Swagger qui a été retenu par Microsoft pour l’intégration avec Azure API management. Le Framework Swagger utiliser les spécifications OpenAPI. Allons configurer la définition de notre API en cliquant sur « API Definition ».

clip_image002

 

Azure Function peut être exposé sous forme d’API de deux manières :

  • Function (Preview pour l’intégration avec Azure API Management)
  • External URL (Utilisation d’un service tierce)

 

Azure API Management étant un service interne Azure, nous allons cliquer sur « Function (Preview) ».

clip_image003

 

Remarque importante, cela ne change strictement rien sur la méthode d’authentification de notre Azure Function. Nous pouvons toujours la solliciter ne direct dès lors que nous disposons de toutes les informations. C’est un sujet sur lequel nous allons revenir dans un prochain billet de la série.

clip_image004

 

Pour décrire notre API, nous allons utiliser les spécifications OpenAPI. L’intégration avec Azure est bien faite puisqu’on nous propose de créer un squelette JSON en cliquant sur le bouton « Generate API Definition template ».

clip_image005

 

OpenAPI va nous permettre de produire une description qui sera consommée par Azure API Management. Heureusement que mon API est simple. Globalement, j’ai indiqué :

  • Que mon API ne supportera que le HTTPS pas le HTTP
  • Que mon API ne supportait que la méthode POST via une URL bien précise
  • Qu’elle consomme ses paramètres au format JSON (même s’il n’y en a pas dans mon exemple)
  • Quelle produira du contenu au format JSON avec un seul message documenté

clip_image006

 

N’oubliez pas d’appuyer sur « Save ».

Avant d’aller plus loin dans l’intégration, nous allons déjà valider que cela fonctionne. Pour cela nous avons besoin d’une clé d’accès pour notre Azure Function. Dans le nœud « Manage » de notre Azure Function, nous allons récupérer une clé d’authentification en cliquant sur le bouton « Copy ».

clip_image007

 

De retour dans la configuration de la définition de notre API, dans la zone droite de l’interface, nous avons la possibilité de valider l’accès à notre future API. Cliquons sur « Change Authentication ».

clip_image008

 

Dans l’interface ci-dessous, nous allons renseigner la clé d’accès précédemment obtenus puis cliquer sur le bouton « Authenticate ».

clip_image009

 

Un peu plus bas dans l’interface, on constate la présence de la description de la seule méthode supportée par notre API : « Post ». En cliquant sur « Try this operation », nous allons réaliser un appel à notre future API avec la clé d’authentification.

clip_image010

 

Côté Request, mon API étant assez simpliste (aucun paramètre en entrée), ayant limité les choix d’utilisation à HTTPS seulement et imposé le JSON pour les paramètres, il ne nous reste qu’à appuyer sur le bouton « Send Request ».

clip_image011

 

Si tout se passe bien, nous devrions avoir le résultat retourné par notre Azure Function.

clip_image012

 

Côté Azure Function, les prérequis sont en place. Prochaine étape, l’exposition de notre première API.

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure API Management 1/5 – Introduction

A forcer de créer des API, je vais finir par créer ma licorne. Problème, avec la GDPR qui arrive, si je commence à annoncer à mes futurs investisseurs et clients que je vais développer ma stack sécurité de bout en bout, autant dire que je vais la tuer ma licorne dans l’œuf.

clip_image002

Si développer ma stack sécurité n’est pas une option viable, autant en choisir une qui offre la possibilité de : S’intégrer avec les principaux fournisseurs du marché (Facebook, LinkedIn, Microsoft, Google, …).

  • Proposer un point d’accès unique pour toutes mes API
  • Permettre la mise en place de politiques de contrôle de l’usage de mes API
  • Permettre de gérer la souscription à mes API

Dans la liste des services Azure à destination des développeurs, on trouve Azure API Management Service. Ça fait tout cela et même plus. Dans cette série de billet, nous allons reprendre mon Azure Function proposée dans le billet Azure Function avec Managed Service Identity. Pour rappel voilà son contenu : Une Azure Function qui permet d’extraire un secret de mon Key Vault avec une identité Azure AD utilisant Managed Service Identity.

clip_image004

 

Dans la situation actuelle, tant qu’on dispose de la clé d’authentification associées à la fonction, on peut la solliciter autant qu’on veut et donc gérer une surfacturation. C’est une des fonction d’Azure API Management de limiter la consommation de notre API a un certain nombre d’appel par minutes.

Nous allons commencer par mettre en œuvre une instance du service API Management Service dans un nouveau groupe de ressources. Le service API Management est disponible en trois éditions :

  • Développeur
  • Standard
  • Premium

Pour le détail, je vous renvoie vers la page prévue à cet effet : API Management pricing . J’ai fait le choix de la version développeurs. Au passage, je me désigne comme l’administrateur de ce service.

clip_image005

 

Le provisionning du service API Management est plutôt lent (genre 40 minutes). Azure API Management supporte plusieurs fournisseurs d’identité. On peut :

  • Gérer les utilisateurs accédant à nos API nous-même car il intègre son propre module de gestion des utilisateurs
  • Gérer les utilisateurs en utilisant un Tenant Azure AD, ce qui permet de séparer les comptes des autres ressources Azure, ce qui est déjà bien mieux (mais on gère toujours les utilisateurs)
  • Gérer des utilisateurs issus d’un Tenant Azure AD B2C ce qui est l’option la plus intéressante puisque chaque utilisateur vient avec son identité, nous n’avons plus à la gérer

Remarque : Attention avec Azure AD B2C, au moment de l’écriture de ce billet, il n’est pas encore disponible en CSP. Pour cette raison, nous allons travailler avec le fournisseur d’authentification standard.

Ce qu’il faut comprendre, c’est que ces identités vont permettre à nos consommateurs de se connecter à un portail pour y souscrire l’accès aux API de notre future licorne. Le consommateur ne va donc pas s’authentifier directement avec un compte / mot de passe mais une clé d’accès générée par Azure API Management.

Une fois le provisioning terminé, l’administrateur est notifié par mail de la création du service. Dans le portail Azure, cela ressemble à l’illustration ci-dessous :

clip_image007

 

Première information, le service API Management est accessible selon deux URL :

  • L’URL de la Gateway pour exposer nos API
  • L’URL que nous mettons à disposition de nos partenaires pour gérer la souscription à nos API

clip_image008

 

Avant de poursuivre, on va commencer par configurer la journalisation pour tout envoyer dans Log Analytics.

clip_image009

 

Un peu plus haut, j’avais indiqué que notre service API Management était accessible selon deux URL. En fait, il y en a un peu plus. Bonus, on peut les personnaliser (notion de Custom Domain) ainsi que d’utiliser nos propres certificats.

clip_image011

 

Notre service API Management peut lui-même exposer ses propres API pour le gérer à distance. Par défaut, ce n’est pas activé.

clip_image013

 

Côté Identité, il y a des multiples fournisseurs. Nous allons conserver le fournisseur par défaut. Le service API Management va alors lui-même prendre en charge l’authentification des utilisateurs. Charge à nous administrateur de créer ou inviter des utilisateurs. Nous on va imposer notre marque avec un message de bienvenu sur les services mis à disposition par notre nouvelle licorne.

clip_image015

 

Voilà pour les fondations. Pour la suite, il faudra attendre la publication des prochains billets :

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Azure Function avec Managed Service Identity

Dans mon précédent billet, j’avais évoqué la possibilité d’utiliser la fonctionnalité Managed Service Identity pour évider de coder une clé d’accès au stockage dans les Application Settings de mon Azure Function App.

clip_image001

Dans le portail, Managed Service Identity se limite à une simple case d’option à activer.

clip_image002

 

Dans les faits, c’est un peu plus compliqué que cela. Déjà, l’interface nous mets sur la piste en nous indiquant que notre Azure Function App sera enregistré auprès d’Azure Active Directory. Première interrogation, le nom du Service Principal. On n’a pas trop le choix, c’est celui du Resource Group qui contient mon Azure Function App :

$cred = Get-Credential

Login-AzureRmAccount -Credential $cred -subscriptionid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

$ServicePrincipal = Get-AzureRmADServicePrincipal | where {$_.displayname -eq ‘tpserverless’}

clip_image003

 

Voilà pour le premier mystère. Passons au suivant, comment allons-nous consommer ce nouveau service. Au début, j’ai pensé que quelques informations seraient visibles directement depuis les Application Settings. Choux blancs. En relisant la documentation disponible, qu’il y avait bien des informations mais sous forme de variables d’environnement. On peut constater leur existence en passant par Kudu dans la console.

clip_image004

 

Nous avons donc deux variables d’environnement. La première référence l’API de Managed Service Identity provisionnée dans notre Azure Function App. Nous allons utiliser ces deux informations pour nous authentifier auprès de Managed Service Identity. Managed Service Identity nous retournera le token qu’il a négocié pour nous avec l’identité du Service Principal qui a été créé. On notera que l’API mise à disposition répond uniquement sur l’adresse 127.0.0.1. N’essayez pas de la solliciter de l’extérieur, c’est pas possible et c’est normal, c’est un mécanisme d’isolation.

Dans ce billet, nous allons utiliser le jeton d’accès généré pour nous par Managed Identity Services pour accéder à un secret dans un Key Vault. J’ai donc créé une instance du service Key Vault pour lequel j’ai positionné une autorisation pour le Service Principal.

clip_image005

 

Dans cette instance du service Key Vault, j’ai positionné un secret dont je récupère l’URL pour plus tard, cela va servir.

clip_image006

Avec un peu de PowerShell, on retrouve bien l’ObjectID de notre Service Principal ainsi la permission assignée.

$Keyvault = Get-AzureRMKeyVault -VaultName tpserverlesskeyvault -ResourceGroupName tpserverless

$keyvault.AccessPolicies

clip_image007

 

C’est maintenant que cela se complique. Lorsqu’on accède au Key Vault en PowerShell, on commence toujours par s’authentifier avec la commande Login-AzureRmAccount. Le problème, c’est que l’authentification a déjà eu lieu par Managed Service Account. A ce jour, le module PowerShell Azure ne prend pas encore en charge le scénario d’authentification avec Managed Service Identity. Pour cette raison, toutes les commandes PowerShell du module Azure nous répondront invariablement de nous authentifier. On va devoir travailler directement avec les API, en PowerShell.

Rentrons dans le vif du sujet. Ci-dessous le code PowerShell que j’ai positionné dans mon Azure Function :

$start = get-date

$endpoint = $env:MSI_ENDPOINT

$secret = $env:MSI_SECRET

#

# Authentification Managed Service Identity

#

$vaultTokenURI = ‘https://vault.azure.net&api-version=2017-09-01’

$header = @{‘Secret’ = $secret}

$authenticationResult = Invoke-RestMethod -Method Get -Headers $header -Uri ($endpoint +’?resource=’ +$vaultTokenURI)

#

# Accès au Key Vault

#

$vaultSecretURI = ‘https://tpserverlesskeyvault.vault.azure.net/secrets/Secret/1b140f9db62f4ece803d1152a7739aaa?api-version=2015-06-01’

$requestHeader = @{ Authorization = « Bearer $($authenticationResult.access_token) »}

$creds = Invoke-RestMethod -Method GET -Uri $vaultSecretURI -ContentType ‘application/json’ -Headers $requestHeader

$perf = (New-TimeSpan -Start $start -end $(get-date)).Milliseconds

write-output « Credetial Value (ms):  » $($creds.value)

write-output « Performance (ms):  » $perf

clip_image008

 

Pour les quatre premières lignes, c’est juste pour rappeler d’où viennent mes variables :

  • Le endpoint du Managed Service Identity
  • Le secret qui doit lui être présenté

C’est maintenant que cela se complique. Nous allons construire un jeton d’accès pour accéder à une instance de Key Vault (lignes 8-11). Ce qui est intéressant, c’est que nous obtenons un jeton d’accès. C’est ce qu’on peut constater dans les logs d’exécution de notre Azure Function :

clip_image009

 

C’est exactement ce que le module PowerShell Azure recherche et ne trouvera pas. Par contre, nous pouvons l’utiliser pour accéder à notre instance du service Key Vault. Avec la ligne 16, nous construisons le jeton qui sera inclus dans le Header de notre appel au Key Vault. En ligne 17, nous appelons l’API du Key Vault avec notre jeton. En retour, il nous retourne le secret. Pour preuve, dans les logs d’exécution, on retrouve bien le secret qui était visible dans l’interface graphique quelques paragraphe plus haut.

clip_image010

 

Maintenant, est-ce cette approche est performance ? C’est un problème que j’avais rencontré dans mon billet « Valet Key avec Azure Function App« . Visiblement, la réponse est oui car pour toutes ces opérations, j’ai un temps d’exécution de 27 millisecondes :

clip_image011

 

Conclusion

Je suis fan. On ne stocke plus d’information sensible. Les seules informations sensibles à notre disposition ne peuvent être consommées que dans notre Azure Function App et en plus c’est performant. Il faudra certainement une nouvelle version du module PowerShell pour prendre en charge l’utilisation de Managed Service Identity nativement dans le module PowerShell.

Secure by design and Business Compliant quoi.

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Valet Key avec Azure Function App

Dans le billet « Azure Functions – Introduction au ServerLess en PowerShell« , on a posé les bases. Cette fois, passons à un usage concret avec le pattern Cloud Valet Key. C’est un des principes fondamentaux dans le cloud.

Introduction

Lorsqu’on regarde le fonctionnement des services de stockage Azure, on constate que la finesse / granularité d’affectation des permissions est binaire. Pour un Storage Account, on dispose de deux clés : Primaire et secondaire (les Access Keys). Le problème, c’est le niveau de privilège que ces clés octroient :

clip_image001

 

Heureusement, toute plateforme cloud qui se respecte est capable de nous proposer la capacité à générer des permissions beaucoup plus fines. Faut juste orchestrer la chose :

  • Etape n°1 : une application cliente soumets une demande d’accès auprès d’un maître des clés (notre Valet Key)
  • Etape n°2 : Le valet Key valide l’identité de l’appelant (authentification) et détermine si celui-ci est éligible à une telle demande (autorisation)
  • Etape n°3 : Le valet Key génère une clé d’accès pour le demandeur en appliquant des contraintes (niveau de permission, limitation dans le temps, limitation sur la source IP, …)
  • Etape n°4 : La clé générée est utilisée par le demandeur pour accéder à sa ressource

 

clip_image002

 

Voilà pour les fondamentaux du pattern. Au passage, je vous conseille la lecture des autres Cloud Design Patterns. Pour ce billet, nous allons mettre en place ce mécanisme pour générer des clés d’accès à un Storage Account, plus particulièrement un ensemble de tables.

Shared Access Signature versus Stored Access Policy

Premier sujet, comment générer des clés d’accès. Voilà un sujet que j’avais déjà abordé dans le billet « Shared Access Signature with stored Access Policy« . Avec le SAS ou les Stored Access Policies, on est capable d’assigner les permissions fines avec même la possibilité d’imposer des restrictions (restriction dans le temps, des services de stockage sollicités, voire même du contenu accédé). Bref, on a le choix. Après, il faut retenir quelques points :

  • On ne peut pas révoquer une Shared Access Signature (SAS), il faut renouveler les clés primaires & secondaires, donc révoquer toutes les SAS simultanément
  • Révoquer une Stored Access Policy implique de révoquer toutes les clés SAS liées à une policy.
  • Un Storage Account ne peut avoir que cinq Stored Access Policies
  • Shared Access Signature et Shared Access Policy peuvent s’appliquer par type d’usage (Table, Blog, Queue, File, …)
  • Une Shared Access Signature peut s’appliquer à un ensemble (toutes les tables) alors qu’une Stored Access Policy ne s’appliquera qu’à une seule table

 

Pour ce billet, nous allons mettre en place notre Key Valet en utilisant une Shared Access Signature. C’est un choix de ma part car j’ai besoin de générer des clés pour toutes les tables contenues dans un Storage Account et non pas pour chaque table. C’est ce que nous rappelle le portail.

clip_image003

 

Choix entre Consumption Plan versus App Service Plan

C’est un des avantages des Function App, la capacité à provisionner automatiquement l’infrastructure à la demande. Sur le papier, c’est bien. En plus cela rend notre API gratuite. Le défaut, c’est que si c’est gratuit, c’est tout sauf performant. Pour rappel un App service Plan Free repose sur des cœurs CPU non dédiés et avec uniquement 1Go de mémoire vive. Pour ces raisons, je provisionne notre Function App en utilisant un App Service Plan existant avec un SKU Standard 1, le minimum vital à mon sens pour partir en production (et encore).

clip_image004

 

Au passage, je vais réutiliser le Storage Account créé pour l’occasion pour notre Valet Key.

 

Besoin d’une identité ?

Dans mon billet « Azure Functions – Introduction au ServerLess en PowerShell » mon Azure Function j’avais précisé la démarche pour permettre à mon Azure Function de disposer d’une identité lui permettant de manipuler des ressources dans Azure. Depuis ce billet, la notion de Managed Identity Service est apparue. Cela simplifie grandement la mise en œuvre.

Question : Ais-je besoin d’une identité (et donc d’un Service Principal) pour accéder à Azure ? Beaucoup vont répondre oui en pensant qu’il faut nécessairement s’authentifier auprès d’Azure pour ensuite demander l’accès aux clés de stockage avec la commande PowerShell Get-AzureRmStorageAccountKey. D’un point de vue technique, c’est vrai, on peut extraire l’information mais combien cela coute-t-il en termes de performance ? Dans le contexte de notre démonstration sur le Key Valet, nous allons volontairement nous en passer, c’est un sujet sur lequel nous reviendrons ultérieurement.

Choix du langage ?

On ne va pas faire de mystère, je vais pas sortir un C# du chapeau (je laisse cela aux experts). Le but de ce billet, c’est de démontrer qu’on peut écrire des API en PowerShell, dont acte, …

clip_image005

 

A ce jour, le support de PowerShell est expérimental (je confirme). Au moment de l’écriture de ce billet, c’est la version 4.0 qui est proposée dans Function App. C’est un point à prendre en considération dans le développement de vos scripts (amère expérience, …). A ce jour seul trois types de déclencheurs sont disponibles pour Azure Function en PowerShell :

  • Le déclencheur HTTP
  • Le déclencheur sur planification
  • Le déclencheur sur injection d’un message dans une Azure Queue d’un Storage Account

 

Le but de ce billet étant d’écrire une API en PowerShell, c’est donc le choix HTTPTrigger qui a été retenu.

Authentification auprès du Key Valet

Mon API sera accessible publiquement via une URL. Si tout le monde peut demander des clés d’accès pour mes Azure Table, ça pose un sérieux problème. C’est pour cela que Function App propose trois niveaux d’authentification :

  • Function : Secret d’accès propre à chaque Azure Function
  • Anonymous
  • Admin : Secret d’accès partagé par toutes les Azure Function hébergées dans une même instance Function App.

clip_image006

 

Dans mon contexte, j’ai retenu le niveau « Function ». Au final, L’URL de mon API est la suivante. Après, on peut aussi exploiter les fonctionnalités natives d’Azure Web App (Azure Active Directory, Google, Microsoft Live, …) mais aussi Azure API Management Gateway.

clip_image007

 

Pour ceux qui ont suivi, notre API est prête pour son premier appel.

 

Premier appel

Avec notre choix HttpTrigger, nous avons automatiquement un premier exemple de code PowerShell pour notre API. Cela se passe de commentaire, c’est un Hello World.

clip_image008

 

Ce qui est bien, c’est que cela permet de poser les bases de l’appel à notre API. API développée en PowerShell, appel en PowerShell, logique :

$HttpRequestUrl= « « 

$parameters = @{name=’SimplebyDesign’}

$json = $parameters |ConvertTo-Json

Invoke-RestMethod -Uri $HttpRequestUrl -Method POST -ContentType ‘application/json’ -Body $json

clip_image009

 

Passons au PowerShell qui tache

En fait, il ne tache pas tant que cela. On commence par récupérer deux variables dans les Application Settings. Celles-ci nous permettent de construire un contexte d’accès à notre Storage Account (commande New-AzureStorageContext). De là, il n’y a plus qu’à générer une clé SAS (commande New-AzureStorageAccountSASToken). Dans le contexte de mon besoin, mon Key valet doit me générer des clés permettant :

  • L’accès à un seul service dans le Storage Account : Table
  • La permission obtenue doit être limitée au stricte nécessaire pour lire les données dans les Azure Table
  • La permission doit être limitée dans le temps
  • L’utilisation du protocole HTTPS doit être obligatoire

 

$StorageAccountName = $env:StorageAccountName

$key = $env:StorageKey

$startdate = Get-date

Try

{

$StorageContext = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $key

[DateTime]$ExpirityTime = (Get-Date).AddMinutes(15)

$sastoken = New-AzureStorageAccountSASToken -Service Table -ResourceType Service,Container,Object -Permission « rl » -Context $StorageContext -Protocol HttpsOnly -ExpiryTime $ExpirityTime

[System.IO.File]::WriteAllText($res,$sastoken)

« Generated $([math]::round((New-TimeSpan -Start $startdate -End (get-date)).TotalSeconds,2)) TotalSeconds »

}

Catch

{

Out-File -Encoding Ascii -FilePath $res -inputObject « INTERNALERROR »

}

 

Petit point d’attention : Pour ceux qui se posent la question pourquoi c’est si compliqué pour retourner le résultat avec la commande : « [System.IO.File]::WriteAllText($res,$sastoken) « . Un Simple Out-File fait la même chose. Oui et non. En PowerShell 4.0 a cette fâcheuse manie d’ajouter un retour à la ligne, ce qui fausse le résultat retourné à l’appelant. Dans PowerShell 5.0, on a bien un paramètre « -NoNewline ». Problème, c’est du PowerShell 4.0

clip_image010

 

Le goudron et les plumes ?

Mon API est prête. Elle a même déjà délivré sa première clé. Certains ont déjà compris que l’Access Key à mon Storage Account est référencé en clair dans les Applications settings et sont déjà parti chercher le goudron et les plumes.

clip_image011

 

Oui, il aurait été possible de ne pas coder l’information en clair dans les Application Settings et d’utiliser un Key Vault. Avec un Service Principal on aurait pu s’authentifier auprès d’Azure et accéder aux Access Keys. C’est ce dernier point qui m’a posé problème. Lorsque j’ai mis en place mon API pour une application que je développe, j’ai été surpris par le temps de réponse moyen : 20 secondes en pleine charge. WTF !!!!!!!!!!!

Heureusement, pour comprendre, j’avais réalisé l’intégration de mon Azure Function App avec Application Insights histoire d’avoir de la télémétrie : Data Driven Decision. Aussi étrange que cela puisse paraître, lorsqu’on consomme directement l’Access Key au lieu de l’extraire, on obtient une performance bien différente comme illustré ci-dessous :

clip_image012

 

Sur la même ligne temporelle on peut constater le niveau de réponse moyen avant et après optimisation. Sur un appel individuel on passe de quatre secondes à une seconde. Mon application est fortement consommatrice de l’API. Il y a donc beaucoup d’accès concurrentiels et mon choix de n’avoir qu’une seule instance de l’App Service Plan en SKU S1 n’aide pas à la performance. Pourtant, une fois la mise à niveau réalisée, la performance de l’API est restée constante : Autour d’une seconde.

Une amélioration serait d’utiliser Managed Identity Service pour ensuite accéder à un Key Vault. Je rentre pas dans le détail, d’autres ont exploré le sujet : « Enabling and using Managed Service Identity to access an Azure Key Vault with Azure PowerShell Functions« . A première vue, cela ne dégraderait pas trop les performances.

Consommons notre Valet Key

Nous avons notre Valet Key, ne nous manque plus qu’une application qui le consomme. Je suis allé au plus simple. L’appel à l’API n’a pas changé. La clé SAS nous est retournée, pour être consommée afin de générer un contexte d’accès au stockage. De là, il n’y a plus qu’à accéder à une table dans le Storage Account :

$HttpRequestUrl= « « 

$token = Invoke-RestMethod -Uri $HttpRequestUrl -Method POST -ContentType ‘application/json’

$newcontext = New-AzureStorageContext -StorageAccountName « tpserverlessstorage » -SasToken $token

Get-AzureStorageTable -Name TestTable -Context $newcontext

clip_image013

 

Voilà un exemple concret d’utilisation des Azure Function App. Après, on peut grandement l’améliorer (méthode d’authentification, différenciation des niveaux d’accès, exposition avec Azure API Management Gateway, utilisation du Key Vault, …)

 

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)