Active Directory : alerte e‑mail à chaque jointure d’ordinateur (Event ID 4741, GPO, SIEM, PowerShell)

Recevez un e‑mail dès qu’un poste est joint au domaine Active Directory. Ce guide pas‑à‑pas (GPO, Event ID 4741, WEF, SIEM, PowerShell) propose scripts prêts à l’emploi, bonnes pratiques et tests pour un déploiement multisite fiable.

Sommaire

Vue d’ensemble de la question

Dans un environnement Active Directory (AD) réparti sur plusieurs sites et administré par des équipes globales, vous souhaitez être notifié automatiquement lorsqu’un nouvel ordinateur est joint au domaine. L’objectif : transformer un événement d’audit (Security) en une alerte exploitable (e‑mail, ticket, webhook) tout en respectant les bonnes pratiques de sécurité et d’exploitation.

Techniquement, la jointure génère des événements d’audit sur les contrôleurs de domaine (DC). En activant l’audit, en centralisant ces journaux et en déclenchant une action (script/alerte), vous obtenez une chaîne d’alerte robuste et traçable.

Réponse et solutions proposées (vue rapide)

ÉtapeActionDétails et bonnes pratiques
1. Activer l’audit détailléGPO → Computer Configuration → Policies → Windows Settings → Security Settings → Advanced Audit Policy ConfigurationSous Audit Policy → Account Management → Audit Computer Account Management, activez Success et, si pertinent, Failure.
Si vous utilisez la stratégie héritée, activez Computer Account Management dans « Audit account management ».
2. Identifier les événements clésDans le journal Security (sur DC) :
4741Computer account was created (jointure réussie)
4742Computer account was changed
4743Computer account was deleted
Ces IDs suffisent pour l’alerte « un nouvel hôte a été joint ».
Optionnel : surveillez également les événements Netlogon d’échec ou inattendus (ex. 5805/5722 selon versions) pour investiguer les tentatives.
3. Collecter et centraliserWindows Event Forwarding (WEF) vers un serveur collecteur
• Ou ingestion dans un SIEM (Microsoft Sentinel, Splunk, QRadar, …)
La centralisation simplifie la corrélation inter‑DC et évite des configurations redondantes. WEF côté DC + une seule règle d’alerte côté collecteur/SIEM.
4. Déclencher la notificationSans SIEM : Task Scheduler sur le collecteur → déclenchement à l’arrivée du 4741 → exécution d’un script PowerShell qui envoie l’e‑mail. Avec SIEM : créez une règle d’alerte déclenchée sur 4741.Voir plus bas : scripts PowerShell, exemples de requêtes KQL (Sentinel) et SPL (Splunk), modèles d’e‑mail, et recommandations anti‑bruit.
5. (Option) Exploiter NetlogonEvent Viewer → Applications and Services Logs → Microsoft → Windows → Netlogon → OperationalUtile pour diagnostiquer l’IP source, les échecs de jointure ou des comportements suspects.

Important : l’action « Send an e‑mail » de la Planification des tâches est dépréciée depuis Windows Server 2012 R2. Utilisez un script PowerShell (SMTP ou Microsoft Graph) ou un connecteur SMTP interne.

Pourquoi ces événements ?

Le cœur de l’alerte repose sur Event ID 4741 (Computer account was created), inscrit dans le journal Security des DC lorsqu’un compte ordinateur est créé en AD. C’est le cas typique lorsqu’une machine est jointe « à la volée ». Si vous précréez des comptes ordinateurs (pré‑staging), la jointure reconfigure un compte existant : vous observerez plutôt un 4742 (Computer account was changed). D’où l’intérêt de surveiller 4741 (création) et, selon vos pratiques, certains 4742 (modification significative : UAC, SPN, DNS).

Pour la chasse aux anomalies, les journaux Netlogon/Operational peuvent compléter l’analyse (pistes d’échec, IP source, contrôleur sollicité, etc.).

Implémentation pas‑à‑pas

Activer l’audit via GPO

  1. Créez/éditez une GPO liée aux Domain Controllers.
  2. Parcours : Computer Configuration → Policies → Windows Settings → Security Settings → Advanced Audit Policy Configuration → Audit Policies → Account Management.
  3. Activez Audit Computer Account Management en Success (et Failure si vous voulez capter les erreurs).
  4. Forcez la GPO : gpupdate /force sur les DC, ou laissez répliquer.

Centraliser avec Windows Event Forwarding (WEF)

WEF est natif, gratuit et scalable pour remonter les événements des DC vers un collecteur.WEF : configuration rapide (source‑initiated)

  1. Sur le collecteur, lancez le service Windows Event Collector et configurez l’écoute WinRM.
  2. Côté DC, via GPO, définissez Computer Configuration → Administrative Templates → Windows Components → Event Forwarding → Configure target Subscription Manager avec une URL du type :
    Server=http://<CollecteurFQDN>:5985/wsman/SubscriptionManager/WEC,Refresh=10
  3. Créez une Subscription sur le collecteur ciblant le journal Security des DC avec un filtre XPath sur 4741/4742 :
<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">* [System[(EventID=4741 or EventID=4742)]]</Select>
  </Query>
</QueryList>

Les événements remontent alors dans Forwarded Events sur le collecteur.

Déclencher un e‑mail sans SIEM (Planificateur + PowerShell)

Sur le collecteur, créez une tâche planifiée déclenchée à l’arrivée d’un 4741 dans Forwarded Events (ou directement Security si vous n’utilisez pas WEF). L’action exécute un script PowerShell qui extrait les données de l’événement et envoie un e‑mail via SMTP ou Microsoft Graph.Créer la tâche (exemple avec schtasks)

schtasks /Create /TN "AD-New-Computer-Alert" ^
  /SC ONEVENT ^
  /EC "ForwardedEvents" ^
  /MO "*[System[(EventID=4741)]]" ^
  /TR "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\On-AD4741.ps1" ^
  /RU "SYSTEM"

Astuce : le script recherchera l’événement le plus récent 4741 au moment de l’exécution pour éviter toute dépendance à des variables d’environnement spécifiques au déclencheur.Script PowerShell prêt à l’emploi (SMTP ou Graph)

Enregistrez sous C:\Scripts\On-AD4741.ps1. Ce script :

  • lit le dernier événement 4741 (Forwarded Events → fallback sur Security),
  • analyse le XML pour extraire auteur, compte machine, DC, horodatage, SPN/DNS si présents,
  • envoie un e‑mail en SMTP ou via Microsoft Graph (si module/méthode disponibles).
param(
  [string]$SmtpServer = "smtp.example.com",
  [int]$SmtpPort = 25,
  [string]$From = "ad-alerts@example.com",
  [string]$To = "it-sec@example.com",
  [switch]$UseGraph
)

function Get-Last4741 {
$logs = @("ForwardedEvents","Security")
foreach ($log in $logs) {
try {
$evt = Get-WinEvent -FilterHashtable @{LogName=$log; Id=4741; StartTime=(Get-Date).AddMinutes(-10)} -MaxEvents 1 -ErrorAction Stop
if ($evt) { return $evt }
} catch { }
}
return $null
}

function Parse-Event4741([System.Diagnostics.Eventing.Reader.EventRecord]$evt) {
if (-not $evt) { return $null }
[xml]$xml = $evt.ToXml()
$sys = $xml.Event.System
$data = @{}
foreach ($d in $xml.Event.EventData.Data) { $data[$d.Name] = $d.'#text' }

# Tolérance aux variations de schéma/versions

$acct     = $data['TargetUserName']; if (-not $acct) { $acct = $data['SamAccountName'] }
$who      = $data['SubjectUserName']; if (-not $who) { $who = $data['CallerUserName'] }
$domain   = $data['SubjectDomainName']
$dns      = $data['DnsHostName']
$spn      = $data['ServicePrincipalNames']
$when     = try { [DateTime]::Parse($sys.TimeCreated.SystemTime) } catch { Get-Date }

[pscustomobject]@{
LogName    = $evt.LogName
RecordId   = $evt.RecordId
EventId    = $evt.Id
TimeCreated= $when
ComputerDC = $sys.Computer
NewAccount = $acct
DnsHost    = $dns
SPN        = $spn
Actor      = if ($domain) { "$domain$who" } else { $who }
}
}

function Send-MailSMTP([string]$subject, [string]$body) {
try {
Send-MailMessage -SmtpServer $SmtpServer -Port $SmtpPort -From $From -To $To -Subject $subject -Body $body
} catch {
Write-Error "Échec d'envoi SMTP: $($_.Exception.Message)"
}
}

function Send-MailGraph([string]$subject, [string]$body) {
try {
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) {
Write-Error "Module Microsoft.Graph absent. Utilisez -UseGraph:$false ou installez le module."
return
}
Import-Module Microsoft.Graph -ErrorAction Stop
Connect-MgGraph -Scopes "Mail.Send"
$message = @{
Subject = $subject
Body = @{ ContentType = "Text"; Content = $body }
ToRecipients = @(@{EmailAddress=@{Address=$To}})
From = @{EmailAddress=@{Address=$From}}
}
Send-MgUserMail -UserId $From -Message $message -SaveToSentItems
} catch {
Write-Error "Échec d'envoi Graph: $($_.Exception.Message)"
}
}

$evt = Get-Last4741
if (-not $evt) { Write-EventLog -LogName Application -Source "AD-Alerts" -EventId 1000 -EntryType Warning -Message "Aucun 4741 récent détecté." -ErrorAction SilentlyContinue; exit 0 }

$info = Parse-Event4741 -evt $evt
if (-not $info) { exit 0 }

$subject = "Nouvelle jointure AD: $($info.NewAccount)"
$body = @"
Type: 4741 (Computer account was created)
Machine: $($info.NewAccount)  (DNS: $($info.DnsHost))
Acteur: $($info.Actor)
Contrôleur: $($info.ComputerDC)
Horodatage: $($info.TimeCreated.ToString("u"))
SPN(s): $($info.SPN)
Log: $($info.LogName) / RecordId: $($info.RecordId)
"@

if ($UseGraph) { Send-MailGraph -subject $subject -body $body } else { Send-MailSMTP -subject $subject -body $body } 

Remarque : Send-MailMessage est déprécié mais reste pratique en environnement on‑prem avec relais SMTP interne. En cloud hybride, privilégiez la voie Microsoft Graph (-UseGraph), après configuration des permissions de l’application.Variante : enrichissement AD (facultatif)

Si le module AD est disponible sur le collecteur (RSAT), vous pouvez enrichir l’e‑mail avec des attributs (OU, dNSHostName, whenCreated, etc.).

Import-Module ActiveDirectory
$comp = $info.NewAccount -replace '\$$','' # retire le $ final des comptes ordinateurs
try {
  $ad = Get-ADComputer $comp -Properties whenCreated, dNSHostName, ManagedBy
  $body += "`r`nOU: $($ad.DistinguishedName)`r`nCréé: $($ad.whenCreated)`r`nManagedBy: $($ad.ManagedBy)"
} catch { $body += "`r`n(Enrichissement AD indisponible: $($_.Exception.Message))" }

Déclencher via SIEM (recommandé en production)

Si vos DC envoient déjà leurs journaux vers un SIEM, créez une règle d’alerte sur le 4741 ; ajustez la sensibilité et ajoutez une suppression du bruit (throttling) pour éviter les rafales lors de déploiements.Exemple KQL (Microsoft Sentinel)

SecurityEvent
| where EventID == 4741
| project TimeGenerated, Computer, AccountCreated = TargetAccount, Actor = SubjectAccount, Activity
| extend Machine = tostring(AccountCreated)
| summarize Count = count() by bin(TimeGenerated, 5m), Machine

Déclenchez une alerte en Near‑real time avec une fréquence de 5 minutes et ajoutez un suppression duration (ex. 30 min) pour empêcher des doublons massifs.Exemple SPL (Splunk)

index=wineventlog sourcetype=WinEventLog:Security EventCode=4741
| stats count by host, Target_Account, Subject_Account

Utilisez une Alert en mode Real‑time ou Scheduled (toutes les 5 min), avec envoi par e‑mail et formatage personnalisé.

Filtrage et réduction du bruit

En grands environnements, vous verrez des pics lors de déploiements ou d’images usine. Pour garder des alertes actionnables :

  • Par site/DC : ajoutez le nom du DC dans l’e‑mail pour identifier le site d’origine.
  • Par OU : ne notifiez que pour certaines unités d’organisation (ex. postes VIP, serveurs).
  • Par plage IP : combinez avec Netlogon/Operational pour récupérer l’IP source et ignorer certaines VLAN de staging.
  • Regroupement : dans un SIEM, agrégez <= N événements sur 10 min en une alerte unique.
  • Semaine blanche : suspendez l’alerte pendant des déploiements planifiés (fenêtrage via calendrier).

Sécurité et gouvernance : ce qu’il faut absolument faire

  • Limiter « Ajouter des postes de travail au domaine » : réduisez le périmètre des comptes autorisés (User Rights Assignment → Add workstations to domain).
  • Fixer ms-DS-MachineAccountQuota à 0 : par défaut, les utilisateurs authentifiés peuvent joindre jusqu’à 10 machines. Pour l’empêcher : Import-Module ActiveDirectory Set-ADDomain -Identity (Get-ADDomain).DNSRoot -Replace @{'ms-DS-MachineAccountQuota'=0} Get-ADDomain | Select-Object 'ms-DS-MachineAccountQuota'
  • Surveiller les 4742 anormaux : changements inattendus d’UAC, SPN, dNSHostName après création.
  • Activer des détections comportementales : outils d’identité (ex. « Unusual addition of a new computer ») pour repérer volumes ou schémas anormaux.
  • Traçabilité : journalisez et conservez au minimum 90 jours sur le collecteur/SIEM.

Diagnostics et vérifications

SymptômeCause probableCorrectif/contrôle
Pas d’événements 4741GPO d’audit non appliquée sur DCVérifiez Advanced Audit Policy, forcez gpupdate, contrôlez Resultant Set of Policy.
Événements sur DC mais pas sur collecteurWEF non configuré, WinRM bloqué, abonnement inactifContrôlez l’URL Subscription Manager, pare‑feu, service Windows Event Collector, état de la subscription.
E‑mails non reçusRelais SMTP ou authentification GraphTestez l’envoi : Send-MailMessage vers une boîte de test ; pour Graph, validez les permissions Mail.Send.
Beaucoup d’alertes en rafaleDéploiements de masseRegroupez (SIEM), mettez un throttle (30 min), filtrez par OU/site.
Alerte manquante en pré‑stagingCompte déjà créé → 4742 au lieu de 4741Ajoutez une règle complémentaire sur 4742 avec critères (ex. dnsHostName défini pour la première fois).

Modèles prêts à copier

Requête XPath (Event Trigger / WEF)

*[System[(EventID=4741)]]

Modèle d’e‑mail (texte)

Sujet: Nouvelle jointure AD: <NOM-MACHINE>

Une machine vient d'être jointe au domaine.

Machine:  ()
Acteur: 
Contrôleur: 
Horodatage: 
SPN(s): 

Référence: Security Event 4741 (et 4742 si modification)

Webhook Teams (option)

Sans e‑mail ? Transformez $subject et $body en JSON pour un webhook entrant, à appeler via Invoke-RestMethod.

$payload = @{
  title = $subject
  text  = ($body -replace "`r`n","<br/>")
} | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri $TeamsWebhook -Body $payload -ContentType 'application/json'

Plan de test E2E (fortement recommandé)

  1. Un DC de labo avec la GPO d’audit appliquée.
  2. Une VM Windows non jointe.
  3. Joindre la VM au domaine : dsregcmd /status n’est pas requis, une simple jointure AD suffit (System Properties → Domain ou Add-Computer PowerShell).
  4. Vérifier 4741 sur le DC : Event Viewer → Security.
  5. Vérifier la collecte sur le collecteur/SIEM.
  6. Vérifier l’e‑mail reçu (délai < 2–3 min avec WEF).
  7. Tester les variantes : pré‑staging (contrôler 4742), jointure sur un autre site/DC, machine renommée.

Aller plus loin : enrichissement et corrélation

  • Attribution par site : cartographiez DC → site AD et incluez le site dans l’e‑mail.
  • Corrélation 4741 + 4742 : recherchez une modification 4742 sur le même compte dans les 10 minutes suivant 4741 (ajout SPN, dNSHostName).
  • Incrémental : Si l’alerte est trop bavarde, restreignez aux OU « serveurs » ou à des préfixes de nommage (ex. SRV-*, VIP-*).
  • Inventaire : stockez chaque alerte dans une table (ex. CSV/DB) pour suivre les volumes par site/équipe.

FAQ

Pourquoi je vois des 4741 mais aucun nom d’IP source ?

Le 4741 est un événement d’account management sur le DC ; il ne contient pas toujours l’IP source de la machine jointe. Croisez avec Netlogon/Operational pour récupérer des éléments réseau.

Le champ du compte cible se termine par un « $ ». Est‑ce normal ?

Oui. Les comptes ordinateurs AD sont des objets « utilisateur » dont le sAMAccountName est suffixé par $. Retirez‑le pour interroger Get-ADComputer.

Dois‑je aussi surveiller 4743 ?

Ce n’est pas nécessaire pour l’alerte « nouvelle jointure », mais utile pour l’audit du cycle de vie (suppression de comptes machines).

Et si je ne veux qu’une alerte par jour et par site ?

Implémentez un regroupement côté SIEM (binning temporel) ou un mécanisme de cache dans votre script (ex. fichier JSON listant les machines déjà notifiées aujourd’hui).

Annexes

Exemple d’extraction robuste des champs 4741

[xml]$x = $evt.ToXml()
$map = @{}; foreach($d in $x.Event.EventData.Data){ $map[$d.Name] = $d.'#text' }
$computer = $map['TargetUserName']; if (-not $computer) { $computer = $map['SamAccountName'] }
$actor = if ($map['SubjectDomainName']) { "$($map['SubjectDomainName'])\$($map['SubjectUserName'])" } else { $map['SubjectUserName'] }
$dc = $x.Event.System.Computer
$time = [datetime]$x.Event.System.TimeCreated.SystemTime

Création de la tâche via PowerShell (XML de subscription)

Les event‑based triggers acceptent un XML. Exemple :

$subXml = @'
<QueryList>
  <Query Id="0" Path="ForwardedEvents">
    <Select>*[System[(EventID=4741)]]</Select>
  </Query>
</QueryList>
'@
$trig = New-ScheduledTaskTrigger -Subscription $subXml
$act  = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\On-AD4741.ps1"
Register-ScheduledTask -TaskName "AD-New-Computer-Alert" -Trigger $trig -Action $act -RunLevel Highest -User "SYSTEM"

Modèle d’alerte 4742 (pré‑staging)

SecurityEvent
| where EventID == 4742
| where TargetAccount endswith "$"
| where isnotempty(AdditionalInformation) or EventData contains "DnsHostName"
| project TimeGenerated, TargetAccount, SubjectAccount, Computer

Conclusion

En combinant audit AD (4741/4742), collecte centralisée (WEF/SIEM) et un déclencheur (script PowerShell ou règle SIEM), vous obtenez une notification rapide, standardisée et exploitable dès qu’un nouvel ordinateur est joint au domaine. Le tout avec des garde‑fous (filtrage par OU/site, throttling, surveillance des 4742 anormaux) et une gouvernance renforcée (quota de jointure, délégations minimales).


Récapitulatif opérationnel

  1. GPO : activer Audit Computer Account Management (Success/Failure).
  2. Événements clés : 4741 (création), 4742 (modification), 4743 (suppression), Netlogon (diagnostic).
  3. Collecte : WEF (Forwarded Events) ou SIEM.
  4. Déclenchement : Tâche planifiée + script PowerShell (SMTP/Graph) ou règle SIEM.
  5. Réduction du bruit : filtrage OU/site, regroupement, throttling, calendrier de déploiement.
  6. Sécurité : restreindre le droit « Add workstations to domain », mettre ms-DS-MachineAccountQuota à 0.
  7. Tests : jointure de test, vérification 4741 sur DC, réception e‑mail, cas pré‑staging.
Sommaire