Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions packages/workday/_dev/build/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The Workday integration for Elastic collects `Activity` logs via the **Workday A
### How it works

- **Activity**: This integration periodically queries the Workday API to retrieve Activity logs.
- **Sign-on**: This integration periodically downloads a Workday Custom Report (for example, a Sign-on report that lists sign-on records along with their key attributes) and ingests each report row as a single event.
- **Sign-on**: This integration periodically calls a Workday Custom Report (RaaS) JSON endpoint. On each poll it supplies the report's required **From Moment** / **To Moment** prompts (`From_Moment` / `To_Moment` query parameters), advances a collection cursor so the time window moves forward, and ingests each `Report_Entry` row as a single event. The first poll looks back by **Initial Interval**; later polls use **Interval**.

## What data does this integration collect?

Expand Down Expand Up @@ -92,8 +92,9 @@ Activity Logging API | https://HOST/ccx/api/privacy/v1/TENANT/activityLogging
2. In the Workday search bar, search for **Create Custom Report**.
3. Create an Advanced report on a data source that exposes sign-on activity (for example, `Signons and Attempted Signons`).
4. Add the columns required for sign-on analytics (for example, `System Account`, `Session Start`, `Session End`, `Authentication Type for Signon`, `Failed Signon`, `Invalid Credentials`, `Account Locked, Disabled or Expired`, `Browser Type`, `Operating System`, `Device Type`, `Request Originator`, `SAML Identity Provider`).
5. On the **Advanced** tab, select **Enable As Web Service** so the report is exposed as Reports as a Service (RaaS).
6. Save the report and note the **Report Name** and the **Report Owner** (the Workday account that owns the report).
5. On the **Prompts** tab, add **From Moment** and **To Moment** prompts for the report's moment range, and mark both as **Required**. The integration supplies these values on each poll as `From_Moment` and `To_Moment` query parameters on the RaaS URL. Do not hard-code a static range in the Report URL — a fixed `To_Moment` stops collecting new events once that time has passed.
6. On the **Advanced** tab, select **Enable As Web Service** so the report is exposed as Reports as a Service (RaaS).
7. Save the report and note the **Report Name** and the **Report Owner** (the Workday account that owns the report).

##### Create the Integration System User (ISU)

Expand Down Expand Up @@ -121,6 +122,8 @@ Where:
- `REPORT_OWNER` is the Workday account that owns the custom report.
- `REPORT_NAME` is the name of the custom report you created above.

Use this base URL (with `?format=json` only) as the **Report URL** in the integration. Do not append `From_Moment` or `To_Moment` yourself, the integration adds those on every poll from **Initial Interval** or the collection cursor and **Interval**.

## How do I deploy this integration?

This integration supports both Elastic Agentless-based and Agent-based installations.
Expand Down Expand Up @@ -154,9 +157,9 @@ Elastic Agent must be installed. For more details, check the Elastic Agent [inst

* To **Collect Workday Sign-on logs via Custom Report API**, you'll need to:

- Configure **Report URL** with the full Workday Custom Report (RaaS) URL noted above.
- Configure **Report URL** with the full Workday Custom Report (RaaS) URL noted above (include `?format=json`; do not add `From_Moment` / `To_Moment` to the URL — the integration adds them automatically).
- Configure **Username** and **Password** for basic authentication against the Workday Custom Report API.
- Adjust the integration configuration parameters if required, including the **Interval** and **Preserve original event** etc. to enable data collection.
- Adjust the integration configuration parameters if required, including the **Interval**, **Initial Interval**, and **Preserve original event** etc. to enable data collection.

6. Select **Save and continue** to save the integration.

Expand All @@ -172,6 +175,14 @@ Elastic Agent must be installed. For more details, check the Elastic Agent [inst

For help with Elastic ingest tools, check [Common problems](https://www.elastic.co/docs/troubleshoot/ingest/fleet/common-problems).

### Sign-on: `Report parameter From Moment is required`

If collection fails with a Workday **400** validation error that **From Moment** (or **To Moment**) is required:

1. Confirm the custom report's **Prompts** tab defines **From Moment** and **To Moment** and that both are marked **Required** (see [Build the Sign-on custom report](#build-the-sign-on-custom-report)).
2. Confirm the Fleet **Report URL** is only the RaaS URL with `?format=json` — without hardcoded `From_Moment` / `To_Moment` values. The integration must append those parameters on each poll.
3. Confirm **Initial Interval** is set (default `24h`) so the first collection has a lookback window.

## Performance and scaling

For more information on architectures that can be used for scaling this integration, check the [Ingest Architectures](https://www.elastic.co/docs/manage-data/ingest/ingest-reference-architectures) documentation.
Expand All @@ -194,6 +205,10 @@ For more information on architectures that can be used for scaling this integrat

{{event "activity"}}

#### Sign-on

{{event "sign_on"}}

### Inputs used

These inputs are used in the integration:
Expand All @@ -205,8 +220,8 @@ These inputs are used in the integration:
This integration uses the following Workday APIs:

- **Activity**: [Workday Activity API documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#privacy/v1/get-/activityLogging).
- **Sign-on**: The Workday **Reports as a Service (RaaS)** JSON endpoint is used to fetch a user-defined Sign-on custom report:
- **Sign-on**: The Workday **Reports as a Service (RaaS)** JSON endpoint is used to fetch a user-defined Sign-on custom report. On each poll the integration appends the report's required moment-range prompts:

```
GET https://HOST/ccx/service/customreport2/TENANT/REPORT_OWNER/REPORT_NAME?format=json
GET https://HOST/ccx/service/customreport2/TENANT/REPORT_OWNER/REPORT_NAME?format=json&From_Moment=...&To_Moment=...
```
98 changes: 98 additions & 0 deletions packages/workday/_dev/deploy/docker/files/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,101 @@ rules:
]
}
`}}
- path: /ccx/service/customreport2/tenant/report_owner/report_name
methods: ['GET']
query_params:
format: ['json']
From_Moment: ['{From_Moment:.*}']
To_Moment: ['{To_Moment:.*}']
responses:
# First evaluation (initial_interval window).
- status_code: 200
headers:
Content-Type:
- application/json
body: |
{{ minify_json `
{
"Report_Entry": [
{
"Account_Locked__Disabled_or_Expired": "0",
"Active_Session": "0",
"Authentication_Type_for_Signon": "SAML",
"Browser_Type": "Chrome",
"Device_Type": "Desktop",
"Device_is_Trusted": "0",
"Failed_Signon": "0",
"Forgotten_Password_Reset_Request": "0",
"Invalid_Credentials": "0",
"Is_Device_Managed": "0",
"Operating_System": "Mac OS X",
"Password_Changed": "0",
"Request_Originator": "UI",
"SAML_Identity_Provider": "IdP_Acme",
"Session_End": "2026-06-18T23:25:22-07:00",
"Session_Start": "2026-06-18T16:59:32-07:00",
"System_Account": "user534.acme / User 534"
},
{
"Account_Locked__Disabled_or_Expired": "0",
"Active_Session": "0",
"Authentication_Type_for_Signon": "OAuth 2.0",
"Device_is_Trusted": "0",
"Failed_Signon": "0",
"Forgotten_Password_Reset_Request": "0",
"Invalid_Credentials": "0",
"Is_Device_Managed": "0",
"Password_Changed": "0",
"Request_Originator": "Internal",
"Session_End": "2026-06-18T22:55:34-07:00",
"Session_Start": "2026-06-18T16:55:34-07:00",
"System_Account": "ISU_Integration_049"
}
]
}
`}}
# Second evaluation after cursor advances (new From_Moment/To_Moment window).
- status_code: 200
headers:
Content-Type:
- application/json
body: |
{{ minify_json `
{
"Report_Entry": [
{
"Account_Locked__Disabled_or_Expired": "0",
"Active_Session": "0",
"Authentication_Type_for_Signon": "SAML",
"Browser_Type": "Workday Phone App",
"Device_Type": "Phone",
"Device_is_Trusted": "0",
"Failed_Signon": "0",
"Forgotten_Password_Reset_Request": "0",
"Invalid_Credentials": "0",
"Is_Device_Managed": "0",
"Operating_System": "iOS",
"Password_Changed": "0",
"Request_Originator": "UI",
"SAML_Identity_Provider": "IdP_Acme",
"Session_End": "2026-06-18T22:59:33-07:00",
"Session_Start": "2026-06-18T16:57:04-07:00",
"System_Account": "user179.acme / User 179"
},
{
"Account_Locked__Disabled_or_Expired": "0",
"Active_Session": "1",
"Authentication_Type_for_Signon": "User Name Password",
"Device_is_Trusted": "0",
"Failed_Signon": "0",
"Forgotten_Password_Reset_Request": "0",
"Invalid_Credentials": "0",
"Is_Device_Managed": "0",
"Password_Changed": "0",
"Request_Originator": "Web Services",
"Session_Start": "2026-06-18T13:18:51-07:00",
"System_Account": "ISU_Integration_001"
}
]
}
`}}
5 changes: 5 additions & 0 deletions packages/workday/changelog.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# newer versions go on top
- version: "0.3.2"
changes:
- description: Fix Sign-on RaaS collection by injecting From_Moment/To_Moment prompts and advancing a collection cursor.
type: bugfix
link: https://github.com/elastic/integrations/pull/20749
- version: "0.3.1"
changes:
- description: Set agentless deployment mode `release` field to `ga`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
wait_for_data_timeout: 1m
input: cel
service: workday
vars:
ssl: |
certificate_authorities:
- |
-----BEGIN CERTIFICATE-----
MIIDJTCCAg2gAwIBAgIUV+67DSMtT/+uSt4qDbY6IDwlJawwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLc3ZjLXdvcmtkYXkwHhcNMjYwNjAxMTMwODIxWhcNMzYw
NTI5MTMwODIxWjAWMRQwEgYDVQQDDAtzdmMtd29ya2RheTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKGf75wHU2B9niPdBS+QKuvQwWhtfPJxWtwpBtj6
hqleBEAD1+fq02u0hflu9rEBxCcXYt8cQzVd0cW4zUvqk+78lwIcv3NelyHcwsEu
sfAhFXnL08axQVYc3oYzy2dQNnYH7lEGmY8/N31umXeTojPlDji9xTaExQ6Mj83y
QXQ1DajdHlirw8Qf8cnKPZtW50zyf7MGSTYiLgOvv9pFhwLL8eMKhJo23+c+B7GI
lGUOzQZV9VfzgQRpDpRnLeKghRwltverMkadxXlVAKlbXuhSDJluo2NCZ3+wBrj4
IuMX0+IB6o6eZWnnJgPrQ5DN4WJdwjmmMQ7LTyUw3/hXjw0CAwEAAaNrMGkwHQYD
VR0OBBYEFDla3DE7Dm0f35w6PiMqslosh2HNMB8GA1UdIwQYMBaAFDla3DE7Dm0f
35w6PiMqslosh2HNMA8GA1UdEwEB/wQFMAMBAf8wFgYDVR0RBA8wDYILc3ZjLXdv
cmtkYXkwDQYJKoZIhvcNAQELBQADggEBAGb/wSyfnXpK9z7GqKnJ2JA3RkRdudHT
sy7j0Q0W0Cr6VeVj+paiRwQtMXriNOMzWvJ2cTMRp4l181XMnLvasQTqOASHpM1G
Ra0eEmITzhdJsiyW585fAL+hyvthzo8f4r/9rm3NwDQdFQt9akGw1nnuFzd2+K/b
c0GJiVBgNKp3Q1u7MeEQK/kKjzKP5qsB34aJNxWdzKF7rFaRQPtBcGh15zfxpcOJ
VrEdkgusmHeI8Q4yKPu2Vopaq29dg3iW/FLsXeTAOYeo9KfgSXt3cxMPrrwtJEwG
nWd6q0A8Ec8udGavtotazOi9g+F16fNTaUVk8bXkoTUezwSe/IfGRw4=
-----END CERTIFICATE-----
data_stream:
vars:
url: https://{{Hostname}}:{{Port}}/ccx/service/customreport2/tenant/report_owner/report_name?format=json
username: test_user
password: test_password
initial_interval: 24h
interval: 10s
preserve_original_event: true
assert:
# First poll returns 2 rows; after cursor advances the next poll returns 2 more.
hit_count: 4
21 changes: 18 additions & 3 deletions packages/workday/data_stream/sign_on/agent/stream/cel.yml.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,31 @@ redact:
fields:
- auth.basic.user
- auth.basic.password
state:
initial_interval: {{initial_interval}}
program: |-
state.with(
get(state.url).as(resp,
(
state.url + (state.url.contains("?") ? "&" : "?") + {
"From_Moment": [state.?cursor.last_timestamp.orValue((now - duration(state.initial_interval)).format(time_layout.RFC3339))],
"To_Moment": [now.format(time_layout.RFC3339)],
}.format_query()
).as(request_url,
get(request_url).as(resp,
(resp.StatusCode == 200) ?
bytes(resp.Body).decode_json().as(body,
{
"events": (has(body.Report_Entry) && size(body.Report_Entry) > 0) ?
body.Report_Entry.map(e, {"message": e.encode_json()})
:
[],
"cursor": {
"last_timestamp":
(has(body.Report_Entry) && size(body.Report_Entry) > 0) ?
now.format(time_layout.RFC3339)
:
state.?cursor.last_timestamp.orValue(now.format(time_layout.RFC3339)),
},
"want_more": false,
}
)
:
Expand All @@ -37,7 +52,7 @@ program: |-
"error": {
"code": string(resp.StatusCode),
"id": resp.Status,
"message": "GET " + state.url + ": " + (
"message": "GET " + request_url + ": " + (
(size(resp.Body) != 0) ?
string(resp.Body)
:
Expand Down
8 changes: 8 additions & 0 deletions packages/workday/data_stream/sign_on/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ streams:
required: true
show_user: true
secret: true
- name: initial_interval
type: text
title: Initial Interval
description: How far back to pull Sign-on logs from the Workday Custom Report API on the first collection. Supported units for this parameter are h/m/s.
default: 24h
multi: false
required: true
show_user: true
- name: interval
type: text
title: Interval
Expand Down
87 changes: 87 additions & 0 deletions packages/workday/data_stream/sign_on/sample_event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"@timestamp": "2026-08-14T10:17:41.830Z",
"agent": {
"ephemeral_id": "ac7e12bf-8acc-4d21-83c5-1bef2dfc7399",
"id": "b458462c-8d38-4125-ac4d-d3a25bff477c",
"name": "elastic-agent-93505",
"type": "filebeat",
"version": "8.19.0"
},
"data_stream": {
"dataset": "workday.sign_on",
"namespace": "15969",
"type": "logs"
},
"device": {
"type": "Desktop"
},
"ecs": {
"version": "9.4.0"
},
"elastic_agent": {
"id": "b458462c-8d38-4125-ac4d-d3a25bff477c",
"snapshot": false,
"version": "8.19.0"
},
"event": {
"action": "user-signon",
"agent_id_status": "verified",
"category": [
"authentication",
"session"
],
"dataset": "workday.sign_on",
"end": "2026-06-19T06:25:22.000Z",
"ingested": "2026-08-14T10:17:44Z",
"kind": "event",
"original": "{\"Account_Locked__Disabled_or_Expired\":\"0\",\"Active_Session\":\"0\",\"Authentication_Type_for_Signon\":\"SAML\",\"Browser_Type\":\"Chrome\",\"Device_Type\":\"Desktop\",\"Device_is_Trusted\":\"0\",\"Failed_Signon\":\"0\",\"Forgotten_Password_Reset_Request\":\"0\",\"Invalid_Credentials\":\"0\",\"Is_Device_Managed\":\"0\",\"Operating_System\":\"Mac OS X\",\"Password_Changed\":\"0\",\"Request_Originator\":\"UI\",\"SAML_Identity_Provider\":\"IdP_Acme\",\"Session_End\":\"2026-06-18T23:25:22-07:00\",\"Session_Start\":\"2026-06-18T16:59:32-07:00\",\"System_Account\":\"user534.acme / User 534\"}",
"outcome": "success",
"start": "2026-06-18T23:59:32.000Z",
"type": [
"start",
"end"
]
},
"host": {
"entity": {
"attributes": {
"managed": false
},
"lifecycle": {
"last_activity": "2026-06-19T06:25:22.000Z"
}
},
"os": {
"name": "Mac OS X"
}
},
"input": {
"type": "cel"
},
"tags": [
"preserve_original_event",
"forwarded",
"workday-sign_on"
],
"user_agent": {
"name": "Chrome"
},
"workday": {
"sign_on": {
"Account_Locked__Disabled_or_Expired": false,
"Active_Session": false,
"Authentication_Type": "SAML",
"Authentication_Type_for_Signon": "SAML",
"Browser_Type": "Chrome",
"Device_is_Trusted": false,
"Failed_Signon": false,
"Forgotten_Password_Reset_Request": false,
"Invalid_Credentials": false,
"Is_Device_Managed": false,
"Password_Changed": false,
"Request_Originator": "UI",
"SAML_Identity_Provider": "IdP_Acme",
"System_Account": "user534.acme / User 534"
}
}
}
Loading
Loading