Collect Druva Backup logs

Supported in:

This document explains how to collect Druva Backup logs by setting up a Google Cloud Run function that retrieves events from the Druva REST API and writes them to a Google Cloud Storage bucket, and then configuring a Google Security Operations feed using Google Cloud Storage V2.

Druva is a cloud-native data protection and management platform that provides backup, disaster recovery, and archival services for endpoints, SaaS applications, and enterprise workloads. The platform generates comprehensive audit trails, backup events, restore activities, and security alerts that can be integrated with SIEM solutions for monitoring and compliance.

Before you begin

Make sure you have the following prerequisites:

  • A Google SecOps instance
  • A Google Cloud project with billing enabled
  • The following Google Cloud APIs enabled:
    • Cloud Run functions API
    • Cloud Scheduler API
    • Cloud Storage API
    • Pub/Sub API
    • IAM API
  • Druva Cloud Administrator access to the Druva Cloud Platform Console
  • Access to the Druva Integration Center for API credential creation

Create a Google Cloud Storage bucket

  1. Go to the Google Cloud console .
  2. Select your project or create a new one.
  3. In the navigation menu, go to Cloud Storage > Buckets.
  4. Click Create bucket.
  5. Provide the following configuration details:

    Setting Value
    Name your bucket Enter a globally unique name (for example, druva-backup-logs )
    Location type Choose based on your needs (Region, Dual-region, Multi-region)
    Location Select the location closest to your Google SecOps instance (for example, us-central1 )
    Storage class Standard (recommended for frequently accessed logs)
    Access control Uniform (recommended)
    Protection tools Optional: Enable object versioning or retention policy
  6. Click Create.

Collect Druva API credentials

To enable the Cloud Run function to retrieve events from Druva, you need to create API credentials with OAuth 2.0 authentication.

Create API credentials

  1. Sign in to the Druva Cloud Platform Console.
  2. From the Global Navigationmenu, select Integration Center.
  3. In the left panel, click API Credentials.
  4. Click New Credentials.
  5. In the New Credentialswindow, provide the following details: Name: Enter a descriptive name (for example, Google SecOps Cloud Storage Integration ).
  6. To apply authorization restrictions:
    1. Select Druva Cloud Administratorto allow full access to data retrieval and modification.
    2. Alternatively, select Product Administratorand choose: Cloud Admin (Read Only) role: To restrict access to data retrieval only with no modification rights (recommended for SIEM integration)
  7. Click Save.

Record API credentials

After creating the API credentials, the Credential Detailswindow appears:

  1. Click the copy icon next to Client IDto copy the value to your clipboard.
  2. Save the Client ID securely (for example, McNkxxxx4Vicxxxx4Ldpxxxx/09Uxxxx ).
  3. Click the copy icon next to Secret Keyto copy the value to your clipboard.
  4. Save the Secret Key securely (for example, Xmcxxxx8j5xxxx6NxxxxRbRxxxxNNyPt ).

Create a dedicated service account for the Cloud Run function to access Google Cloud Storage.

  1. In the Google Cloud console, go to IAM & Admin > Service Accounts.
  2. Click Create Service Account.
  3. Provide the following configuration details:
    • Service account name: Enter druva-backup-function (or a descriptive name)
    • Service account description: Enter Service account for Druva Backup Cloud Run function
  4. Click Create and Continue.
  5. In the Grant this service account access to projectsection, add the following roles:
    1. Click Select a roleand select Storage Object Admin.
    2. Click Add another roleand select Cloud Run Invoker.
  6. Click Continue.
  7. Click Done.
  8. Record the service account email (for example, druva-backup-function@PROJECT_ID.iam.gserviceaccount.com ).

Create a Pub/Sub topic

Create a Pub/Sub topic that Cloud Scheduler will use to trigger the Cloud Run function.

  1. In the Google Cloud console, go to Pub/Sub > Topics.
  2. Click Create Topic.
  3. Provide the following configuration details:
    • Topic ID: Enter druva-backup-trigger
  4. Uncheck Add a default subscription.
  5. Click Create.

Create the Cloud Run function

Prepare the function code

Create a Cloud Run function that authenticates with the Druva API using OAuth 2.0 client credentials, retrieves events via the events endpoint with pagination, and writes the results as NDJSON to the GCS bucket.

Deploy the Cloud Run function

  1. In the Google Cloud console, go to Cloud Run functions.
  2. Click Create Function.
  3. Provide the following configuration details:

    • Environment: Select 2nd gen
    • Function name: Enter druva-backup-to-gcs
    • Region: Select the region closest to your GCS bucket (for example, us-central1 )
    • Trigger type: Select Cloud Pub/Sub
    • Cloud Pub/Sub topic: Select druva-backup-trigger
    • Service account: Select druva-backup-function@PROJECT_ID.iam.gserviceaccount.com
    • Memory allocated: 512 MiB
    • Timeout: 540 seconds
    • Maximum number of instances: 1
  4. Click Next.

  5. Select Python 3.11as the Runtime.

  6. Set the Entry pointto main .

  7. In the Source codeeditor, replace the contents of main.py with the following:

      import 
      
     base64 
     import 
      
     json 
     import 
      
     os 
     import 
      
     time 
     from 
      
     datetime 
      
     import 
     datetime 
     , 
     timezone 
     , 
     timedelta 
     import 
      
     requests 
     from 
      
     google.cloud 
      
     import 
      storage 
     
     GCS_BUCKET 
     = 
     os 
     . 
     environ 
     [ 
     "GCS_BUCKET" 
     ] 
     GCS_PREFIX 
     = 
     os 
     . 
     environ 
     . 
     get 
     ( 
     "GCS_PREFIX" 
     , 
     "druva_backup" 
     ) 
     STATE_KEY 
     = 
     os 
     . 
     environ 
     . 
     get 
     ( 
     "STATE_KEY" 
     , 
     "druva_state.json" 
     ) 
     DRUVA_BASE_URL 
     = 
     os 
     . 
     environ 
     . 
     get 
     ( 
     "DRUVA_BASE_URL" 
     , 
     "apis.druva.com" 
     ) 
     CLIENT_ID 
     = 
     os 
     . 
     environ 
     [ 
     "CLIENT_ID" 
     ] 
     CLIENT_SECRET 
     = 
     os 
     . 
     environ 
     [ 
     "CLIENT_SECRET" 
     ] 
     MAX_RECORDS 
     = 
     int 
     ( 
     os 
     . 
     environ 
     . 
     get 
     ( 
     "MAX_RECORDS" 
     , 
     "10000" 
     )) 
     PAGE_SIZE 
     = 
     int 
     ( 
     os 
     . 
     environ 
     . 
     get 
     ( 
     "PAGE_SIZE" 
     , 
     "500" 
     )) 
     LOOKBACK_HOURS 
     = 
     int 
     ( 
     os 
     . 
     environ 
     . 
     get 
     ( 
     "LOOKBACK_HOURS" 
     , 
     "24" 
     )) 
     def 
      
     get_oauth_token 
     (): 
      
     """Obtain OAuth 2.0 access token using client credentials grant.""" 
     token_url 
     = 
     f 
     "https:// 
     { 
     DRUVA_BASE_URL 
     } 
     /token" 
     payload 
     = 
     { 
     "grant_type" 
     : 
     "client_credentials" 
     , 
     "scope" 
     : 
     "read" 
     , 
     } 
     resp 
     = 
     requests 
     . 
     post 
     ( 
     token_url 
     , 
     data 
     = 
     payload 
     , 
     auth 
     = 
     ( 
     CLIENT_ID 
     , 
     CLIENT_SECRET 
     ), 
     timeout 
     = 
     30 
     , 
     ) 
     resp 
     . 
     raise_for_status 
     () 
     return 
     resp 
     . 
     json 
     ()[ 
     "access_token" 
     ] 
     def 
      
     load_state 
     ( 
     storage_client 
     ): 
      
     """Load the persisted state (last event time and tracker) from GCS.""" 
     bucket 
     = 
     storage_client 
     . 
      bucket 
     
     ( 
     GCS_BUCKET 
     ) 
     blob 
     = 
     bucket 
     . 
     blob 
     ( 
     f 
     " 
     { 
     GCS_PREFIX 
     } 
     / 
     { 
     STATE_KEY 
     } 
     " 
     ) 
     if 
     blob 
     . 
     exists 
     (): 
     return 
     json 
     . 
     loads 
     ( 
     blob 
     . 
      download_as_text 
     
     ()) 
     return 
     {} 
     def 
      
     save_state 
     ( 
     storage_client 
     , 
     state 
     ): 
      
     """Persist state to GCS.""" 
     bucket 
     = 
     storage_client 
     . 
      bucket 
     
     ( 
     GCS_BUCKET 
     ) 
     blob 
     = 
     bucket 
     . 
     blob 
     ( 
     f 
     " 
     { 
     GCS_PREFIX 
     } 
     / 
     { 
     STATE_KEY 
     } 
     " 
     ) 
     blob 
     . 
      upload_from_string 
     
     ( 
     json 
     . 
     dumps 
     ( 
     state 
     ), 
     content_type 
     = 
     "application/json" 
     , 
     ) 
     def 
      
     fetch_events 
     ( 
     token 
     , 
     state 
     ): 
      
     """Fetch events from Druva API with pagination via nextPageToken.""" 
     events_url 
     = 
     f 
     "https:// 
     { 
     DRUVA_BASE_URL 
     } 
     /insync/eventmanagement/v2/events" 
     headers 
     = 
     { 
     "Authorization" 
     : 
     f 
     "Bearer 
     { 
     token 
     } 
     " 
     , 
     "Accept" 
     : 
     "application/json" 
     , 
     } 
     params 
     = 
     { 
     "pageSize" 
     : 
     PAGE_SIZE 
     } 
     tracker 
     = 
      state 
     
     . 
     get 
     ( 
     "tracker" 
     ) 
     last_event_time 
     = 
      state 
     
     . 
     get 
     ( 
     "last_event_time" 
     ) 
     if 
     tracker 
     : 
     params 
     [ 
     "tracker" 
     ] 
     = 
     tracker 
     elif 
     last_event_time 
     : 
     params 
     [ 
     "fromTime" 
     ] 
     = 
     last_event_time 
     else 
     : 
     lookback 
     = 
     datetime 
     . 
     now 
     ( 
     timezone 
     . 
     utc 
     ) 
     - 
     timedelta 
     ( 
     hours 
     = 
     LOOKBACK_HOURS 
     ) 
     params 
     [ 
     "fromTime" 
     ] 
     = 
     lookback 
     . 
     strftime 
     ( 
     "%Y-%m- 
     %d 
     T%H:%M:%SZ" 
     ) 
     all_events 
     = 
     [] 
     total_fetched 
     = 
     0 
     while 
     total_fetched 
    < MAX_RECORDS 
     : 
     resp 
     = 
     requests 
     . 
     get 
     ( 
     events_url 
     , 
     headers 
     = 
     headers 
     , 
     params 
     = 
     params 
     , 
     timeout 
     = 
     60 
     , 
     ) 
     resp 
     . 
     raise_for_status 
     () 
     data 
     = 
     resp 
     . 
     json 
     () 
     events 
     = 
     data 
     . 
     get 
     ( 
     "events" 
     , 
     []) 
     all_events 
     . 
     extend 
     ( 
     events 
     ) 
     total_fetched 
     += 
     len 
     ( 
     events 
     ) 
     new_tracker 
     = 
     data 
     . 
     get 
     ( 
     "tracker" 
     ) 
     next_page_token 
     = 
     data 
     . 
     get 
     ( 
     "nextPageToken" 
     ) 
     if 
     new_tracker 
     : 
     state 
     [ 
     "tracker" 
     ] 
     = 
     new_tracker 
     if 
     next_page_token 
     : 
     params 
     [ 
     "nextPageToken" 
     ] 
     = 
     next_page_token 
     params 
     . 
     pop 
     ( 
     "tracker" 
     , 
     None 
     ) 
     params 
     . 
     pop 
     ( 
     "fromTime" 
     , 
     None 
     ) 
     else 
     : 
     break 
     if 
     all_events 
     : 
     last_ts 
     = 
     all_events 
     [ 
     - 
     1 
     ] 
     . 
     get 
     ( 
     "eventTime" 
     , 
     "" 
     ) 
     if 
     last_ts 
     : 
     state 
     [ 
     "last_event_time" 
     ] 
     = 
     last_ts 
     return 
     all_events 
     , 
     state 
     def 
      
     write_events_to_gcs 
     ( 
     storage_client 
     , 
     events 
     ): 
      
     """Write events as NDJSON to GCS.""" 
     if 
     not 
     events 
     : 
     return 
     now 
     = 
     datetime 
     . 
     now 
     ( 
     timezone 
     . 
     utc 
     ) 
     filename 
     = 
     now 
     . 
     strftime 
     ( 
     "%Y%m 
     %d 
     _%H%M%S" 
     ) 
     + 
     ".ndjson" 
     blob_path 
     = 
     f 
     " 
     { 
     GCS_PREFIX 
     } 
     / 
     { 
     now 
     . 
     strftime 
     ( 
     '%Y/%m/ 
     %d 
     ' 
     ) 
     } 
     / 
     { 
     filename 
     } 
     " 
     ndjson_lines 
     = 
     " 
     \n 
     " 
     . 
     join 
     ( 
     json 
     . 
     dumps 
     ( 
     event 
     ) 
     for 
     event 
     in 
     events 
     ) 
     bucket 
     = 
     storage_client 
     . 
      bucket 
     
     ( 
     GCS_BUCKET 
     ) 
     blob 
     = 
     bucket 
     . 
     blob 
     ( 
     blob_path 
     ) 
     blob 
     . 
      upload_from_string 
     
     ( 
     ndjson_lines 
     , 
     content_type 
     = 
     "application/x-ndjson" 
     , 
     ) 
     print 
     ( 
     f 
     "Wrote 
     { 
     len 
     ( 
     events 
     ) 
     } 
     events to gs:// 
     { 
     GCS_BUCKET 
     } 
     / 
     { 
     blob_path 
     } 
     " 
     ) 
     def 
      
     main 
     ( 
     event 
     , 
     context 
     ): 
      
     """Cloud Run function entry point triggered by Pub/Sub.""" 
     storage_client 
     = 
      storage 
     
     . 
      Client 
     
     () 
     token 
     = 
     get_oauth_token 
     () 
     state 
     = 
     load_state 
     ( 
     storage_client 
     ) 
     events 
     , 
     updated_state 
     = 
     fetch_events 
     ( 
     token 
     , 
     state 
     ) 
     write_events_to_gcs 
     ( 
     storage_client 
     , 
     events 
     ) 
     save_state 
     ( 
     storage_client 
     , 
     updated_state 
     ) 
     print 
     ( 
     f 
     "Completed: fetched 
     { 
     len 
     ( 
     events 
     ) 
     } 
     events" 
     ) 
     return 
     f 
     "OK: 
     { 
     len 
     ( 
     events 
     ) 
     } 
     events" 
     
    
  8. Replace the contents of requirements.txt with the following:

     requests>=2.31.0
    google-cloud-storage>=2.14.0 
    

Configure environment variables

  1. In the Cloud Run function configuration, go to the Runtime, build, connections and security settingssection.
  2. Under Runtime environment variables, add the following variables:

    • GCS_BUCKET : The name of your GCS bucket (for example, druva-backup-logs )
    • GCS_PREFIX : The prefix path for log files (for example, druva_backup )
    • STATE_KEY : The state file name (for example, druva_state.json )
    • DRUVA_BASE_URL : The Druva API base URL:
      • apis.druva.com for Druva Cloud (Standard)
      • govcloudapis.druva.com for Druva GovCloud
    • CLIENT_ID : The Client ID from the Druva API credentials
    • CLIENT_SECRET : The Secret Key from the Druva API credentials
    • MAX_RECORDS : Maximum number of records to fetch per invocation (for example, 10000 )
    • PAGE_SIZE : Number of events per API page (maximum 500 )
    • LOOKBACK_HOURS : Number of hours to look back on first run (for example, 24 )
  3. Click Deploy.

  4. Wait for the deployment to complete successfully.

Create a Cloud Scheduler job

Create a Cloud Scheduler job to trigger the Cloud Run function at regular intervals.

  1. In the Google Cloud console, go to Cloud Scheduler.
  2. Click Create Job.
  3. Provide the following configuration details:

    • Name: Enter druva-backup-scheduler
    • Region: Select the same region as your Cloud Run function (for example, us-central1 )
    • Description: Enter Triggers Druva Backup log collection every 30 minutes
    • Frequency: Enter */30 * * * * (every 30 minutes)
    • Timezone: Select your preferred timezone (for example, UTC )
  4. Click Continue.

  5. Configure the Target:

    • Target type: Select Pub/Sub
    • Cloud Pub/Sub topic: Select druva-backup-trigger
    • Message body: Enter {"trigger": "scheduled"}
  6. Click Create.

Test the Cloud Scheduler job

  1. In the Cloud Schedulerlist, locate druva-backup-scheduler .
  2. Click Force Runto trigger the function immediately.
  3. Verify the execution by checking:
    • The Cloud Run function logs in Cloud Run functions > druva-backup-to-gcs > Logs
    • The GCS bucket for new NDJSON files in Cloud Storage > druva-backup-logs
  1. Go to SIEM Settings > Feeds.
  2. Click Add New Feed.
  3. Click Configure a single feed.
  4. In the Feed namefield, enter a name for the feed (for example, Druva Backup Events ).
  5. Select Google Cloud Storage V2as the Source type.
  6. Select Druva Backupas the Log type.
  7. Click Get Service Account. A unique service account email will be displayed, for example:

     chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com 
    
  8. Copy this email address for use in the next step.

Configure the feed

  1. Click Next.
  2. Specify values for the following input parameters:

    • Storage bucket URL: Enter the GCS bucket URI with the prefix path:

       gs://druva-backup-logs/druva_backup/ 
      
    • Source deletion option: Select the deletion option according to your preference:

      • Never: Never deletes any files after transfers (recommended for testing)
      • Delete transferred files: Deletes files after successful transfer
      • Delete transferred files and empty directories: Deletes files and empty directories after successful transfer
    • Maximum File Age: Include files modified in the last number of days (default is 180 days)

    • Asset namespace: The asset namespace

    • Ingestion labels: The label to be applied to the events from this feed

  3. Click Next.

  4. Review your new feed configuration in the Finalizescreen, and then click Submit.

The Google SecOps service account needs Storage Object Viewerrole on your GCS bucket to read the log files written by the Cloud Run function.

  1. Go to Cloud Storage > Buckets.
  2. Click on your bucket name (for example, druva-backup-logs ).
  3. Go to the Permissionstab.
  4. Click Grant access.
  5. Provide the following configuration details:
    • Add principals: Paste the Google SecOps service account email (for example, chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com )
    • Assign roles: Select Storage Object Viewer
  6. Click Save.

UDM mapping table

Log Field UDM Mapping Logic
inSyncUserID, eventsGroupId, FilesMissed, FilesBackedup, TotalBackupSize, TotalBytesTransferred, facility, inSyncDataSourceID, initiator, event_type
additional.fields Merged with labels created from each field if not empty
initiator
extensions.auth.type Set to "AUTHTYPE_UNSPECIFIED" if initiator matches email regex
metadata.event_type Set to "USER_LOGIN" if has_target_user true and has_principal true; "STATUS_UPDATE" if has_principal true and has_target false; else "GENERIC_EVENT"
eventID
metadata.product_log_id Converted to string
metadata.product_name Set to "DRUVA_BACKUP"
clientVersion
metadata.product_version Value copied directly
inSyncDataSourceName
principal.asset.hostname Value copied directly
ip
principal.asset.ip Merged from ip
inSyncDataSourceName
principal.hostname Value copied directly
ip
principal.ip Merged from ip
clientOS
principal.platform Set to "LINUX" if matches (?i)Linux; "WINDOWS" if matches (?i)windows; "MAC" if matches (?i)mac
profileName
principal.resource.name Value copied directly
profileID
principal.resource.product_object_id Converted to string
eventState
security_result.action Set to "ALLOW" if matches (?i)Success, else "BLOCK"
eventState
security_result.action_details Value copied directly
severity
security_result.severity Set to "LOW" if in [0,1,2,3,LOW]; "MEDIUM" if in [4,5,6,MEDIUM,SUBSTANTIAL,INFO]; "HIGH" if in [7,8,HIGH,SEVERE]; "CRITICAL" if in [9,10,VERY-HIGH,CRITICAL]
inSyncUserEmail, initiator
target.user.email_addresses Merged from inSyncUserEmail; also from initiator if matches email regex
inSyncUserName
target.user.userid Value copied directly
metadata.vendor_name Set to "DRUVA_BACKUP"

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

Design a Mobile Site
View Site in Mobile | Classic
Share by: