Organizational Awareness with PowerShell
Friday, 24 June 2011
One of the things that’s always unsettled me a bit in a big organization is keeping yourself informed about changes that you care about, even when those changes never end up making it through the grape vine. For example:
- Promotions. Of course you want to congratulate them!
- Transfers. Finding out that a team has moved to another manager, but no “re-org” mail to let you know.
- Leaving the company. Finding out that a co-worker has left the company or division.
Naturally, PowerShell is here to save the day! I’ve been running a script I call “OrgDiff” as a scheduled task now for a long time – it came about organically, and I look forward to its results every Monday.
Here a couple of great examples of a few little bits of code (75 lines) coming together to make something of surprising utility.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 |
param($User = 'mybigboss', $Limit = [Int32]::MaxValue, [Switch] $IncludeManager)
$depth = 0 ## A list of all the domains that you want to search for## a given user's alias. Try to organize them in order ## of popularity so that your script runs as quick as possible. $domains = "LDAP://DC=domain1,DC=corp,DC=contoso,DC=com", "LDAP://DC=vendors,DC=corp,DC=contoso,DC=com", "LDAP://DC=international,DC=corp,DC=contoso,DC=com" ## Find all of the direct reports for a given user function GetReports($username, $depth) { if(-not $username) { return } if($depth -gt $Limit) { return } ## Go through all of the domains and try to find the user ## account in that domain foreach($domain in $domains) { $adsi = [ADSI] $domain $searcher = New-Object System.DirectoryServices.DirectorySearcher $adsi if($user) { break } } if(-not $user) { Write-Error "Could not find $username" return } ## If we want the report to include information about the user's manager, ## prepare that portion of the output. if($IncludeManager) { $manager = " - " + (($user.Properties.manager -split ',')[0] -replace 'CN=','') } ## Display the user's name, alias, title, and (optionally) manager - ## indented according to how deep we're investigating (" " * ($depth * 2)) + $user.Properties.name + " (" + $user.Properties.mailnickname + ") - $($user.Properties.title)$manager" $depth++ ## If the user has direct reports, call this function again to show { $report = [ADSI] "GC://$directReport" GetReports ($report.mailNickname) $depth } } GetReports $user $depth |
Normal output looks like this:
Some Body (somebody) - GENERAL MANAGER
Jeffrey Snover (jsnover) - DISTINGUISHED ENGINEER
Cool Person (coolbeans) - BUSINESS ADMINISTRATOR
Super Developer (superdev) - DIRECTOR OF DEVELOPMENT
Dev Manager (devmgr) - DEVELOPMENT MANAGER
Dev Lead (devlead) _ DEVELOPMENT LEAD
Dev Manager2 (devmgr2) - DEVELOPMENT MANAGER
(etc)
Now, if you run a weekly script to generate this report into a text file on your hard drive, you can use PowerShell to compare last week’s file with this week’s file. However, when we just compare files line-by-line, you’ll lose the indentation – and therefore the context of who their manager is. That leads to the need for the “-IncludeManager” switch, which generates output more like this:
Some Body (somebody) - GENERAL MANAGER
Jeffrey Snover (jsnover) - DISTINGUISHED ENGINEER - Some Body
Cool Person (coolbeans) - BUSINESS ADMINISTRATOR - Some Body
Super Developer (superdev) - DIRECTOR OF DEVELOPMENT - Some Body
Dev Manager (devmgr) - DEVELOPMENT MANAGER - Super Developer
Dev Lead (devlead) _ DEVELOPMENT LEAD - Dev Manager
Dev Manager2 (devmgr2) - DEVELOPMENT MANAGER - Dev Manager
(etc)
Here's “Compare-OrgChart.ps1”, a script that starts to show useful differences between two files:
001
002
003
004
005
006
007
008
009
010
011
012
013
014 param($BeforePath, $AfterPath) ## Get the content from the first file and the second file, then sort them by name.
## This lets Compare-Object focus primarily on changes to people, and not be
## impacted by random orderings that might come back from Active Directory
$chart1 = Get-Content $BeforePath | % { $_.Trim() } | Sort-Object
$chart2 = Get-Content $AfterPath | % { $_.Trim() } | Sort-Object ## The format of the lines are:
## Super Developer (superdev) - DIRECTOR OF DEVELOPMENT
## So we tell Compare-Object to look for any changes, sort by only their name,
## and after that sort by "Before" then "After"
Compare-Object $chart1 $chart2 |
Sort { ($_.InputObject -split '-')[0] },SideIndicator
Now, pretend the following happens:
- Jeffrey gets promoted to Technical Fellow (congrats!)
- “Dev Manager” leaves the group
- “Dev Lead” moves under “Dev Manager2”
Here’s the org chart:
Some Body (somebody) - GENERAL MANAGER
Jeffrey Snover (jsnover) - TECHNICAL FELLOW - Some Body
Cool Person (coolbeans) - BUSINESS ADMINISTRATOR - Some Body
Super Developer (superdev) - DIRECTOR OF DEVELOPMENT - Some Body
Dev Manager2 (devmgr2) - DEVELOPMENT MANAGER - Dev Manager
Dev Lead (devlead) _ DEVELOPMENT LEAD - Dev Manager2(etc)
From there, we get this output:
PS C:\Users\Lee> Compare-OrgChart C:\temp\org_before.txt C:\temp\org_after.txt
InputObject SideIndicator
----------- -------------
Dev Lead (devlead) _ DEVELOPMENT LEAD - Dev Manager <=
Dev Lead (devlead) _ DEVELOPMENT LEAD - Dev Manager2 =>
Dev Manager (devmgr) - DEVELOPMENT MANAGER - Super Developer <=
Jeffrey Snover (jsnover) - DISTINGUISHED ENGINEER - Some Body <=
Jeffrey Snover (jsnover) - TECHNICAL FELLOW - Some Body =>
That’s pretty cool! Now all we need is a little way to stitch it all together. Here’s a very simple “Update-OrgDiff.ps1” script. All it does is fetch the newest org chart, run the comparison, and email me the results.
001
002
003
004
005
006
007
008
009
010 $mailRecipient = "[email protected]"$file = "OrgChart-{0}-{1}-{2}.txt" -f (Get-Date).Month,(Get-Date).Day,(Get-Date).Year
Get-OrgChart somebody -IncludeManager > "c:\temp\$file"
$results = dir c:\temp\OrgChart-* | Sort
60;LastWriteTime | Select -Last 2
$report = Compare-OrgChart $results[0].FullName $results[1].FullName |
Format-Table -Auto | Out-String -Width 100Send-MailMessage -To $mailRecipient -From $mailRecipient -Subject OrgDiff
`
-BodyAsHtml "<html><body><pre>$report</pre></body></html>" -SmtpServer smtphost
Set this up as a scheduled task, and you’re golden!
No. 1 — June 24th, 2011 at 9:21 am
[…] Organizational Awareness with PowerShell (Lee Holmes) […]