Skip to content

Commit

Permalink
Outlook activity datastream for o365 package (#11937)
Browse files Browse the repository at this point in the history
* Initial draft of the o365_metrics package with the `outlook_activity` data stream.
  • Loading branch information
ritalwar authored Dec 16, 2024
1 parent 24b79b1 commit 9838908
Show file tree
Hide file tree
Showing 17 changed files with 574 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,4 @@
/packages/zscaler_zpa @elastic/security-service-integrations
/packages/cisco_meraki_metrics @elastic/obs-infraobs-integrations
/packages/panw_metrics @elastic/obs-infraobs-integrations
/packages/o365_metrics @elastic/obs-infraobs-integrations
3 changes: 3 additions & 0 deletions packages/o365_metrics/_dev/build/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies:
ecs:
reference: "[email protected]"
44 changes: 44 additions & 0 deletions packages/o365_metrics/_dev/build/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Microsoft Office 365 Metrics Integration

This integration uses the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/overview) to collect essential metrics from Microsoft Office 365, offering detailed insights into user activity, application usage, and overall system performance.

## Setup

To use this package you need to enable datastreams you want to collect metrics for and register an application in [Microsoft Entra ID (formerly known as Azure Active Directory)](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id).

Once the application is registered, configure and/or note the following to setup O365 metrics Elastic integration:
1. Note `Application (client) ID` and the `Directory (tenant) ID` in the registered application's `Overview` page.
2. Create a new secret to configure the authentication of your application.
- Navigate to `Certificates & Secrets` section.
- Click `New client secret` and provide some description to create new secret.
- Note the `Value` which is required for the integration setup.
3. Add permissions to your registered application. Please check [O365 Graph API permissions](https://learn.microsoft.com/en-us/graph/reportroot-authorization) for more details.
- Navigate to `API permissions` page and click `Add a permission`
- Select `Office 365 Management APIs` tile from the listed tiles.
- Click `Application permissions`.
- If `User.Read` permission under `Microsoft.Graph` tile is not added by default, add this permission.
- After the permissions are added, the admin has to grant consent for these permissions.

Once the secret is created and permissions are granted by admin, setup Elastic Agent's Microsoft O365 integration:
- Click `Add Microsoft Office 365`.
- Enable `Collect Office 365 metrics via Graph API using CEL Input`.
- Add `Directory (tenant) ID` noted in Step 1 into `Directory (tenant) ID` parameter. This is required field.
- Add `Application (client) ID` noted in Step 1 into `Application (client) ID` parameter. This is required field.
- Add the secret `Value` noted in Step 2 into `Client Secret` parameter. This is required field.
- Oauth2 Token URL can be added to generate the tokens during the oauth2 flow. If not provided, above `Directory (tenant) ID` will be used for oauth2 token generation.
- Modify any other parameters as necessary.



## Compatibility



## Metrics

### OutlookActivity

Uses the Office 365 Management Graph API to retrieve metrics from Office 365.


{{fields "outlook_activity"}}
6 changes: 6 additions & 0 deletions packages/o365_metrics/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# newer versions go on top
- version: "0.1.0"
changes:
- description: Initial draft of the o365_metrics package with the `outlook_activity` data stream.
type: enhancement
link: https://github.com/elastic/integrations/pull/11937
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dynamic_fields:
"event.ingested": ".*"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"events": [
{
"outlookactivity": "{\"Meeting Created\":\"0\",\"Meeting Interacted\":\"\",\"Read\":\"\",\"Receive\":\"1\",\"Report Date\":\"2024-11-25\",\"Report Period\":\"7\",\"Send\":\"\",\"Report Refresh Date\":\"2024-11-30\"}"
}
]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"expected": [
{
"ecs": {
"version": "8.16.0"
},
"o365": {
"metrics": {
"outlook": {
"activity": {
"emails_read": {
"count": ""
},
"emails_received": {
"count": "1"
},
"emails_sent": {
"count": ""
},
"meeting_created": {
"count": "0"
},
"meeting_interacted": {
"count": ""
},
"report_date": "2024-11-25",
"report_period": "7",
"report_refresh_date": "2024-11-30"
}
}
}
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
config_version: 2
interval: {{interval}}
auth.oauth2:
client.id: {{client_id}}
client.secret: {{client_secret}}
provider: azure
scopes:
{{#each token_scopes as |token_scope|}}
- {{token_scope}}
{{/each}}
endpoint_params:
grant_type: client_credentials
{{#if token_url}}
token_url: {{token_url}}/{{azure_tenant_id}}/oauth2/v2.0/token
{{else if azure_tenant_id}}
azure.tenant_id: {{azure_tenant_id}}
{{/if}}

resource.url: {{url}}

{{#if resource_ssl}}
resource.ssl:
{{resource_ssl}}
{{/if}}
{{#if resource_timeout}}
resource.timeout: {{resource_timeout}}
{{/if}}
{{#if enable_request_tracer}}
resource.tracer.filename: "../../logs/cel/http-request-trace-*.ndjson"
{{/if}}
{{#if resource_proxy_url}}
resource.proxy_url: {{resource_proxy_url}}
{{/if}}
{{#if resource_retry_max_attempts}}
resource.retry.max_attempts: {{resource_retry_max_attempts}}
{{/if}}
{{#if resource_retry_wait_min}}
resource.retry.wait_min: {{resource_retry_wait_min}}
{{/if}}
{{#if resource_retry_wait_max}}
resource.retry.wait_max: {{resource_retry_wait_max}}
{{/if}}
{{#if resource_redirect_forward_headers}}
resource.redirect.forward_headers: {{resource_redirect_forward_headers}}
{{/if}}
{{#if resource_redirect_headers_ban_list}}
resource.redirect.headers_ban_list:
{{#each resource_redirect_headers_ban_list as |item|}}
- {{item}}
{{/each}}
{{/if}}
{{#if resource_redirect_max_redirects}}
resource.redirect.max_redirects: {{resource_redirect_max_redirects}}
{{/if}}
{{#if resource_rate_limit_limit}}
resource.rate_limit.limit: {{resource_rate_limit_limit}}
{{/if}}
{{#if resource_rate_limit_burst}}
resource.rate_limit.burst: {{resource_rate_limit_burst}}
{{/if}}
tags:
{{#if preserve_original_event}}
- preserve_original_event
{{/if}}
{{#each tags as |tag|}}
- {{tag}}
{{/each}}
{{#contains "forwarded" tags}}
publisher_pipeline.disable_host: true
{{/contains}}
{{#if processors}}
processors:
{{processors}}
{{/if}}

state:
want_more: false
base:
tenant_id: "{{azure_tenant_id}}"
period: "{{period}}"

redact:
fields:
- base.tenant_id



program: |
state.with(
request(
"GET",
state.url + "/reports/getEmailActivityCounts(period='" + state.base.period + "')"
).do_request().as(resp,
resp.StatusCode == 200
?
bytes(resp.Body).mime("text/csv; header=present").as(events, {
"events": events.map(e, {"outlookactivity": e.encode_json()}),

})
:
{
"events": {
"error": {
"code": string(resp.StatusCode),
"id": string(resp.Status),
"message": "GET:"+(
size(resp.Body) != 0 ?
string(resp.Body)
:
string(resp.Status) + ' (' + string(resp.StatusCode) + ')'
),
},
},
"want_more": false,
}
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
description: Pipeline for renaming object
processors:
- set:
field: ecs.version
value: "8.16.0"
- json:
field: outlookactivity
target_field: o365.metrics.outlook.activity
- script:
lang: painless
description: Replace spaces and dashes in field names under o365.metrics.outlook.activity.
tag: painless_purge_spaces_and_dashes
if: ctx.o365.metrics?.outlook.activity instanceof Map
source: |
String underscore(String s) {
return /[ -]/.matcher(s).replaceAll('_').toLowerCase();
}
def out = [:];
for (def item : ctx.o365.metrics.outlook.activity.entrySet()) {
out[underscore(item.getKey())] = item.getValue();
}
ctx.o365.metrics.outlook.activity = out;
- remove:
if: ctx.outlookactivity != null
field: outlookactivity
ignore_missing: true

- rename:
field: o365.metrics.outlook.activity.read
target_field: o365.metrics.outlook.activity.emails_read.count
ignore_missing: true
- rename:
field: o365.metrics.outlook.activity.receive
target_field: o365.metrics.outlook.activity.emails_received.count
ignore_missing: true
- rename:
field: o365.metrics.outlook.activity.send
target_field: o365.metrics.outlook.activity.emails_sent.count
ignore_missing: true
- rename:
field: o365.metrics.outlook.activity.meeting_created
target_field: o365.metrics.outlook.activity.meeting_created.count
ignore_missing: true
- rename:
field: o365.metrics.outlook.activity.meeting_interacted
target_field: o365.metrics.outlook.activity.meeting_interacted.count
ignore_missing: true

on_failure:
- append:
field: error.message
value: "{{{_ingest.on_failure_message}}}"
- append:
field: event.kind
value: pipeline_error
allow_duplicates: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
- name: cloud
title: Cloud
group: 2
description: Fields related to the cloud or infrastructure the events are coming from.
footnote: 'Examples: If Metricbeat is running on an EC2 host and fetches data from its host, the cloud info contains the data about this machine. If Metricbeat runs on a remote machine outside the cloud and fetches data from a service running in the cloud, the field contains cloud data from the machine the service is running on.'
type: group
fields:
- name: image.id
type: keyword
description: Image ID for the cloud instance.
- name: host
title: Host
group: 2
description: 'A host is defined as a general computing instance. ECS host.* fields should be populated with details about the host on which the event happened, or from which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes.'
type: group
fields:
- name: containerized
type: boolean
description: >
If the host is a container.
- name: os.build
type: keyword
example: "18D109"
description: >
OS build information.
- name: os.codename
type: keyword
example: "stretch"
description: >
OS codename, if any.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- name: data_stream.type
type: constant_keyword
description: Data stream type.
- name: data_stream.dataset
type: constant_keyword
description: Data stream dataset.
- name: data_stream.namespace
type: constant_keyword
description: Data stream namespace.
- name: '@timestamp'
type: date
description: Event timestamp.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
- name: o365.metrics.outlook.activity
type: group
fields:
- name: meeting_created.count
type: integer
description: |
The count of calendar meetings created by users during the reporting period.
- name: meeting_interacted.count
type: integer
description: |
The count of meetings where users interacted (e.g., accepted, declined, or modified) during the reporting period.
- name: emails_read.count
type: integer
description: |
The count of email messages read by users during the reporting period.
- name: emails_received.count
type: integer
description: |
The count of email messages received by users during the reporting period.
- name: emails_sent.count
type: integer
description: |
The count of email messages sent by users during the reporting period.
- name: report_date
type: date
description: |
The specific date for which the report data applies.
- name: report_period
type: integer
description: |
The duration (e.g., 7 days) over which the quota status data is aggregated.
- name: report_refresh_date
type: date
description: |
The date when the report data was last updated.
Loading

0 comments on commit 9838908

Please sign in to comment.