Last active
May 11, 2026 07:47
-
-
Save Sirofjelly/184c7ffcfc242486f66a092fa7dc223e to your computer and use it in GitHub Desktop.
Syncing of Google Workspace Directory to Personal Contacts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // --- README --- | |
| // This script fetches all google workspace directory contacts of ipt and adds them to the user directory. | |
| // This allows to see who is calling and searching for contacts on ios. | |
| // The script deletes old contacts with an email adress ending in @ipt.ch from the user directory. | |
| // This guarantees that no stale contacts exist from previous employees. | |
| function syncDirectoryToConnections() { | |
| try { | |
| // --- STEP 1: Fetch Directory Users (Including Aliases) --- | |
| let pageToken; | |
| let directoryUsers = []; | |
| const directoryEmails = new Set(); | |
| do { | |
| const response = AdminDirectory.Users.list({ | |
| customer: 'my_customer', | |
| maxResults: 500, | |
| orderBy: 'email', | |
| viewType: 'domain_public', | |
| pageToken: pageToken | |
| }); | |
| if (response.users) { | |
| directoryUsers = directoryUsers.concat(response.users); | |
| response.users.forEach(u => { | |
| // Add Primary Email | |
| directoryEmails.add(u.primaryEmail.toLowerCase()); | |
| // Add all Aliases/Secondary Emails if they exist | |
| if (u.emails) { | |
| u.emails.forEach(e => directoryEmails.add(e.address.toLowerCase())); | |
| } | |
| }); | |
| } | |
| pageToken = response.nextPageToken; | |
| } while (pageToken); | |
| Logger.log(`Found ${directoryUsers.length} users and ${directoryEmails.size} total valid email variations (including aliases).`); | |
| // --- STEP 2: Fetch Existing Contacts --- | |
| // We store the full person object now to inspect all their emails at once | |
| let allContacts = []; | |
| let connectionToken; | |
| do { | |
| const peopleResp = People.People.Connections.list('people/me', { | |
| personFields: 'emailAddresses,names', | |
| pageSize: 1000, | |
| pageToken: connectionToken | |
| }); | |
| if (peopleResp.connections) { | |
| allContacts = allContacts.concat(peopleResp.connections); | |
| } | |
| connectionToken = peopleResp.nextPageToken; | |
| } while (connectionToken); | |
| Logger.log(`Found ${allContacts.length} existing contacts.`); | |
| // --- STEP 3: Batch Create New Contacts --- | |
| let createQueue = []; | |
| let totalAdded = 0; | |
| const BATCH_SIZE = 50; | |
| // Create a quick-lookup set of what we ALREADY have in Contacts | |
| const currentContactEmails = new Set(); | |
| allContacts.forEach(c => { | |
| if (c.emailAddresses) { | |
| c.emailAddresses.forEach(e => currentContactEmails.add(e.value.toLowerCase())); | |
| } | |
| }); | |
| directoryUsers.forEach(user => { | |
| const userEmail = user.primaryEmail.toLowerCase(); | |
| // Only create if the primary email isn't already in our contacts | |
| if (!currentContactEmails.has(userEmail)) { | |
| const contactPayload = { | |
| contactPerson: { | |
| names: [{ givenName: user.name.givenName, familyName: user.name.familyName }], | |
| emailAddresses: [{ value: user.primaryEmail, type: 'work' }] | |
| } | |
| }; | |
| // (Optional fields: phones/orgs can be re-added here if needed) | |
| createQueue.push(contactPayload); | |
| if (createQueue.length >= BATCH_SIZE) { | |
| sendCreateBatch(createQueue); | |
| totalAdded += createQueue.length; | |
| createQueue = []; | |
| } | |
| } | |
| }); | |
| if (createQueue.length > 0) { | |
| sendCreateBatch(createQueue); | |
| totalAdded += createQueue.length; | |
| } | |
| // --- STEP 4: Delete Old Internal Contacts --- | |
| let deleteQueue = []; | |
| let totalDeleted = 0; | |
| allContacts.forEach(contact => { | |
| if (!contact.emailAddresses) return; | |
| // Filter for @ipt.ch addresses in this specific contact | |
| const iptEmails = contact.emailAddresses | |
| .map(e => e.value.toLowerCase()) | |
| .filter(email => email.endsWith('@ipt.ch')); | |
| // If they have @ipt.ch emails, check if AT LEAST ONE is valid in the directory | |
| if (iptEmails.length > 0) { | |
| const hasValidEmail = iptEmails.some(email => directoryEmails.has(email)); | |
| if (!hasValidEmail) { | |
| // No valid @ipt.ch email found for this staff contact -> Delete | |
| deleteQueue.push(contact.resourceName); | |
| Logger.log(`Queueing deletion (No valid directory match): ${iptEmails.join(', ')}`); | |
| if (deleteQueue.length >= BATCH_SIZE) { | |
| sendDeleteBatch(deleteQueue); | |
| totalDeleted += deleteQueue.length; | |
| deleteQueue = []; | |
| } | |
| } | |
| } | |
| }); | |
| if (deleteQueue.length > 0) { | |
| sendDeleteBatch(deleteQueue); | |
| totalDeleted += deleteQueue.length; | |
| } | |
| Logger.log(`Sync complete. Added: ${totalAdded}, Deleted: ${totalDeleted}`); | |
| } catch (e) { | |
| Logger.log('FATAL ERROR: ' + e.message); | |
| } | |
| } | |
| // --- Helper Functions --- | |
| function sendCreateBatch(contactsList) { | |
| try { | |
| People.People.batchCreateContacts({ contacts: contactsList }); | |
| Logger.log(`Created batch of ${contactsList.length} contacts.`); | |
| Utilities.sleep(2000); | |
| } catch (e) { Logger.log('Error creating: ' + e.message); } | |
| } | |
| function sendDeleteBatch(resourceNamesList) { | |
| try { | |
| People.People.batchDeleteContacts({ resourceNames: resourceNamesList }); | |
| Logger.log(`Deleted batch of ${resourceNamesList.length} contacts.`); | |
| Utilities.sleep(2000); | |
| } catch (e) { Logger.log('Error deleting: ' + e.message); } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment