Google SecOps
Forward audit logs to Google Security Operations (formerly Chronicle) so your audit trail is searchable in SecOps and available to detection rules.
Willow ingests through the Chronicle API's logs:import method, authenticating with a Google Cloud service account. This is the modern replacement for the legacy Ingestion API (malachiteingestion-pa.googleapis.com), which Google closed to new instances in October 2026 and removes entirely in July 2027.
Prerequisites
- A Google SecOps instance, and the Google Cloud project it is bound to.
- Your instance ID (the SecOps customer ID). Find it in the SecOps console under Settings > Profile.
- The region your instance runs in, for example
us,eu, oreurope-west3. - A service account with permission to import logs, plus a JSON key for it (see below).
- A log type that exists in your instance for these events.
- Network egress allowed from your deployment to
https://chronicle.<region>.rep.googleapis.comandhttps://oauth2.googleapis.comon port 443.
Create the service account
- In the Google Cloud console for the project bound to your SecOps instance, enable the Chronicle API.
- Go to IAM & Admin > Service Accounts and create a service account, for example
willow-audit-ingest. - Grant it the Chronicle API Editor role, which includes the
chronicle.logs.importpermission the import call requires. - Open the service account, go to Keys > Add key > Create new key, and choose JSON. The key file downloads once — store it securely.
- Paste the entire contents of that JSON file into the Service Account Key field in Willow.
Do not use a legacy ingestion service account for this — accounts whose email contains bk or malachite-cx only work with the retired API.
Choose a log type
logs:import writes to one log type, which must already exist in your instance. Willow sends JSON, and no built-in Google parser understands Willow's audit log shape, so create a custom log type (for example WILLOW_AUDIT) and attach a custom parser to it.
Until a parser is attached, events still arrive and are retained as raw logs — they are searchable in Raw Log Search but will not populate UDM fields or drive detections.
Configuration fields
| Field | Required | Description |
|---|---|---|
| Google Cloud Project ID | Yes | The project that owns your SecOps instance, e.g. my-secops-project. |
| Region | Yes | Your instance's location. Selects the regional service endpoint. |
| Instance ID | Yes | Your SecOps customer ID, a UUID from Settings > Profile. |
| Log Type | Yes | The log type events are ingested as, e.g. WILLOW_AUDIT. Must already exist in the instance. |
| Service Account Key (JSON) | Yes | The full contents of the service account's JSON key file. |
| Namespace | No | Sets environmentNamespace on every event, tagging its data domain (e.g. production). |
| Custom Endpoint | No | Overrides the derived regional endpoint. Leave empty unless your instance is only reachable at https://<region>-chronicle.googleapis.com. |
| Anonymous Mode | No | Toggle to send only statistical data (see Anonymous Mode). |
How it works
Willow exchanges the service account key for an access token at https://oauth2.googleapis.com/token using the JWT-bearer grant and the cloud-platform scope. Tokens are cached and reused until shortly before they expire, so a token request is not made per event. If SecOps rejects a token with 401, the cached token is discarded and the next delivery mints a fresh one.
Each audit log is then POSTed to:
https://chronicle.<region>.rep.googleapis.com/v1alpha/projects/<project>/locations/<region>/instances/<instance>/logTypes/<log-type>/logs:import
The audit log record is base64-encoded into the data field of a single-entry inline source:
{
"inlineSource": {
"logs": [
{
"data": "<base64-encoded audit log JSON>",
"logEntryTime": "2024-01-15T10:30:00.000Z",
"collectionTime": "2024-01-15T10:30:00.412Z",
"environmentNamespace": "production"
}
]
}
}
Decoded, data is the same audit log record every provider receives:
{
"id": "…",
"org_id": "org_123",
"action": "tool_call",
"created_at": "2024-01-15T10:30:00.000Z",
"user": { "id": "…", "name": "…", "email": "…" },
"data": { "…": "…" }
}
logEntryTime comes from the audit log's own created_at, and collectionTime is when Willow sent it. SecOps requires the collection time to be strictly later than the entry time, so an event with a missing or clock-skewed timestamp is pinned just behind the collection time rather than dropped. In non-anonymous mode the payload includes the user object; in anonymous mode the user object and data payload are omitted. The request timeout is 5 seconds.
Tips
- A
logs:importbatch is capped at 4 MB. Because base64 makes the payload about a third larger, Willow shortens any event whose JSON exceeds roughly 2.5 MB before sending; truncated values are marked with...[truncated]. - SecOps deduplicates at the batch level by hashing the customer ID and log content, so a genuinely identical event resent under the same log type is rejected as a duplicate.
- Use the Test button on the log provider to send a sample event, then confirm it lands with a Raw Log Search scoped to your log type.
- Keep the region consistent: it appears in both the endpoint host and the resource path, and must match where the instance actually lives.
Troubleshooting
401 UNAUTHENTICATED: the key is invalid, disabled, or was deleted. Confirm you pasted the whole JSON file and that the service account is still enabled.403 PERMISSION_DENIED: the service account is missingchronicle.logs.import. Grant it Chronicle API Editor on the project. Also confirm the Chronicle API is enabled and that the project really is the one bound to your instance.404 NOT_FOUND: usually the region, project ID, instance ID, or log type is wrong. Some instances are only served by the older locational host — set Custom Endpoint tohttps://<region>-chronicle.googleapis.comand retry.400 INVALID_ARGUMENT: most often the log type does not exist in the instance, or the timestamps were rejected. Create the custom log type first.- Events arrive but fields are empty: the log type has no parser attached, so events are retained as raw logs only. Attach a parser to populate UDM fields.
If no logs appear at all, confirm the credentials and identifiers are correct and that outbound egress is allowed. See Log Settings for shared guidance.
Related
- Log Settings: retention, content options, and Anonymous Mode
- Logs: view and search the logs themselves
- Chronicle API ingestion methods
- Migrate from the legacy SIEM API