Collect PingIDM (formerly ForgeRock OpenIDM) logs
This document explains how to ingest PingIDM (formerly known as ForgeRock OpenIDM) logs to Google Security Operations using Google Cloud Storage V2.
PingIDM is an identity management platform that provides user provisioning, synchronization, password management, and access governance. It logs identity lifecycle events, authentication attempts, reconciliation operations, and configuration changes to audit logs accessible over REST.
Before you begin
Make sure that you have the following prerequisites:
- A Google SecOps instance.
- A GCP project with the Cloud Storage API enabled.
- Permissions to create and manage GCS buckets and IAM policies.
- Permissions to create Cloud Run services, Pub/Sub topics, and Cloud Scheduler jobs.
- Privileged access to a ForgeRock OpenIDM or PingIDM instance with administrative credentials.
Create Google Cloud Storage bucket
- Go to the Google Cloud Console.
- Select your project or create a new one.
- In the navigation menu, go to Cloud Storage > Buckets.
- Click Create bucket.
Provide the following configuration details:
Setting Value Name your bucket Enter a globally unique name (e.g., forgerock-openidm-audit-logs)Location type Choose based on your needs (Region, Dual-region, Multi-region) Location Select the location (e.g., us-central1)Storage class Standard (recommended for frequently accessed logs) Access control Uniform (recommended) Protection tools Optional: Enable object versioning or retention policy Click Create.
Collect ForgeRock OpenIDM credentials
Get ForgeRock OpenIDM base URL
- Sign in to your ForgeRock OpenIDM or PingIDM instance.
- Note your base URL from the browser address bar.
- Format:
https://openidm.example.com - Do not include trailing slashes or paths like
/admin.
- Format:
Get administrative credentials
- Obtain administrative credentials for your ForgeRock OpenIDM instance.
- You will need:
- Username: Administrative username (e.g.,
openidm-admin) - Password: Administrative password
- Username: Administrative username (e.g.,
Verify permissions
- Sign in to ForgeRock OpenIDM.
- Go to Configure > System Preferences > Audit.
- If you can see audit configuration and topics, you have the required permissions.
- If you cannot see this option, contact your administrator to grant audit read permissions.
Test API access
Test your credentials before proceeding with the integration:
# Replace with your actual credentials OPENIDM_BASE_URL="[https://openidm.example.com](https://openidm.example.com)" OPENIDM_USERNAME="openidm-admin" OPENIDM_PASSWORD="your-admin-password" # Test API access to authentication audit topic curl -v \ -H "X-OpenIDM-Username: ${OPENIDM_USERNAME}" \ -H "X-OpenIDM-Password: ${OPENIDM_PASSWORD}" \ -H "Accept-API-Version: resource=1.0" \ -H "Accept: application/json" \ "${OPENIDM_BASE_URL}/openidm/audit/authentication?_queryFilter=true&_pageSize=1"
Expected response: HTTP 200 with JSON containing audit events.
Create service account for Cloud Run function
The Cloud Run function needs a service account with permissions to write to the GCS bucket and be invoked by Pub/Sub.
Create service account
- In the GCP Console, go to IAM & Admin > Service Accounts.
- Click Create Service Account.
- Provide the following configuration details:
- Service account name:
forgerock-openidm-collector-sa - Service account description:
Service account for Cloud Run function to collect ForgeRock OpenIDM logs
- Service account name:
- Click Create and Continue.
- In the Grant this service account access to project section, add the following roles:
- Storage Object Admin: To write logs to the GCS bucket and manage state files.
- Cloud Run Invoker: To allow Pub/Sub to invoke the function.
- Cloud Functions Invoker: To allow function invocation.
- Click Continue then Done.
Grant IAM permissions on GCS bucket
- Go to Cloud Storage > Buckets.
- Click on your bucket name (e.g.,
forgerock-openidm-audit-logs). - Go to the Permissions tab.
- Click Grant access.
- Provide the following configuration details:
- Add principals: Enter the service account email (e.g.,
forgerock-openidm-collector-sa@PROJECT_ID.iam.gserviceaccount.com). - Assign roles: Select Storage Object Admin.
- Add principals: Enter the service account email (e.g.,
- Click Save.
Create Pub/Sub topic
- In the GCP Console, go to Pub/Sub > Topics.
- Click Create topic.
- Provide the following configuration details:
- Topic ID:
forgerock-openidm-trigger - Leave other settings as default.
- Topic ID:
- Click Create.
Create Cloud Run function to collect logs
- In the GCP Console, go to Cloud Run.
- Click Create service.
- Select Function (use an inline editor to create a function).
In the Configure section, provide the following configuration details:
Setting Value Service name forgerock-openidm-collectorRegion Select region matching your GCS bucket (e.g., us-central1)Runtime Select Python 3.12 or later In the Trigger section:
- Click + Add trigger.
- Select Cloud Pub/Sub.
- In Select a Cloud Pub/Sub topic, choose
forgerock-openidm-trigger. - Click Save.
In the Authentication section:
- Select Require authentication.
- Check Identity and Access Management (IAM).
Scroll down and expand Containers, Networking, Security.
Go to the Security tab:
- Service account: Select
forgerock-openidm-collector-sa.
- Service account: Select
Go to the Containers tab:
- Click Variables & Secrets.
- Click + Add variable for each environment variable:
Variable Name Example Value Description GCS_BUCKETforgerock-openidm-audit-logsGCS bucket name GCS_PREFIXopenidmPrefix for log files STATE_KEYopenidm/state.jsonState file path OPENIDM_BASE_URLhttps://openidm.example.comOpenIDM base URL OPENIDM_USERNAMEopenidm-adminOpenIDM admin username OPENIDM_PASSWORDyour-admin-passwordOpenIDM admin password AUDIT_TOPICSaccess,activity,authentication,config,syncComma-separated topics PAGE_SIZE100Records per page MAX_PAGES50Max pages per topic In the Requests section:
- Request timeout:
600seconds.
- Request timeout:
Go to the Settings tab:
- Resources: 512 MiB memory, 1 CPU.
Click Create. After the service is created, the inline code editor will open.
Add function code
- Enter main in the Entry point field.
Create two files in the editor:
- main.py:
import functions_framework from google.cloud import storage import json import os import urllib3 from datetime import datetime, timezone # Initialize HTTP client with timeouts http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=5.0, read=30.0), retries=False, ) # Initialize Storage client storage_client = storage.Client() # Environment variables GCS_BUCKET = os.environ.get('GCS_BUCKET') GCS_PREFIX = os.environ.get('GCS_PREFIX', 'openidm') STATE_KEY = os.environ.get('STATE_KEY', 'openidm/state.json') OPENIDM_BASE_URL = os.environ.get('OPENIDM_BASE_URL') OPENIDM_USERNAME = os.environ.get('OPENIDM_USERNAME') OPENIDM_PASSWORD = os.environ.get('OPENIDM_PASSWORD') AUDIT_TOPICS = os.environ.get('AUDIT_TOPICS', 'access,activity,authentication,config,sync').split(',') PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '100')) MAX_PAGES = int(os.environ.get('MAX_PAGES', '50')) @functions_framework.cloud_event def main(cloud_event): """ Cloud Run function triggered by Pub/Sub to fetch ForgeRock OpenIDM logs and write to GCS. """ if not all([GCS_BUCKET, OPENIDM_BASE_URL, OPENIDM_USERNAME, OPENIDM_PASSWORD]): print('Error: Missing required environment variables') return try: bucket = storage_client.bucket(GCS_BUCKET) state = load_state(bucket, STATE_KEY) all_events = [] for topic in AUDIT_TOPICS: topic = topic.strip() print(f"Fetching audit logs for topic: {topic}") events = fetch_audit_logs(topic, state.get(topic, {})) all_events.extend(events) if events: latest_timestamp = max(e.get('timestamp', '') for e in events) state[topic] = { 'last_timestamp': latest_timestamp, 'last_run': datetime.now(timezone.utc).isoformat(), 'events_count': len(events) } if all_events: write_to_gcs(bucket, all_events) save_state(bucket, STATE_KEY, state) print(f"Successfully processed {len(all_events)} audit events") else: print("No new audit events to process") except Exception as e: print(f'Error processing logs: {str(e)}') raise def load_state(bucket, key): try: blob = bucket.blob(key) if blob.exists(): return json.loads(blob.download_as_text()) except Exception as e: print(f"Warning: Could not load state: {e}") return {} def save_state(bucket, key, state): try: blob = bucket.blob(key) blob.upload_from_string(json.dumps(state, indent=2), content_type='application/json') except Exception as e: print(f"Warning: Could not save state: {e}") def fetch_audit_logs(topic, topic_state): base_url = OPENIDM_BASE_URL.rstrip('/') all_events = [] last_timestamp = topic_state.get('last_timestamp') query_filter = 'true' if not last_timestamp else f'timestamp gt "{last_timestamp}"' page_offset = 0 page_count = 0 while page_count < MAX_PAGES: try: url = f"{base_url}/openidm/audit/{topic}" params = { '_queryFilter': query_filter, '_pageSize': str(PAGE_SIZE), '_pagedResultsOffset': str(page_offset), '_sortKeys': 'timestamp' } headers = { 'X-OpenIDM-Username': OPENIDM_USERNAME, 'X-OpenIDM-Password': OPENIDM_PASSWORD, 'Accept-API-Version': 'resource=1.0', 'Accept': 'application/json' } response = http.request('GET', url, fields=params, headers=headers) if response.status != 200: print(f"API error for topic {topic}: {response.status}") break data = json.loads(response.data.decode('utf-8')) events = data.get('result', []) if not events: break all_events.extend(events) page_offset += PAGE_SIZE page_count += 1 if len(events) < PAGE_SIZE: break except Exception as e: print(f"Error for topic {topic}: {str(e)}") break return all_events def write_to_gcs(bucket, events): timestamp = datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S') filename = f"{GCS_PREFIX}/openidm_audit_{timestamp}.json" ndjson_content = '\n'.join([json.dumps(event) for event in events]) bucket.blob(filename).upload_from_string(ndjson_content, content_type='application/x-ndjson')- requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0Click Deploy. Deployment takes 2–3 minutes.
Create Cloud Scheduler job
- In the GCP Console, go to Cloud Scheduler.
- Click Create Job.
Provide the following configuration details:
Setting Value Name forgerock-openidm-collector-hourlyRegion Select same region as Cloud Run function Frequency 0 * * * *(every hour)Timezone Select timezone (UTC recommended) Target type Pub/Sub Topic forgerock-openidm-triggerMessage body {}Click Create.
Schedule frequency options
| Frequency | Cron Expression | Use Case |
|---|---|---|
| Every 5 minutes | */5 * * * * |
High-volume, low-latency |
| Every hour | 0 * * * * |
Standard (recommended) |
| Daily | 0 0 * * * |
Batch collection |
Test the integration
- In Cloud Scheduler, click Force run on your job.
- Verify the run in Cloud Run logs. Look for:
Successfully processed X audit events. - Confirm a new
jsonfile exists in your Cloud Storage bucket under theopenidm/folder.
Retrieve the Google SecOps service account
Configure a feed in Google SecOps
- Go to SIEM Settings > Feeds.
- Click Add New Feed > Configure a single feed.
- Feed name:
ForgeRock OpenIDM Audit Logs. - Source type: Google Cloud Storage V2.
- Log type: FORGEROCK_OPENIDM.
- Click Get Service Account and copy the email provided (e.g.,
chronicle-12345...). - Click Next.
- Storage bucket URL:
gs://forgerock-openidm-audit-logs/openidm/. - Source deletion option: Select according to preference (e.g., Never).
- Click Next, review, and click Submit.
Grant IAM permissions
- Go to Cloud Storage > Buckets.
- Select your bucket and click the Permissions tab.
- Click Grant access.
- Add principals: Paste the Google SecOps service account email.
- Assign roles: Select Storage Object Viewer.
- Click Save.
UDM mapping table
| Log Field | UDM Mapping | Logic |
|---|---|---|
fluenttag_label, topic_label, context_roles_label |
additional.fields |
Key-value pairs for context. |
Via |
intermediary.hostname |
Hostname of the proxy/intermediary. |
x_forwarded_ip, caller.callerIps |
intermediary.ip |
IP of the intermediary. |
timestamp |
metadata.event_timestamp |
Event timestamp. |
transactionId |
metadata.product_deployment_id |
Unique transaction ID. |
eventName |
metadata.product_event_type |
Event name from ForgeRock. |
_id |
metadata.product_log_id |
Unique log identifier. |
request_method |
network.http.method |
HTTP method used. |
response.statusCode |
network.http.response_code |
HTTP status code. |
user_agent |
network.http.user_agent |
Full user agent string. |
client.ip, context.ipAddress |
principal.ip |
Source IP of the actor. |
userId, principalData.0 |
principal.user.userid |
ID of the acting user. |
security_action |
security_result.action |
Security decision taken. |
level |
security_result.severity |
Mapped severity level. |
server.ip |
target.ip |
IP of the target system. |
http.request.path |
target.url |
Target URL/Path. |
| N/A | metadata.product_name |
Set to ForgeRock OpenIDM. |
| N/A | metadata.vendor_name |
Set to ForgeRock. |
Need more help? Get answers from Community members and Google SecOps professionals.