
Collection organisée de requêtes KQL pour Microsoft Sentinel et de tutoriels pour la chasse aux menaces, l'analyse des journaux de connexion Azure AD, la détection d'anomalies et la création de modèles de détection efficaces.
Quelques astuces, trucs et exemples pour utiliser KQL avec Microsoft Sentinel.
Kusto Query Language est le langage utilisé dans Azure Monitor, Azure Data Explorer et Azure Log Analytics (ce que Microsoft Sentinel utilise sous le capot). J'ai toujours trouvé cette visualisation de KQL utile -

Nous voulons utiliser KQL pour créer des requêtes précises et efficaces afin de détecter les menaces, les détections, les modèles et les anomalies au sein de notre jeu de données plus large.
Prenons la requête ci-dessous comme exemple```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | where AppDisplayName == "Microsoft Teams" | project TimeGenerated, Location, IPAddress, UserAgent
Lorsque nous exécutons une requête comme celle-ci, la première ligne indique à Microsoft Sentinel dans quelle table chercher les données. Dans ce cas, nous voulons interroger la table SigninLogs, qui est l'endroit où les données de connexion Azure AD sont envoyées. Vous pouvez consulter une liste des tables [ici](https://docs.microsoft.com/en-us/azure/sentinel/data-source-schema-reference).
Microsoft Sentinel exécutera ensuite votre requête de manière séquentielle, c'est-à-dire qu'il traitera chaque ligne une par une jusqu'à la fin, ou jusqu'à ce qu'une erreur survienne. Décomposons donc notre requête ligne par ligne.```kql
SigninLogs
Donc d'abord, nous avons choisi notre table SigninLogs.```kql SigninLogs | where TimeGenerated > ago(14d)
Ensuite, nous demandons à Sentinel d'examiner les 14 derniers jours de données dans cette table.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
Ensuite, nous demandons à Sentinel de ne trouver que les journaux où UserPrincipalName est égal à "[email protected]"```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0"
Ensuite, nous recherchons uniquement les journaux où ResultType == 0, ce qui correspond à une connexion réussie à Azure AD.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| where AppDisplayName == "Microsoft Teams"
Ensuite, nous recherchons uniquement les connexions à Microsoft Teams.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | where AppDisplayName == "Microsoft Teams" | project TimeGenerated, Location, IPAddress, UserAgent
Notre dernière ligne utilise l'opérateur project, pour ne renvoyer que 4 champs de nos journaux, afin que nous voyions uniquement les TimeGenerated, Location, IPAddress et UserAgent renvoyés de nos données SigninLogs.
Voilà comment vous construisez des requêtes, passons maintenant aux bases.
## Les bases
### Les bases du temps
Microsoft Sentinel et KQL sont hautement optimisés pour les filtres de temps, donc si vous connaissez la période des données que vous souhaitez rechercher, vous devez filtrer la plage de temps immédiatement. Récupérer les 14 derniers jours de journaux, puis rechercher un nom d'utilisateur comme la requête ci-dessous -```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
Est beaucoup plus efficace que de rechercher d'abord un nom d'utilisateur puis la période comme ceci -```kql SigninLogs | where UserPrincipalName == "[email protected]" | where TimeGenerated > ago(14d)
KQL propose de nombreuses options pour interroger des périodes de temps spécifiques.```kql
SigninLogs
| where TimeGenerated > ago(14d)
Comme dans le premier exemple, cela recherchera les 14 derniers jours.```kql SigninLogs | where TimeGenerated > ago(14h)
Vous pouvez également faire des heures.```kql
SigninLogs
| where TimeGenerated > ago(14m)
Et les minutes.
KQL prend également en charge les requêtes entre plages de temps -```kql SigninLogs | where TimeGenerated between (ago(14d) .. ago(7d))
Cela trouvera les données SigninLogs entre il y a 14 jours et il y a 7 jours.```kql
SigninLogs
| where TimeGenerated between (ago(14h) .. ago(7h))
Entre il y a 14 heures et il y a 7 heures.```kql SigninLogs | where TimeGenerated between (ago(14m) .. ago(7m))
Et entre il y a 14 minutes et il y a 7 minutes.
### Les bases de Where
Where est un opérateur que vous utiliserez dans pratiquement chaque requête que vous écrirez. C'est ainsi que vous indiquez à Microsoft Sentinel de rechercher des données spécifiques. La syntaxe est très importante avec l'opérateur where. Si nous utilisons notre même exemple.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
Cela recherchera dans notre table SigninLogs, sur les 14 derniers jours, les correspondances exactes où notre UserPrincipalName est égal à [email protected]. En KQL, == est sensible à la casse, donc si vous recherchez [email protected] alors que le nom d'utilisateur est en réalité [email protected], vous n'obtiendrez aucun résultat. L'équivalent insensible à la casse est =```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName = "[email protected]"
Cela trouvera toutes les correspondances pour [email protected], indépendamment de la sensibilité à la casse.
Au lieu de equals, on peut aussi utiliser contains.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName contains "reprise_99"
Cela permettra de trouver toutes les entrées de journal où le UserPrincipalName contient reprise_99 ; si vous disposiez de données [email protected] et [email protected], il les trouverait toutes les deux. L'opérateur contains n'est pas sensible à la casse, mais vous pouvez utiliser contains_cs pour le rendre sensible à la casse.
Vous pouvez utiliser startswith ou endswith si vous recherchez des modèles particuliers.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName startswith "reprise_99"
SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName endswith "testdomain.com"
Ni startswith ni endswith ne sont sensibles à la casse, mais vous pouvez utiliser startswith_cs ou endswith_cs pour les rendre sensibles à la casse.
Si vous recherchez des mots entiers (de plus de quatre caractères), dans KQL vous pouvez utiliser l'opérateur has. Utiliser 'has' est plus efficace que 'contains' car les données sont indexées pour vous.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where AppDisplayName has "Teams"
Cela trouvera tous les SigninLogs où le nom d'affichage de l'application contient le mot Teams, ce qui peut inclure « Microsoft Teams » et « Microsoft Teams Web Client », les deux satisfaisant la requête.
Si vous recherchez plusieurs mots, vous pouvez utiliser has_any ou has_all.```kql SigninLogs | where TimeGenerated > ago(14d) | where AppDisplayName has_any ("Teams","Outlook")
Cela retournera des résultats où le nom d'affichage de l'application contient soit "Teams" soit "Outlook"```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where AppDisplayName has_all ("Teams","Outlook")
Cela renverra des résultats où le nom d'affichage de l'application contient "Teams" et "Outlook".
Si vous ne savez pas dans quels champs rechercher, vous pouvez également utiliser des caractères génériques, c'est inefficace mais peut vous mettre sur la bonne voie pour trouver ce que vous voulez.```kql SigninLogs | where TimeGenerated > ago(14d) | where * contains "reprise_99"
Ceci recherchera dans la table SigninLogs tout champ contenant reprise_99.
Un certain nombre de ces options prennent également en charge l'utilisation de ! pour inverser la requête et trouver les résultats où elle n'est pas vraie.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName != "[email protected]"
Cette requête trouverait tous les SigninLogs dont le UserPrincipalName n'est pas égal à [email protected]```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName !contains "reprise_99"
Cette requête trouverait tous les SigninLogs où le UserPrincipalName ne contient pas reprise_99```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where AppDisplayName !has "Teams"
Cette requête trouverait les SigninLogs où le nom d'affichage de l'application ne contient pas « Teams ».
Project nous permet de sélectionner les colonnes renvoyées dans notre requête et dans quel ordre.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | where AppDisplayName == "Microsoft Teams" | project TimeGenerated, Location, IPAddress, UserAgent
Cette requête recherche les données SigninLogs des 14 derniers jours, où UserPrincipalname est égal à [email protected], où ResultType est 0, où le nom d'affichage de l'application est égal à « Microsoft Teams », puis pour chaque correspondance de cette requête, elle renvoie TimeGenerated, Location, IPAddress et UserAgent.
Nous pouvons renommer les colonnes dans le cadre de la même fonction.```kql
| project LogTime=TimeGenerated, SigninLocation=Location, IP=IPAddress, Agent=UserAgent
Ceci renvoie les mêmes données, mais renomme les colonnes en LogTime, SigninLocation, IP et Agent.
Nous pouvons même manipuler la sortie en ligne avec l'opérateur project.```kql | project LocalTime=TimeGenerated+5h, Location, IPAddress, UserAgent
Cela renvoie les mêmes données, mais change le nom TimeGenerated en LocalTime et convertit vers un fuseau horaire +5h si vous travaillez dans ce fuseau horaire.
project-away est l'opposé de project et supprimera les colonnes de votre requête.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| project-away UserAgent
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| where AppDisplayName == "Microsoft Teams"
Dans cette requête, nous supprimons UserAgent. N'oubliez pas que si vous supprimez une colonne, vous ne pourrez plus y accéder plus tard dans votre requête.
Summarize produit une table qui agrège le contenu de votre requête. Summarize dispose d'un certain nombre de fonctions d'agrégation sous-jacentes. Si nous reprenons notre requête exemple, nous pouvons manipuler les résultats de différentes manières à l'aide de summarize.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize count() by AppDisplayName
Cette requête recherche dans la table SigninLogs tous les événements des 14 derniers jours, pour toute correspondance avec [email protected], où le résultat est un succès (ResultType == 0), puis résume ces événements par le nom d'affichage de l'application.
Vous pouvez éventuellement nommer la colonne de résultat.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize AppCount=count() by AppDisplayName
Cela renvoie les mêmes données mais met à jour le nom de la colonne retournée en AppCount.
Au lieu d'un comptage total, vous pouvez résumer un comptage distinct.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize DistinctAppCount=dcount(AppDisplayName) by AppDisplayName
Cela retournera un seul enregistrement pour chaque application distincte à laquelle [email protected] s'est connecté.
Vous pouvez utiliser les fonctions arg_max et arg_min pour renvoyer l'enregistrement le plus récent ou le plus ancien qui correspond à votre requête.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
Cette requête recherche tous les journaux de connexion des 14 derniers jours, qui ont [email protected] comme UserPrincipalname, qui sont des connexions réussies, puis renvoie l'enregistrement le plus récent.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize arg_min(TimeGenerated, *) by UserPrincipalName
C'est la même chose mais renvoie l'enregistrement le plus ancien.
Vous pouvez utiliser countif pour fournir une logique à vos sommes.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize TeamsLogons=countif(AppDisplayName has "Teams"), SharePointLogons=countif(AppDisplayName has "SharePoint")
Cela résume les données dans deux nouvelles colonnes, TeamsLogons où le nom d'affichage de l'application contient "Teams" et SharePointLogons où le nom d'affichage de l'application contient "SharePoint"
Vous pouvez ensuite manipuler vos données en indiquant à KQL de placer vos données dans des 'bins' de temps.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize AppCount=count() by AppDisplayName, bin(TimeGenerated, 1d)
Cela renvoie les mêmes données que notre premier exemple de synthèse, puis regroupe ces données en bins 1D.
Vous pouvez combiner ces fonctions ensemble lorsque cela est utile.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize TeamsLogons=countif(AppDisplayName has "Teams"), SharePointLogons=countif(AppDisplayName has "SharePoint") by bin(TimeGenerated, 1d)
Il s'agit d'une combinaison de nos fonctions countif et bin, où nous résumons en fonction du nom d'affichage de notre application et plaçons également les résultats dans des bins 1d.
Vous pouvez créer un ensemble d'éléments dans une requête.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize AppList=make_set(AppDisplayName) by UserPrincipalName
Cela produira une liste des applications auxquelles [email protected] s'est connecté, dans une liste appelée AppList.
Vous pouvez combiner cela avec notre bin temporel.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize AppList=make_set(AppDisplayName) by UserPrincipalName, bin(TimeGenerated, 1d)
Cela créera une liste des applications auxquelles [email protected] s'est connecté, séparée en une liste par jour.
L'opérateur render permet à KQL de visualiser les données dans différents formats tels que des diagrammes circulaires, des graphiques temporels ou en aires, ainsi que des graphiques en colonnes et en barres.
Si nous reprenons notre exemple de la table Signinlogs, nous pouvons voir comment visualiser les données de différentes manières.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize AppCount=count()by AppDisplayName | render piechart
Cette requête résume toutes les applications auxquelles [email protected] s'est connecté au cours des 14 derniers jours, puis affiche le résultat sous forme de graphique en secteurs.

Vous pouvez également l'afficher sous forme de graphique en colonnes.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize AppCount=count()by AppDisplayName
| render columnchart

Ou un graphique à barres.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize AppCount=count()by AppDisplayName | render barchart

Pour les données temporelles, vous commencez par résumer vos données en 'bins' de temps, comme indiqué dans la section summarize, puis vous pouvez visualiser vos données sur une période donnée.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize SigninCount=count() by bin(TimeGenerated, 1d)
| render timechart
Ceci visualise toutes les connexions de [email protected] par jour sur les 14 derniers jours et les affiche sous forme de timechart.

Vous pouvez également utiliser render en tant qu’areachart.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize SigninCount=count() by bin(TimeGenerated, 1d) | render areachart

Les graphiques en colonnes et les graphiques en barres peuvent également être utilisés avec des données temporelles. Vous obtiendrez une colonne ou une barre par « bin » de temps sur votre période de temps plus large.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize SigninCount=count() by bin(TimeGenerated, 1d)
| render columnchart
Ceci est la même requête que notre timechart, mais rendue sous forme de graphique en colonnes avec une colonne par jour.
```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize SigninCount=count() by bin(TimeGenerated, 1d)
| render barchart
Et un graphique à barres.

Avec les graphiques en colonnes ou à barres, vous pouvez les empiler (ce qui est le comportement par défaut).```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize SigninCount=count() by AppDisplayName, bin(TimeGenerated, 1d)
| render columnchart
Cette requête trouve toutes les connexions pour notre compte, compte les connexions par application, puis crée une seule colonne pour chaque jour.

Si vous souhaitez que chaque application ait sa propre colonne, vous pouvez la définir comme non empilée.```kql SigninLogs | where TimeGenerated > ago(14d) | where UserPrincipalName == "[email protected]" | where ResultType == "0" | summarize SigninCount=count() by bin(TimeGenerated, 1d) | render columnchart with (kind=unstacked)

Vous pouvez également renommer l'axe et le titre de votre graphique conformément à KQL.```kql
SigninLogs
| where TimeGenerated > ago(14d)
| where UserPrincipalName == "[email protected]"
| where ResultType == "0"
| summarize SigninCount=count() by AppDisplayName, bin(TimeGenerated, 1d)
| render columnchart with (kind=unstacked, ytitle="Total Sign Ins", xtitle="Day", title="Application Signins Per Day")

Vous pouvez combiner la logique dans vos actions summarize pour créer un contenu dynamique pour votre opérateur render.```kql SigninLogs | where TimeGenerated > ago(14d) | where ResultType == "0" | summarize TeamsCount=countif(AppDisplayName has "Teams"), OneDrive=countif(AppDisplayName has "OneDrive"), SharePointCount=countif(AppDisplayName has "SharePoint") by bin(TimeGenerated, 1d) | render columnchart with (kind=unstacked, ytitle="Sign In Count", xtitle="Day", title="Teams vs OneDrive vs SharePoint Sign Ins Per Day")
Cette requête recherche toutes les connexions dans votre locataire, puis compte trois groupes - un où le nom d'affichage de l'application contient "Teams", un où le nom d'affichage de l'application contient "OneDrive" et un où le nom d'affichage de l'application contient "SharePoint" pour chaque jour sur les 14 derniers jours, puis restitue le résultat sous forme de graphique en colonnes non empilé.

### Notions de base sur Parse et Split
Parse et split sont deux manières différentes d'étendre une chaîne de données en plusieurs colonnes en fonction des correspondances. Beaucoup de journaux ingérés dans Microsoft Sentinel peuvent arriver sous la forme d'une seule chaîne longue (comme sysmon), parse et split vous permettent de les manipuler pour en faire des données lisibles.
Pour ces exemples, nous utiliserons les données de test suivantes```kql
let ExampleText = datatable(TestData:string)
[
'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account'
];
Ces données ne sont qu'une simple chaîne qui ressemble à ceci.

Nous pouvons extraire des correspondances de données particulières avec ce qui suit```kql let ExampleText = datatable(TestData:string) [ 'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account' ]; ExampleText | parse TestData with * 'Name=' DisplayName ',' * | project DisplayName
Ceci extraira toutes les données entre Name= et , dans une nouvelle colonne appelée 'DisplayName'.

Vous pouvez extraire plusieurs colonnes dans la même commande en cherchant des correspondances le long de la chaîne.```kql
let ExampleText = datatable(TestData:string)
[
'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account'
];
ExampleText
| parse TestData with * 'Name=' DisplayName ',UPNSuffix=' DomainSuffix ',AadTenantId=' AzureADTenantId ',' *
| project DisplayName, DomainSuffix, AzureADTenantId
Cela analyse trois nouvelles colonnes - DisplayName, DomainSuffix et AzureADTenantId

En gardant à l'esprit que KQL exécute ses opérations de manière séquentielle, une fois que nous analysons, nous pouvons ensuite analyser à nouveau sur la colonne nouvellement créée.```kql let ExampleText = datatable(TestData:string) [ 'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account', ]; ExampleText | parse TestData with * 'Name=' DisplayName ',UPNSuffix=' DomainSuffix ',AadTenantId=' AzureADTenantId ',' * | project DisplayName, DomainSuffix, AzureADTenantId | parse DomainSuffix with * '.' TopLevelDomain | project DisplayName, DomainSuffix, TopLevelDomain, AzureADTenantId
Ceci analyse davantage notre domaine pour trouver le domaine de premier niveau, dans ce cas un .com

Lors de l'utilisation de l'opérateur parse, KQL parcourra toutes vos lignes de données et renverra même les résultats où il n'y a pas de correspondance. Ainsi, selon la structure de vos données, vous pourriez vous retrouver avec de nombreuses lignes de données vides. Si nous étendons nos données d'exemple pour inclure une autre ligne de données, avec des noms différents, et exécutons la même requête, vous verrez des résultats vides.```kql
let ExampleText = datatable(TestData:string)
[
'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account',
'Display=Reprise99,UPN=testdomain.com,AadDirectoryId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadObjectId=cf6f2df6-b754-48dc-b7bc-c8339caf211,Name=Test User,AccountType=account'
]
;
ExampleText
| parse TestData with * 'Name=' DisplayName ',UPNSuffix=' DomainSuffix ',AadTenantId=' AzureADTenantId ',' *
| project DisplayName, DomainSuffix, AzureADTenantId
| parse DomainSuffix with * '.' TopLevelDomain
| project DisplayName, DomainSuffix, TopLevelDomain, AzureADTenantId

Pour contrer cela, vous pouvez utiliser l'opérateur 'parse-where', qui ne renvoie que les résultats où il y a des correspondances avec votre requête.```kql let ExampleText = datatable(TestData:string) [ 'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account', 'Display=Reprise99,UPN=testdomain.com,AadDirectoryId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadObjectId=cf6f2df6-b754-48dc-b7bc-c8339caf211,Name=Test User,AccountType=account' ] ; ExampleText | parse-where TestData with * 'Name=' DisplayName ',UPNSuffix=' DomainSuffix ',AadTenantId=' AzureADTenantId ',' * | project DisplayName, DomainSuffix, AzureADTenantId | parse DomainSuffix with * '.' TopLevelDomain | project DisplayName, DomainSuffix, TopLevelDomain, AzureADTenantId
Nous pouvons voir que nous revenons à un seul résultat où nous avions une correspondance lors de notre analyse.

Split sépare une chaîne de texte en un tableau en fonction d'un délimiteur. Si nous revenons à nos données de test d'origine, nous pouvons effectuer la séparation en fonction du signe virgule.```kql
let ExampleText = datatable(TestData:string)
[
'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account',
]
;
ExampleText
| extend SplitData = split(TestData,',')
| project SplitData
Nous obtiendrons un tableau avec notre chaîne découpée.

Split est sensible à l'index, donc si vos données sont dans le même ordre, vous pouvez découper directement en nouvelles colonnes.```kql let ExampleText = datatable(TestData:string) [ 'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account', ] ; ExampleText | extend Name = split(TestData,',')[0] | extend DomainSuffix = split(TestData,',')[1] | extend AzureADTenantId = split(TestData,',')[2] | extend AzureADUserId = split(TestData,',')[3] | extend DisplayName = split(TestData,',')[4] | extend AccountType = split(TestData,',')[5] | project Name, DomainSuffix, AzureADTenantId, AzureADUserId, DisplayName, AccountType
Si nous connaissons l'emplacement de nos données dans la chaîne, nous pouvons la diviser directement en colonnes nommées.

Une fois nos données divisées, nous pouvons les interroger comme si elles étaient structurées dès le départ. Ainsi, si nous ajoutons un deuxième enregistrement à nos données, puis effectuons une recherche sur des correspondances spécifiques, nous trouverons ce que nous cherchons.```kql
let ExampleText = datatable(TestData:string)
[
'Name=Reprise99,UPNSuffix=testdomain.com,AadTenantId=345c1234-a833-43e4-1d34-123440a5bcdd1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User,Type=account',
'Name=Reprise103,UPNSuffix=testdomain.com,AadTenantId=331c1234-a841-43e5-1d31-12220a5bcee1,AadUserId=cf6f2df6-b754-48dc-b7bc-c8339caf211,DisplayName=Test User 2,Type=account'
]
;
ExampleText
| extend Name = split(TestData,',')[0]
| extend DomainSuffix = split(TestData,',')[1]
| extend AzureADTenantId = split(TestData,',')[2]
| extend AzureADUserId = split(TestData,',')[3]
| extend DisplayName = split(TestData,',')[4]
| extend AccountType = split(TestData,',')[5]
| project Name, DomainSuffix, AzureADTenantId, AzureADUserId, DisplayName, AccountType
| where Name contains "Reprise99"
Nous n'obtenons qu'un seul résultat, où Name contient "Reprise99" ; notre deuxième enregistrement, où Name contient "Reprise103", n'est pas trouvé.
