Skip to content
Notifications
Clear all

Walkthrough: Creating custom compliance reports for our auditors.

10 Posts
10 Users
0 Reactions
0 Views
(@data_pipeline_guy_42)
Estimable Member
Joined: 2 months ago
Posts: 119
Topic starter   [#23159]

Alright, let's get this straight. Orca's built-in compliance dashboards are fine for a high-level glance, but when the auditors show up with their 50-point checklist, you need something exact and exportable. The generic views don't cut it. I had to build custom reports to map our cloud assets (AWS, Azure) directly to specific control requirements, and here's the pattern that worked.

The core of it is leveraging Orca's Data Lake. You're not going to get far just clicking around the UI. You need the raw findings and asset data. I set up a daily export from the Orca Data Lake to our Snowflake. The key tables are `public.assets` and `public.findings`. Once you have that, it's a SQL game.

Here's a simplified example for a report on unencrypted S3 buckets, mapped to control CIS AWS 2.1.1. You build a view that the auditors can query directly.

```sql
CREATE VIEW compliance.s3_encryption_audit AS
SELECT
a.asset_id,
a.account_name,
a.region,
a.name as bucket_name,
f.finding_id,
'CIS AWS 2.1.1' as control_id,
'Ensure all S3 buckets employ encryption-at-rest' as control_description,
CASE
WHEN f.state = 'OPEN' THEN 'FAIL'
ELSE 'PASS'
END as compliance_status,
f.severity,
f.first_seen
FROM
orca_data.public.assets a
JOIN
orca_data.public.findings f ON a.asset_id = f.asset_id
WHERE
a.type = 'AWS S3 Bucket'
AND f.rule_id = 's3_bucket_no_encryption' -- Orca-specific rule identifier
AND f.state IN ('OPEN', 'RESOLVED');
```

The process is:

* Identify the Orca `rule_id` for the specific compliance check you need.
* Join assets to findings on `asset_id`.
* Tag each row with your internal control framework ID and description.
* Export this view to CSV/PDF per audit period, or better yet, give the auditors read access to this schema.

The real work is curating the mapping of Orca rules to your compliance controls. I ended up creating a mapping table in our warehouse to manage this relationship, because one Orca finding can map to multiple controls (e.g., a single unencrypted RDS instance might fail multiple CIS and PCI controls). Without that mapping table, you'll drown in manual work.

Biggest pitfall? Orca's Data Lake schema isn't always intuitive. The `asset_inventory` field in the findings table is a JSON blob that sometimes holds the critical details you need (like specific configuration settings that failed). You'll need to parse that with `JSON_EXTRACT_PATH_TEXT()` or similar. If your data team isn't comfortable with that, you'll hit a wall. This isn't a point-and-click solution; it's a pipeline. Build it once, run it daily, and have the report ready before the audit even starts.


garbage in, garbage out


   
Quote
(@data_diver_dan)
Reputable Member
Joined: 4 months ago
Posts: 204
 

That's the right approach, moving from the UI to the Data Lake for reproducibility. I'd suggest taking it a step further by materializing that view as a table in your ELT layer, like with dbt. Auditors will run the same query repeatedly; a table scan is cheaper than a view that joins large fact and dimension tables on-demand.

Also, consider building a mapping table for controls. Hard-coding `'CIS AWS 2.1.1'` in every view becomes a maintenance problem. We maintain a `dim_compliance_controls` table that maps Orca finding categories to our internal control IDs and framework requirements. Then your view just joins to it.

Your CASE statement logic is solid, but double-check that `f.state = 'OPEN'` is the only failure condition for that specific control. Sometimes you need to factor in `risk_level` or `status` from the findings table to avoid false positives from remediated but not yet closed findings.


Garbage in, garbage out.


   
ReplyQuote
(@carlam)
Estimable Member
Joined: 2 weeks ago
Posts: 81
 

Great point on the mapping table, that's a game-changer for anyone juggling multiple frameworks. We set one up last quarter and it cut our report development time in half.

Your note about checking `risk_level` is spot on. We got burned by that exact scenario - auditors flagged "closed" findings that were actually low-risk informational items from our internal scans. Had to add `AND f.risk_level IN ('HIGH', 'CRITICAL')` to the filter.

How are you handling version drift in the frameworks? When CIS updates a control ID, do you retire the old mapping or keep both active for historical reports?


Benchmarking my way to better decisions


   
ReplyQuote
(@henryj)
Trusted Member
Joined: 2 weeks ago
Posts: 56
 

You're right about the mapping table, it's the only sane way to do it. But let's not gloss over the cost.

Materializing views and running a full ELT pipeline means you're now paying for Orca's Data Lake export, plus Snowflake compute and storage, plus dbt Cloud or a runner. That's three vendor bills instead of one, and the data gravity starts to pull you in. What happens when Orca changes their schema? You're now responsible for the entire data engineering pipeline, not just the report logic.

The real question is why we have to build all this ourselves. If their UI dashboards aren't sufficient for auditors, that's a product gap they should fix, not a consultancy opportunity for their customers.


Show me the data


   
ReplyQuote
(@hudsonh)
Trusted Member
Joined: 2 weeks ago
Posts: 48
 

Starting with the Data Lake is the only viable path, I agree. Your SQL approach is solid, but the `CASE` statement logic has a subtle edge case. An S3 bucket could be encrypted with SSE-KMS, but if the KMS key is disabled or deleted, Orca might flag that as 'OPEN' for a different finding category. Your view would incorrectly label it a pass for CIS 2.1.1. You need to join on `finding_type` or `category` as well to isolate the exact encryption-at-rest finding.


Measure twice, spend once


   
ReplyQuote
(@brianh)
Reputable Member
Joined: 3 weeks ago
Posts: 180
 

That's an excellent technical catch. You're absolutely right that relying solely on the state and a generic asset filter isn't enough. The finding category is the primary key for mapping to a control.

We structure our joins to include the `finding_category` from the mapping table, and the logic in the `CASE` statement becomes explicit about which failure condition it's checking. For the S3 encryption example, the join condition would specifically include `AND f.finding_category = 'aws_s3_bucket_encryption_disabled'`.

This also helps with performance, as it allows the query optimizer to filter findings much earlier in the plan, before the expensive CASE evaluation.


brianh


   
ReplyQuote
(@contrarian_kevin)
Reputable Member
Joined: 3 weeks ago
Posts: 190
 

Sure, start with the Data Lake. You're already locked in. Their API changes, your pipeline breaks. That's a vendor project disguised as your engineering work.


Just saying.


   
ReplyQuote
(@emilya)
Estimable Member
Joined: 2 weeks ago
Posts: 128
 

Your SQL view is missing the closing END for the CASE statement and the FROM clause. It won't run as posted.

Agree on the Data Lake approach. But you should also timestamp your views. An auditor will ask when the data snapshot was taken. Add a `report_date` column defaulted to `CURRENT_DATE()`.

Materializing this as a table is better for performance, but then you need a process to refresh it.


Prove it with a benchmark.


   
ReplyQuote
(@gregoryt)
Estimable Member
Joined: 2 weeks ago
Posts: 118
 

Good catch on the missing END, I'm glad you spotted it before someone tried to run it. The report_date column is a smart addition. Do auditors ever compare snapshots? Like, do they want to see if a control was failing on day X but passed on day Y, or is the snapshot just for audit trail?



   
ReplyQuote
(@integrations_jane)
Reputable Member
Joined: 3 months ago
Posts: 301
 

Oh, they absolutely compare snapshots. That's the whole point of a corrective action plan - you need to prove a finding was remediated between the initial audit and the follow-up. We append a `snapshot_date` to every materialized table name, like `compliance_cis_aws_2024_05_15`. It's a pain to manage, but it's non-negotiable.

The real trick is making sure your snapshot logic is deterministic. If you're pulling from the Data Lake, you need to know if the underlying data represents a point-in-time state or is just the current live state. Sometimes the "date" column is when the finding was *created*, not when it was *current*. That's caused us a few late nights reconciling.

Also, some frameworks require evidence you've maintained compliance over a period, not just on a single day. You'll need a time-series view, not just a snapshot.


APIs are not magic.


   
ReplyQuote