Skip to content

Instantly share code, notes, and snippets.

@santeri3700
Created November 9, 2024 21:55
Show Gist options
  • Save santeri3700/67668ba287dca9970b0c790be0d45294 to your computer and use it in GitHub Desktop.
Save santeri3700/67668ba287dca9970b0c790be0d45294 to your computer and use it in GitHub Desktop.
Active Directory User Photos on Linux

I've written a neat little script for synchronizing the local user profile picture (also known as "face") on domain joined Linux workstations from Active Directory (thumbnailPhoto user attribute).

It should work on all GNOME based systems and probably on other desktop environments aswell which utilize the AccountsService.

Read more about this on my website: https://santeri.pikarinen.com/posts/ad_user_photo_on_linux/


Usage manually

$ export LDAP_URI='ldaps://dc01.contoso.com:636'
$ export LDAP_BASE='DC=contoso,DC=com'

$ ./update_face_from_ad.sh
Successfully updated local profile picture for user "santeri"!

$ ./update_face_from_ad.sh
Local profile picture for user "santeri" is already up-to-date!

Usage with Systemd

  • Save the attached script to /usr/local/bin/update_face_from_ad and make it executable.
  • Create and enable a Systemd user service /etc/systemd/user/update-face-from-ad.service (see attached example user service file)
  • Create and enable+start a Systemd user timer /etc/systemd/user/update-face-from-ad.timer (see attached example user timer file)

Reload the Systemd daemon after creating the unit files.

$ sudo systemctl daemon-reload
$ systemctl --user daemon-reload

Enable the service to make the profile picture automatically after login.

$ systemctl --user enable update-face-from-ad.service
Created symlink ...

Enable & start the timer to additionally update the profile picture daily at 12:00 PM (example).

$ systemctl --user enable --now update-face-from-ad.timer
Created symlink ...
[Unit]
Description=Update user profile picture from Active Directory
[Service]
Type=simple
# Environment variables for ldapsearch
Environment="LDAP_URI=ldaps://dc01.contoso.com:636"
Environment="LDAP_BASE=DC=contoso,DC=com"
# Wait 15 minutes to allow time for the user to connect to the company network.
ExecStartPre=/bin/sleep 900
ExecStart=/usr/local/bin/update_face_from_ad
[Install]
WantedBy=default.target
[Unit]
Description=Update user profile picture from Active Directory at 12:00 PM
[Timer]
OnCalendar=*-*-* 12:00:00
Persistent=false
[Install]
WantedBy=timers.target
#!/bin/bash
# Script to update the user's profile picture from Active Directory using ldapsearch
# Copyright (C) 2024 Santeri Pikarinen (santeri3700)
# Version: 2024-11-09
# License: MIT (https://opensource.org/licenses/MIT)
# Variables
LDAP_URI=${LDAP_URI:-"ldaps://dc01.contoso.com:636"}
LDAP_BASE=${LDAP_BASE:-"DC=contoso,DC=com"}
LDAP_TIMEOUT=${LDAP_TIMEOUT:-60} # ldapsearch timeout in seconds
LDAP_WAIT=${LDAP_WAIT:-true} # Wait for domain controller to come online
LDAP_WAIT_TIMEOUT=${LDAP_WAIT_TIMEOUT:-300} # Wait timeout in seconds
DEBUG=${DEBUG:-false}
# Enable debug logging if DEBUG is set to true
if [ "$DEBUG" = "true" ] || [ "$DEBUG" = "1" ]; then
set -x
fi
# Ensure ldapsearch is installed
if ! command -v ldapsearch 1>/dev/null 2>/dev/null; then
echo "ERROR: ldapsearch is not installed!"
exit 1
fi
# Prevent running as root
if [ "$(id -u)" = "0" ]; then
echo "ERROR: This script must not be run as root!"
exit 1
fi
# Ensure LDAP host is reachable
if [ "$LDAP_WAIT" = "true" ] || [ "$LDAP_WAIT" = "1" ]; then
until ldapsearch -x -H "$LDAP_URI" -s base -b '' -l "$LDAP_TIMEOUT" -o nettimeout="$LDAP_TIMEOUT" 1>/dev/null 2>/dev/null; do
sleep 10
LDAP_WAIT_TIMEOUT=$((LDAP_WAIT_TIMEOUT - 10))
if [ "$LDAP_WAIT_TIMEOUT" -le 0 ]; then
echo "ERROR: Domain controller is not reachable!"
exit 1
fi
done
fi
# Get the current user's username without domain suffix (aAMAccountName)
username="${USER%%@*}"
# Download the user's profile picture with ldapsearch
ad_user_thumbnailphoto_result=$(ldapsearch -Q -l "$LDAP_TIMEOUT" -Y GSSAPI -N -o 'sasl_secprops=minssf=0,maxssf=0' -H "$LDAP_URI" -b "$LDAP_BASE" -s sub "(&(objectClass=user)(sAMAccountName=$username))" -t "thumbnailPhoto" 2>/dev/null)
# Extract the temporary file path from the ldapsearch result
ad_user_thumbnailphoto_file=$(echo "$ad_user_thumbnailphoto_result" | grep "^thumbnailPhoto:" | awk -F'file://' '{print $2}' | awk '{print $1}' | tr -cd '[:print:]')
# Abort if the user has no profile picture (thumbnailPhoto attribute is empty or missing)
if [ -z "$ad_user_thumbnailphoto_file" ]; then
echo "ERROR: User \"$username\" has no profile picture in Active Directory!"
exit 1
fi
# Copy the temporary file to the user's profile picture file
if file -b --mime-type "$ad_user_thumbnailphoto_file" | grep -q -E '^image/(jpeg|png|gif)'; then
# Skip if current ~/.face is already up-to-date
if [ -f ~/.face ] && cmp -s "$ad_user_thumbnailphoto_file" ~/.face; then
echo "Local profile picture for user \"$username\" is already up-to-date!"
exit 0
fi
# Rename old ~/.face to ~/.face.bak
if [ -f ~/.face ]; then
mv ~/.face ~/.face.bak
fi
# Copy the thumbnailPhoto to ~/.face
cp "$ad_user_thumbnailphoto_file" ~/.face
chmod 644 ~/.face
# Ensure ~/.face.icon is a symlink to ~/.face
if [ -f ~/.face.icon ] || [ -L ~/.face.icon ]; then
mv ~/.face.icon ~/.face.icon.bak
ln -rs ~/.face .face.icon
fi
echo "Successfully updated local profile picture for user \"$username\"!"
exit 0
else
echo "ERROR: Profile picture for user $USER is not a valid JPEG/PNG/GIF image!"
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment