AD Security Group matrix

‘Sup PSHomies,

So last blog was about adding members to a security group efficiently. Which got me thinking, can I reverse this process? If given the security groups with specified members, can I recreate the csv? I do love me a challenge!

So to pick up where we left off, I’ll be using the $addADGroupMembers to repopulate the csv…

#Security Matrix
$Groups = $addADGroupMembers.Keys
function Convert-ArrayToHash($a){
    begin { $hash = @{} }
    process { $hash[$_] = $null }
    end { return $hash }

$template = [PSCustomObject]([Ordered]@{UserID=$null} + $($Groups | Convert-ArrayToHash))

$addADGroupMembers has all the group names we need. I’m converting the group names into an empty hashtable. The $template variable is a custom object I’ll be using to move things  along, I’ll explain as we go…


Now for the tricky part…

$arrMatrix = @()

$Groups |
   $GroupName = $_
      $addADGroupMembers.$_ |
         if($arrMatrix.Count -eq 0) {
            $newItem = $template.PSObject.Copy()
            $newItem.UserID = $_
            $newItem.$GroupName = '1'
            $arrMatrix += $newItem
               $index = [array]::IndexOf($arrMatrix.UserID, $_)
               $arrMatrix[$index].$GroupName = '1'
               $newItem = $template.PSObject.Copy()
               $newItem.UserID = $_
               $newItem.$GroupName = '1'
               $arrMatrix += $newItem

First we have an array to save the results. We only need to worry about groups with members. The $template has been initialized with $null. The first time it runs, $arrMatrix.Count will be zero, so just add this group to get things started. Here’s where it gets interesting, in order to add a newItem to the array I have to clone it first. Adding and saving this way makes sure I have a row with a unique UserID. Truth be told I had to google to figure this one out. Modifying $template and then assigning it to $newItem will only assign the reference. Change the value once more and every item in the array changes! I read it somewhere, it was fun to stumble on this… The more you know…

The next trick was to find the index of a UserID already saved. Google to the rescue! I found this neat trick of using [array]::IndexOf(). This will give you the first index with that value. Lucky for me, my UserIDs are unique 😉
Once I have my index I can add a value of ‘1’ to the group if the UserID is a member. If I can’t find a UserID then a new unique UserID is added to the $arrMatrix

Ok enough chit-chat here’s some code to play with

This should be your endresult


No too shabby eh? 😛

Ok Urv that’s all good and well but when am I going to use this?

Why thank you for asking!

If you’ve ever been in charge of implementing Role Based Access Control then you could appreciate this. A security matrix like this is where I’d start, only now you don’t have to start from scratch… 😉

Here’s how it works…


I created a security group Rol-Consultant for RBAC purposes. This group is a member of all the APP-* groups giving any member access by way of group nesting. Users who are a member of Rol-Consultant don’t have to be a direct member for access. The down side of RBAC is it’s all or nothing, exceptions are real deal breakers…

I did a blog about reporting a user’s nested group membership. Let take user ‘dlbouchlaghmi’. This is what his effective user group membership looks like in list form


The security matrix makes it a bit more visual. Granted, it takes some getting use to but the information is there. Now you can ‘fix’ any issues and reapply the way you see fit! 😉

This has been on my radar for quite some time. Processing security groups this way, makes scripting, I wouldn’t say easier, but more easier to manipulate if you catch my drift…

Ok here’s the code to get the ADSecuirtyMatrix. Do be careful with groups with large memberships. I tried my hand at a group with more than 3800 member, took a couple of minutes, but it worked.

I got one more use to go… Some Operation validation! Stay tuned…

Hope it’s worth something to you…




9 thoughts on “AD Security Group matrix


    Hey Man, nice bit of coding. This script is pretty similar. I admit your script has more style, but also a lot more lines of code. Input is a CV file (ordered) with a list of users to group relationships. Left column user, right column group. The CSV can easily be swapped for a get-adgroupmembers command dumped into an array and then sorted. Output to a CSV, but can also be piped to FT right at the end of the loop.

    $oList = ipcsv e:\temp\usergroups.csv -Delimiter “;”
    $sTemp = $null
    $sLinks = ‘User ID’
    $sRechts = ‘Group Name’

    foreach ($oEntry in $oList){

    if ($oEntry.$sLinks -ne $sTemp){
    if ($sTemp -ne $Null){$oColumns|Export-Csv [some_file] -Delimiter “;” -NoTypeInformation -Append}

    $oColumns = New-Object PSObject
    $i = 1
    $sTemp = $null

    $oColumns | Add-Member -type NoteProperty -Name ‘User’ -Value $oEntry.$sLinks
    $oColumns | Add-Member -type NoteProperty -Name $i -Value $oEntry.$sRechts
    }elseif ($oEntry.$sLinks -eq $sTemp){
    $oColumns | Add-Member -type NoteProperty -Name $i -Value $oEntry.$sRechts

    $sTemp = $oEntry.$sLinks


    Happy coding.

    Liked by 1 person

  2. Dirk

    Hi Irwin,
    I really like your ideas. You could use hashtables instead of an array of PSObjects instead, making use of the fact that hashtables only permit unique keys, to build your matrix. This should work faster too.

    Liked by 1 person

    1. Chris Morley

      Hi i am new to powershell and trying to wrap my head around the code (nice and compact btw!), I can see the code as-is gives a matrix of the groups, but please advise how I can get the users listed down the left colum, and how to get their UPN (as opposed to the ObjectGUID). Thanks in advance!

      $groups = Get-ADGroup -Filter {GroupCategory -eq ‘security’} -SearchBase ‘OU=Groups,DC=corp,DC=test,DC=local’
      $users = Get-ADUser -Filter * -SearchBase “dc=corp,dc=test,dc=local”

      $addADGroupMembers = @{}

      foreach($group in $groups) {
      $addADGroupMembers.Add($group.Name, $group.ObjectGUID)

      $htUsers = @{}
      $htProps = @{}
      $addADGroupMembers.Keys | foreach {$htProps.$_=$null}
      foreach ($group in $addADGroupMembers.GetEnumerator()){
      foreach ($user in $group.Value){
      if (!$htUsers.ContainsKey($user)){
      $htProps.UserID = $user
      $htUsers.$user = $htProps.Clone()
      ($htUsers.$user).$($group.Name) = 1

      $htUsers.GetEnumerator() | foreach{
      } | sort UserID | ft


      1. Irwin Strachan Post author

        Hi Chris,

        So instead of the user’s SamAccountName, you’d like the user’s UPN, right? That could be tricky as the UPN isn’t mandatory/or always filled in…

        Also, a group member could be a security group (no UPN available then)…

        Here’s something you could try:

        You might have to select at the end depending on if you have any prefixes.



  3. Pingback: Protecting output content from prying eyes | pshirwin

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s