Collect Trellix Endpoint Security HX (formerly FireEye HX) logs
This guide explains how to ingest Trellix Endpoint Security HX (formerly known as FireEye HX) logs to Google Security Operations using Google Cloud Storage V2. Trellix Endpoint Security HX is an endpoint detection and response (EDR) platform that provides advanced threat detection, investigation, and containment capabilities. It uses intelligence-led protection and real-time indicators of compromise (IOC) to detect and respond to sophisticated attacks on endpoints.
Before you begin
Make sure you have the following prerequisites:
- A Google SecOps instance.
- A GCP project with 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 the Trellix Endpoint Security HX console with API access enabled.
- A Trellix HX API user account with at least Analyst role.
Create Google Cloud Storage bucket
- Go to the Google Cloud Console.
- Select your project.
- 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 (for example, fireeye-hx-logs)Location type Choose based on your needs (Region, Dual-region, Multi-region) Location Select the location closest to your Google SecOps instance Storage class Standard (recommended for frequently accessed logs) Access control Uniform (recommended) Protection tools Optional: Enable object versioning or retention policy Click Create.
Collect Trellix Endpoint Security HX API credentials
Get the HX console URL
- Sign in to the Trellix Endpoint Security HX web console.
- Note the console URL from the browser address bar (Format:
https://<hx-hostname>:<port>).
Create or verify API user account
- Sign in to the Trellix HX console with an administrator account.
- Go to Admin > User Accounts.
- Create a new user or select an existing user for API access.
- Ensure the user has the api_analyst or api_admin role.
- Note the username and password for API access.
Verify API access
Test your credentials before proceeding with the integration:
HX_HOST="[https://hx-console.example.com:3000](https://hx-console.example.com:3000)" HX_USER="api_user" HX_PASS="api_password" # Authenticate and get token curl -k -X POST "$HX_HOST/hx/api/v3/token" \ -u "$HX_USER:$HX_PASS"
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:
fireeye-hx-collector-sa - Service account description:
Service account for Cloud Run function to collect Trellix HX 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
- Cloud Run Invoker
- Cloud Functions Invoker
- Click Continue then Done.
Grant IAM permissions on GCS bucket
- Go to Cloud Storage > Buckets.
- Click on the bucket name (
fireeye-hx-logs). - Go to the Permissions tab.
- Click Grant access.
- Add principals: Enter the service account email.
- Assign roles: Select Storage Object Admin.
- Click Save.
Create Pub/Sub topic
Create a topic that Cloud Scheduler will publish to and the Cloud Run function will subscribe to.
- In the GCP Console, go to Pub/Sub > Topics.
- Click Create topic.
- Topic ID:
fireeye-hx-logs-trigger. - Click Create.
Create Cloud Run function to collect logs
- In the GCP Console, go to Cloud Run.
- Click Create service.
- Select Function.
In the Configure section, provide the following details:
Setting Value Service name fireeye-hx-collectorRegion Select region matching your GCS bucket Runtime Python 3.12 or later In the Trigger section:
- Click + Add trigger.
- Select Cloud Pub/Sub.
- In Select a Cloud Pub/Sub topic, choose
fireeye-hx-logs-trigger. - Click Save.
In the Authentication section, select Require authentication and check Identity and Access Management (IAM).
Scroll down to Containers, Networking, Security.
In the Security tab, select the service account
fireeye-hx-collector-sa.In the Containers tab, click Variables & Secrets and add the following:
Variable Name Example Value Description GCS_BUCKETfireeye-hx-logsGCS bucket name GCS_PREFIXhx-alertsPrefix for log files STATE_KEYhx-alerts/state.jsonState file path HX_HOSThttps://hx-console.example.com:3000HX console URL HX_USERapi_userAPI username HX_PASSapi_passwordAPI password MAX_RECORDS1000Max records per run PAGE_SIZE100Records per page LOOKBACK_HOURS24Initial lookback period In Requests, set Request timeout to
600seconds.Click Create. After the service is created, the inline code editor will open.
Add function code
- Enter main in the Entry point field.
Create the following files in the inline editor:
- main.py:
import functions_framework from google.cloud import storage import json import os import urllib3 from datetime import datetime, timezone, timedelta import time # Disable SSL warnings for self-signed certs urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # Initialize HTTP client with timeouts http = urllib3.PoolManager( timeout=urllib3.Timeout(connect=5.0, read=30.0), retries=False, cert_reqs='CERT_NONE', ) # Initialize Storage client storage_client = storage.Client() # Environment variables GCS_BUCKET = os.environ.get('GCS_BUCKET') GCS_PREFIX = os.environ.get('GCS_PREFIX', 'hx-alerts') STATE_KEY = os.environ.get('STATE_KEY', 'hx-alerts/state.json') HX_HOST = os.environ.get('HX_HOST') HX_USER = os.environ.get('HX_USER') HX_PASS = os.environ.get('HX_PASS') MAX_RECORDS = int(os.environ.get('MAX_RECORDS', '1000')) PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '100')) LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24')) def get_api_token(host, username, password): url = f"{host}/hx/api/v3/token" auth_string = f"{username}:{password}" import base64 auth_b64 = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8') headers = {'Authorization': f'Basic {auth_b64}'} response = http.request('POST', url, headers=headers) if response.status == 204: return response.headers.get('X-FeApi-Token') else: raise Exception(f"Authentication failed: HTTP {response.status}") @functions_framework.cloud_event def main(cloud_event): if not all([GCS_BUCKET, HX_HOST, HX_USER, HX_PASS]): print('Error: Missing required environment variables') return try: bucket = storage_client.bucket(GCS_BUCKET) state = load_state(bucket, STATE_KEY) now = datetime.now(timezone.utc) last_time = None if isinstance(state, dict) and state.get("last_event_time"): try: last_time = parse_datetime(state["last_event_time"]) - timedelta(minutes=2) except Exception as e: print(f"Warning: Could not parse last_event_time: {e}") if last_time is None: last_time = now - timedelta(hours=LOOKBACK_HOURS) print(f"Fetching alerts from {last_time.isoformat()} to {now.isoformat()}") token = get_api_token(HX_HOST, HX_USER, HX_PASS) alerts, newest_time = fetch_alerts(HX_HOST, token, last_time, now, PAGE_SIZE, MAX_RECORDS) hosts = fetch_hosts(HX_HOST, token) all_records = alerts + hosts if not all_records: print("No new records found.") save_state(bucket, STATE_KEY, now.isoformat()) return timestamp = now.strftime('%Y%m%d_%H%M%S') object_key = f"{GCS_PREFIX}/logs_{timestamp}.ndjson" ndjson = '\n'.join([json.dumps(record, ensure_ascii=False) for record in all_records]) + '\n' bucket.blob(object_key).upload_from_string(ndjson, content_type='application/x-ndjson') print(f"Wrote {len(all_records)} records to gs://{GCS_BUCKET}/{object_key}") save_state(bucket, STATE_KEY, newest_time if newest_time else now.isoformat()) release_token(HX_HOST, token) print(f"Successfully processed {len(all_records)} records") except Exception as e: print(f'Error processing logs: {str(e)}') raise def parse_datetime(value): if value.endswith("Z"): value = value[:-1] + "+00:00" return datetime.fromisoformat(value) 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, last_event_time_iso): try: state = {'last_event_time': last_event_time_iso} bucket.blob(key).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_alerts(host, token, start_time, end_time, page_size, max_records): endpoint = f"{host}/hx/api/v3/alerts" headers = {'X-FeApi-Token': token, 'Accept': 'application/json'} records = []; newest_time = None; offset = 0; backoff = 1.0 while True: if len(records) >= max_records: break start_iso = start_time.strftime('%Y-%m-%dT%H:%M:%S.000Z') end_iso = end_time.strftime('%Y-%m-%dT%H:%M:%S.000Z') url = f"{endpoint}?sort=reported_at+asc&min_reported_at={start_iso}&max_reported_at={end_iso}&offset={offset}&limit={min(page_size, max_records - len(records))}" try: response = http.request('GET', url, headers=headers) if response.status == 429: time.sleep(int(response.headers.get('Retry-After', str(int(backoff))))); backoff = min(backoff * 2, 30.0); continue backoff = 1.0 if response.status != 200: break data = json.loads(response.data.decode('utf-8')) page_results = data.get('data', {}).get('entries', []) if not page_results: break records.extend(page_results) for alert in page_results: event_time = alert.get('reported_at') if event_time and (newest_time is None or event_time > newest_time): newest_time = event_time offset += len(page_results) if offset >= data.get('data', {}).get('total', 0): break except Exception: break return records, newest_time def fetch_hosts(host, token): endpoint = f"{host}/hx/api/v3/hosts" headers = {'X-FeApi-Token': token, 'Accept': 'application/json'} try: response = http.request('GET', f"{endpoint}?limit=100&offset=0", headers=headers) if response.status != 200: return [] return json.loads(response.data.decode('utf-8')).get('data', {}).get('entries', []) except Exception: return [] def release_token(host, token): try: http.request('DELETE', f"{host}/hx/api/v3/token", headers={'X-FeApi-Token': token}) except Exception: pass- requirements.txt:
functions-framework==3.* google-cloud-storage==2.* urllib3>=2.0.0Click Deploy to save and deploy the function.
Create Cloud Scheduler job
- In the GCP Console, go to Cloud Scheduler.
- Click Create Job.
Provide the following configuration details:
Setting Value Name fireeye-hx-collector-hourlyRegion Select same region as Cloud Run function Frequency 0 * * * *(every hour, on the hour)Timezone Select timezone (UTC recommended) Target type Pub/Sub Topic fireeye-hx-logs-triggerMessage body {}Click Create.
Test the integration
- In Cloud Scheduler, find your job and click Force run.
- Go to Cloud Run > Services > Logs to verify success.
- Check Cloud Storage to confirm a new
.ndjsonfile exists in thehx-alerts/folder.
Configure a feed in Google SecOps
- Go to SIEM Settings > Feeds.
- Click Add New Feed > Configure a single feed.
- Feed name:
Trellix HX Logs. - Source type: Google Cloud Storage V2.
- Log type: FireEye HX.
- Click Get Service Account and copy the email (e.g.,
chronicle-12345678@...). - Click Next.
- Storage bucket URL:
gs://fireeye-hx-logs/hx-alerts/(Include the trailing slash). - Source deletion option: Select according to your preference.
- Click Next, review, and click Submit.
Grant IAM permissions to the service account
The Google SecOps service account needs Storage Object Viewer role on your bucket.
- Go to Cloud Storage > Buckets.
- Select your bucket and go to 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 |
|---|---|---|
alert.event_type |
metadata.product_event_type |
Direct mapping. |
alert.reported_at |
metadata.event_timestamp |
Parsed as timestamp. |
alert.source |
principal.hostname |
Direct mapping. |
alert.agent._id |
principal.asset_id |
Direct mapping. |
alert.indicator.display_name |
security_result.threat_name |
Direct mapping. |
alert.md5values |
target.file.md5 |
Direct mapping. |
| N/A | metadata.vendor_name |
Set to Trellix. |
| N/A | metadata.product_name |
Set to Endpoint Security HX. |
| N/A | metadata.log_type |
Set to FIREEYE_HX. |
Need more help? Get answers from Community members and Google SecOps professionals.