
The purpose of this project is to show how to use WMI and Grafana to build an Active Directory dashboard.
The Python script uses the linux wmic client you can find here
The software requirements are the following :
- a debian / ubuntu system (you can choose another os but prefer those ones if you need help :))
- influxdb database
- grafana
- python (for me I have the version 2.7.3)
- InfluxDBClient for python
- wmic tool
In this example, you will have to create one influxdb database called admon
The first step, is to use a user account only member of “Domain Users” to query WMI : for us, I will use the username called wmiusername
On each domain controller, you will have to add this user to the WMI security :

When it is done, restart the WMI service.
Configure the config file below according to your environnement
[config] FOREST=forestname.net NSZONE=_msdcs DNSRECTYPE=NS WMIC=/usr/bin/wmic WMIUSR=wmiusername WMIPWD=wmipassword DBUSER=dbusername DBPWD=dnpassword DBNAME=admon DBIP=10.1.2.3 [mon---1] WMICLASS=Win32_PerfFormattedData_PerfOS_Processor WMIINSTANCE=_total WMIOBJECT=PercentIdleTime [mon---2] WMICLASS=Win32_PerfFormattedData_PerfOS_Memory WMIINSTANCE= WMIOBJECT=PagesOutputPersec [mon---3] WMICLASS=Win32_PerfFormattedData_PerfDisk_LogicalDisk WMIINSTANCE=_total WMIOBJECT=PercentIdleTime [mon---4] WMICLASS=Win32_PerfFormattedData_DirectoryServices_DirectoryServices WMIINSTANCE= WMIOBJECT=DSDirectoryReadsPersec [mon---5] WMICLASS=Win32_PerfFormattedData_DirectoryServices_DirectoryServices WMIINSTANCE= WMIOBJECT=DSDirectorySearchesPersec [mon---6] WMICLASS=Win32_PerfFormattedData_DirectoryServices_DirectoryServices WMIINSTANCE= WMIOBJECT=DSDirectoryWritesPersec [mon---7] WMICLASS=Win32_PerfFormattedData_DNS_DNS WMIINSTANCE= WMIOBJECT=UDPQueryReceivedPersec [mon---8] WMICLASS=Win32_PerfFormattedData_DNS_DNS WMIINSTANCE= WMIOBJECT=TCPQueryReceivedPersec [mon---9] WMICLASS=Win32_PerfFormattedData_DNS_DNS WMIINSTANCE= WMIOBJECT=RecursiveQueriesPersec [mon---10] WMICLASS=Win32_PerfFormattedData_PerfDisk_LogicalDisk WMIINSTANCE=C: WMIOBJECT=PercentFreeSpace [mon---11] WMICLASS=Win32_PerfFormattedData_PerfDisk_LogicalDisk WMIINSTANCE=C: WMIOBJECT=FreeMegabytes [mon---12] WMICLASS=Win32_PerfFormattedData_Lsa_SecuritySystemWideStatistics WMIINSTANCE= WMIOBJECT=NTLMAuthentications [mon---13] WMICLASS=Win32_PerfFormattedData_Lsa_SecuritySystemWideStatistics WMIINSTANCE= WMIOBJECT=KerberosAuthentications [mon---14] WMICLASS=Win32_PerfFormattedData_Lsa_SecuritySystemWideStatistics WMIINSTANCE= WMIOBJECT=KDCTGSRequests [mon---15] WMICLASS=Win32_PerfFormattedData_PerfNet_Server WMIINSTANCE= WMIOBJECT=ServerSessions [mon---16] WMICLASS=Win32_PerfFormattedData_PerfNet_Server WMIINSTANCE= WMIOBJECT=ErrorsLogon [mon---17] WMICLASS=Win32_PerfFormattedData_NTDS_NTDS WMIINSTANCE= WMIOBJECT=LDAPBindTime
Copy the python script to your linux box and run it
#!/usr/bin/python
from __future__ import division
import socket
import dns.resolver
import ConfigParser
import os
import subprocess
import threading
import re
import time
from influxdb import InfluxDBClient
config = ConfigParser.RawConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__))+'/config.ini')
forestname = config.get('config', 'FOREST')
nszone = config.get('config', 'NSZONE')
recordtype = config.get('config', 'DNSRECTYPE')
wmic = config.get('config', 'WMIC')
wmiusr = config.get('config', 'WMIUSR')
wmipwd = config.get('config', 'WMIPWD')
dbuser = config.get('config', 'DBUSER')
dbpwd = config.get('config', 'DBPWD')
dbname = config.get('config', 'DBNAME')
dbip = config.get('config', 'DBIP')
sct = re.compile("^mon---", re.IGNORECASE)
client = InfluxDBClient(dbip, 8096, dbuser, dbpwd, dbname)
monsct = filter(sct.search, config.sections())
forestzonefqdn = nszone + '.' + forestname
dclist = []
threadLimiter = threading.BoundedSemaphore(value=200)
class myThread (threading.Thread):
def __init__(self, threadID, name, wmicmd, wmimoninfo):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.wmicmd = wmicmd
self.wmimoninfo = wmimoninfo
def run(self):
threadLimiter.acquire()
try:
print_adrepl(self.name, self.wmicmd, self.wmimoninfo )
finally:
threadLimiter.release()
def print_adrepl(threadName, wmicmd, wmimoninfo):
timeserie = threadName + wmimoninfo
proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errorlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stderr))
if errorlines :
t = 1
outputlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stdout))
if "NumConsecutiveSyncFailures_MSAD_ReplNeighbor" in wmimoninfo:
oulen = len(outputlines)
fsynctot = 0
for line in outputlines[2:]:
fsyncsgl = int(line.split(";")[1])
fsynctot += fsyncsgl
fsync = fsynctot / oulen
json_body = [{
"points": [
[int(time.time()), float(fsync)]
],
"name": timeserie,
"columns": ["time", "value"]
}]
client.write_points(json_body, time_precision='s')
else:
if len(outputlines)>2 :
json_body = [{
"points": [
[int(time.time()), int(re.findall(r'\b\d+\b', outputlines[2:][0])[0])]
],
"name": timeserie,
"columns": ["time", "value"]
}]
client.write_points(json_body, time_precision='s')
threads = []
cred = '-U'+forestname+'/'+wmiusr+'%'+wmipwd
wmidstip = "//"+socket.gethostbyname(str(forestname))
wmiselect = "select * from MSAD_NamingContext"
wminamespace = "--namespace=Root/MicrosoftActiveDirectory"
wmidelimiter = "--delimiter=;"
wmicmd = [wmic , cred , wmidstip , wmiselect , wminamespace , wmidelimiter ]
while 1:
i = 1
for rdata in dns.resolver.query(forestzonefqdn, recordtype) :
print str(rdata.target)
dcip = socket.gethostbyname(str(rdata.target))
dcdomain = str(rdata.target).split('.',1)[1]
dclist.append([str(rdata.target) , dcdomain , dcip])
wmidstip = "//" + dcip
wmiselect = "select NumConsecutiveSyncFailures,sourceDsaCN,TimeOfLastSyncAttempt,TimeOfLastSyncSuccess from MSAD_ReplNeighbor"
wmicmd = [wmic , cred , wmidstip , wmiselect , wminamespace , wmidelimiter ]
wmimoninfo = "NumConsecutiveSyncFailures_MSAD_ReplNeighbor"
thread = myThread(i, str(rdata.target), wmicmd, wmimoninfo )
thread.start()
threads.append(thread)
i += 1
for monitor in monsct :
if config.get(monitor, 'WMIINSTANCE') != "" :
wmiinstance = " where name='"+config.get(monitor, 'WMIINSTANCE')+"'"
else :
wmiinstance = " "
wmiselect = "select " + config.get(monitor, 'WMIOBJECT') + " from " + config.get(monitor, 'WMICLASS') + wmiinstance
wmidelimiter = "--delimiter=;"
wmicmd = [wmic , cred , wmidstip , wmiselect , wmidelimiter ]
wmimoninfo = config.get(monitor, 'WMICLASS')+"_"+config.get(monitor, 'WMIOBJECT')
thread = myThread(i, str(rdata.target), wmicmd, wmimoninfo )
thread.start()
threads.append(thread)
i += 1
#limitation on x86 system
while threading.active_count() > 200 :
time.sleep(1)
for t in threads:
t.join()
print "Exiting Main Thread"
You can use the following AD_Grafana_Dashboard to import in Grafana to get a dashboard like this :
You can download the archive that contains all required files admon
Do not hesitate to contact me if you have any questions
My Powershell script categories
- Active Directory
- Cluster
- Database
- Exchange
- Files and folders
- Hardware
- Network
- Operating System
- PKI
- SCCM
- Service and process
- Tips
- VMWare





This looks really cool but how did you get this all set up on the server? As someone who is fairly new to linux, I am not sure how to get this all up and running. Thanks
This looks really cool but how did you get this all set up on the server? As someone who is fairly new to linux, I am not sure how to get this all up and running. Thanks