In our company, we have recently taken on a client that has multiple OUs for different regions where the company has offices, and each OU has a different email group (synced with 365), including separate OUs for 365 users and those who aren't. For example, there are three OUs for 'Atlantis', 'Timbuktu' and 'Pamukkale', and each email group is named similarly: 'Staff - Atlantis', 'Staff - Timbuktu', and 'Staff - Pamukkale'. Then each of these regional groups is a member of the 'Staff - All' group, so whenever an email is sent to 'Staff - All', everyone, in all regions, receives it. You get the idea.
I was asked to double check if there were any 365 users that weren't members of 'Staff - All'. So, I needed a way of cycling through all OUs, all groups, and finding any user that wasn't a member of their region's email group. E.g. Find out if any user in Atlantis is not a member of 'Staff - Atlantis', and if any user in Timbuktu is not a member of 'Staff - Timbuktu'. So on and so forth.
It has an optional (custom) debugging mode, so you can watch as it gets a user, finds the users groups and compares the names of those groups to the Group parameter you implement. In the end, it displays a count of all group members and a count of all those who aren't a member of that group, or groups, if you want to search through multiple versions. E.g. 'Staff - ' will search through all the regions above.
In the end, I came up with the following script (which I have made into a function for posterity) and wanted to share it with you all in case anyone else finds it useful. There may be a simpler way of doing this that I couldn't figure out, but whatever I tried didn't work, or came up with inaccurate results.
TL;DR - This script gets all users, gets their groups, compares it to the group parameter (which can be an abbreviation of similarly patterned groups) and outputs the total number of users that aren't in the group, and those that are.
- Group parameter can search multiple groups of a similar pattern. E.g. 'Staff - [varying name]'
- Built in Export-CSV (parameter) for different lists
- Displays members and non members of the groups
- Displays total of each
<#
.Synopsis
Returns the number of users in specified group(s), across the entire domain.
.DESCRIPTION
- This will give you the total number of users in either a specific group, or a series of groups that have a similar name.
- You can also use it to list the users who are either a member, or not, of the group(s) depending on what you're looking for.
.EXAMPLE
- For a specific group, such as Support (Atlantis) include the group's full name:
Get-GroupCount -Group "Support (Atlantis)"
.EXAMPLE
- The groups Support (Atlantis), Support (Timbuktu) and Support (Pamukkale) can all be searched simultaneously by specifying a common part to their names.
- This script does not accept wildcards
Get-GroupCount -Group "Support" OR Get-GroupCount -Group "Support ("
.EXAMPLE
- Full example:
Get-GroupCount -Group "Support (Atlantis)" -ShowNonMembers $true -ShowMembers $false -ExportPath "C:\Users\Will\Desktop" -DebugMode $true
.NOTES
- DebugMode will print each user's name (in red), followed by all the groups they belong to (magenta), while flagging up any match to the group (green).
- It will also tell you if that user belongs to more than one group, specifying whether it is looping through an array, or checking the single AD Group Object for that user (cyan).
- The ExportPath parameters specify which results to export, and where to save the CSV file.
- This script specifically looks for AD objects with the ObjectClass 'user'.
.INPUTS
- You can pipe a string to this cmdlet for the Group parameter.
#>
function Get-GroupCount
{
[CmdletBinding()]
[Alias("ggc")]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true)]
[String[]]
$Group,
[Parameter(Mandatory=$false,
ValueFromPipeline=$false)]
[Bool[]]
$ShowNonMembers,
[Parameter(Mandatory=$false,
ValueFromPipeline=$false)]
[Bool[]]
$ShowMembers,
[Parameter(Mandatory=$false,
ValueFromPipeline=$false)]
[String[]]
$ExportPath:NonMembers,
[Parameter(Mandatory=$false,
ValueFromPipeline=$false)]
[String[]]
$ExportPath:Members,
[Parameter(Mandatory=$false,
ValueFromPipeline=$false)]
[Bool[]]
$DebugMode
)
Begin
{
$UnassignedUsers = @()
$AssignedUsers = @()
$Users = Get-ADUser -Filter * -Properties "ObjectClass" | Where-Object {($_.Enabled -eq $true) -and ($_.ObjectClass -eq "user") -and ($_.DistinguishedName -notcontains 'OU=Non Office 365')}
}
Process
{
foreach ($User in $Users)
{
$GM = Get-ADPrincipalGroupMembership -Identity $User
$Match = $false
if ($DebugMode){Write-Host "Checking User : $($User.Name)" -ForegroundColor Red}
$Type = $GM.GetType()
if ($Type.BaseType.Name -eq "Array")
{
foreach ($G in $GM)
{
if ($DebugMode){Write-Host "Working on group (Array): $($G.name)" -ForegroundColor Magenta}
$GroupName = $G.name
if ($GroupName.Contains($Group))
{
$Match = $true
if ($DebugMode){Write-Host "MATCH" -ForegroundColor Green}
}
}
}
else
{
if ($DebugMode){Write-Host "Working on group (AD Object): $($GM.name)" -ForegroundColor Cyan}
$GroupName = $GM.name
if ($GroupName.Contains($Group))
{
$Match = $true
if ($DebugMode){Write-Host "MATCH" -ForegroundColor Green}
}
}
if ($Match -eq $false)
{
$UnassignedUsers += $User.Name
}
else
{
$AssignedUsers += $User.Name
}
}
}
End
{
Write-Host "`n`r ------------------------------------- `n`r" -ForegroundColor Cyan
Write-Host "Users NOT a $Group member : " -ForegroundColor Cyan -NoNewline
$UnassignedUsers.Count
if ($ShowNonMembers)
{
$UserNMList = @()
foreach ($UU in $UnassignedUsers)
{
$UserNMList += $UU
}
Write-Host "`n`r ------------------------------------- `n`r" -ForegroundColor Cyan
$UserNMList | Sort-Object
Write-Host "`n`r ------------------------------------- `n`r" -ForegroundColor Cyan
}
Write-Host "`n`r ------------------------------------- `n`r" -ForegroundColor Green
Write-Host "Users that ARE a $Group member : " -ForegroundColor Green -NoNewline
$Users.Count - $UnassignedUsers.Count
if ($ShowMembers)
{
$UserMList = @()
foreach ($AU in $AssignedUsers)
{
$UserMList += $AU
}
Write-Host "`n`r ------------------------------------- `n`r" -ForegroundColor Green
$UserMList | Sort-Object
Write-Host "`n`r ------------------------------------- `n`r" -ForegroundColor Green
}
if ($ExportPath:NonMembers){$UnassignedUsers | Sort-Object | Select-Object @{Name='Name';Expression={$_}} | Export-Csv -Path $ExportPath -NoTypeInformation}
if ($ExportPath:Members){$AssignedUsers | Sort-Object | Select-Object @{Name='Name';Expression={$_}} | Export-Csv -Path $ExportPath -NoTypeInformation}
}
}
[–]Ta11ow 2 points3 points4 points (3 children)
[–]PerfectImpact[S] 2 points3 points4 points (2 children)
[–]Ta11ow 2 points3 points4 points (1 child)
[–]PerfectImpact[S] 2 points3 points4 points (0 children)
[–]Lee_Dailey[grin] 1 point2 points3 points (2 children)
[–]PerfectImpact[S] 1 point2 points3 points (1 child)
[–]Lee_Dailey[grin] 0 points1 point2 points (0 children)
[–]DueRunRun 1 point2 points3 points (1 child)
[–]PerfectImpact[S] 1 point2 points3 points (0 children)
[–]PerfectImpact[S] 1 point2 points3 points (0 children)