Skip to content

Instantly share code, notes, and snippets.

@Sirofjelly
Last active May 11, 2026 07:47
Show Gist options
  • Select an option

  • Save Sirofjelly/184c7ffcfc242486f66a092fa7dc223e to your computer and use it in GitHub Desktop.

Select an option

Save Sirofjelly/184c7ffcfc242486f66a092fa7dc223e to your computer and use it in GitHub Desktop.
Syncing of Google Workspace Directory to Personal Contacts
// --- 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