Generate an HTML Report of a User’s Group Memberships with PowerShell

In my last post I shared a PowerShell function which provides a full list of an Active Directory user’s group memberships, including all of those inherited from other groups.

Having a massive list of groups in an array is useful, but you can use and abuse that data and convert into all sorts of interesting formats to simplify IT administration. In my case I can use the list to help me implement Role-Based Access Control.

Background

I’ve been working on transitioning to Role-Based Access Control as described in the book Windows Administration Resource Kit: Productivity Solutions for IT Professionals. Without getting into too many details, the goal of RBAC is to provide single points of management for business roles and IT resources.  I’ve been working on restructuring our Active Directory to implement RBAC by modifying group memberships and naming groups that have similar functions using common prefixes.  For example:

  • Groups that control access to a resource have the format ACL_Resource Name_Permission
  • Groups that control access to a VDI pool have the format VDI_VDI Pool Name
  • Groups that control deployment of a group policy have the format GPO_Policy Name
  • Groups that control deployment of an application have the format APP_Application Name
  • Groups that represent a Role or business logic responsibility have no prefix (such as “Administrative Director“)

As long as I stick to these standards, finding groups and using them to manage capabilities is simple.

The Script

Using a little PowerShell magic, I can generate pretty reports on-the-fly that show me the full story behind a user’s group memberships. My script separates types of groups based on their prefix and displays different information for different types of groups. Here’s an example report that I generated for my user account.

You might not find my script very useful to you as-is, unless of course you use the name naming scheme that I do.  However it should serve as an example of how you can use Active Directory and PowerShell together to generate informative reports about the capabilities of your users.

Note: If you plan to use this script, you’ll also have to download my Get-AdPrincipalGroupMembershipRecursive module and put it at the proper place on PowerShell’s import path.

<# .SYNOPSIS Generates a report in HTML format of all a user's group memberships. .DESCRIPTION Generates a report in HTML format of all a user's group memberships. The groups are separated into categories based on the group's prefix. Groups with no prefix are Role Groups. Groups with the ACL_ prefix are access control groups. Groups with the GPO_ prefix control deployment of Group Policy. Groups with the APP_ prefix control deployment of an application. Groups with the VDI_ prefix control access to a specific virtual desktop pool. .NOTES Author : Brian Reich <breich@reich-consulting.net .LINK http://wp.me/p2A8jT-1np #> # Imports the Get-ADPrincipalGroupMembershipRecursive # See https://www.reich-consulting.net/2013/12/05/retrieving-recursive-group-memberships-powershell/ Import-Module GroupManagement\Get-ADPrincipalGroupMembershipRecursive # Ask the user to enter a username $user = Get-ADUser -Identity (Read-Host "Username") # Get the user's group memberships $groups = Get-ADPrincipalGroupMembershipRecursive -dsn $user.DistinguishedName # Split groups based on prefix $acls = $groups | Where-Object { $_.Name -like "ACL_*" } $gpos = $groups | Where-Object { $_.Name -like "GPO_*" } $apps = $groups | Where-Object { $_.Name -like "APP_*" } $vdis = $groups | Where-Object { $_.Name -like "VDI_*" } # Role groups are the groups that didn't fit the other criteria $roles = $groups | Where-Object { $_ -notin $acls -and $_ -notin $gpos -and $_ -notin $apps -and $_ -notin $vdis } <# .SYNOPSIS Returns an HTML fragment listing all of the user's Role Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's Role Groups. Roles Groups are displays with the name of the group and the group's description. .PARAMETER $roles An array of Role Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-RoleHtml( $roles ) { $html = "<h2>Roles</h2>" $html += "<p>Role groups represent the user's job position and " $html += "responsibilities within the organization.</p>" $html += $roles | Select-Object -Property Name,Description | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's ACL Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's ACL Groups. ACL Groups are named ACL_Resource Name_Permission. The name of the group is displayed alongside the Resource it controls access to and the permission it provides. .PARAMETER $acls An array of ACL Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-AclHtml( $acls ) { $html = "<h2>Access Control Groups</h2>" $html += "<p>Access Control Groups (groups whose name begins with 'ACL_') are " $html += "groups that control access rights to resources.</p>" $html += $acls | Select-Object -Property ` Name, @{Name="Resource"; Expression = {$_.Name.split("_")[1]}}, @{Name="Permission"; Expression = {$_.Name.split("_")[2]}} | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's GPO Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's GPO Groups. GPO Groups are displays with the name of the group and the name of the GPO it controls. .PARAMETER $roles An array of GPO groups. .RETURNS Returns a string containing the HTML fragment. #> function Get-GpoHtml( $gpos ) { $html = "<h2>Group Policy Groups</h2>" $html += "<p>Group Policy Groups (those that start with 'GPO_') are groups " $html += "that control Group Policy assignments.</p>" $html += $gpos | Select-Object -Property ` Name, @{Name="Policy Name"; Expression = {$_.Name.split("_")[1]}} | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's App Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's App Groups. App Groups are displays with the name of the group alongside the Application that it controls access to. .PARAMETER $roles An array of App Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-AppHtml( $apps ) { $html = "<h2>Application Groups</h2>" $html += "<p>Application Groups (those that start with 'APP_') are groups " $html += "that control deployment of specific applications. Applications may " $html += "be deployed by Group Policy, ThinApp, or other means.</p>" $html += $apps | Select-Object -Property ` Name, @{Name="Application"; Expression = {$_.Name.split("_")[1]}} | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's VDI Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's VDI Groups. VDI Groups are displays with the name of the group alongside the name of the VDI pool they control access to. .PARAMETER $roles An array of VDI Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-VdiHtml( $vdis ) { $html = "<h2>VDI Groups</h2>" $html += "<p>VDI Groups (those that start with 'VDI_') are groups that " $html += "that assign users to virtual desktop pools.</p>" $html += $vdis | Select-Object -Property Name,Description | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } # Get the account name, which we'll use to name the HTML File. $username = $user.SamAccountName $file = $HOME + $username + "_group_memberships.html" #The following lines generate HTML. We're generating some pretty simple markup #which contains a stylesheet to make the whole thing look decent on-screen. $html = '<html>' $html += "<head><title>RBAC Report for $username</title>" $html += "<style>" $html += " body { width: 800px; margin: 1em auto; font-family: 'Proxima Nova Regular', 'Helvetica Neue', Calibri, 'Droid Sans', Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-weight: normal; } h1, h2, h3, h4, h5, h6, p { border-radius: 5px; padding: 10px 20px; } h1 { background: #333; color: #f0f0f0; } h2 { background: #666; color: #f0f0f0; } p { background: #eee; color: #333 } table th { text-align: left; background: eee }" $html += "</style>" $html += "</head>" $html += '<body>'; $html += "<h1>RBAC Report for $username</h1>" $html += "<p>This report provides a list of all $username's group memberships," $html += "including all of those inherited from other groups. Groups are " $html += "organized based on their prefix. Groups with no prefix are assumed to" $html += "be Role Groups.</p>" $html += Get-RoleHtml $roles $html += Get-AclHtml $acls $html += Get-GpoHtml $gpos $html += Get-AppHtml $apps $html += Get-VdiHtml $vdis # Output the HTML to the file $HOME\$username_group_memberships.html $html | Out-File "$file" # Open the HTML file Invoke-Expression "$file"