Skip to content

Instantly share code, notes, and snippets.

@rthill
Forked from rjnienaber/orgchartBuilder.py
Created April 6, 2017 09:09
Show Gist options
  • Select an option

  • Save rthill/fbe4b3dde8378fed3183037335200308 to your computer and use it in GitHub Desktop.

Select an option

Save rthill/fbe4b3dde8378fed3183037335200308 to your computer and use it in GitHub Desktop.
Script to build an organizational chart from Active Directory
'''Dependencies:
ldap - http://pypi.python.org/pypi/python-ldap/
graphviz - http://www.graphviz.org/Download_windows.php
pydot - pip install pydot
'''
import argparse
import ldap
import re
from pydot import Dot, Node, Edge
class LdapSearcher(object):
def __init__(self, server, partDomain, user, password):
l = ldap.initialize(server)
l.set_option(ldap.OPT_REFERRALS, 0)
l.protocol_version = 3
l.simple_bind_s(user, password)
self.ldap = l
self.scope = ldap.SCOPE_SUBTREE
self.attributes = ['directReports', 'displayName', 'title']
self.partDomain = partDomain
def search(self, base, filter=None):
return self.ldap.search_s(base, self.scope, filter, self.attributes)
def search_by_accountName(self, base, name):
filter = '(&(objectClass=user)(sAMAccountName=' + name + '))'
return self.search(base, filter)
def retrieve_hierarchy(self, rootUserName, maxDepth):
root = self.search_by_accountName(self.partDomain, rootUserName)
rootName = self.getName(root)
visited = set([])
reports = {}
self.recurse_direct_reports(root, visited, reports, 0, maxDepth)
return rootName, reports
def getName(self, entry):
attributes = entry[0][1]
title = '' if not 'title' in attributes else attributes['title'][0]
return (attributes['displayName'][0], title)
def recurse_direct_reports(self, entry, visited, reports, depth, maxDepth):
if depth == maxDepth:
return
dn, properties = entry[0]
if dn in visited:
return
visited.add(dn)
if not 'directReports' in properties:
return
name = self.getName(entry)
reports[name] = []
for report in properties['directReports']:
reportEntry = self.search(report, '(objectClass=*)')
reports[name].append(self.getName(reportEntry))
self.recurse_direct_reports(reportEntry, visited, reports, depth + 1, maxDepth)
def render_graph(root, hierarchy, args):
g = Dot()
g.set_root(root[0])
for manager in hierarchy:
g.add_node(Node(manager[0], shape='box'))
for subordinate in hierarchy[manager]:
g.add_node(Node(subordinate[0], shape='box'))
g.add_edge(Edge(manager[0], subordinate[0]))
g.write_svg(args.file, args.imageType, args.layout)
def main(args):
#Doing something a bit silly here, you may have to tweak it
domain = re.search('ldap://((.*\.?)+)', args.server)
split = domain.groups()[0].split('.')[1:]
partDomain = 'dc=' + ',dc='.join(split)
print ''
print 'Creating hierarchy...'
searcher = LdapSearcher(args.server, partDomain, args.user, args.password)
root, hierarchy = searcher.retrieve_hierarchy(args.root, args.depth)
print 'Rendering graph...'
render_graph(root, hierarchy, args)
print 'Completed'
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--server', '-s', help='The FQDN of the ldap server e.g. ldap://domain.server.com', required=True)
parser.add_argument('--user', '-u', help='The user to log into the ldap server with e.g. user@domain.server.com', required=True)
parser.add_argument('--password', '-p', help='The password of the user to use to login into the ldap server', required=True)
parser.add_argument('--root', '-r', help='The root user''s login name e.g. name.surname', required=True)
parser.add_argument('--file', '-f', help='The file to output to e.g. c:\chart.png', required=True)
parser.add_argument('--depth', '-d', type=int, default=max, help='The amount of levels to represent in the hierarchy.')
parser.add_argument('--imageType', '-i', help='An image type understood by GraphViz e.g. png, svg, etc.')
parser.add_argument('--layout', '-l', help='A layout type understood by GraphViz e.g. dot, neato, circo, etc.')
args = parser.parse_args()
main(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment