Script repository
The scripts synchronize memberships of a Google Apps group based on members of an Active Directory group. To execute the script, create a business rule, custom command or scheduled task configured for the Group object type.
Script 1: Using GAM
Before using the script, install and configure the GAM Tool on the computer where Adaxes service runs. For details, see GAM Wiki.
Parameters
$gamPath- the path to the GAM executable file.$waitTimeMilliseconds- Specifies the time to wait for GAM response. It is recommended not to set a time exceeding the 10 minutes’ limit applied by Adaxes to scripts executed by business rules, custom commands and scheduled tasks. If a script runs for more time than you specify, it will be completed, but the errors, warnings and other messages will not be added to the Execution log.$groupId- a value reference for the AD property that serves as the group identifier in Google Apps. The script will search Google Apps groups by the specified property.$memberIdentityAttribute- the name of the property that will be used to identify users in Google Apps.
$gamPath = "C:\Scripts\Gam\gam.exe" # TODO: modify me
$waitTimeMilliseconds = 8 * 60 * 1000 # TODO: modify me
$groupId = "%sAMAccountName%" # TODO: modify me
$memberIdentityAttribute = "userprincipalName" # TODO: modify me
function StartProcess ($arguments)
{
# Start GAM process.
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
$processInfo.FileName = $gamPath
$processInfo.RedirectStandardOutput = $true
$processInfo.RedirectStandardError = $true
$processInfo.UseShellExecute = $false
$processInfo.CreateNoWindow = $true
$processInfo.Arguments = $arguments
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $processInfo
[void]$process.Start()
$processCompleted = $process.WaitForExit($waitTimeMilliseconds)
if (!$processCompleted)
{
$process.Kill()
Write-Error "The process timeout."
return $null
}
$resultErrors = $process.StandardError.ReadToEnd()
$resultOutput = $process.StandardOutput.ReadToEnd()
return @{
"Output" = $resultOutput.Trim()
"Error" = $resultErrors.Trim()
}
}
function SyncGroup ($googleGroupInfo, $membersToAdd)
{
# Get group properties.
$googleGroupName = ($googleGroupInfo.Output | Select-String -Pattern "name:.+").Matches[0].Value.Replace("name:", "").Trim()
# Sync group members.
$matchInfo = $googleGroupInfo.Output | Select-String -Pattern "member:.+" -AllMatches
if ($matchInfo -ne $NULL)
{
foreach ($record in $matchInfo.Matches)
{
$recordValue = $record.Value.Trim()
$memberEmail = ($recordValue | Select-String -Pattern "\s.+\s").Matches[0].Value.Trim()
if ($membersToAdd.Remove($memberEmail))
{
continue
}
# Remove member from group.
$operationRemoveResult = StartProcess "update group $googleGroupName remove user $memberEmail"
if (([System.String]::IsNullOrEmpty($operationRemoveResult.Error)) -or ($operationRemoveResult.Error.Trim() -eq "removing $memberEmail"))
{
continue
}
$Context.LogMessage("Operation output: " + $operationRemoveResult.Output, "Warning")
$Context.LogMessage("An error occurred while removing a user from the Google group. Error: " + $operationRemoveResult.Error, "Error")
}
}
$groupMail = ($googleGroupInfo.Output | Select-String -Pattern "email:.+").Matches[0].Value.Replace("email:", "").Trim()
foreach ($memberIdentity in $membersToAdd)
{
# Add member to group.
$operationAddResult = StartProcess "update group $groupMail add user $memberIdentity"
if ((([System.String]::IsNullOrEmpty($operationAddResult.Error)) -and ($operationAddResult.Output -notlike "*Error*")) -or ($operationAddResult.Error.Trim() -eq "adding member $memberIdentity`..."))
{
continue
}
$Context.LogMessage("Operation output: " + $operationAddResult.Output, "Warning")
$Context.LogMessage("An error occurred while adding a user to the Google group. Error: " + $operationAddResult.Error, "Error")
}
# Sync group description and name.
$propertiesToUpdate = ""
$googleGroupDescription = ($googleGroupInfo.Output | Select-String -Pattern "description:.+").Matches[0].Value.Replace("description:", "").Trim()
if ($googleGroupDescription -ne "%description%")
{
$processArguments += 'description "%description%"'
}
if ($googleGroupName -ne "%name%")
{
$processArguments += 'name "%name%"'
}
if ([System.String]::IsNullOrEmpty($processArguments))
{
return
}
$operationUpdateResult = StartProcess "update group $googleGroupName $processArguments"
if (([System.String]::IsNullOrEmpty($operationUpdateResult.Error)) -or ($operationUpdateResult.Output -notlike "*error*"))
{
continue
}
$Context.LogMessage("Operation output: " + $operationUpdateResult.Output, "Warning")
$Context.LogMessage("An error occurred when updating the Google group. Error: " + $operationUpdateResult.Error, "Error")
}
function GetUsersMailAddress($guidsBytes, $userIdentity)
{
$filter = New-Object "System.Text.StringBuilder"
[void]$filter.Append("(&(sAMAccountType=805306368)($userIdentity=*)(|")
foreach ($guidBytes in $guidsBytes)
{
$filterPart = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("ObjectGuid", $guidBytes)
[void]$filter.Append($filterPart)
}
[void]$filter.Append("))")
$searcher = $Context.BindToObject("Adaxes://rootDSE")
$searcher.SearchFilter = $filter.ToString()
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$searcher.PageSize = 500
$searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$searcher.SetPropertiesToLoad(@($userIdentity))
$searcher.VirtualRoot = $True
try
{
$searchResultIterator = $searcher.ExecuteSearch()
$searchResults = $searchResultIterator.FetchAll()
foreach ($searchResult in $searchResults)
{
[void]$memberIdentities.Add($searchResult.Properties[$userIdentity].Value)
}
}
finally
{
if ($searchResultIterator){ $searchResultIterator.Dispose() }
}
}
# Check group id.
if ([System.String]::IsNullOrEmpty($groupId))
{
return # Don't perform the sync
}
# Search group by group id.
$gamResult = StartProcess "print groups id"
if (-not([System.String]::IsNullOrEmpty($gamResult.Output)))
{
# Parse info
$records = $gamResult.Output.Split("`n")
$googleGroupInfo = $NULL
for ($i = 1; $i -lt $records.Length; $i++)
{
$googleGroupValues = $records[$i].Split(",")
$googleGroupId = $googleGroupValues[1].Trim()
if ($googleGroupId -ne $groupId)
{
continue
}
# Get AD group members.
try
{
$membersGuidsBytes = $Context.TargetObject.GetEx("adm-DirectMembersGuid")
}
catch
{
$membersGuidsBytes = $NULL
}
$memberIdentities = New-Object "System.Collections.Generic.HashSet[System.String]"
if ($membersGuidsBytes -ne $NULL)
{
GetUsersMailAddress $membersGuidsBytes $memberIdentityAttribute
}
# Get Google group info.
$googleGroupMail = $googleGroupValues[0].Trim()
$googleGroupInfo = StartProcess "info group $googleGroupMail"
SyncGroup $googleGroupInfo $memberIdentities
return
}
if ($googleGroupInfo -eq $NULL)
{
$Context.LogMessage("Google group not found. Id: $groupId", "Warning")
}
}
else
{
$Context.LogMessage("An error occurred while getting a Google groups list. Error: " + $gamResult.Error, "Error")
}Script 2: Using gShell
Before using the script, perform the steps listed in gShell Getting Started document. Step Enter the Client ID and Secret must be performed on the computer where Adaxes service is installed using the credentials of the Adaxes service account (specified during the service installation).
Parameters
$groupIdentity- a value reference for the AD property that serves as the group identifier in Google Apps. The script will search Google Apps groups by the specified property.$memberIdentityAttribute- the name of the property that will be used to identify users in Google Apps.
$groupIdentity = "%sAMAccountName%" # TODO: modify me
$memberEmailAttribute = "mail" # TODO: modify me
function GetUsersMailAddress($guidsBytes, $userIdentity)
{
$filter = New-Object "System.Text.StringBuilder"
[void]$filter.Append("(&(sAMAccountType=805306368)($userIdentity=*)(|")
foreach ($guidBytes in $guidsBytes)
{
$filterPart = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("ObjectGuid", $guidBytes)
[void]$filter.Append($filterPart)
}
[void]$filter.Append("))")
$searcher = $Context.BindToObject("Adaxes://rootDSE")
$searcher.SearchFilter = $filter.ToString()
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$searcher.PageSize = 500
$searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$searcher.SetPropertiesToLoad(@($userIdentity))
$searcher.VirtualRoot = $True
try
{
$searchResultIterator = $searcher.ExecuteSearch()
$searchResults = $searchResultIterator.FetchAll()
foreach ($searchResult in $searchResults)
{
[void]$memberFromAd.Add($searchResult.Properties[$userIdentity].Value)
}
}
finally
{
if ($searchResultIterator){ $searchResultIterator.Dispose() }
}
}
# Get AD group members.
try
{
$membersGuidsBytes = $Context.TargetObject.GetEx("adm-DirectMembersGuid")
}
catch
{
$membersGuidsBytes = $NULL
}
# Get email addresses of all members.
$memberFromAd = New-Object "System.Collections.Generic.HashSet[System.String]"
if ($membersGuidsBytes -ne $NULL)
{
GetUsersMailAddress $membersGuidsBytes $memberEmailAttribute
}
$scriptBlock = {
param($groupIdentity, [System.Collections.Generic.HashSet[System.String]]$memberFromAd)
Import-Module gshell
# Find the Google group.
try
{
$googleGroup = Get-GAGroup -GroupName $groupIdentity
}
catch
{
$errorMessage = "An error occurred when searching for Google group '$groupIdentity'. Error: " + $_.Exception.Message
Write-Error $errorMessage
return
}
# Get current members of the Google group.
try
{
$googleGroupMembers = Get-GAGroupMember -GroupName $googleGroup.Email -ErrorAction Stop
}
catch
{
$googleGroupMembers = @()
}
foreach ($member in $googleGroupMembers)
{
if ($memberFromAd.Remove($member.Email))
{
continue
}
try
{
# Remove member from the Google group.
Remove-GAGroupMember -GroupName $groupIdentity -UserName $member.Email -Force -ErrorAction Stop
}
catch
{
$errorMessage = "An error occurred when removing member '" + $member.Email + "' from group '$groupIdentity'. Error: " + $_.Exception.Message
Write-Error $errorMessage
}
}
# Add new members.
foreach ($memberIdentity in $memberFromAd)
{
try
{
Add-GAGroupMember -GroupName $groupIdentity -UserName $memberIdentity -Role MEMBER -ErrorAction Stop
}
catch
{
$errorMessage = "An error occurred when adding member '$memberIdentity' to group '$groupIdentity'. Error: " + $_.Exception.Message
Write-Error $errorMessage
}
}
}
Invoke-Command -ComputerName localhost -ScriptBlock $scriptBlock -ArgumentList $groupIdentity, $memberFromAd
Comments 0
You must be signed in to comment.