Archives de catégorie : Azure

Ma session au Powershell Saturday 2018

Ce samedi s’est déroulé l’édition française du PowerShell Saturday dans les locaux de Cellenza. Pendant, cette édition, j’ai eu l’occasion de présenter l’avancement sur Resource Group As a Service. Resource Group As a Service est un sujet que j’avais déjà présenté lors du Global Azure Bootcamp de 2018.

clip_image001

A l’époque, on était plus proche du PoC of Concept, entre temps, le développement a beaucoup avancé. Aujourd’hui, nous sommes maintenant plus proche du Minimum Viable Product. L’objectif de cette session n’était pas de présenter la solution en elle-même mais ce que son développement m’a permis d’apprendre sur Azure Function, Azure Automation et sur PowerShell lui-même. C’est donc plus une session de retour sur expérience.

Pour ceux que cela intéresse, la présentation ainsi que les exemples PowerShell présentés sont disponibles à cette URL.

 

Benoît – Simple and Secure by Design but Business compliant

Cloner des machines virtuelles dans Azure

Avec l’arrivée des Managed Disks, il a été introduit la capacité à cloner ces objets sous forme d’objets Snapshots. Après avoir pas mal joué avec j’y vois plusieurs avantages :

  • Recréer une machine virtuelle à partir d’un snapshot est bien plus rapide que de la restaurer depuis Azure Site Recovery (60Mb/S pour la vitesse).
  • Changer le nom des managed Disks histoire de s’aligner avec notre politique de nommage (genre après une bascule depuis On-Premises avec Azure Site Recovery)
  • Changer le nom de l’objet machine virtuelle (pas le nom du système d’exploitation)
  • Changer la performance des disques utilisés
  • Relocaliser les Managed Disks dans d’autres groupes de ressources (ca ne se déplace pas encore mais on peut cloner)
  • Réaliser une sauvegarde rapide et être en mesure de reconstruire rapidement (updates, opérations de maintenance critiques, mises en production le vendredi, …)
  • Changer l’Availability Zone / Availability Set / région Azure de la machine virtuelle
  • Relocaliser une machine virtuelle sur un autre Virtual network

 

Bref, il y a plein de raisons qui font que cloner une machine virtuelle peut être intéressant. Dans le détail des opérations voilà comment cela se présente :

  • Etape n°1 : Désallouer la machine virtuelle
  • Etape n°2 : Créer des snapshot des managed Disks
  • Etape n°3 : Créer des Managed Disks à partir des snapshots
  • Etape n°4 : Créer une nouvelle machine virtuelle
  • Etape n°5 : Ajouter les disques de données
  • Etape n°6 : Suppression des Snapshots

Etape n°1 : Désallouer la machine virtuelle

Même si l’opération peut être réalisé à chaud, on va quand même désallouer la machine virtuelle histoire de créer une image consistante de tous les Managed Disks. C’est un snapshot de chaque Manage Disks qu’il faudra réaliser. Rappel, désallouer ne veut pas dire shutdown dans le système d’exploitation.

 

Etape n°2 : Créer des snapshot des managed Disks

Nous allons créer autant d’objets Snapshots que de Managed Disks associés à notre machine virtuelle. Le seul disque que l’on ne pourra pas snapshoter. Commencez par identifier les noms Managed Disks associés à votre machine virtuelle. Dans l’exemple ci-dessous ma machine virtuelle comprend un disque OS et un disque de données.

clip_image001

 

Pour chaque Managed Disk, on va utiliser le bouton « Create Snapshot » comme illustré ci-dessous :

clip_image002

 

L’objet Snapshot sera créé dans la même région Azure. Par contre, il est tout à fait possible de créer l’objet dans un Resource Group différent. On peut même change la performance. As-t-on réellement besoin d’avoir notre snapshot en SSD?

clip_image003

Etape n°3 : Créer des Managed Disks à partir des snapshots

Il n’est pas possible de créer une machine virtuelle avec des Snapshots. On doit créer des Managed Disks à partir de nos snapshots.

clip_image004

 

Ici encore, ce qui est intéressant, c’est de pouvoir créer le Managed Disk dans une région Azure, une autre Availability zone. Vu que nous créons l’objet nous pouvons réappliquer notre charte de nommage (ceux qui ont migré des machines virtuelles depuis On-Premises me comprendrons, …).

clip_image005

 

Une fois l’opération terminée, nous devirons avoir autant de Managed Disks que de snapshots comme illustré ci-dessous :

clip_image006

Etape n°4 : Créer une nouvelle machine virtuelle

Avec des disques, nous pouvons créer une nouvelle machine virtuelle. Pour cela nous allons reprendre le Managed Disk contenant notre système d’exploitation. Nous allons utiliser le bouton « Create VM ».

clip_image007

 

Pour la création de la machine virtuelle, il n’y a pas grand-chose à dire.

clip_image008

 

Par contre, à cette étape, il y a un point intéressant. Nous pouvons redéfinir l’Availability zone, voire même l’Availability Set. Il y a même un bonus avec la possibilité de changer à quel Virtual Network la nouvelle carte réseau sera associée.

clip_image009

Etape n°5 : Ajouter les disques de données

N’oubliez pas de redéclarer vos disques de données et de sauvegarder la configuration.

clip_image010

Etape n°6 : Suppression des Snapshots

Conserver les snapshots n’a pas de sens sur le long terme. C’est du stockage facturé, tout comme les Managed Disks. Pensez donc à les supprimer dès que le clonage sera terminé.

clip_image011

 

 

Benoît – Simple and secure by design but Business compliant

Arretons de stocker os tokens Azure avec le module Powershell

Ca faisait quelques temps que j’avais remarqué un comportement « étrange » de certains de mes scripts Azure. Pour beaucoup d’entre eux, je commence par m’assurer avec un Get-AzuremContext que je suis bien authentifié. A ma grande surprise, je découvre que oui, pourtant, je n’ai renseigné aucun credential. Magie ? Nan. J’ai creusé un peu plus le sujet pour redécouvrir le module AzureRM.Profile. Ma recherche m’a amenée sur cette page : Automatic-Context-Autosave avec la découverte de quelques commandes inconnues :

clip_image001

 

Trois commandes ont attiré mon attention :

  • Disable-AzureRmContexteAutoSave
  • Enable-AzurermContextAutosave
  • Get-AzureRmContextAutoSaveSettings

J’ai donc commencé par Get-AzureRmContextAutoSaveSettings avec une surprise. Mes tokens sont bien conservés dans mon profil, c’est la configuration par défaut.

clip_image002

 

En ce qui me concerne, d’un point de vue sécurité, c’est moche. D’une part, je me balade avec les tokens de mes clients (MFA n’est pas encore chez tous et cela ne résout pas toujours le problème). C’est dommage qu’on fasse attention à utiliser des navigateurs en mode privé pour se connecter à Azure si le module PowerShell ignore le même ce concept. D’autre part, cela induit un risque. J’ai tendance à utiliser beaucoup de scripts chez mes différents clients. Avec cette fonctionnalité, je risque de travailler sur la mauvaise souscription sans m’en rendre compte avec un impact tout aussi industriel que le script.

Pour cette raison, j’ai pris l’habitude de désactiver cette fonctionnalité avec la commande Disable-AzureRmContextAutoSave comme illustré ci-dessous :

clip_image003

 

Maintenant, il n’y a plus de risque pour moi.

clip_image004

 

Benoît – Simple and secure by design but Business compliant

Un peu de PowerShell pour gérer les Network Security Groups

De temps en temps, j’ai des clients avec des challenges. Quand on mélange Azure et PowerShell, il y a risque que je réponse présent (même si finalement, je vais me faire mal). Je vous partage donc le challenge du moment. Un de mes clients est en phase de montée en puissance sur Azure avec une exigence, un contrôle strict des flux réseaux entrants et sortants, tout comme il le pratique On-Premises. Tout de suite, ça va faire beaucoup des règles dans les Network Security group. Autant dire tout de suite que le portail ne va pas être d’une grande aide sur le sujet.

Challenge intéressant, challenge Accepté, voici AzureImportNSG.PS1. Ce script PowerShell permet de :

  • Créer l’objet Network Security group (réalise une sauvegarde si existe déjà pour le rollback)
  • Injecter les règles en provenance d’un fichier XML
  • S’assure de la mise en place de la journalisation des flux dans votre instance Log Analytics préférée

clip_image001

Que reste-t-il à faire alors ? Juste lier le Network Security group à l’objet Subnet. Simple and Secure by design ! Et comme il faut pouvoir retravailler les règles, s’il y a un script d’import, il y a aussi un script d’export pour générer notre fichier XML.

clip_image002

Avec cela, plus d’excuse pour ne pas avoir des réseaux en béton. Les deux scripts sont disponibles sur mon repo GitHub.

Benoît – Simple and secure by design but Business compliant

Resource Group As a service – Mise en place du stockage

Après, un précédent billet pour poser les bases, il est temps de poser les fondations pour nos API. Première brique, un Resource Group. L’intégralité des ressources Azure sont stockées dans un groupe de ressources : New-AzureRMResourceGroup -ResourceGroupName ResourceGroupAsAService -Location « West Europe »

clip_image001

 

Côté stockage, j’ai identifié trois usages. Etant donné que nous allons avoir deux instances du service Azure Function, nous avons déjà deux instances de Storage Account. Techniquement, on peut très bien mutualiser mais je préfère isoler. Cela ne change rien au niveau de la facturation. La dernière instance, sera destinée à stocker les données utilisées par les API (Azure Table). La nature de ces données étant relativement sensibles, j’ai volontairement retenu une instance dédiée. Ci-dessous la commande PowerShell pour créer la première instance du Storage Account :

New-AzureRmStorageAccount -ResourceGroupName resourcegroupasaservice -AccountName resourcegroupasaservice1 -Location « West Europe » -SKUName « Standard_LRS » -Kind StorageV2 -EnableHttpsTrafficOnly $True

clip_image002

 

Puis la seconde instance : New-AzureRmStorageAccount -ResourceGroupName resourcegroupasaservice -AccountName resourcegroupasaservice2 -Location « West Europe » -SKUName « Standard_LRS » -Kind StorageV2 -EnableHttpsTrafficOnly $True

clip_image003

 

Et enfin la troisième instance : New-AzureRmStorageAccount -ResourceGroupName resourcegroupasaservice -AccountName resourcegroupasaservice3 -Location « West Europe » -SKUName « Standard_LRS » -Kind StorageV2 -EnableHttpsTrafficOnly $True

clip_image004

 

Nous allons passer un peu de temps avec cette troisième instance puisqu’elle va contenir des Azure tables qui vont être utilisées par les API. Commençons par récupérer les clés de cette instance de Storage Account : $keys = Get-AzureRmStorageAccountKey -ResourceGroupName resourcegroupasaservice -Name resourcegroupasaservice3

$keys

clip_image005

 

Nous allons utiliser la première clé pour nous créer un contexte pour créer les Azure tables suivantes :

  • AuthorizedCallers
  • AuthorizedEnvironments
  • AuthorizedIAMTemplates
  • DefaultCostCenter

 

$context = New-AzureStorageContext -StorageAccountName resourcegroupasaservice3 -StorageAccountKey $keys[0].value

« AuthorizedCallers AuthorizedEnvironments AuthorizedIAMTemplateRole AuthorizedPolicyAssignment DefaultCostCenter ».Split() | new-AzureStorageTable -Context $context

clip_image006

 

Avoir des tables c’est bien mais on a aussi besoin d’un peu de contenu. Pour cela, nous allons faire appel au module PowerShell AzureRmStorageTable disponible sur la PowerShell Gallery. Commençons par poser quelques bases :

Install-Module -Name AzureRmStorageTable

$resourcegroupname = « resourcegroupasaservice »

$Storageaccountname = « resourcegroupasaservice3 »

$location = « West Europe »

clip_image007

 

Jusqu’ici rien de bien sorcier. Mes tables sont localisées dans le groupe de ressources resourcegroupasaservice3, lui-même dépendant du Storage Account resourcegroupasaservice. Continuons avec l’alimentation de la première table Authorizedcallers. Celle-ci permet aux API de déterminer :

  • Si l’utilisateur est accrédité à utiliser le service ou non pour une souscription Azure donnée
  • La liste des valeurs acceptées pour le tag CostCenter qui sera associé au groupe de ressources à créer en fonction du demandeur
  • La liste des valeurs acceptées pour le tag Environnment qui sera associé au groupe de ressources à créer en fonction du demandeur
  • La liste des régions azure autorisées pour la création du groupe de ressources en fonction du demandeur

 

$table = Get-AzureStorageTableTable -resourceGroup $resourceGroupname -tableName « Authorizedcallers » -storageAccountName $Storageaccountname

$partitionkey = (Get-AzureRmContext).Subscription.ID

$rowkey = ‘Simplebydesign@bsautierehotmail.onmicrosoft.com’

Add-StorageTableRow -table $table -partitionKey $partitionKey -rowKey $rowkey -property @{« Authorized »= »true »; »AuthorizedCostCenters »= »ITDEPT,FINANCEDEPT »; »AuthorizedDefaultRegion »= »WestEurope »;

« AuthorizedEnvironments »= »TESTS »; »AuthorizedRegions »= »WestEurope,NorthEurope »}

clip_image008

 

L’utilisateur indiqué est déclaré autorisé à demander la création d’un groupe de ressources dans la souscription courante avec des valeurs prédéfinies pour les tags CostCenter et Environment. Voilà le mécanisme d’autorisation.

Passons à la table AuthorizedEnvironments. Lors de l’appel à l’API Request-ResourceGroup, l’appelant peut préciser quelle valeur associer au tag Environnement (dans la liste des valeurs autorisées). Si l’appelant ne précise aucun environnement en particulier, alors, l’API va consulter cette table pour déterminer la valeur par défaut.

$table = Get-AzureStorageTableTable -resourceGroup $resourceGroupname -tableName « AuthorizedEnvironments » -storageAccountName $Storageaccountname

$partitionkey = (Get-AzureRmContext).Subscription.ID

$rowKey = « TESTS »

Add-StorageTableRow -table $table -partitionKey $partitionKey -rowKey $rowkey

clip_image009

 

Maintenant, si nous appelions l’API Request-ResourceGroup sans préciser de valeur pour le tag Environnement, la valeur par défaut retenue serait « TESTS ». Pour le tag CostCenter, c’est le même principe. Si l’appelant ne précise pas une des valeurs qui lui sont autorisées pour la création d’un groupe de ressources dans une souscription Azure donnée, alors, l’API ira chercher la valeur par défaut à utiliser dans la table DefaultCostCenter

$table = Get-AzureStorageTableTable -resourceGroup $resourceGroupname -tableName « DefaultCostCenter » -storageAccountName $Storageaccountname

$partitionkey = (Get-AzureRmContext).Subscription.ID

$rowkey = ‘Simplebydesign@bsautierehotmail.onmicrosoft.com’

Add-StorageTableRow -table $table -partitionKey $partitionKey -rowKey $rowkey -property @{« CostCenter »= »ITDEPT »}

clip_image010

 

Ceci clos la partie stockage. Prochaine étape, on s’attaque à la gestion des secrets dans la solution avec du KeyVault à tout va.

 

BenoîtS – Simple and Secure by Design but Business compliant

Resource Group as A service – Introduction

Lors de ma session sur la gouvernance Azure au Global Azure Bootcamp, j’avais fait la démonstration du premier prototype de mon ensemble d’API « Resource Group as a Service ». Pour rappel, l’idée générale était de répondre à la problématique de la gouvernance Azure, en particulier au niveau des groupes de ressources. Le but de Resource Group as a service est de proposer un intermédiaire qui prend en charge la création et la configuration des Resources Groups pour le compte des demandeurs. Le développement a maintenant bien avancé (Pas encore une V1 mais au moins une Beta stable). Cette série de billets sera consacrée à cette API. Nous allons commencer par les Design Principles retenus pour ce développement.

Design Principles / contraintes imposées

  • Langage de développement : OK, c’est pas un vrai Design Principle. Normalement, le langage utilisé ne rentre pas en ligne de compte. Dans mon contexte, c’est plus ma contrainte imposée pour permettre un développement simple : PowerShell
  • Support de nos API : Azure Function. C’est mon choix. Pas la peine de mettre en œuvre une instance de Service Fabric pour porter quatre API. Pour rappel, une Azure Function a un temps d’exécution limité à 300 secondes, ce qui sera largement suffisant pour créer un groupe de Ressource. Ici aussi ce choix implique une contrainte car :

    • A ce jour le support de PowerShell en encore expérimental dans Azure Function
    • Le niveau de performance ne sera pas équivalent si on avait retenu C#
  • Authentification : Chaque appel à nos différentes API devront être authentifiés. Dans le contexte d’Azure Function, le choix le plus simple a été retenu avec la prise en charge de l’authentification via Azure AD. On reviendra sur ce choix ultérieurement car il aura quelques conséquences
  • Autorisation : L’authentification et l’autorisation sont deux choses bien distinctes. Ce n’est pas par ce qu’on va avoir accès aux API que tout nous sera autorisé. Dans mes Design Principles, j’ai retenu les points suivants :

    • Chaque utilisateur devra être enregistré comme autorisé pour pouvoir soumettre une demande de création de groupe de ressource
    • Le niveau d’autorisation sera individualisé au niveau de chaque souscription prise en charge par l’API
    • Le niveau d’autorisation prendra la notion de CostCenter au niveau de chaque souscription. Dans notre contexte, c’est un TAG positionné au niveau du groupe de ressources. Chaque utilisateur sera accrédité à un ou plusieurs codes d’imputation comptables qui sera associé au tag CostCenter
    • Le niveau d’autorisation prendra en compte la notion d’environnement au niveau de chaque souscription. Dans notre contexte, c’est un TAG positionné au niveau du groupe de ressources. Chaque utilisateur sera accrédité à demander la création d’un groupe de ressources avec une sélection de valeurs pour le TAG environnement.
    • Le niveau d’autorisation prendra en compte la notion de région pour limiter la création du groupe de ressources pour un ensemble de régions Azure donnée
  • Contexte d’exécution : Pour créer des ressources Azure, on a besoin d’une identité. Hors de question de coder des comptes / mots de passe ou même des clés d’accès au stockage. Choix a été fait d’utiliser la fonctionnalité Managed Identity Services pour nos Azure Function. Cela a quand même quelques implications :

    • Toutes les API regroupées dans une même instance du service Azure Function utiliseront donc la même identité
    • L’identité en question prendra la forme d’une application Azure AD et d’un Service Principal, lequel devra disposer des rôles nécessaires
  • Support de multiples souscriptions : Un contexte d’exécution propre à chaque souscription Azure sera nécessaire. Etant donné que ces multiples souscriptions peuvent ne pas utiliser le même tenant Azure AD comme fournisseur d’authentification, il ne sera pas possible d’utiliser l’identité des Azure Function (Managed Identity Services). Pour adresser cette problématique, il a été retenu que la solution associer un contexte d’exécution pour chaque souscription. Les secrets nécessaires pour chaque souscription seront stockés dans une instance distincte du service KeyVault. Cette séparation des secrets permettra de proposer aux gestionnaires des souscriptions de mettre à jour eux même le contexte de chaque souscription
  • Informer les consommateurs de leurs droits : Les principes retenus pour le mécanisme d’autorisation font que les consommateurs pourront disposer de privilèges bien différents selon les souscriptions. Afin de les informer des privilèges qui leurs sont associés, des API spécifiques seront proposées afin leur permettre de déterminer :

    • Les souscriptions auxquelles ils ont accès
    • Les tags Environnements qu’ils peuvent utiliser au sein d’une souscription Azure donnée
    • Les tags CostCenters qu’ils peuvent utiliser au sein d’une souscription Azure donnée
    • Les régions Azure qu’ils peuvent utiliser au sein d’une souscription Azure donnée
  • Stockage des données : Dans notre contexte, il a été retenu d’utiliser le service Azure Table pour gérer le mécanisme d’autorisation des API
  • Principe du moindre privilège : Principe essentiel pour le développement des API. A chaque fois que ce sera possible, le niveau d’accès de « moindre privilège » sera utilisé.

 

Ressource créée dans la souscription

  • Groupe de ressources : C’est quand même le but. Je n’ai pas encore prévu d’intégrer de contrôle sur le charte de nommage
  • Tags : Les groupes de ressources créé par l’API seront configurés avec un ensemble de Tags obligatoires et d’autres optionnels. Le notions d’environnements et de CostCenter sont eux obligatoires. L’utilisation de certains tags sera contrainte par les autorisations précisées dans les différentes tables Azure Table.
  • RBAC : Selon la configuration mise en place au niveau des Azure table, les groupes Azure AD seront positionnés avec des rôles builtin ou custom en fonction de la souscription Azure
  • Policies : Selon la configuration documentée dans les Azure Table, une ou plusieurs Azure policies seront positionnées en fonction de la souscription Azure.

 

Liste des API

Resource Group as A service, ce n’est pas qu’une seule API. En fait, c’est un peu plus compliqué que cela. Le tableau ci-dessous résume les API qui seront proposées via Azure Function :

Nom

Accès public

Méthode d’authentification

Managed Identity Service

Get-AuthorizedSubscription Oui Azure AD Activé
Get-AuthorisedEnvironments Oui Azure AD Activé
Get-AuthorizedCostCenters Oui Azure AD Activé
Get-AuthorizedRegions Oui Azure AD Activé
Request-ResourceGroup Oui Azure AD Activé
Get-ValetkeyForAzureTableRead Non Function Key Activé

 

Pour les quatre premières API, on comprend que c’est l’implémentation de la fonctionnalité permettant aux consommateurs de déterminer de quels privilèges ils disposent au sein des souscriptions prises en charge. Logiquement, ces APIs sont directement accessibles par les consommateurs pour peu qu’ils soient correctement authentifiés par Azure AD. Il en est de même pour l’API Request-ResourceGroup as a Service. Par contre, pour l’API Get-ValetkeyForAzureTableRead, la méthode d’authentification sera différente. Cette API sera utilisée pour respecter le principe du moindre privilège. Toutes les données relatives au mécanisme d’autorisation seront stockées dans des Azure Table au sein d’un Storage Account. La clé primaire de ce Storage Account sera bien stockée dans un Key Vault mais nous allons utiliser le Cloud Design Pattern Valet-Key pour générer un contexte d’accès limité à la permission Read sur les Azure Table, avec une durée de vie limitée.

clip_image001

 

Cette API ne sera pas consommée directement par les consommateurs mais en tant qu’API interne pour générer une clé d’accès au stockage. Il aurait été possible d’utiliser Azure AD comme méthode d’authentification (avec Managed Service Identity) mais cela introduisait un risque. Dès lors qu’un consommateur de l’API connait l’URL et se présente avec une identité Azure AD vérifiée, il aurait eu la possibilité de se générer des clés d’accès au stockage. Clairement une fausse bonne idée d’un point de vue sécurité. Pour cette raison, nous allons introduire une segmentation au niveau des API. Cette segmentation permet :

  • D’isoler une API qui donne accès à des informations sensible (il sera nécessaire de connaitre la Function Key qui sert de secret d’accès)
  • De distinguer les identités (Managed Identity Services) qui vont accéder aux instances de KeyVault et donc respecter le principe du moindre privilège

Quand on sait que toutes les API hébergées dans une même instance Azure Function partagent la même identité Managed Identity Service et la même méthode d’authentification, il apparait donc nécessaire d’introduire deux instances du service Azure Function pour héberger nos API.

  • Resourcegroupasaservicepublicapi
  • Resourcegroupasaserviceinternalapi

Voilà pour l’introduction. Dans le prochain billet, on va rentrer dans le dur du sujet.

 

BenoîtS – Simple and Secure by Design but Business compliant

Authentifiez vos Azure Function avec Azure AD

De retour sur Azure Function. Après avoir exploré les possibilité d’intégration entre Azure Function et Azure API Management, revenons dans Azure Function avec un aspect sécurité. Comment sécuriser l’accès à vos API? Techniquement, Azure API Management nous propose ce type de service avec la capacité de supporter de multiples fournisseurs, cependant lorsqu’on a qu’une seule Azure Function à exposer, ça fait un peu lourd et cher. On va donc voir ce que nous propose App Service out of the box que nous allons aborder en plusieurs étapes :

  • Mise en place des fondations
  • Prise en charge de l’authentification Azure AD
  • Bonus : Postman

 

Mise en place des fondations

La première chose dont on va avoir besoin, c’est effectivement d’une instance du service Azure Function. Jusque-là, rien de bien compliqué.

clip_image001

De là, il ne nous reste plus qu’à mettre en place notre Azure Function à sécuriser. Celle-ci sera configurée sans authentification. Elle sera donc librement accessible à toute personne connaissant l’URL. La fonction d’authentification sera déportée dans Azure AD plus tard.

clip_image002

 

Pour finir notre mise en place, on a juste besoin d’une petite dose de code, de PowerShell en fait. Ce qu’il faut comprendre, c’est que Web App sui sert de socle à Azure Function va faire la majorité du travail pour nous. Tout ce que nous attendons, c’est l’identité de l’utilisateur connecté que nous allons retrouver dans les Server Variables dans notre session. Ce que va faire Azure Web App, c’est peupler des variables pour nous dont la variable « HTTP_X_MS_CLIENT_PRINCIPAL_NAME ». Attention, nous allons n’avoir que l’identité de l’appelant à notre Azure Function pas le claims obtenu (un peu plus de travail). En PowerShell, c’est aussi simple que cela :

$requestBody = Get-Content $req -Raw | ConvertFrom-Json

$result = Get-Variable -name REQ_HEADERS_X-MS-CLIENT-PRINCIPAL-NAME -ErrorAction SilentlyContinue

if ($result.name -ne $null)

{

Out-File -Encoding Ascii -FilePath $res -inputObject « Hello $($result.value) »

}

else

{

Out-File -Encoding Ascii -FilePath $res -inputObject « Unauthenticated. »

}

 

On peut tester immédiatement, l’authentification Azure AD n’étant pas encore en place, la variable n’existe pas pour l’instant. D’où le retour « Unauthentified ».

clip_image003

 

Prise en charge de l’authentification Azure AD

C’est maintenant que les choses se compliquent. On a bien une fonctionnalité « Authentication / Authorization ». Activer la fonctionnalité va impacter toutes les Azure Functions qui sont portées par l’instance de notre service.

clip_image004

 

Activer la fonctionnalité, c’est ce qu’il y a de plus simple. On va donc forcer l’authentification pour toutes les connexions et indiquer que nous allons utiliser le fournisseur Azure Active Directory.

clip_image005

 

Pour les besoins de la démonstration, on sa se contenter de la configuration Express. Maintenant challenge : avez-vous le privilège Administrateur général au niveau AD ? Si oui, alors on peut créer immédiatement l’application (et le service principal associé). Dans le cas contraire, il faudra demander à celui qui détient les droits de gérer cela pour vous pour ensuite sélectionner l’application Azure AD nouvellement déclarée. Dans notre contexte, je dispose des privilèges, on continue.

clip_image006

 

Ne surtout pas oublier de sauvegarder la configuration.

clip_image007

 

Derrière, on sait tout de suite que cela fonctionne, il suffit de tenter de réexécuter notre Azure Function. Il est clairement indiqué que l’authentification bloque l’utilisation du débugger.

clip_image008

 

Revenons sur la configuration de cette application déclarée dans Azure AD. Nous allons accorder à notre application des permissions déléguées pour autoriser l’authentification des utilisateurs et ne surtout pas oublier de sauvegarder la configuration.

clip_image009

 

Le message d’avertissement nous indique que dans la configuration actuelle aucun utilisateur de Tenant Azure Active Directory n’a accès à cette application. On a deux choix :

  • Une assignation manuelle pour chaque utilisateur ou collective (bouton Grant permissions)
  • Une assignation selon appartenance de groupe (Pas disponible avec une SKU Free d’Azure AD)

 

On va donc procéder à une activation massive pour tous les utilisateurs de notre Tenant Azure AD, que ceux-ci soient des utilisateurs dépendant du tenant ou simplement invités.

clip_image010

 

De là, on peut tester immédiatement (si on n’a pas limité le verbe GET dans notre Azure Function) en appelant simplement d’Azure Function depuis notre navigateur. Si nous avons été préalablement authentifiés, notre token sera utilisé. Sinon, une mire d’authentification nous sera proposée.

clip_image011

 

Bonus : Postman

Pour le bonus, pas de PowerShell. En fait, j’ai découvert qu’obtenir un token Oauth2 et le soumettre pour négocier un token pour s’authentifier auprès de notre Azure Function est à ce jour d’une complexité incroyable. Je me suis donc mis en quête d’une méthode plus simple pour tester mon Azure Function : Postman. C’est un client REST qui a pour avantage de prendre en charge Swagger pour générer la documentation associée à note API.

Avant de rentrer dans Postman, nous allons collecter quelques informations nécessaires. La première est l’identité de notre application Azure Active Directory. Celle-ci utilise l’identité d’un Service Principal (équivalent d’un compte de service). On va commencer par collecter l’identifiant unique de notre application déclarée dans Azure.

clip_image012

 

Passons à la suivante avec un peu de PowerShell pour retrouver l’identifiant unique de notre Tenant Azure Active Directory dans l’attribut TenantID :

$cred = get-credential

Connect-AzureAD -Credential $Cred

Get-AzureADCurrentSessionInfo | fl

clip_image013

 

Continuons en PowerShell en récupérant la ReplyURL de notre application enregistrée dans Azure Active Directory : Get-AzureRMADApplication -DisplayNameStartWith authenticatedaz

clip_image014

 

Maintenant, un peu de construction :

Retournons au niveau de notre application publiée dans Azure. Nous avons besoin de générer un secret qui sera utilisé lors de l’authentification. Pour les besoins de la démonstration, nous allons générer ce secret sans date d’expiration. La bonne pratique consiste à renouveler ces secrets régulièrement et bien sûr de ne pas partager un même secret entre plusieurs applications. Attention à copier la clé générée avant de quitter l’interface. Après-cela, elle ne sera plus divulguée.

clip_image015

 

Maintenant, tout se passe dans Postman. Si on tente le direct, on va clairement nous répondre que nous ne sommes pas autorisés.

clip_image016

 

En fait, on a besoin de construire une authentification Oauth2.

clip_image017

 

Ci-dessous la configuration à utiliser avec quelques explications :

  • Token name : Your choice
  • Grant Type : Authorization Code
  • CallBack URL : Celle que nous avons collectée
  • Auth URL : Celle que nous avons générée
  • Client ID : C’est l’Application ID de notre application (ne cherchez pas ClientID dans le portail, ça sert à rien)
  • Client Secret : Normalement, vous l’avez conservé
  • Scope : Inutile dans notre cas
  • State : Inutile dans notre cas
  • Client Authentication : Send client credentials in Body

 

clip_image018

 

Ne reste plus qu’à cliquer sur « Request Token » pour obtenir notre Access Token. Une mire d’authentification Azure AD nous sera proposée. Attention, dans la configuration par défaut d’Azure AD, la durée de vie est d’une heure. Après, c’est personnalisable : Configurable token lifetimes in Azure Active Directory (Public Preview). Et cela fonctionne.

clip_image019

 

C’est une utilisation très basique de Postman car l’outil va bien plus loin que cela, en particulier pour réaliser des tests unitaires, voire même des tests automatiques.

Benoît – Simple and secure by design but business compliant

Azure Serial console (Preview)

Lorsque je délivre mes cours Azure, quand on aborde le IaaS, je rappelle toujours que nous client consommons des ressources mises à disposition par notre fournisseur sur sa plateforme. Cette plateforme est prévue pour tirer parti de la moindre ressource mise à disposition. Dans le contexte d’Azure, cela signifie que par défaut nos ressources sont hébergées sur des hosts mutualisés. Les équipes de Microsoft ont pour charge de nous garantir une parfaite isolation entre les workloads des clients. D’un point de vue sécurité, il serait catastrophique pour Microsoft que deux de ses clients distincts puissent accéder aux ressources d’un autre sans accords explicit préalable.

Ce rappel fait, on commence à comprendre pourquoi la fonctionnalité « Azure Serial Console » n’est pas native. Quand un Windows ou un Linux ne démarre plus, on a besoin d’un accès direct au système d’exploitation. On-Premises, nos équipes peuvent nous permettre d’accéder à la vue console, que ce soit avec une solution de virtualisation (VMWARE, Hyper-V, …) ou en bare-metal (carte ILO). Dans le Cloud, c’est un peu plus compliqué. En fait, elle n’est disponible en preview que depuis aujourd’hui. Pour tout fournisseur cloud, développer ce type de fonctionnalité pose plusieurs challenges. Le plus important d’entre eux, c’est l’accès à l’infrastructure : Le fait que les clients n’ait pas accès à l’infrastructure Azure est une forme de garantie à destination des clients. S’ils n’ont pas accès aux Hosts Azure, on réduit le risque d’exposition à des failles 0 Day.

La fonctionnalité devrait être visible dans le portail Azure. Dans le blade Virtual Machines, on doit maintenant constater la présence de « Serial (Console Preview) ».

 

Le fait que la fonctionnalité soit visible dans votre portail ne veut pas dire qu’on peut l’utiliser immédiatement. Il y a nécessairement quelques prérequis :

  • L’activation de la fonctionnalité Boot Diagnostics
  • Disposer au minimum du rôle VM Contributor
  • Disposer d’un OS pour lequel la fonctionnalité a été préalablement activée.

Prenons les choses dans l’ordre.

 

Activation de la fonctionnalité Boot Diagnostics

Jusqu’à maintenant, cette fonctionnalité était nécessaire pour journaliser les opérations réalisées par la machine virtuelle pendant la phase de boot ainsi que pour prendre des captures d’écran de la vue console. Maintenant, c’est un prérequis pour l’utilisation de la fonctionnalité Serial Console.

clip_image002

Disposer au minimum du rôle VM Contributor

Heureusement que cette fonctionnalité n’est pas accessible à tout le monde. Effectivement, on peut utiliser le rôle « Built-In » Virtual Machine Contributor. Juste un bémol. Bien faire attention au scope lorsqu’on va assigner ce rôle et bien le limiter aux machines virtuelles pour lesquelles la fonctionnalité sera nécessaire.

Disposer d’un OS pour lequel la fonctionnalité a été préalablement activée.

La fonctionnalité est nativement disponible dans Windows et dans les principales distributions Linux du marché. Pour certaines, des actions supplémentaires sont nécessaire pour activer la prise en charge du Single user Mode.

Utilisation

Quand nos prérequis sont en place, voilà à quoi cela ressemble depuis le portail Azure pour une machine virtuelle de type Windows.

clip_image003

 

C’est maintenant que cela devient un peu plus subtil. La première chose, c’est de se créer un canal de communication avec la commande CMD. Celle-ci retourne à la fois un identifiant et un nom pour ce canal.

clip_image004

 

Mon canal identifiant 0001 a été créé avec le nom Cmd0001, ne reste plus qu’à nous y connecter avec la commande suivante :

CH -SN Cmd0001

clip_image005

 

Ne nous reste plus qu’à nous authentifier. Subtilité des systèmes d’exploitation Windows non raccordés au domaine, comment spécifier qu’on va s’authentifier avec un compte de la base SAM locale ? Réponse ci-dessous :

clip_image006

 

Si l’authentification est acceptée, nous devrions avoir une console comme illustré ci-dessous :

clip_image007

 

Conclusion

Le principal scénario d’usage concerne Active Directory. Maintenant qu’on dispose d’un mode console, on peut enfin reconfigurer un contrôleur de domaine pour démarrer en mode restauration d’annuaire et procéder à une restauration autoritaire de tout ou partie de l’Active Directory.

BenoitS – Simple and Secure by design but Business compliant (with disruptive flag enabled)

Azure Automation en panne de certificats?

Azure Automation, quand ça fonctionne, on ne s’en occupe plus. C’est un service que nous avons provisionné voilà presque un an, nous avons maintenant quantité de Runbooks qui s’exécutent régulièrement. C’est peut-être un service qui fonctionne tout seul mais de temps en temps y a un peu de maintenance et un matin, tout s’arrête de fonctionner. Bizarrement, tous les Runbooks semblent avoir le même problème d’authentification avec Azure, …

clip_image001

clip_image002

Tout de suite ça ressemble à un problème de certificat. Normalement, cela devrait faire tilt. Azure Automation référence des certificats (au moins deux) comme méthodes d’authentifications :

clip_image003

Et oui, même un certificat auto-signé ça expire. Le problème, c’est que ce sont ces certificats qui sont utilisés pour mettre en œuvre les objets connections que nous consommons dans le code PowerShell de nos Runbooks. Le portail Azure ne nous prévient pas (ou tout du moins pas encore) de l’imminence de la date fatidique. C’est une fois les certificats expirés qu’on constate le message suivant dans notre instance Azure Automation.

clip_image004

La solution la plus simple, c’est de suivre le processus pour renouveler les certificats pour chaque RunAs Accounts. Le portail vous proposera de renouveler les deux certificats.

clip_image005

C’est bien mais on peut faire mieux. Renouveler des certificats auto-signés, c’est prendre le risque d’oublier que l’année prochaine on va encore les oublier. Il est temps de reprendre en main le sujet et d’imposer nos propres dates de renouvellement de certificats auto-signés. Pour rappel, c’est un sujet que j’avais abordé dans le billet : Parlons d’identité avec Azure Automation. On peut même faire mieux, demander à notre équipe sécurité de nous fournir un certificat. Si leurs process sont bien faits, ils doivent vous prévenir de l’expiration des certificats qu’ils vous ont mis à disposition.

BenoitS – Simple and Secure by design but Business compliant (with disruptive flag enabled)

Managed Service Identity pour machines virtuelles Windows

Managed Service Identity est une réponse élégante au problème de la sécurité du contexte d’exécution de code pour des services comme Virtual Machines, Web App et Azure Functions pour ne citer qu’eux (pas encore disponible pour d’autres ressources providers pendant la phase de preview). Pour les machines virtuelles, c’est intéressant car cela va nous permettre de ne plus stocker de credentials directement dans la machine virtuelle pour exécuter du code. En fait, Managed Service Identity aurait pu trouver sa place dans un billet de 2016 : Cacher les mots de passe dans les scripts PowerShell.

Dans le contexte d’une machine virtuelle Azure, Managed Services Identity propose une API accessible localement uniquement. Un secret local sera présenté à cette API qui va ensuite nous permettre de récupérer un « Access Token » que nous allons pouvoir consommer pour accéder à différentes ressources dans Azure.

A ce jour, Managed Service Identity est en Preview. La fonctionnalité s’active dans le portail, au niveau du blade « Configuration » d’une machine virtuelle. Activer cette fonctionnalité va réaliser plusieurs opérations que nous allons tenter de détailler dans ce billet.

clip_image001

 

La partie la plus visible de Managed Service Identity, c’est la présence d’une VM Extension au sein de notre machine virtuelle. Voilà pour la surface des choses.

clip_image002

 

Pourtant, on ne parle pas encore d’identité. En fait Managed Service Identity a déjà fait le job (pour peu qu’on dispose des privilèges Azure AD). Si on explore un peu notre Azure AD, on constatera la présence d’un nouveau Service Principal au nom de notre machine virtuelle :

Get-AzureRMADServicePrincipal | Where {$_.displayname -eq « <Nom machine virtuelle> »} | Fl -Property *

clip_image003

 

Sujet intéressant, on peut constater que ce Service Principal utilise un certificat auto-signé comme méthode d’authentification. A ce stade, cela me pose un problème. Pour utiliser Managed Service Identity, il faut impérativement avoir le rôle d’administration le plus élevé dans Azure AD. Il n’est pas encore possible d’utiliser un Service Principal existant. Ce Service Principal, on peut l’utiliser pour positionner des permissions. Dans l’illustration ci-dessous, j’associe le rôle « Virtual Machine Contributor » pour permettre à la machine virtuelle de gérer les ressources de type machine virtuelle dans le groupe de ressources « TESTVSIVM ».

clip_image004

 

Maintenant, allons voir ce qui se passe au sein de la machine virtuelle. S’il y a une VM Extension installée, on doit en trouver la trace. En fait, on trouve même mieux que cela avec la configuration de l’API présente au sein de notre machine virtuelle. La VM Extension, c’est une API qui écoute en localhost sur le port 50432 (par défaut). D’un point de vue sécurité, c’est parfait.

clip_image005

 

En creusant un peu dans le même répertoire, on va mettre la main sur le Service Principal à consommer par la VM Extension :

[xml]$xmldocument = Get-Content C:\Packages\Plugins\Microsoft.ManagedIdentity.ManagedIdentityExtensionForWindows\1.0.0.10\RuntimeSettings\20180211172356Z.xml

$xmldocument.RDConfig.SecureIdentity

clip_image006

 

On connait l’identité, reste maintenant, le certificat associé au Service Principal. Logiquement, on le trouve dans le magasin personnel de d’ordinateur : Get-ChildItem Cert:\LocalMachine\My

clip_image007

 

Ne cherchez pas, la clé privée du certificat n’est pas exportable. Pour la suite, il faut prendre soin de disposer d’une version d’Azure PowerShell datant d’au moins Janvier 2018

clip_image008

 

Avant de pouvoir nous authentifier, nous devons demander un jeton d’accès auprès de l’API. C’est ce jeton d’accès que nous allons utiliser pour nous authentifier avec la commande Login-AzureRmAccount en spécifiant :

  • L’utilisation d’un AccessToken
  • L’identité que nous utilisons (‘MSI@50342’ par défaut

$response = Invoke-WebRequest -Uri http://localhost:50342/oauth2/token -Method GET -Body @{resource= »https://management.azure.com/ »} -Headers @{Metadata= »true »}

$content =$response.Content | ConvertFrom-Json

$access_token = $content.access_token

Login-AzureRmAccount -AccessToken $access_token -AccountId « MSI@50342 »

Get-AzureRmResourceGroup

Get-AzureRmVM

clip_image009

 

L’authentification Azure est bien fonctionnelle et nous sommes bien capables d’accéder aux ressources auxquelles le Service Principal a été accrédité.

Bonus

Allons plus loin avec un KeyVault, en accordant au Service Principal la possibilité d’accéder à des secrets (Get et List pour les permissions).

clip_image010

 

Pour vérifier, voilà le secret à révéler dans le coffre-fort numérique.

clip_image011

 

$response = Invoke-WebRequest -Uri http://localhost:50342/oauth2/token -Method GET -Body @{resource= »https://vault.azure.net »} -Headers @{Metadata= »true »}

$content = $response.Content | ConvertFrom-Json

$KeyVaultToken = $content.access_token

$return = (Invoke-WebRequest -Uri -Method GET -Headers @{Authorization= »Bearer $KeyVaultToken »}).content

$return

($return | convertfrom-json).value

clip_image012

 

Conclusion

Au moment de l’écriture de ce billet, la fonctionnalité Managed Service Identity est encore en preview et ne concerne pas encore tous les services. La seule contrainte identifiée à ce jour, c’est l’impossibilité de spécifier le Service Principal à utiliser. Sinon, c’est un excellent moyen pour ne plus code ses credentials dans le code de ses machines virtuelles.

 

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