JANV 15

Verrouillez vos ressources via ARM

Verrouiller ses ressources sur Azure est un moyen simple de s’affranchir d’erreurs d’inattention. Il existe plusieurs types de verrous que l’on peut poser sur Azure qui sont les suivants :

  • ReadOnly
  • CanNotDelete

Il est possible de les mettre soit sur chacune de vos ressources, sur les groupes de ressources ou alors sur votre souscription.

Maintenant voyons comment faire cela en ARM.

Verrouiller une ressource

{
    "type": "Microsoft.Network/virtualNetworks/providers/locks",
    "apiVersion": "2016-09-01",
    "name": "[concat('VirtualNetwork1', '/Microsoft.Authorization/vnetLock')]",
    "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', 'VirtualNetwork1')]"
    ],
    "properties": {
        "level": "CanNotDelete",
        "notes": "VNET can not delete"
    }
}

Cette partie de template ARM contient les éléments suivants :

  • Type : Type de la ressource suivi de /providers/locks
  • Name : Nom de la ressource suivi de /Microsoft.Authorization/ puis le nom du Lock

Verrouiller un groupe de resource

{
    "type": "Microsoft.Authorization/locks",
    "apiVersion": "2016-09-01",
    "name": "rgLock",
    "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', 'VirtualNetwork1')]"
    ],
    "properties": {
        "level": "CanNotDelete",
        "notes": "RG can not delete"
    }
}

Cette partie de template est utilisable via la commande New-AzureRmResourceGroupDeployment. Bien entendu faites bien attention aux dépendances, surtout si vous mettez un level à ReadOnly

Verrouiller une souscription

{
    "type": "Microsoft.Authorization/locks",
    "apiVersion": "2016-09-01",
    "name": "subLock",
    "properties": {
        "level": "CanNotDelete",
        "notes": "sub can not delete"
    }
}

Cette partie de template est utilisable via la commande New-AzureRmDeployment uniquement.


JANV 11

Service Fabric 6.4 - Récupérer les évènements via les REST API

Dans la dernière version de Service Fabric, il y a une nouveauté qui est arrivée sur les clusters, il s’agit bien entendu de l’apparition de l’onglet Events qui est disponible sur toutes les pages de votre cluster.

On peut le voir ci-dessous sur mon cluster local :

image

Dans cet onglet on y retrouve tout ce qu’on a toujours voulu trouver concernant l’historique de l’état de votre cluster Service Fabric. Plus bessoin donc d’aller dans les archives du Table Storage liées aux diagnostics.

Voici, un exemple de ce que l’on peut retrouver sur les évènements liés à un noeud :

image

Bien que ce soit disponible via le Service Fabric Explorer, il n’existe pas à ce jour de commande Powershell qui récupère ces informations.

Vu que l’explorer se base sur des REST API, il est possible pour vous aussi de récupérer les mêmes informations via la REST API.

Voici l’url qui est appelée pour afficher vos events : http://localhost:19080/EventsStore/Nodes/_Node_0/$/Events?starttimeutc=2018-12-14T23:00:00Z&endtimeutc=2018-12-17T11:06:00Z&api-version=6.4

On retrouve donc les éléments suivants :

  • L’endpoint web de votre cluster : http://localhost:19080/
  • Le path de la ressource : /Nodes/_Node_0/
  • Le service utilisé : /$/Events
  • Les paramètres utilisés en GET : starttimeutc=2018-12-14T23:00:00Z&endtimeutc=2018-12-17T11:06:00Z&api-version=6.4

Il est possible de retrouver les différentes API disponibles à cette url : https://docs.microsoft.com/en-us/rest/api/servicefabric/sfclient-index-eventsstore


DECE 13

Intégrer Event Grid dans vos applications

Il existe de nombreux services de messaging sur Azure, qui ont chacun leur propre cas d’utilisation. Pour ma part j’utilise de plus en plus Event Grid pour monitorer ma plateforme Azure, car il offre les avantages suivants :

  • Evènement déclenché rapidement après l’action
  • Intégration Azure Function
  • Intégration Logic Apps
  • Possibilité d’avoir des évènements personnalisés.

Le premier point pour moi est important, car si je prends le cas d’usage alerting décrit dans la documentation, il s’agit le plus souvent d’une mise en place d’alerte suite à l’analyse de données ingérées via Log Analytics, ce qui ne me convient pas toujours en terme de délai, car on peut rapidement arriver à 15 min de latence, alors que je peux être à moins d’1 minute via Event Grid.

Nous allons voir comment mettre en place le dernier point dans une application C#. Pour cela dans le portail, nous allons créer un objet de type Event Grid Topic

Outre les informations typiques, il vous est demandé de choisir entre Event Grid Schema et Cloud Event Schema

Event Grid est un produit made by Microsoft, avec un schéma spécifique, alors qu’un Cloud Event doit respecter une spécification qu’on peut retrouver ici : https://github.com/cloudevents/spec/blob/master/json-format.md

Voici le schéma pour Event Grid :

[
  {
    "id": string,
    "eventType": string,
    "subject": string,
    "eventTime": string-in-date-time-format,
    "data":{
      object-unique-to-each-publisher
    },
    "dataVersion": string
  }
]

Ici, nous allons utiliser le schéma Event Grid en créant un object C# qui correspond à un item de ce schéma :

public class GridEvent<T> where T : class
{
	public string Id { get; set; }
	public string EventType { get; set; }
	public string Subject { get; set; }
	public DateTime EventTime { get; set; }
	public T Data { get; set; }
	public string DataVersion { get; set; }
}

Sous Linqpad, j’utilise le script suivant pour envoyer un message sur mon topic Event Grid :

private const string Key = "GnXsbgmdlfklqzrjz/ddsfj="; 
private const string Endpoint = "https://demo-eg.westeurope-1.eventgrid.azure.net/api/events";

async Task Main()
{
	HttpClient client = new HttpClient();
	client.DefaultRequestHeaders.Add("aeg-sas-key", Key);
	
	var events = new [] {
		new GridEvent<object>() {
			Id = Guid.NewGuid().ToString(),
			EventType = "CustomEventGrid.Demo",
			Subject = "Test me !",
			EventTime = DateTime.UtcNow,
			Data = null,
			DataVersion = "0.1"
		}
	};
	
	string jsonData = JsonConvert.SerializeObject(events);

	HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Endpoint)
	{
		Content = new StringContent(jsonData, Encoding.UTF8, "application/json")
	};

	HttpResponseMessage response = await client.SendAsync(request);
}

J’ai mis en place une Azure Function qui lira mes messages postés sur mon topic Event Grid assez simplement comme on peut le voir ci-dessous :

#r "Microsoft.Azure.EventGrid"
#r "Newtonsoft.Json"

using Newtonsoft.Json;
using Microsoft.Azure.EventGrid.Models;

public static void Run(EventGridEvent eventGridEvent, ILogger log)
{
    var evt = JsonConvert.SerializeObject(eventGridEvent);

    log.LogInformation(evt);
}

Voici un log de démo :

2018-10-29T15:59:04.485 [Information] {"id":"3a6cd1ed-a4ea-4eb5-8f4c-05a4388842df","topic":"/subscriptions/e7bd1bb5-e9af-49c7-b5aa-ac09992fdfeb/resourceGroups/eventgrid-test/providers/Microsoft.EventGrid/topics/demo-eventgrid","subject":"Test me !","data":null,"eventType":"CustomEventGrid.Demo","eventTime":"2018-10-29T15:59:06.0856579Z","metadataVersion":"1","dataVersion":"0.1"}

On peut donc voir ici qu’entre l’envoi du message et la lecture de celui-ci, l’opération est de moins de 2 secondes. Ce qui peut être utile quand vous souhaitez avoir un système extrêmement réactif.


DECE 07

ARM - Etendre vos templates grâce à Azure Function

Pour construire une infrastructure sur Azure, il y a plusieurs moyens qui s’offrent à vous, notamment les suivants :

  • Le portail Azure et votre souris (ou votre trackpad)
  • Les REST API pour les courageux
  • Azure CLI
  • Azure Powershell
  • Terraform
  • Template ARM

Ici, on va plutôt parler du dernier, car c’est celui que je préfère, je n’ai pas encore été rattrapé par la hype qui touche Terraform.

Bien que l’on puisse faire beaucoup de choses avec les templates ARM, je trouve qu’on est limité en terme de fonction built in.

Dans ma liste de souhait pour Noël, j’aimerais entre autres les fonctions suivantes :

  • Calcul de dates : Date du jour, date dans 1 an, dans 1 heure …
  • Calcul de timespan : Utile dans les templates ARM créant des secrets dans des KeyVaults par exemple.

J’ai trouvé une solution pour m’offrir ces fonctionnalités dans mes templates ARM qui se base sur Azure Function et le compilateur Roslyn.

J’ai donc créé une Azure Function qui référence le package Nuget suivant : Microsoft.CodeAnalysis.Scripting en version 2.3.0

Cette fonction basée sur un trigger HTTP exécute le code suivant :

string script = req.Query["script"];
string result = await Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync<string>(script);

StringBuilder template = new StringBuilder();  
template.AppendLine("{");
template.AppendLine("    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",");
template.AppendLine("    \"contentVersion\": \"1.0.0.0\",");
template.AppendLine("    \"parameters\": {},");
template.AppendLine("    \"variables\": {},");
template.AppendLine("    \"resources\": [],");
template.AppendLine("    \"outputs\": {");
template.AppendLine("        \"eval\": {");
template.AppendLine("           \"type\": \"string\",");
template.AppendLine("           \"value\": \""+ result +"\"");
template.AppendLine("        }");
template.AppendLine("    }");
template.AppendLine("}");

return (ActionResult)new OkObjectResult(template.ToString());

Je récupère via ma querystring une chaine de caractère que j’évalue grâce à Roslyn pour ensuite la passer dans l’output d’un template ARM que je contruis dans mon code.

Il est possible d’appeler cette fonction via un template ARM, grâce à la méthode des linkedTemplate comme on peut le voir ci dessous :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {
        "functionUrl": "https://myfunc.azurewebsites.net/api/executecsharp",
        "script": "System.DateTime.UtcNow.AddYears(10).ToString()"
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2015-01-01",
            "name": "arm-dep",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "uri": "[concat(variables('functionUrl'), '?script=', variables('script'))]",
                    "contentVersion": "1.0.0.0"
                }
            }
        }
    ],
    "outputs": {
        "fromLinked": {
           "type": "string",
           "value": "[reference('arm-dep').outputs.eval.value]"
        }
    }
}

Grâce à cette méthode j’arrive à récupérer dans ce cas la date du jour dans 10 ans, il est bien entendu possible de réutiliser la sortie de mon template dans d’autres ressources comme c’est le cas pour des templates linkés.

On peut utiliser ce trick dans plusieurs scénarios plus ou moins legit. Cependant bien que cela soit possible, je ne vous conseille pas cette astuce en premier choix d’implémentation.


DECE 03

Créez vos Logic Apps via des templates ARM

Si vous avez déjà créé des workflows Logic Apps depuis le portail Azure, vous avez pu voir qu’il est possible de faire énormément de choses avec. Pour ma part, je l’utilise souvent lié à EventGrid afin d’envoyer des mails pour faire ce que j’appelle du Reactive Monitoring. Bien qu’il soit très simple de configurer notre Logic Apps depuis le portail Azure, la mise en place de ces workflows en ARM n’est pas chose aisée.

Si je prends mon cas d’usage, mes workflows Logic Apps utilisent entre autre les étapes suivantes :

  • Trigger Event Grid
  • Parsing JSON
  • Conditions
  • Appel Azure Function
  • Appel Azure Automation
  • Appel Azure AD
  • Envoi de mail

Voyons maintenant comment construire notre template ARM. Nous allons commencer par le début, en définissant la structure de notre template ARM :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Logic/workflows",
            "apiVersion": "2016-06-01",
            "name": "mylogic",
            "location": "[resourceGroup().location]",
            "tags": {
               "displayName": "LogicApp"
            },
            "properties": {
               "state": "Disabled",
               "definition": {
                  "$schema": "https://schema.management.azure.com/schemas/2016-06-01/Microsoft.Logic.json",
                  "contentVersion": "1.0.0.0",
                  "parameters": {},
                  "triggers": {},
                  "actions": {},
                  "outputs": {}
               },
               "parameters": {}
            }
         }
    ],
    "outputs": {}
}

On peut voir que les propriétés de notre Logic Apps sont en fait représentées par un JSON qui s’imbrique dans notre template ARM.

Une fois la structure définie, il faut commencer par notre trigger Event Grid. Pour cela il faut créer un objet de type Microsoft.Web/connections

{
    "name": "[variables('eventGridConnexion')]",
    "type": "Microsoft.Web/connections",
    "apiVersion": "2016-06-01",
    "location": "[resourceGroup().location]",
    "properties": {
        "displayName": "[variables('eventGridConnexion')]",
        "api": {
            "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azureeventgrid')]"
        }
    }
}

Cet objet connexion ne contient pas les informations nécessaires pour initier la connexion dans Logic Apps, elles seront à renseigner plus tard via le portail Azure, ou via ce script Powershell : https://github.com/logicappsio/LogicAppConnectionAuth

Pour les autres managedApis que j’utilise, j’utilise les id suivants :

  • Azure Event Grid : azureeventgrid
  • Office 365 : office365
  • Azure Active Directory : azuread

On rajoute par la suite cette connexion en dépendance requise pour notre Logic Apps.

"dependsOn": [
    "[resourceId('Microsoft.Web/connections', variables('eventGridConnexion'))]",
    "[resourceId('Microsoft.Web/connections', variables('AADConnexion'))]",
    "[resourceId('Microsoft.Web/connections', variables('O365Connexion'))]"
]

Ensuite, on passe en paramètre de notre Logic Apps les informations nécessaires pour notre connexion, ici je passe par un objet $connexions, le même qui est généré par l’export ARM depuis le portail Azure.

"$connections": {
    "value": {
        "azureeventgrid": {
            "connectionId": "[resourceId('Microsoft.Web/connections', variables('eventGridConnexion'))]",
            "connectionName": "[variables('eventGridConnexion')]",
            "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('location'), '/managedApis/azureeventgrid')]"
        },
        "azuread": {
            "connectionId": "[resourceId('Microsoft.Web/connections', variables('AADConnexion'))]",
            "connectionName": "[variables('AADConnexion')]",
            "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('location'), '/managedApis/azuread')]"
        },
        "office365": {
            "connectionId": "[resourceId('Microsoft.Web/connections', variables('O365Connexion'))]",
            "connectionName": "[variables('O365Connexion')]",
            "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('location'), '/managedApis/office365')]"
        }
}

Et je peux ainsi définir mon trigger de la sorte :

"triggers": {
    "Event_Grid_Trigger": {
        "inputs": {
            "body": {
                "properties": {
                    "destination": {
                        "endpointType": "webhook",
                        "properties": {
                            "endpointUrl": "@{listCallbackUrl()}"
                        }
                    },
                    "filter": {
                        "includedEventTypes": [
                            "Microsoft.Resources.ResourceWriteSuccess"
                        ]
                    },
                    "topic": "[subscription().id]"
                }
            },
            "host": {
                "connection": {
                    "name": "@parameters('$connections')['azureeventgrid']['connectionId']"
                }
            },
            "path": "[concat('/subscriptions/', uriComponent(subscription().subscriptionId), '/providers/', uriComponent('Microsoft.Resources.Subscriptions'), '/resource/eventSubscriptions')]",
            "queries": {
                "subscriptionName": "[variables('eventGridConnexion')]",
                "x-ms-api-version": "2017-06-15-preview"
            }
        },
        "splitOn": "@triggerBody()",
        "type": "ApiConnectionWebhook"
    }
}

Il est possible de nommer votre trigger comme vous le souhaitez, par défaut les underscores sont remplacés par des espaces dans le designer Logic Apps.

Ensuite, il faut lire le contenu de notre trigger en ajoutant une action qui nous permettra de lire notre JSON et de valider le schéma d’entrée. Pour cela, on va utiliser cette action :

"Parse_EventGrid_JSON": {
    "runAfter": {},
    "type": "ParseJson",
    "inputs": {
        "content": "@triggerBody()?['data']",
        "schema": {
            /* Schéma JSON */
        }
    }
}

Pour cette première étape, il n’est pas nécessaire d’indiquer qu’elle s’exécute après notre trigger, cependant pour les autres il s’agit d’un prérequis, le service de management Azure ne prendra pas en compte l’ordre des élément dans votre template.

Afin de ne pas faire 50 pages de templates ARM ici même, voici quelques astuces pour construire votre template Logic Apps