Wednesday, April 4, 2018

Powershell Hash table of Arrays

The Problem

I am working on a project that has me reviewing all our groups in AD. These groups are nested in places multiple layers deep.

All Staff 
-> Technical
- - > Server Team
- - -> Server Architecture & Build
- - -> Server M&O
- - > Network
-> HR

With this, I've wanted to modify one of those nested groups, changing it so it could be replicated to O365 and used as a distribution list. Unfortunately, to do this, I need to mail-enable the on-premise copy, then have that replicate up. As this is a legacy group, it was created as a Global group. You can't mail-enable Global groups and you can't convert children of Global groups to universal. So you need to determine all the 'parent' groups of a specific child.

The Process

This script creates a hash table with 2 properties (Parents and Children) and attempts to create a flat hierarchy.
  • "Technical" 
    • parents are "All Staff" 
    • children are "Server Team", "Server Architecture & Build", "Server M&O" and "Network". 

Once the script is ran, it returns the hash table for all groups in the environment with these properties populated. I am still working out a bug to populate parents and children several layers up/down (for example if Server Architecture and Build has more layers underneath it).. I have worked around this by simply running the meat of the script twice. I haven't verified if possibly need a 3rd or 4th iteration to capture deeply nested groups yet.

A Hash table of Arrays:
My hash table starts off as a fairly basic hash table. 

$MyGroups = @{}

I then populate it with two different array objects. I pulled this concept from the Hey Scripting Guy Blog.

#Capture all groups with email addresses populated.
$AllGroups = Get-Group -resultsize unlimited  ### For DLS ## -filter {WindowsEmailAddress -like "*"} 
$AllGroups | %{$MyGroups[$_.identity] = @{Parents=@(); $Children=@()}}

Now you can access 


and get something like: 

Name                      Value
Parents                   {}
Children                  {}

As the script loops through all the groups, 
  1. it populates 'children' with all member/child groups (groups that are members of this group) 
  2. loops through each child group and sets itself as a parent
  3. copies this existing (parents of this group) parents to all those children. 
  4. copies children from all it's children. 
So depending on the order the groups are populated, one pass appears to do 80% of the copying, but not all. If "Server Team" was reviewed before "All Staff", then they won't know about each other. As I hinted at, running this loop twice, appears to pick these changes up. 

The Script

The final step is something like:

$Groups = .\Report-GroupHierarchy.ps1
$Groups["Technical"].Parents | Set-Group -Universal

Note: On-premise AD, the object identity was the fully qualified object path, and in O365, the object identity is the same as a the object name. So depending on where you are gathering the data, you may need to enter different values.


My Own Grandpa

With a little playing around, you could use the same construct to look for "I'm My Own Grandpa" situations.

Let's see, this works:

$grandpa = get-group | ?{Compare-Object $Groups[$_.identity].parents $Groups[$_.identity].children -ExcludeDifferent -IncludeEqual}

The real problem revisited:

The reason I was reviewing groups, was to see why a user wasn't receiving any email. With 1500 distribution lists it's actually very easy for an end-user to not end up on any of the 195 distribution lists that are the children of the 'All Staff' distribution list.

So where should this employee go? Let's find out who they work for.. First let's start off with a little query about direct reports..Over on the LazyWinAdmin there's a nice recursive script to pull all direct reports to their manager.

Find manager's subordinates -> find groups these people belong to -> find the group that has a parent of "All Staff".

Get-ADdirectReports -samaccountname "Manager" | %{(.\memberof-O365DL.ps1 -identity $_).memberof } | select -unique identity | ?{$Groups[$_.identity].parents -eq "All Staff"}

No comments:

Post a Comment