Accéder à l'en-tête Accéder au contenu principal Accéder au pied de page
Retour aux actualités
Non classé
19/08/2019 Cyrille Martraire

Taguer ses ressources Azure avec son créateur

 

Il y a des lundi matins où, arrivant tranquillement au travail et reposé du weekend, on est tellement heureux d’entamer une nouvelle semaine pleine de promesses de belles réalisations disruptives et innovantes, qui vont changer la donne. On fait quelques pas dans le bureau et là on voit toutes les têtes baissées, les regards fuyants… la tension est palpable. Pourtant, il n’y a pas eu de mise en production vendredi dernier et le monitoring indique que tout va bien.

Finalement, on découvre la raison de cette frayeur à la machine à café

Dans notre belle architecture Azure, quelqu’un a honteusement glissé une IP Publique, non prévue, n’apparaissant sur aucun schéma et dont l’usage reste un mystère.

Pas de panique, il existe un moyens de trouver son créateur, l’Activity Logs.

Il contient les informations sur les événements qui se sont produits dans la souscription Azure…On va donc pouvoir trouver le coupable. Connexion au portail, on va sur la bonne souscription ensuite sur l’IP publique en question et on affiche le journal d’activité.

Et la, rien. Aucune information. Le journal d’activité n’est conservé que 90 jours, la ressource étant plus ancienne, plus aucune information disponible.

Bilan de l’investigation : Elle date de plus de 3 mois. C’est un peu léger. Hélas, cette fois, on ne pourra pas faire grand chose de plus.

Comment remédier à cela?

Deux moyens pour se prémunir de ce genre de problème : Archiver l’Activity Logs dans un Storage Account, dont la persistance dans le temps ne sera pas limitée. Aussi, il est possible de mettre un Tag sur les ressources avec leur créateur.

Archivage de l’Activity Log

Il est possible d’exporter le journal d’activité vers un Storage Account ou un Event Hub (ou les deux).

La documentation pour le faire est ici : https://docs.microsoft.com/en-us/azure/azure-monitor/platform/activity-log-export.

A partir du moment où cet export est en place, il sera toujours possible de retrouver les informations sur le cycle de vie des ressources Azure.

Taguer les ressources

Pour aller un peu plus loin et avoir immédiatement l’information, on peut également poser un Tag sur les ressources créées avec son créateur (utilisateur ou Service Principal).

Il n’existe pas de mécanisme natif pour réaliser cette action, mais on peut le mettre un en place, en se basant principalement sur deux services Azure : Event Grid Subscription et Azure Function.

Event Grid Subscription sera utilisé pour récupérer les événements de création, de mise à jour ou de suppression de ressources. Les Azure Functions permettront d’effectuer un traitement suite à ces événements.

L’ensemble du code est disponible ici : https://github.com/chatoninthecloud/azure-tag-created-by

Fonctionnement

Création on modification d’une ressource

Voici le workflow lors de la création d’une nouvelle ressource où lors de sa mise à jour.

 

 

Lors de la création d’une ressource, un Event Grid Subscription va permettre de récupérer l’événement. On applique un filtre sur cette souscription, pour ne prendre en compte que les événements du type « Microsoft.Resources.ResourceWriteSuccess »

Il sera ensuite déposé dans une Azure Storage Queue pour être exploité de façon asynchrone.

Une Azure Function, avec un bidding sur cette queue, va effectuer le traitement contenant trois étapes :

Managed Service Identity est activée sur l’Azure Function, lui permettant d’avoir le rôle de Contributor sur la souscription et de pouvoir poser un Tag sur les ressources.

Suppression d’une ressource

Voici le workflow lors de la suppression d’une ressource.

Lors de la suppression d’une ressource, un Event Grid Subscription va permettre de récupérer l’événement. On applique un filtre sur cette souscription, pour ne prendre en compte que les événements du type « Microsoft.Resources.ResourceDeleteSuccess »

Il sera ensuite déposé dans une Azure Storage Queue pour être exploité de façon asynchrone.

Une Azure Function, avec un bidding sur cette queue, va effectuer le traitement qui ne contient qu’une seule étape :

  • Supprimer la ligne dans la table référentiel associée à la ressource

Déploiement

Commencer par cloner le repository https://github.com/chatoninthecloud/azure-tag-created-by

Tools

Le déploiement nécessite les outils suivants :

Service Principal 

Créer un Service Principal pour Terraform. Il doit avoir le rôle de Owner sur la souscription car il a la responsabilité de :

 

  • Créer toutes les ressources nécessaires (rôle Contributor obligatoire)
    • Le Resource Group
    • Le Storage Account
    • La Function App, avec Managed Service Identity activé
    • Un Application Insight pour le monitoring
    • Les 2 Event Grid Subscription
  • Affecter le rôle Contributor sur la souscription à l’identité managée de la Function App (rôle Owner obligatoire)

Il faudra créer un secret, que nous utiliserons plus tard dans la configuration de Terraform.

Storage Account

Créer un Storage Account et un container pour stocker les fichiers d’état de terraform (tfstate)

Terraform

Mettez à jour le fichier dev.tfvars.json à l’intérieur du dossier terraformenvironment

{
    "region" : "region dans laquelle les ressources seront déployées",
    "resourceGroupName" : "Nom du groupe de ressources dans lequel les ressources seront créées",
    "storageAccountName" : "Nom du Storage Account",
    "resourceCreatedQueue" : "Nom de la queue utilisée comme endpoint pour l'Event Grid Subscription sur les ressources créées",
    "resourceDeletedQueue" : "Nom de la queue utilisée comme endpoint pour l'Event Grid Subscription sur les ressources supprimées",
    "tableName" : "Nom de la table utilisée comme référentiel des ressources créées",
    "resourceCreatedSubscription" : "Nom de l'Event Grid Subscription pour les ressources créées",
    "resourceDeletedSubscription" : "Nom de l'Event Grid Subscription pour les ressources supprimées",
    "appServicePlanName" : "Nom de l'App Service Plan",
    "functionAppName" : "Nom de la Function App",
    "applicationInsightName" : "Nom de l'Application Insight utilisée pour monitorer la Function App"
}

Il est maintenant possible d’utiliser le format json pour gérer les variables dans Terraform. Je trouve personnellement ce format plus lisible.

Une valeur par défaut est présente pour toutes les ressources, n’ayant pas de contraintes d’unicité au niveau du nommage (c’est à dire le Storage Account et la Function App)

Mettez également à jour le fichier main.tf dans le dossier terraform

"terraform" : {
    "backend" : {
        "azurerm": {
            "storage_account_name" : "le nom du compte de stockage",
            "container_name" : "le nom du container",
            "key" : "le nom du fichier tfstate (dev.terraform.tfstate par exemple)"
         }
    }
 }

Il faut aussi définir les variables d’environnement suivantes dans votre shell pour configurer Terraform. Par exemple pour Windows

SET ARM_CLIENT_ID=L'applicationId du Service Principal 
SET ARM_CLIENT_SECRET=Un secret du Service Principal 
SET ARM_SUBSCRIPTION_ID=L'Id de la souscription
SET ARM_TENANT_ID=L'Id du Tenant utilisé pour l'authentification
SET ARM_ACCESS_KEY=Une des access key du compte de stockage pour les fichiers tfstate

Exécuter les commandes suivantes dans le dossier terraform

terraform init
terraform plan -var-file=.environmentdev.tfvars.json

Si le plan d’exécution vous semble cohérent, vous pouvez déployer le template

terraform apply -var-file=.environmentdev.tfvars.json

Toutes les ressources Azure nécessaires sont maintenant déployées, on peut passer à l’Azure Function

Azure Function

La Function App va contenir 2 fonctions, resourceCreatedFunction, qui sera déclenchée lors de la création d’une ressource et resourceDeletedFunction, déclenchée lors de la suppression d’une ressource.

Le worker PowerShell est maintenant disponible dans les Azure Function V2, on va donc l’utiliser.

Au niveau du code, deux petites choses à noter.

  • Le dossier Modules, à la racine du dossier function, va permettre d’importer automatiquement tous les modules se trouvant à l’intérieur au démarrage de l’Azure Function. Dans notre cas, le module AzTable est utilisé pour les opérations de manipulation de l’Azure Storage Table. Au sein de ce dossier, on peut enregistrer des modules provenant de la galerie PowerShell (https://www.powershellgallery.com/packages) ou alors ses propres modules
  • le fichier profile.ps1, lui aussi exécuté au démarrage de l’Azure Function, permet de se connecter en utilisant l’identité managée. Le contexte d’exécution (local ou Azure) est déterminé en vérifiant l’existence de la variable d’environnement MSI_SECRET, définie automatiquement dans le cadre d’une exécution dans Azure. Cela permet d’exécuter ses fonctions localement dans l’émulateur ou alors dans le Cloud, sans avoir à toucher au code responsable de l’authentification (ce qui est bien pratique!).

Si vous avez modifié le nom par défaut des queues, il faudra modifié en conséquence les 2 fichiers function.json à l’intérieur de chaque fonction, en indiquant le nom de la queue servant comme endpoint, pour les messages de création dans le dossier resourceCreatedFunction, et celle servant de endpoint pour les messages de suppression dans le dossier resouceDeletedFunction

{
     "bindings": [
        {
              "name": "QueueItem",
              "type": "queueTrigger",
              "direction": "in",
              "queueName": Mettre le nom de la queue ici,
              "connection": "AzureWebJobsStorage"
        }
    ]
}

On peut maintenant déployé ces 2 Azure functions en utilisant les commandes suivantes, dans le dossier function

Connect-AzAccount
Get-AzSubscription -SubscriptionId "The subscription Id" | Select-AzSubscription
func azure functionapp publish lenomdelafunctionapp

Tout est maintenant déployé.

Essayons de créer une nouvelle adresse IP publique

New-AzPublicIpAddress -Name "iamnothere" -ResourceGroupName azuretag-rg -Location northeurope -AllocationMethod Dynamic

Le traitement est asynchrone, et un petit délai est nécessaire pour que l’événement soit collecté. Après quelques (jusqu’à 2 minutes) secondes…Voici!

Le tag est bien posé.

Si on essaie de le supprimer, il sera créé de nouveau, et sa valeur sera prise depuis le référentiel.

Conclusion

On a maintenant en place un mécanisme permettant de poser un tag automatiquement sur les ressources le permettant, avec le nom du créateur.

C’est déjà un bon point. On pourra compléter cela en utilisant notamment :

  • Des Azure Policies. Si les IPs Publiques n’étaient pas authorisées sur la souscription, on pourrait bloquer leur création à l’aide d’une Azure Policy.
  • Une convention de nommage. Elle permet de pouvoir identifier rapidement le projet, l’équipe, la raison d’être d’une ressource
  • Des Tags. On peut utiliser d’autres Tags (jusqu’à 50 par ressources), en ségréguant les ressources par environnement, produits, centres de coûts …