When working with Snow Leopard Server's Open Directory, group membership is typically stored as attributes on the group entry rather than the user entries. This is a common LDAP design pattern where groups maintain references to their members through attributes like memberUid
or member
.
The core issue arises when you want to export users filtered by their group membership. Your current approach:
ldapsearch -xLLL -H ldap://server.domain.net \
-b "cn=users,dc=server,dc=domain,dc=net" objectClass \
uid uidNumber cn userPassword > directorycontents.ldif
correctly exports user attributes but lacks group membership context.
To get users belonging to specific groups, we need to:
- First query the group to get member lists
- Then query users matching those memberUid values
Here's a practical example to export users from group "developers":
# First get member list from group
GROUP_MEMBERS=$(ldapsearch -xLLL -H ldap://server.domain.net \
-b "cn=developers,cn=groups,dc=server,dc=domain,dc=net" \
memberUid | grep memberUid: | awk '{print $2}')
# Then query each member's full details
for USER in $GROUP_MEMBERS; do
ldapsearch -xLLL -H ldap://server.domain.net \
-b "cn=users,dc=server,dc=domain,dc=net" \
"(&(objectClass=inetOrgPerson)(uid=$USER))" \
uid uidNumber cn userPassword
done > group_members.ldif
For more complex scenarios, you can use LDAP filters to combine group membership with other criteria:
ldapsearch -xLLL -H ldap://server.domain.net \
-b "cn=users,dc=server,dc=domain,dc=net" \
"(&(objectClass=inetOrgPerson)(|(memberOf=cn=developers,cn=groups,dc=server,dc=domain,dc=net)(memberOf=cn=admins,cn=groups,dc=server,dc=domain,dc=net)))" \
uid uidNumber cn userPassword
For directories with many users, consider these optimizations:
- Use paged results with
-E pr=100/noprompt
- Cache group membership data first
- Process in batches
When preparing your LDIF for import to OpenLDAP:
# Clean up Apple-specific attributes
sed -i '' '/apple-/d' group_members.ldif
sed -i '' '/^dn:/ s/,dc=server,dc=domain,dc=net//' group_members.ldif
Remember to verify your target server's schema requirements before import.
When migrating from Snow Leopard Server's Open Directory to external LDAP servers, I encountered a fundamental architectural difference in how group memberships are stored. Unlike some directory services that maintain membership lists as user attributes, Open Directory follows the POSIX model where groups contain member references.
Here's the structural difference we're dealing with:
# User entry example
dn: uid=jsmith,cn=users,dc=example,dc=com
objectClass: posixAccount
uid: jsmith
# Group entry example
dn: cn=developers,cn=groups,dc=example,dc=com
objectClass: posixGroup
memberUid: jsmith
memberUid: mjones
To extract complete membership information, we need to:
# 1. First get all groups
ldapsearch -xLLL -H ldap://od.example.com -b "cn=groups,dc=example,dc=com" "(objectClass=posixGroup)" cn memberUid
# 2. Then get user details for each member
ldapsearch -xLLL -H ldap://od.example.com -b "cn=users,dc=example,dc=com" "(&(objectClass=posixAccount)(uid=*))" uid uidNumber cn userPassword
Here's a practical script to combine both queries:
#!/bin/bash
SERVER="ldap://od.example.com"
BASE_DN="dc=example,dc=com"
# Get all groups and their members
ldapsearch -xLLL -H $SERVER -b "cn=groups,$BASE_DN" "(objectClass=posixGroup)" cn memberUid | \
awk '/^dn:/ {gsub(/cn=|,.*/, "", $2); group=$2} /^memberUid:/ {print group","$2}' > groups.csv
# Get all users with necessary attributes
ldapsearch -xLLL -H $SERVER -b "cn=users,$BASE_DN" "(&(objectClass=posixAccount)(uid=*))" \
uid uidNumber cn userPassword > users.ldif
For environments with many groups, this more efficient query might help:
ldapsearch -xLLL -H ldap://od.example.com -b "cn=users,dc=example,dc=com" \
"(&(objectClass=posixAccount)(memberOf=cn=developers,cn=groups,dc=example,dc=com))" \
uid cn userPassword
Note: This requires the memberOf overlay to be configured on your OpenLDAP server.
The complete migration script would combine these elements:
#!/bin/bash
# Full migration script example
OUTPUT="migration_data_$(date +%Y%m%d).ldif"
{
# Output user entries
ldapsearch -xLLL -H ldap://od.example.com -b "cn=users,dc=example,dc=com" \
"(&(objectClass=posixAccount)(uid=*))" uid uidNumber cn userPassword
# Output group entries with members
ldapsearch -xLLL -H ldap://od.example.com -b "cn=groups,dc=example,dc=com" \
"(objectClass=posixGroup)" cn gidNumber memberUid
} > $OUTPUT