Skip to content
Ashish.
All posts
Diagram illustrating the architecture of an identity data lake with log ingestion, partitioning, and query layers.

Building an Identity Data Lake: Analytics for Access Patterns

An examination of building an identity data lake to enable analytics for access patterns, security data lakes, identity analytics, and compliance reporting.

By Ashish Srivastava

The fundamental challenge in building an identity data lake is not storage capacity, but the mismatch between the transactional nature of identity systems and the analytical nature of access pattern recognition. Operational identity stores like Active Directory or Okta are optimized for low-latency, high-fidelity authentication checks. They are not designed to answer questions like "Which users accessed sensitive resources between 2 AM and 4 AM last Tuesday?" or "What is the correlation between failed MFA attempts and subsequent privilege escalation?" To answer these, you must construct a dedicated analytical layer that ingests raw event streams, preserves their granularity, and allows for flexible schema evolution.

The Mechanism of Log Ingestion: Schema-on-Read

The first step in this architecture is the ingestion mechanism. Traditional data warehousing relies on schema-on-write, where data is validated and transformed before it enters the system. In an identity context, this is fragile because identity schemas change frequently—new attributes are added, MFA providers swap, and authentication protocols evolve. An identity data lake instead employs a schema-on-read mechanism.

When an event occurs, such as a user alice@example.com authenticating via SAML, the system captures the raw JSON payload from the Identity Provider (IdP) log stream. This payload is written immediately to object storage (e.g., S3, Azure Blob) in a columnar format like Apache Parquet. Parquet is critical here because it compresses data significantly while allowing the query engine to scan only the specific columns needed for a given analysis, such as auth_method or resource_id, ignoring the massive metadata blocks.

{
  "event_id": "evt_9a8b7c6d",
  "timestamp": "2023-10-27T14:30:00Z",
  "subject": {
    "id": "usr_alice",
    "email": "alice@example.com",
    "groups": ["engineering", "admins"]
  },
  "action": "login",
  "context": {
    "ip_address": "192.168.1.50",
    "auth_provider": "okta_saml",
    "mfa_status": "success"
  },
  "resource": "s3://internal-data-bucket"
}

By writing this raw JSON as Parquet, the data lake retains the full fidelity of the event. If next month the IdP adds a device_fingerprint field, the existing historical data remains valid, and new data simply includes the new column. The schema is applied only when a query is run, allowing the analytics engine to interpret the data dynamically.

Handling High Cardinality: Partitioning Strategies

Identity data presents a unique cardinality challenge. While the number of distinct users might be in the thousands or tens of thousands, the number of distinct events can reach billions. A naive approach of storing all logs in a single flat file or partitioning only by date creates a "hot partition" problem where queries for specific users become inefficient, or conversely, queries for global trends require scanning terabytes of irrelevant data.

The solution lies in multi-level partitioning. The primary partition key must be event_time (typically by day) to leverage the temporal nature of access patterns and enable time-range pruning. However, secondary partitioning by subject_id (user ID) is often necessary for granular investigations.

Consider a scenario where a security analyst needs to investigate a specific user's activity over the last 30 days. If the data is partitioned only by date, the system must scan 30 partitions. If it is partitioned by subject_id within each date partition, the system can jump directly to the subject_id folder. This structure minimizes the amount of data scanned during a "needle-in-haystack" search.

However, this introduces a trade-off. Over-partitioning creates millions of small files, which degrades query performance due to the overhead of managing metadata. The mechanism here involves a compaction process that periodically merges small files within a partition into larger, more efficient Parquet files. This is a standard operation in data lake architectures like Delta Lake or Apache Hudi, ensuring that the physical storage layout matches the logical query patterns.

The Join Problem: Contextualizing Access

A common anti-pattern in identity analytics is to pre-join access logs with user profile data during the ETL phase. This creates a rigid data model where adding a new user attribute (e.g., department_cost_center) requires a full table rewrite. In a data lake, we avoid this by performing joins at query time.

The data lake separates the "log stream" (access events) from the "identity graph" (user attributes). The log stream is immutable and append-only. The identity graph is a separate dataset that is updated as user attributes change. When an analyst wants to know "Which users in the 'Finance' department accessed the payroll database?", the query engine joins the two datasets.

SELECT 
  l.subject.id,
  l.action,
  l.timestamp,
  u.department,
  u.role
FROM identity_logs_lake l
JOIN identity_profiles_staging u 
  ON l.subject.id = u.user_id
WHERE l.timestamp >= '2023-10-01'
  AND u.department = 'Finance'
  AND l.action = 'read'

This mechanism allows for dynamic filtering. If the definition of "Finance" changes or if a user moves departments, the query automatically reflects the current state of the identity graph without needing to re-process the historical logs. The join is performed by the query engine (e.g., Presto, Spark, or Athena), which leverages the columnar storage to efficiently match keys. This decoupling is essential for maintaining a "single source of truth" where the history of an event is never overwritten by the current state of a user profile.

Security and Compliance: Immutability and Encryption

An identity data lake is not just a repository; it is a forensic artifact. It must guarantee that logs cannot be altered or deleted after ingestion. This requirement drives the choice of storage backend and access controls. The mechanism for this is Object Locking (or WORM - Write Once, Read Many) support in the underlying object storage.

For example, enabling Object Lock on an S3 bucket ensures that even a root user or an administrator cannot delete or overwrite objects within the retention period. This is critical for compliance reporting under standards like SOC 2, HIPAA, or GDPR. If a breach occurs, the data lake must provide an unalterable trail of who did what and when.

Furthermore, the data lake must handle PII (Personally Identifiable Information) with care. While the logs contain emails and names, they may also contain session tokens or other sensitive identifiers. The mechanism here is field-level encryption. Before data is written to the Parquet files, sensitive fields are encrypted using customer-managed keys (CMK). A policy engine or KMS wrapper enforces access control and decrypts data before passing it to the query engine, ensuring that the query engine itself does not perform dynamic per-field permission checks.

# Conceptual pseudocode for field-level encryption before write
def secure_write_event(event):
    event['user_email'] = encrypt(event['user_email'], kms_key_id)
    event['session_token'] = encrypt(event['session_token'], kms_key_id)
    parquet_writer.write(event)

This ensures that even if the storage layer is compromised, the PII remains protected. It also allows the data lake to serve both security teams (who need full visibility) and compliance auditors (who need to verify privacy controls) from the same dataset.

Operationalizing Analytics: From Lake to Insight

The final piece of the architecture is exposing this data to downstream consumers. The goal is to avoid creating silos where security teams maintain one copy of the data and compliance teams another. Instead, the data lake acts as the central hub.

For real-time threat detection, the data lake can stream events to a SIEM (Security Information and Event Management) system via a message queue like Kafka. The SIEM consumes the raw events for immediate alerting. For historical analysis and reporting, business intelligence tools connect directly to the data lake via SQL. This eliminates the ETL latency that often plagues traditional data warehouses.

Consider the workflow for a compliance report:

  1. Ingestion: Identity Provider logs are streamed to the lake in Parquet.
  2. Processing: A scheduled job partitions the data by date and subject.
  3. Query: A compliance dashboard runs a SQL query against the lake to aggregate access counts by role and location.
  4. Export: The result is exported as a PDF or CSV for the auditor.

This architecture supports the "identity analytics" use case by providing a unified view of access patterns. It allows organizations to detect anomalies, such as a user accessing resources from an unusual geographic location at an unusual time, by correlating the location field with historical baselines stored in the same lake.

Conclusion

Building an identity data lake is an exercise in balancing flexibility with rigor. By using schema-on-read, you preserve the ability to adapt to changing identity schemas without losing historical data. By implementing strategic partitioning, you ensure that queries for specific users or global trends remain performant. By separating the log stream from the identity graph, you enable dynamic contextualization of access events. Finally, by enforcing immutability and field-level encryption, you transform the data lake from a passive storage system into an active tool for security and compliance.

The mechanism is not about moving data faster; it is about making the data more accessible to the questions that matter. In an era where access patterns are the primary indicator of compromise, the identity data lake provides the necessary depth of insight to distinguish between routine administrative work and malicious activity.

Common Pitfalls

Even with a sound architectural design, implementation errors can undermine the value of an identity data lake.

  1. Over-Partitioning: As discussed, creating too many partitions (e.g., partitioning by subject_id for every single user without a secondary tier) results in the "small file problem." Query engines spend more time managing metadata than processing data. Always monitor file counts and implement compaction strategies.
  2. Schema Drift: While schema-on-read is flexible, ignoring schema drift can lead to query failures. If an IdP changes the structure of a JSON payload (e.g., renaming a field or changing a data type) without updating the downstream query logic, analyses may break silently or return nulls. Implement schema validation at the ingestion layer to alert on structural changes.
  3. PII Leakage via Logs: A common oversight is assuming that because data is encrypted, it is safe. If logs inadvertently include full session tokens, authentication cookies, or unmasked credit card numbers in the raw JSON before encryption, those values are stored in the lake. Ensure a strict data classification policy is applied at the source to filter or mask sensitive fields before they ever hit the storage layer.

Practical Takeaways

To successfully build and maintain an identity data lake, adopt these mental models:

  1. Immutable History, Mutable Context: Treat the raw log stream as an immutable ledger of truth. Keep user profiles (department, role, manager) in a separate, mutable dataset and join them at query time. Never update the original event records.
  2. Partition for Query Patterns, Not Write Speed: Design your partition keys based on how analysts will query the data (e.g., "Show me all activity for User X in Q3"), not just how the data arrives. Optimize for the most frequent query shapes.
  3. Decouple Compute from Storage: Leverage the cloud-native separation of storage and compute. This allows you to scale your query engines independently of your storage costs, enabling cost-effective historical deep-dives without impacting ingestion performance.

FAQ

Q: Can I use an identity data lake for real-time threat detection? A: Not directly. Data lakes are optimized for batch processing and complex analytical queries. For real-time detection, you should stream the raw logs from the ingestion pipeline to a specialized streaming engine (like Kafka Streams or Flink) or a SIEM, while the data lake serves as the long-term repository for historical context.

Q: How do I handle PII compliance if the data is encrypted? A: Encryption protects data at rest, but compliance also requires access control. You must enforce strict IAM policies where only authorized service accounts or users can decrypt specific fields. Additionally, consider tokenizing highly sensitive data (like SSNs) before ingestion so that the plaintext never touches the data lake.

Q: Is it better to use a data warehouse or a data lake for identity analytics? A: It depends on your needs. A data warehouse is excellent for structured, highly normalized reporting with strict governance. However, identity data is often semi-structured (JSON logs) and requires flexible schema evolution. A data lake is generally superior for the initial ingestion and storage of raw identity events, which can then be curated into a warehouse for specific reporting needs.

Related posts