Collect Workday HCM logs

Supported in:

This document explains how to ingest Workday HCM logs to Google Security Operations by setting up a feed using the Third Party API.

The parser extracts Workday HCM user data from JSON-formatted logs. It handles various data transformations, including renaming fields, merging nested objects, parsing dates, and populating UDM fields for user attributes, employment details, and organizational structure.

Before you begin

Ensure that you have the following prerequisites:

  • A Google SecOps instance
  • Privileged access to Workday with Security Administrator or equivalent permissions

Configure Workday API authentication

Create an Integration System User (ISU)

  1. Sign in to Workday with administrative privileges.
  2. In the search bar, type Create Integration System User and select the task.
  3. Enter a Username (for example, ISU_SecOps_HCM).
  4. Set a Password.
  5. Set Session Timeout Minutes to 0 to prevent the ISU from timing out.
  6. Enable Do Not Allow UI Sessions to enhance security by restricting UI logins.
  7. Go to the Maintain Password Rules task.
  8. Add the integration system user to the System Users exempt from password expiration field.

Create an integration security group

  1. In the search bar, type Create Security Group and select the task.
  2. Locate the Type of Tenanted Security Group field, and select Integration System Security Group (Unconstrained).
  3. Provide a Name for the security group (for example, ISG_SecOps_HCM).
  4. Click OK.
  5. Click Edit for the newly created security group.
  6. Assign the Integration System User from the previous step to the security group.
  7. Click Done.

Grant domain access to the security group

The Google SecOps feed retrieves data from four Workday REST API endpoints. Each endpoint requires specific Domain Security Policy permissions to be granted to the integration security group.

  1. In the search bar, type Maintain Permissions for Security Group and select the task.
  2. Choose the security group you created (for example, ISG_SecOps_HCM) from the Source Security Group list.
  3. Click OK.
  4. Go to Domain Security Policy Permissions.
  5. Add GET access for each of the following domains:

    API Endpoint Required Domain Security Policies
    /workers — Worker list and profiles Worker Data: Public Worker Reports
    /workers/{id}/timeOffEntries — Time off balances Worker Data: Time Off (Time Off Balances), Worker Data: Time Off (Time Off Balances Manager View)
    /workers/{id}/history — Worker staffing history Worker Data: Historical Staffing Information
    /supervisoryOrganizations — Org structure Manage: Supervisory Organization or View: Supervisory Organization
  6. Click OK.

  7. Click Done to save changes.

Activate security policy changes

  1. In the search bar, type Activate Pending Security Policy Changes and select the task.
  2. Enter a reason for the change in the comment field (for example, Granting API access for Google SecOps HCM integration).
  3. Click OK.
  4. Select Confirm, then click OK.

Register API client for integrations

  1. In the search bar, type Register API Client for Integrations and select it.
  2. Click Create.
  3. Provide the following configuration details:

    • Client Name: Enter a name (for example, Google SecOps HCM Client).
    • System User: Select the Integration System User you created (for example, ISU_SecOps_HCM).
    • Scope: Select the following scopes:

      Scope Required for
      Staffing /workers and /workers/{id}/history endpoints
      Time Off and Leave /workers/{id}/timeOffEntries endpoint
      Organizations and Roles /supervisoryOrganizations endpoint
  4. Click Save.

  5. Click OK.

  6. Copy and save the Client ID and Client Secret immediately.

Generate OAuth 2.0 refresh token

  1. In the search bar, type Manage Refresh Tokens for Integrations and select it.
  2. Click Generate New Refresh Token.
  3. In the Workday Account field, search for and select the Integration System User (for example, ISU_SecOps_HCM).
  4. Select the API client you created and click OK.
  5. Copy and save the Refresh Token.

Get API endpoint URLs

  1. In the search bar, type View API Clients and select it.
  2. Under API Clients for Integrations, locate the client you created (for example, Google SecOps HCM Client).
  3. Copy and save the following details:

    • Token Endpoint: The URL to obtain an access token (for example, https://wd2-impl-services1.workday.com/ccx/oauth2/YOUR_TENANT/token).
    • Workday REST API Endpoint: The base URL for API calls (for example, https://wd2-impl-services1.workday.com/ccx/api/v1/YOUR_TENANT).

Generate OAuth access token

  • Use curl or a similar HTTP client to send a POST request to the Token Endpoint:

    curl -X POST "https://HOSTNAME/ccx/oauth2/TENANT/token" \
      -d "grant_type=refresh_token" \
      -d "client_id=YOUR_CLIENT_ID" \
      -d "client_secret=YOUR_CLIENT_SECRET" \
      -d "refresh_token=YOUR_REFRESH_TOKEN"
    

This returns an access token (for example, "access_token": "abcd1234"). Copy and save the access token.

Verify API access

  • Before configuring the feed, verify that the ISU has the required permissions for the key endpoints. Replace the variables with your actual values:

    TOKEN="your-access-token"
    HOST="your-workday-host"
    TENANT="your-tenant"
    
    # Test 1: Workers (should return worker list)
    curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $TOKEN" \
      "https://$HOST/ccx/api/v1/$TENANT/workers?limit=1"
    
    # Test 2: Time off entries (replace WORKER_ID with an ID from Test 1)
    curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $TOKEN" \
      "https://$HOST/ccx/api/v1/$TENANT/workers/WORKER_ID/timeOffEntries"
    
    # Test 3: Worker history (replace WORKER_ID with an ID from Test 1)
    curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $TOKEN" \
      "https://$HOST/ccx/api/v1/$TENANT/workers/WORKER_ID/history"
    
    # Test 4: Supervisory organizations
    curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $TOKEN" \
      "https://$HOST/ccx/api/v1/$TENANT/supervisoryOrganizations"
    

Each test should return HTTP status 200. If any endpoint returns 403, see the Troubleshooting section.

Configure a feed in Google SecOps to ingest Workday HCM data

Set up the feed

  1. Go to SIEM Settings > Feeds.
  2. Click Add New Feed.
  3. On the next page, click Configure a single feed.
  4. In the Feed name field, enter a name for the feed (for example, Workday HCM).
  5. Select Third Party API as the Source type.
  6. Select Workday as the Log type.
  7. Click Next.

Configure feed parameters

  1. Specify values for the following input parameters:

    • API Hostname: The fully qualified domain name of your Workday REST API endpoint (for example, wd2-impl-services1.workday.com).

    • Tenant: The last path element of your Workday REST API endpoint that identifies your Workday instance.

    • Access token: The OAuth access token generated in the previous section.

    Advanced Options:

    • Asset namespace: The asset namespace.
    • Ingestion labels: The label to be applied to the events from this feed.
  2. Click Next.

  3. Review your new feed configuration in the Finalize screen, and then click Submit.

Troubleshooting

403 Forbidden on specific endpoints

If the feed reports errors or the verification curl commands return 403 for specific endpoints, the Integration System User is missing permissions.

Failing endpoint Fix
/workers/{id}/timeOffEntries Add GET access for Worker Data: Time Off (Time Off Balances) and Worker Data: Time Off (Time Off Balances Manager View) domains. Add Time Off and Leave scope to the API client.
/workers/{id}/history Add GET access for Worker Data: Historical Staffing Information domain. Verify the Staffing scope is assigned to the API client.
/supervisoryOrganizations Add GET access for Manage: Supervisory Organization or View: Supervisory Organization domain. Add Organizations and Roles scope to the API client.

After making permission changes:

  1. Run Activate Pending Security Policy Changes in Workday.
  2. If you added new scopes to the API client, generate a new refresh token via Manage Refresh Tokens for Integrations and then generate a new access token.
  3. Update the feed configuration with the new access token if changed.

Authentication errors

  • 401 Unauthorized: The access token has expired. Generate a new token using the refresh token and update the feed.
  • Invalid client: Verify the Client ID and Client Secret are correct.
  • Invalid refresh token: The refresh token may have been revoked. Generate a new one via Manage Refresh Tokens for Integrations.

UDM mapping table

Log Field UDM Mapping Logic
@timestamp metadata.event_timestamp.seconds Parsed as a timestamp in seconds since epoch
businessTitle entity.entity.user.title Directly mapped
descriptor entity.entity.user.user_display_name Directly mapped
Employee_ID entity.entity.user.employee_id Directly mapped
Employee_ID entity.metadata.product_entity_id Mapped when id is not present
gopher-supervisor.descriptor entity.entity.user.managers.user_display_name Directly mapped
gopher-supervisor.id entity.entity.user.managers.product_object_id Directly mapped
gopher-supervisor.primaryWorkEmail entity.entity.user.managers.email_addresses Directly mapped
gopher-time-off.date entity.entity.user.time_off.interval.start_time Parsed as a date
gopher-time-off.descriptor entity.entity.user.time_off.description Directly mapped
Hire_Date entity.entity.user.hire_date Parsed as a date
id entity.metadata.product_entity_id Directly mapped when present
Job_Profile entity.entity.user.title Mapped when businessTitle is not present
Legal_Name_First_Name entity.entity.user.first_name Directly mapped
Legal_Name_Last_Name entity.entity.user.last_name Directly mapped
location.descriptor entity.entity.location.city Directly mapped
primarySupervisoryOrganization.descriptor entity.entity.user.department Directly mapped
primaryWorkEmail entity.entity.user.email_addresses Directly mapped
primaryWorkPhone entity.entity.user.phone_numbers Directly mapped
Termination_Date entity.entity.user.termination_date Parsed as a date
Work_Email entity.entity.user.email_addresses Mapped when primaryWorkEmail is not present
collection_time metadata.event_timestamp.collected_timestamp Directly mapped

Need more help? Get answers from Community members and Google SecOps professionals.