Skip to main content

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

  1. A Google SecOps instance, and the Google Cloud project it is bound to.
  2. Your instance ID (the SecOps customer ID). Find it in the SecOps console under Settings > Profile.
  3. The region your instance runs in, for example us, eu, or europe-west3.
  4. A service account with permission to import logs, plus a JSON key for it (see below).
  5. A log type that exists in your instance for these events.
  6. Network egress allowed from your deployment to https://chronicle.<region>.rep.googleapis.com and https://oauth2.googleapis.com on port 443.

Create the service account

  1. In the Google Cloud console for the project bound to your SecOps instance, enable the Chronicle API.
  2. Go to IAM & Admin > Service Accounts and create a service account, for example willow-audit-ingest.
  3. Grant it the Chronicle API Editor role, which includes the chronicle.logs.import permission the import call requires.
  4. Open the service account, go to Keys > Add key > Create new key, and choose JSON. The key file downloads once — store it securely.
  5. 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

FieldRequiredDescription
Google Cloud Project IDYesThe project that owns your SecOps instance, e.g. my-secops-project.
RegionYesYour instance's location. Selects the regional service endpoint.
Instance IDYesYour SecOps customer ID, a UUID from Settings > Profile.
Log TypeYesThe log type events are ingested as, e.g. WILLOW_AUDIT. Must already exist in the instance.
Service Account Key (JSON)YesThe full contents of the service account's JSON key file.
NamespaceNoSets environmentNamespace on every event, tagging its data domain (e.g. production).
Custom EndpointNoOverrides the derived regional endpoint. Leave empty unless your instance is only reachable at https://<region>-chronicle.googleapis.com.
Anonymous ModeNoToggle 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:import batch 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 missing chronicle.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 to https://<region>-chronicle.googleapis.com and 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.