A question that comes up on almost every identity management project is deceptively simple: where should the rules for group membership live?
When you integrate PeopleSoft with an identity provider like Okta, the default recommendation from the cloud vendor is almost always the same: sync your raw database attributes (department, job code, enrollment status) into Okta, and then use Okta’s native group rules engine to dynamically assign users to groups.
It sounds clean, but in my experience, this approach creates an organizational nightmare.
In this case study, I’ll walk through a real-world project where we rejected Okta’s native rule builder and instead built a custom PeopleSoft-driven sync engine. By shifting the group membership logic back into PeopleSoft queries and security roles, we aligned technical implementation with organizational ownership.
We were working with a multi-institution university system that needed to manage access controls for tens of thousands of users. In Okta, application access, licensing, and security policies are governed by groups. We had to populate several distinct categories of groups:
These memberships are highly dynamic. If an employee transfers departments or a student drops all their classes, their group assignments need to change immediately.
We evaluated three potential designs, balancing technical complexity against long-term maintenance.
In this model, PeopleSoft syncs raw HR attributes to Active Directory (AD) attributes (e.g., custom extension attributes). We then define dynamic security groups in AD using LDAP filters.
(&(objectCategory=person)(extensionAttribute1=dept:Engineering)(extensionAttribute2=type:Faculty))
This is the vendor-recommended path. We would push all relevant PeopleSoft attributes into the Okta user profile, then write rules in Okta’s expression language to populate groups.
profile.department == "College of Engineering" && profile.status == "Active" && profile.userType == "Faculty"
In this model, we treat PeopleSoft as the absolute source of truth for both the data and the rules. We define group membership using standard PeopleSoft security roles or public queries. A batch process (Application Engine: CHG_OKTA_GPSYNC) compares PeopleSoft’s calculated membership against current Okta group membership, determines the differences (adds and removes), and pushes the changes to Okta.
The solution we built uses a declarative configuration model in PeopleSoft.
flowchart LR
subgraph peoplesoft ["PeopleSoft"]
Config["Configuration\n(Role or Query)"] --> AE["Sync Engine\n(CHG_OKTA_GPSYNC)"]
Data["Business Data\n(JOB, ENROLLMENT)"] --> AE
AE --> Diff{"Calculate Diff\n(Adds & Removes)"}
end
subgraph gateway ["Integration Gateway"]
Diff -->|Async Web Service| IB["Integration Broker\nQueue"]
end
subgraph cloud ["Cloud"]
IB -->|REST API| Okta["Okta API"]
end
We configure each group mapping in a custom setup table. The sync engine runs nightly (or more frequently for high-priority groups), pulls the current membership from Okta, executes the local PeopleSoft query or role, calculates the diff, and queues the changes.
PS_CHG_OK_GP_CFG (Sync Rules)
| Field | Purpose |
|---|---|
CHG_OKTA_GRP_ID |
The Okta internal GUID (e.g., 00g1a2b3c4d5e6f...) |
OPRID_ACTIVE |
Toggle to enable/disable the sync rule |
CHG_OK_DATA_SRC |
Data source type: Q (Query) or R (Role) |
PS_QRY_NAME |
Public Query name (if data source is Query) |
PS_ROLE_ID |
Security Role name (if data source is Role) |
CHG_ALLOW_EXCPTN |
If checked, preserve manually added Okta members (additive sync) |
CHG_REPORT_ONLY |
Run in dry-run mode (generate report, make no changes) |
CHG_IDP_TYPES |
Comma-separated list of IDP User Types to target |
PS_CHG_OK_GP_HST (Audit / Diff Log)
| Field | Purpose |
|---|---|
CHG_OKTA_GRP_ID |
Okta Group GUID |
SYNC_RUN_DTTM |
Timestamp of the sync run |
OPRID |
PeopleSoft Operator ID being evaluated |
CHG_OKTA_ID |
The cached Okta GUID for the user |
CHG_SYNC_ACTION |
Action calculated: A (Add), D (Delete), I (Invalid), N (No Change) |
DESCRLONG |
Details or audit notes (e.g., if preserved via exception flag) |
One of the trickiest details of this integration was handling users with multiple roles. In higher education, it is common for an adjunct faculty member to also be a student, or a staff member to take classes.
Our identity architecture provisioned distinct Okta accounts for these different roles to isolate security contexts (e.g., jsmith@univ.edu for their staff account and jsmith@student.univ.edu for their student account).
When syncing a group like “Engineering Faculty,” we only wanted to add the user’s Faculty Okta account, not their Student account. The CHG_IDP_TYPES configuration field solves this. The sync engine uses this field to filter our local cross-reference table (PS_CHG_OPR_OKTA_ID) to fetch the Okta GUID matching that specific identity type.
For a large, system-wide student group, we configure the sync to run against a public query:
00gydbhibjQUGIKMNORFCHG_ACTIVE_STUDENTS)StudentCHG_ACTIVE_STUDENTS)Note how we join the business table (PS_ENROLLMENT) to the Okta cross-reference table (PS_CHG_OPR_OKTA_ID). Because business tables store EMPLID and the cross-reference table is keyed by OPRID, we join through PSOPRDEFN to correctly map the keys:
SELECT DISTINCT a.EMPLID, b.CHG_OKTA_ID
FROM PS_ENROLLMENT a
JOIN PSOPRDEFN c ON a.EMPLID = c.EMPLID
JOIN PS_CHG_OPR_OKTA_ID b ON c.OPRID = b.OPRID
WHERE a.TERM_BEGIN_DATE <= %CurrentDate%
AND a.TERM_END_DATE > %CurrentDate%
AND a.ENROLL_STATUS = 'E'
AND b.CHG_IDP_UTYPE = 'Student'
The sync engine runs this query, compares it to the membership retrieved from the Okta API, and pushes the diff.
Sometimes, you need automated group management but still need to allow manual overrides (e.g., adding a visiting scholar who isn’t in the HR system yet).
00gydbhibj8IkWQ7bF3CHG_ENG_FACULTY)Faculty, AdjunctCHG_ENG_FACULTY)SELECT DISTINCT a.EMPLID, b.CHG_OKTA_ID
FROM PS_JOB a
JOIN PSOPRDEFN c ON a.EMPLID = c.EMPLID
JOIN PS_CHG_OPR_OKTA_ID b ON c.OPRID = b.OPRID
WHERE a.SETID_DEPT = 'ENG'
AND a.JOB_FUNCTION = 'FACULTY'
AND a.EFF_STATUS = 'A'
AND a.EFFDT = (SELECT MAX(EFFDT) FROM PS_JOB
WHERE EMPLID = a.EMPLID AND EFFDT <= %CurrentDate%)
AND b.CHG_IDP_UTYPE IN ('Faculty', 'Adjunct')
Because Allow Exception is checked, the sync engine will:
This design taught us a few key lessons about building large-scale security integrations:
Okta is the policy enforcement point, but it has no business context. The people who understand HR data and academic enrollment rules are the business analysts running PeopleSoft. Keeping the rules in PeopleSoft allows the analysts to update SQL queries in Query Manager and deploy new group criteria immediately, without having to wait for a security administrator to modify Okta rules.
When you automate group syncs, a bad query can de-provision thousands of users in minutes. We built a CHG_REPORT_ONLY flag into the configuration. When checked, the sync engine runs the entire diff process and writes the results to our custom audit log (PS_CHG_OK_GP_HST) and outputs a CSV, but makes zero API calls. We always run new sync mappings in report-only mode for a few days to verify the diff before turning it loose on the production API.
We also had to handle the case of Okta accounts that existed but had no cross-reference data in PeopleSoft. The sync engine processes these “orphaned” accounts by deleting them synchronously during the run rather than queuing them. Because our async queue depends on a valid local database key to track transactions, we handle these anomalous records immediately and log the cleanup action to the standard system log.
Driving authorization groups from the ERP isn’t the most fashionable approach—modern cloud architects will always push for cloud-native rule engines. But for complex, rule-heavy environments like higher education or large enterprises, putting the logic inside PeopleSoft trades minor operational complexity for absolute context. It ensures that the team that owns the data also owns the rules.
Chris Malek is a PeopleTools® Technical Consultant with over two decades of experience. He is available for consulting engagements.
Work with ChrisSWS turns SQL into production REST APIs — ready for AI, modern apps, and partner integrations. One install, unlimited potential.
A powerful PeopleSoft bolt-on that makes REST web services easy. You bring the SQL, SWS handles the rest.
Traditional PeopleSoft web services cost $3,600–$13,000 each to develop. SWS deploys production REST APIs in under 5 minutes through configuration alone.
Turn PeopleSoft data into clean REST APIs for AI integrations, modern applications, and vendor data feeds. Configuration-driven — no PeopleCode required.
Look up any record, field, page, or component, audit security, and monitor Integration Broker across every database — in seconds.
A web console built for the PeopleSoft community — operational monitoring, security auditing, and metadata browsing in one tool.
On-demand security and operational reports for your PeopleSoft environment — no client install required.
Research any PeopleSoft object and monitor system health from a single browser tab — no App Designer, no SQL.