Revisiting SharePoint Profile Sync

Wonderfully decorated tree...

Happy Holidays! Back in July I posted how to sync data from WorkDay to SharePoint profiles. Six months in, I’ve come up with some improvements.

The first removes a dependency on organizational reference beginning with the text string Admin_Support. While this is simple, we found in production that these reference IDs are frequently not predictably named. Here’s how the xPath was formed:

wd:Worker_Data/wd:Organization_Data/wd:Worker_Organization_Data/wd:Organization_Data[starts-with(wd:Organization_Reference_ID/text(),'Admin_Support_')]/wd:Organization_Support_Role_Data/wd:Organization_Support_Role/wd:Organization_Role_Data/wd:Worker_Reference/wd:ID[@wd:type='Employee_ID' or @wd:type='Contingent_Worker_ID']/text()

Instead we can look for admin assignments by looking for a given organization role reference ID. These change far less frequently than organizational support references. Here’s the revised xPath

wd:Worker_Data/wd:Organization_Data/wd:Worker_Organization_Data/wd:Organization_Data/wd:Organization_Support_Role_Data/wd:Organization_Support_Role[starts-with(wd:Organization_Role_Reference/wd:ID[@wd:type='Organization_Role_ID']/text(),'Admin_Support')]/wd:Organization_Role_Data/wd:Worker_Reference/wd:ID[@wd:type='Employee_ID' or @wd:type='Contingent_Worker_ID']/text()

The second optimization is to page the requests rather than request everything from Workday in one API call. This reduces overall memory utilization and, obviously, scales to larger organizations.

To do that, add a response filter to the SOAP request, with count and page, choosing a count that works for you.

        <bsvc:Get_Workers_Request bsvc:version="v35.2">
            <bsvc:Request_Criteria>
                <bsvc:Exclude_Inactive_Workers>1</bsvc:Exclude_Inactive_Workers>
            </bsvc:Request_Criteria>
            <bsvc:Response_Filter>
                <bsvc:Count>25</bsvc:Count>
                <bsvc:Page>1</bsvc:Page>
            </bsvc:Response_Filter>
   		    <bsvc:Response_Group>
                <bsvc:Include_Organizations>1</bsvc:Include_Organizations>
            </bsvc:Response_Group>
        </bsvc:Get_Workers_Request>

Next, iterate for the pages, something similar to

$totalPages = 1
$pageNumber = 1
do {
    # Set Page number in request
    $request.Envelope.Body.Get_Workers_Request.Response_Filter.Page `
        = $pageNumber.toString()
    $response = Invoke-WebRequest -useBasicParsing -Uri $endPoint `
        -Headers (@{SOAPAction='Read'})`
         -Method Post -Body $request -ContentType application/xml
    $content = [xml]$response.content    
    # process workers as before
    $totalPages `
        = $content.Envelope.Body.Get_Workers_Response.Response_Results.Total_Pages
    $pageNumber++
} while($pageNumber -lt $totalPages)

Note that this approach still requires a lookup hash to map worker IDs to UserPrincipalNames.

With a smaller company you can make a minimized request to build that hash. For larger organizations, build that hash as you go and store the worker->admin mappings in a temporary hash until you’ve got all the data, then call set-pnpuserprofileproperty with the mapped UPNs for worker and admin assistant.

I hope this works for you.