FEVR 06

ARM - Rejoindre un domaine Active Directory avec prestaging

Créer des machines virtuelles sur Azure est plutôt simple. Cela se corse lorsque vous voulez les créer avec diverses règles de sécurité “built-in”.

Par exemple, dans mon cas, je souhaite associer ma VM fraichement créée à un active directory on premise (connecté via un Express Route) dans une OU spécifique avec l’obligation de faire du préstaging de machine dans mon OU.

Pour cela j’ai utiisé l’extension JsonADDomainExtension fournie par Microsoft qui est présente dans les QuickStart ARM https://github.com/Azure/azure-quickstart-templates/tree/master/201-vm-domain-join-existing.

Cet exemple est parfait pour créer une nouvelle VM dans mon OU. Le prestaging demande un peu plus d’exploration, en effet tout se base sur la variable domainJoinOptions qui est fixée à 3. Si vous faîtes une recherche sur votre moteur préférée vous allez trouver des notions de magic number 3 ce qui ne va pas vous aider.

En allant plus loin vous allez tomber sur cette documentation qui vous liste les différentes options : https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netjoindomain

Celle qui nous intéresse dans ce cas précis est l’option 20 :

  • NETSETUP_DOMAIN_JOIN_IF_JOINED
  • 0x00000020
  • Allows a join to a new domain even if the computer is already joined to a domain.

Du coup cela me donne en ARM le code suivant :

{
    "name": "[concat(parameters('vmName'),'/', 'JoinADDomain')]",
    "apiVersion": "2015-06-15",
    "type": "Microsoft.Compute/virtualMachines/extensions",
    "location": "[variables('location')]",
    "tags": "[variables('consolidateTags')]",
    "condition": "[parameters('joinActiveDirectory')]",
    "dependsOn": [
        "[concat('Microsoft.Compute/virtualMachines/', parameters('vmName'), '/extensions/AzureDiskEncryption')]"
    ],
    "properties": {
        "publisher": "Microsoft.Compute",
        "type": "JsonADDomainExtension",
        "typeHandlerVersion": "1.3",
        "autoUpgradeMinorVersion": true,
        "settings": {
            "Name": "[parameters('domainActiveDirectory')]",
            "User": "[parameters('domainOperator')]",
            "Restart": "true",
            "Options": "20",
            "OUPath": "[parameters('domainOUPath')]"
        },
        "protectedSettings": {
            "Password": "[parameters('domainOperatorPassword')]"
        }
    }
}

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.