Mise en place de l’Azure Function Resourcegroupasaservicepublicapi

La première instance Azure Function était dédiée à une API interne. Maintenant, nouvelle Azure Function dédiée à l’hébergement des API accessibles par les consommateurs. C’est au travers de ces API que les consommateurs pourront :

  • Savoir s’ils ont accès au service Resource Group As a Service
  • Dans quelle souscription pourront-ils demander la création de groupes de ressources
  • Quelles régions Azure sont autorisées
  • Quel CostCenter associer au groupe de ressource
  • Demander la création d’un groupe de ressources

Pour cette nouvelle instance d’Azure Function, j’ai encore fait le choix de la performance e choisissant le mode de fonctionnement « Hosting Plan ». Certes, ça ne fait pas très Server-Less. La création de l’instance est tout ce qu’il y a de plus classique. Un point de détail, le choix du Storage Account associé. J’aurai pu utiliser un seul Storage Account pour mes deux Azure Function mais je voulais maintenir une isolation entre un composant font-end et un composant backend. En plus, de cette manière ce sont des clés d’accès au stockage bien distinctes.

clip_image001

 

Tout comme pour la première instance Azure Function, nous allons uploader le contenu de nos différentes fonctions sous la forme d’un fichier Zip. Quand j’aurai le temps, j’industrialiserai le tout avec un beau template ARM disponible sur mon GitHub.

clip_image002

 

L’importation du contenu se déroule de la même manière que pour la première instance d’Azure Function, pas grand-chose à dire de plus sur ce sujet.

$username = « <Compte FTP> »

$password = « <Mot de passe FTP> »

$filePath = « <emplacement local du fichier resourcegroupasaservicepublicapi.zip> »

$apiUrl = « « 

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes((« {0}:{1} » -f $username, $password)))

$userAgent = « powershell/1.0 »

Invoke-RestMethod -Uri $apiUrl -Headers @{Authorization=(« Basic {0} » -f $base64AuthInfo)} -UserAgent $userAgent -Method POST -InFile $filePath -ContentType « multipart/form-data »

clip_image003

 

Par contre, c’est au niveau des Applications Settings que cela se complique. Il y en a beaucoup plus à configurer, d’où l’intérêt d’avoir un peu de PowerShell pour réaliser l’opération.

Nom

Contenu

AuthorizationModuleAuthorizeTableName AuthorizedCallers
AuthorizationModuleBackupTagName Backup
AuthorizationModuleCostCenterTagName CostCenter
AuthorizationModuleCreatedOnTagName CreatedOn
AuthorizationModuleDefaultCostCenterTableName DefaultCostCenter
AuthorizationModuleEnvironnementTagname Environnement
AuthorizationModuleIAMTemplateRoleTableName AuthorizedIAMTemplateRole
AuthorizationModuleKeyVault <Nom du groupe de ressources de la solution>
AuthorizationModuleOwnerTagName Owner
AuthorizationModulePolicyAssignmentTableName AuthorizedPolicyAssignment
AuthorizationModuleProjectTagName ProjectName
AuthorizationModuleSLAName SLALevel
AuthorizationModuleStorageAccountMasterKey AuthorizationModuleStorageAccountMasterKey
AuthorizationModuleStorageAccountName Nom du Storage Account de la solution
AuthorizationModuleReadKeyvaletURL <URL API du Key-Valet avec secret>

 

On va distinguer trois types de paramètres :

  • Ceux qui désignent des constantes pour le code des API
  • Ceux qui désignent des Tags
  • Ceux qui sont utilisés pour configurer la solution

 

Dans mon développement, mes constantes, ce sont principalement des noms de table Azure table :

  • AuthorizationModuleAuthorizeTableName : Cette table référence les souscriptions accessibles pour chaque consommateur.
  • AuthorizationModuleIAMTemplateRoleTableName : Nom de la table dans Azure Table qui référence les identités et les rôles à associer lors de la création d’un groupe de ressources pour le Role-Based Access Control
  • AuthorizationModulePolicyAssignmentTableName : Nom de la table dans Azure Table qui référence les Azure Policy à positionner sur les groupes de ressources nouvellement créés.

La solution va positionner des tags sur les groupes de ressources nouvellement créés. En externalisant les noms dans les Applications Settings, je vous permets de personnaliser la solution en fonction de votre environnement.

  • AuthorizationModuleBackupTagName : Nom du tag qui devra être positionné sur le groupe de ressources nouvellement créé pour indiquer le besoin de sauvegarder ou non le contenu du groupe de ressources.
  • AuthorizationModuleCostCenterTagName : Nom du Tag qui devra être positionné sur le groupe de ressources nouvellement créé pour référencer le centre de coût pour la refacturation des usages.
  • AuthorizationModuleCreatedOnTagName : Nom du Tag qui devra être positionné sur le groupe de ressources nouvellement créé pour indiquer la date de création de la ressource (utile pour distinguer des ressources éphémères)
  • AuthorizationModuleDefaultCostCenterTableName : Nom de la table qui référence le CostCenter qui devra être utilisé lors de la création d’un groupe de ressources si le consommateur ne précise rien.
  • AuthorizationModuleEnvironnementTagName : Nom du Tag qui devra être positionné sur le groupe de ressources nouvellement créé pour désigner le type d’environnement (production, dev, …)
  • AuthorizationModuleOwnerTagName : Désigne le nom du Tag qui devra être positionné sur le groupe de ressources nouvellement créé pour désigner le propriétaire / responsable des ressources hébergées/
  • AuthorizationModuleProjectTagName : Nom du Tag qui devra être positionné sur le groupe de ressources nouvellement créé pour regrouper les ressources composant un même projet
  • AuthorizationModuleSLAName : Nom du Tag qui devra être positionné sur le groupe de ressources nouvellement créé pour signaler le niveau de SLA associé aux ressources qui sont contenues dans le groupe de ressources.

 

Il ne nous reste plus que les paramètres propres au fonctionnement de la solution.

  • AuthorizationModuleKeyVault : Désigne le nom de l’instance KeyVault qui a été mise en place pour stocker les secrets de la solution. Dans cette version, on référence le nom. Quand j’aurai un peu de temps, on référencera l’URL complète pour permettre à la solution de fonctionner dans tous les environnements Azure, y compris Azure Stack.
  • AuthorizationModuleStorageAccountMasterKey : Désigne le nom du secret dans l’instance de Key Vault de la solution qui contient la clé primaire du Storage Account qui héberge mes Azure table.
  • AuthorizationModuleStorageAccountName : Désigne le nom du Storage Account qui contient les Azure Table de la solution
  • AuthorizationModuleReadKeyvaletURL : Référence l’URL de l’API Get-ValetKeyforAzureTableRead hébergé par notre première instance Azure Function. Attention à bien référencer l’URL avec la Function Key qui sert de mécanisme d’authentification. Clairement, dans ma todo-list, c’est un truc qu’il faudra réviser pour stocker cette information dans le Key vault.

 

Tout comme pour la première instance, nous allons importer ces Applications Settings en prenant soin de ne pas écraser les paramètres déjà positionnés.

$webapp = Get-AzureRmWebApp -ResourceGroupName ResourceGroupAsAService -Name resourcegroupasaservicepublicapi

$AppSettings = @{}

$AppSettings = $webapp.SiteConfig.AppSettings

$hash = @{}

ForEach ($kvp in $AppSettings) {

$hash[$kvp.Name] = $kvp.Value

}

$hash[‘AuthorizationModuleAuthorizeTableName’] = « AuthorizedCallers »

$hash[‘AuthorizationModuleBackupTagName’] = « BACKUP »

$hash[‘AuthorizationModuleCostCenterTagName’] = « CostCenter »

$hash[‘AuthorizationModuleCreatedOnTagName’] = « CreatedOn »

$hash[‘AuthorizationModuleDefaultCostCenterTableName’] = « DefaultCostCenter »

$hash[‘AuthorizationModuleDisplayName’] = « DisplayName »

$hash[‘AuthorizationModuleEnvironnementTagname’] = « Environnement »

$hash[‘AuthorizationModuleIAMTemplateRoleTable’] = « AuthorizedIAMTemplateRole »

$hash[‘AuthorizationModuleKeyVault’] = « resourcegroupasaservice »

$hash[‘AuthorizationModuleKeyVaultSubscriptionLogin’] = « SubscriptionLogin »

$hash[‘AuthorizationModuleKeyVaultSubscriptionPassword’] = « SubscriptionPassword »

$hash[‘AuthorizationModuleOwnerTagName’] = « Owner »

$hash[‘AuthorizationModulePolicyAssignmentTable’] = « AuthorizedPolicyAssignment »

$hash[‘AuthorizationModuleProjectTagName’] = « ProjectName »

$hash[‘AuthorizationModuleReadKeyvaletURL’] = «  clé>« 

$hash[‘AuthorizationModuleSLAName’] = « SLALevel »

$hash[‘AuthorizationModuleStorageAccountName’] = « resourcegroupasaservice3 »

Set-AzureRMWebApp -ResourceGroupName resourcegroupasaservice -AppServicePlan resourcegroupasaservicepublicapi -Name resourcegroupasaservicepublicapi -AppSettings $hash

clip_image004

 

Pour cette nouvelle instance d’Azure Function, nous allons aussi activer la fonctionnalité Managed Service Identity. La fonctionnalité sera utilisé par l’API Request-ResourceGroup pour extraire les secrets nécessaires de l’instance Key Vault de la solution.

clip_image005

 

Maintenant, la partie la plus intéressante, à savoir la mise en place de l’authentification Azure AD. Comme déjà vu dans le billet Authentifiez vos Azure Function avec Azure AD , nous commençons par activer la fonctionnalité.

clip_image006

 

C’est ici que cela se complique. Après avoir activé la fonctionnalité et exigé l’authentification, nous allons demander la création d’une application Azure AD.

clip_image007

 

A ce niveau, cela implique que notre compte dispose du rôle « Global Administrator » pour déclarer l’application Azure AD. Cette application Azure AD devra disposer de permissions déléguées pour Azure Active Directory.

clip_image008

 

Côté application, c’est OK. Maintenant passons côté utilisateurs. Il faut autoriser nos utilisateurs à accéder à notre application. J’ai retenu de faire simple, tous les utilisateurs de mon Tenant Azure AD seront accrédités à consommer mon application. Plus tard dans le développement de Resource Group as a service, nous introduirons la notion de rôle au sein de l’application (consommateur versus administrateur).

clip_image009

 

C’est maintenant qu’on passe sous la moquette et qu’on sort le PowerShell. Commençons par identifier le Service Principal associé à la fonctionnalité Managed Service Identity pour lui accorder le droit de lire les secrets dans l’instance de Key Vault utilisé par notre solution.

$ADDObject = Get-AzureADServicePrincipal | where {$_.displayname -eq « resourcegroupasaservicepublicapi »}

$ADDObject

Set-AzureRmKeyVaultAccessPolicy -VaultName resourcegroupasaservice -ObjectId $ADDObject.ObjectID -PermissionsToSecrets Get, List

clip_image010

 

A ce stade, les permissions sur notre première instance de KeyVault doivent ressembler à l’illustration ci-dessous :

(Get-AzureRmKeyVault -VaultName resourcegroupasaservice).accesspolicies

clip_image011

 

Cependant, il ne faut pas oublier la seconde instance. Dans mon design j’ai retenu de séparer les secrets de chaque souscription dans des instances de Key Vault séparées. Notre Azure Function portant nos API publique doit pouvoir extraire les secrets (Login & mot de passe) pour se connecter à la souscription Azure donnée :

$AzureADApplication = Get-AzureRmADApplication -DisplayNameStartWith resourcegroupasaservicepublicapi

$AzureADApplication

Set-AzureRmKeyVaultAccessPolicy -VaultName $KeyVaultName -ObjectID $AzureADApplication.ObjectId -PermissionsToSecrets Get

clip_image012

 

Ne reste plus qu’à positionner les secrets dans cette souscription. Pour chaque souscription utilisable par l’API, nous avons une instance de KeyVault dédiée avec toujours les mêmes secrets :

  • SubscriptionLogin
  • SubscriptionPassword

 

$SubscriptionCredential = Get-Credential (Get-AzureRmContext).account.ID

$SubscriptionLoginSecret = ConvertTo-SecureString -String $SubscriptionCredential.username -AsPlainText -Force

Set-AzureKeyVaultSecret -VaultName $KeyVaultName -Name ‘SubscriptionLogin’ -SecretValue $SubscriptionLoginSecret

Set-AzureKeyVaultSecret -VaultName $KeyVaultName -Name ‘SubscriptionPassword’ -SecretValue $SubscriptionCredential.Password

clip_image013

 

Maintenant, ce qui manque, c’est comment l’API va déterminer dans quelle instance de Key Vault se trouve les secrets d’une souscription Azure donnée. Tout simplement en créant un secret ayant comme nom l’identifiant unique de la souscription. Ce secret référencera l’URL de l’instance de Key Vault contenant les secrets pour la souscription. Il ne nous reste donc plus qu’à créer un secret dans l’instance de Key Vault utilisée pour les secrets partagés par les API :

$SubscriptionVault = Get-AzurermKeyVault -VaultName $KeyVaultName -ResourceGroupName $resourcegroupname

$SubscriptionSecret = ConvertTo-SecureString -String $($SubscriptionVault.VaultUri) -AsPlainText -Force

Set-AzureKeyVaultSecret -VaultName resourcegroupasaservice -Name $((Get-AzureRmContext).Subscription.ID) -SecretValue $SubscriptionSecret

clip_image014

 

D’un point de vue technique, l’API est maintenant en place. Prochaine étape, consommer nos différentes API avec Postman.

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

Benoit

Simple, yes, Secure Maybe, by design for sure, Business compliant always!

Les derniers articles par Benoit (tout voir)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.