After a three-month implementation and tuning period for PingDirectory at our organization, I’ve concluded that the most critical—and often under-documented—component is establishing a robust, fault-tolerant synchronization from your HR system of record. Our use case involved synchronizing user attributes from SAP SuccessFactors, but the principles apply broadly to Workday, Oracle HCM, or even custom-built systems. The goal is a single source of truth for identity data, minimizing manual provisioning and ensuring compliance.
I will outline the architectural decisions, configuration specifics, and pitfalls we encountered. This is not a simple copy-paste tutorial, but a framework for thinking through the integration.
**Core Architecture Decision: Push vs. Pull**
We evaluated both models:
* **HR-Driven Push (e.g., SCIM 2.0, LDIF/DSML over HTTP):** The HR system initiates changes. This is event-driven and near real-time, but requires the HR system to have network egress to your PingDirectory instances and robust error handling.
* **Directory-Driven Pull (e.g., Periodic LDAP LDIF import, custom scripts using HR APIs):** PingDirectory periodically fetches changes. This centralizes control within the directory environment but introduces latency and requires secure credential management for API access.
We opted for a **hybrid model**: a lightweight middleware application (a Python service on a secured host) that **pulls** from SuccessFactors APIs on a scheduled basis, transforms the data, and **pushes** changes to PingDirectory via its REST API (LDAP SDK is also an option). This intermediary layer gave us superior logging, transformation logic, and the ability to handle API rate limiting.
**Critical Configuration & Mapping**
The synchronization hinges on a precise attribute mapping. Your HR "Employee ID" must map immutably to PingDirectory's `uid`. We also established clear rules for handling missing or terminated user data.
Our primary mapping included:
* `employeeNumber` -> `uid`
* `email` -> `mail`
* `firstName` + `lastName` -> `cn` (commonName)
* `departmentCode` -> `departmentNumber`
* `businessTitle` -> `title`
* `hrStatus` -> `employeeType` (with logic: "ACTIVE" vs. "TERMINATED")
A significant hurdle was managing the `inetOrgPerson` object class versus our custom needs. We extended the schema carefully for `departmentNumber` and `employeeType` to support downstream provisioning rules in PingFederate.
**Operational Pitfalls & Lessons Learned**
* **Idempotency is Non-Negotiable:** Your sync process must handle the same input data multiple times without causing duplicate entries or errors. We implemented checks using the `modifyTimestamp` attribute and HR system transaction IDs.
* **Handle Deletions and Disables Explicitly:** Do not simply delete directory entries. We map terminated users to a "disabled" state, moving them to a separate branch of the Directory Information Tree (DIT) and stripping group memberships, preserving the record for audit.
* **Batch Operations and Change Rate:** Initial loads can be massive. We had to tune the PingDirectory `maxbersize` and `idletimeout` parameters on our proxy to handle bulk operations without timeouts.
* **Monitoring & Alerting:** Beyond checking sync job logs, we monitor the `numsubordinates` attribute for critical OUs to detect unexpected population drops, and set up alerts for failed REST API calls from our middleware.
The integration now runs with 99.9% reliability over the past six months. The key was treating the sync not as a one-time setup but as a living data pipeline with its own operational support. I am happy to elaborate on any specific area, such as the custom transformation logic for department codes or our approach to handling historical data reconciliation.
Data over opinions