Public/Target/Get-TargetDataGoogle.ps1
|
<#
.SYNOPSIS Retrieves Google Workspace users, groups, group memberships, and organizational units. .DESCRIPTION Connects to Google Workspace using the provided authentication headers and configuration. Returns a single object containing: - All users (with a CurrentGroups property listing their group memberships) - All groups (excluding "classroom_teachers") - All organizational units For each group, retrieves its members and builds a hashtable mapping user emails to their group memberships. Each user object in the returned collection has a CurrentGroups property containing the list of groups they belong to. .OUTPUTS PSCustomObject An object with properties: - Users: Array of user objects, each with a CurrentGroups property. - Groups: Array of group objects (excluding "classroom_teachers"). - OrgUnits: Array of organizational unit objects. .EXAMPLE $googleData = Get-TargetDataGoogle $googleData.Users $googleData.Groups $googleData.OrgUnits .NOTES Author: Sam Cattanach Requires: Google API access, supporting functions (Get-GoogleData, Write-Log) #> function Get-TargetDataGoogle { [CmdletBinding()] param () Write-Log -Message "Google: Starting Google Target Data Retrieval" -Level Info #region Import Configuration try { $IDConfig = Get-IDBridgeConfig } catch { Throw $_ } #endregion Import Configuration #region Import Google Headers #Import Google API Headers (with access token) try { $headers = Get-GoogleHeaders } catch { Throw $_ } #endregion Import Google Headers #region Get Google Users try { Write-Log -Message "Google: Fetching Users" -Level Trace $googleUsers = Get-GoogleData -GoogleHeaders $headers -APIUri "https://www.googleapis.com/admin/directory/v1/users?customer=my_customer&maxResults=500" -ErrorAction Stop Write-Log -Message "Google: Successfully fetched users" -Level Trace } catch { Throw $_ } #Get Students & Staff Only $googleStudents = $googleUsers | Where-Object {$_.orgUnitPath -like "*/Students/*" -and $_.orgUnitPath -notlike "*Trash*"} $googleStaff = $googleUsers | Where-Object {$_.orgUnitPath -like "*/Staff/*" -and $_.orgUnitPath -notlike "*Trash*"} #endregion Get Google Users #region Google Groups and Memberships #Get Google Groups - Stores data in $googleGroups try { Write-Log -Message "Google: Fetching Groups" -Level Trace $googleGroups = Get-GoogleData -GoogleHeaders $headers -APIUri "https://www.googleapis.com/admin/directory/v1/groups?customer=my_customer&maxResults=500" -ErrorAction Stop #Remove Classroom Teachers Group $googleGroups = $googleGroups | Where-Object {$_.email -notlike "classroom_teachers@*"} Write-Log -Message "Google: Successfully fetched groups" -Level Trace } catch { Throw $_ } <# This section has been modified to use ForEach-Object -Parallel for better performance #Get Google Group Memberships - Stores memberships in $userGoogleGroupsCurrent #Hashtable to store users and their groups $userGoogleGroupsCurrent = @{} #Loop through each group and retrieve its members foreach ($item in $googleGroups | Where-Object {$_.directMembersCount -ne 0}) { Write-Log -Message ("Google: Getting users for Group: " + $item.email) -Level Trace try { #Get group Memebers $groupMemberResults = $null $groupMemberResults = Get-GoogleData -GoogleHeaders $headers -APIUri ("https://admin.googleapis.com/admin/directory/v1/groups/" + $item.email + "/members?customer=my_customer&maxResults=500") -ErrorAction Stop foreach ($member in $groupMemberResults) { #Add user to hashtable, create entry if it doesn't exist if (-not $userGoogleGroupsCurrent.ContainsKey($member.email)) { $userGoogleGroupsCurrent[$member.email] = @() } #Add the group to the user's list $userGoogleGroupsCurrent[$member.email] += $item.email } } catch { Throw (Write-Log -Message ("Google: No users retrieved for Group: " + $item.email) -Level Error) } } Write-Log -Message "Google: Successfully retrieved group memberships" #> Write-Log -Message "Google: Fetching Group Members" -Level Trace #Get Google Group Memberships - Stores memberships in $userGoogleGroupsCurrent $sharedLogs = [hashtable]::Synchronized(@{}) foreach ($item in $googleGroups) { $item | Add-Member -MemberType NoteProperty -Name Headers -Value $headers -Force $item | Add-Member -MemberType NoteProperty -Name GetGoogleData -Value (Get-Command Get-GoogleData).Definition -Force $item | Add-Member -MemberType NoteProperty -Name TraceLogging -Value $IDConfig.Debug.TraceLogging -Force } #Loop through each group and retrieve its members $finalResults = $googleGroups | Where-Object {$_.directMembersCount -ne 0} | ForEach-Object -Parallel { $null = New-Item -Path function: -Name 'Get-GoogleData' -Value ($_.GetGoogleData) -force $headers = $_.headers $email = $_.email if ($_.TraceLogging) { ($using:sharedLogs).(Get-Date -Format "yyyy-MM-dd_HH:mm:ss:ffff") = [PSCustomObject]@{ Message = ("Google: Getting users for Group: " + $email) Level = "trace" } } try { #Get group Memebers $groupMemberResults = $null $groupMemberResults = Get-GoogleData -GoogleHeaders $headers -APIUri ("https://admin.googleapis.com/admin/directory/v1/groups/" + $email + "/members?customer=my_customer&maxResults=500") -ErrorAction Stop [PSCustomObject]@{ GroupEmail = $email Members = $groupMemberResults } if ($_.TraceLogging) { ($using:sharedLogs).(Get-Date -Format "yyyy-MM-dd_HH:mm:ss:ffff") = [PSCustomObject]@{ Message = ("Google: Received $($groupMemberResults.count) users for Group: " + $email) Level = "trace" } } } catch { ($using:sharedLogs).(Get-Date -Format "yyyy-MM-dd_HH:mm:ss:ffff") = [PSCustomObject]@{ Message = ("Google: No users retrieved for Group: " + $email) Level = "error" } ($using:sharedLogs).(Get-Date -Format "yyyy-MM-dd_HH:mm:ss:ffff") = [PSCustomObject]@{ Message = $_ Level = "error" } } } -ThrottleLimit 10 #Write Logs from Parallel Processing foreach ($logEntry in $sharedLogs.GetEnumerator() | Sort-Object Name) { $entry = $logEntry.Value Write-Log -Message $entry.Message -Level $entry.Level } #Check for errors during parallel processing if ($sharedLogs.GetEnumerator() -like "*error*") { Throw "Errors occurred while retrieving group memberships. Check the log for details." } #Process final results into hashtable $userGoogleGroupsCurrent = @{} foreach ($item in $finalResults) { foreach ($member in $item.Members) { #Add user to hashtable, create entry if it doesn't exist if (-not $userGoogleGroupsCurrent.ContainsKey($member.email)) { $userGoogleGroupsCurrent[$member.email] = @() } #Add the group to the user's list $userGoogleGroupsCurrent[$member.email] += $item.GroupEmail } } Write-Log -Message "Google: Successfully fetched group members" -Level Trace #Add Groups to User Object foreach ($user in $googleUsers) { #Check if the user is in the hashtable and add groups if they exist if ($userGoogleGroupsCurrent.ContainsKey($user.primaryEmail)) { $user | Add-Member -MemberType NoteProperty -Name CurrentGroups -Value $userGoogleGroupsCurrent[$user.primaryEmail] } else { $user | Add-Member -MemberType NoteProperty -Name CurrentGroups -Value @() } } #endregion Google Groups and Memberships #region Get Google Org Units try { Write-Log -Message "Google: Fetching Org Units" -Level Trace $googleOrgUnits = Get-GoogleData -GoogleHeaders $headers -APIUri "https://admin.googleapis.com/admin/directory/v1/customer/my_customer/orgunits?type=all&maxResults=500" -ErrorAction Stop Write-Log -Message ("Google: Fetched $($googleOrgUnits.count) organizational units") -Level Trace } catch { Throw $_ } #endregion Get Google Org Units #region Get Duplicate IDs #Get Google users with duplicate organization external ID $duplicateUsers = ($googleUsers | ForEach-Object { $pkID = ($_.externalIds | Where-Object { $_.Type -eq "organization" }).value if ($pkID) { [PSCustomObject]@{ UPN = $_.primaryEmail FullName = $_.name.fullName OrgID = $pkID } } } | Group-Object -Property OrgID | Where-Object { $_.Count -gt 1 }).group if ($duplicateUsers) { Write-Log -Message ("Google: Users found with Duplicate External IDs: " + ($duplicateUsers | ConvertTo-Json -Compress)) } #endregion Get Duplicate IDs #region Lookup Table Creation # Build the lookup tables once to make the search faster $googleUsersLookupByID = @{} foreach ($gUser in $googleUsers) { foreach ($extId in $gUser.externalIDs) { if ($extId.value -notin $duplicateUsers.OrgID) { $googleUsersLookupByID[$extId.value] = $gUser } } } #endregion Lookup Table Creation Write-Log -Message "Google: Finished Google Target Data Retrieval" -Level Info #Return a single object with all data return [PSCustomObject]@{ Users = $googleUsers Groups = $googleGroups OrgUnits = $googleOrgUnits DuplicateUsers = $duplicateUsers LookupByID = $googleUsersLookupByID Students = $googleStudents Staff = $googleStaff } } |