Begleitbeitrag zum Data-Governance-Grundpfeiler. Der Grundpfeiler führt das Argument für Behaviour-first Governance auf der Ebene warum die Artefakte erzeugt und nicht getippt werden sollten. Dieser Beitrag setzt genau dort an, wo der Grundpfeiler aufhört: bei der Frage, wie man sie tatsächlich erzeugt — auf dem AWS-nativen Stack aus Fabric + Mesh auf AWS.
Was der Grundpfeiler offen ließ
Der zentrale Anspruch des Grundpfeilers, wert wiederholt zu werden:
Governance ist das, was die Plattform durchsetzt, was die Fachbereiche tun und was die Nachweise zeigen — in dieser Reihenfolge. Die Artefakte (Richtlinien, RACIs, RoPAs) sind Nebenprodukte dieses Verhaltens, keine Lieferergebnisse an sich.
Das zu glauben ist eine Sache. Die Rohre zu bauen, die es wahr machen, ist eine andere. Dieser Beitrag geht vier Mechanismen durch — die, die „wir sollten den RoPA aus Lineage erzeugen" in ein echtes Terraform-Modul und eine echte SQL-Abfrage verwandeln — auf dem AWS-nativen Stack, den die Fabric + Mesh auf AWS-Referenz bereits etabliert: LakeFormation + DataZone + Glue, plus OpenLineage als anbieter-neutrales Lineage-Rückgrat.
Die vier Mechanismen:
- RoPA aus Lineage — DSGVO-Art.-30-Datensätze, projiziert aus OpenLineage-Ereignissen + LF-Tag-Ontologie + DataZone-Subscription-Log.
- RACI aus Katalog-Eigentum — die RACI-Matrix abgeleitet aus DataZone-Projekt-Eigentum + LF-Tag-Metadaten + Abonnement-Genehmigungen.
- Audit-Bericht aus Subscription-Log — die Frage „wer hat was in den letzten 90 Tagen gelesen" beantwortet aus CloudTrail + DataZone-Ereignissen, in dem Format, das Aufseher akzeptieren.
- Änderungs-Autorisierungs-Workflow — GitOps für Policy-as-Code, bei dem der signierte Commit das audit-belegte Änderungs-Protokoll ist, mit automatisch erzeugten Betriebsrats-Meldungsentwürfen bei RBAC-relevanten Änderungen.
Keiner davon ist Cordata-spezifische Erfindung. Die Primitive existieren. Was den meisten Programmen fehlt, ist die Verdrahtung — die spezifischen Projektionen, die Feld-Zuordnungen, die Frage, wo die menschliche Prüfung sitzt. Das ist, was dieser Beitrag behandelt.
§ 1 — Woher die Ereignisse kommen
Vor den vier Mechanismen die Ereignisquellen, aus denen sie alle lesen. Alle vier Projektionen ziehen aus denselben drei, und diese Quellen zum Laufen zu bringen ist Voraussetzung dafür, dass irgendeine der Projektionen ehrlich ist:
- OpenLineage-Ereignisse — jeder Job (dbt-Modell-Lauf, Glue-ETL-Job, Airflow-Task, EMR-Spark-Job) emittiert ein
RunEventmit Input-Datensätzen, Output-Datensätzen, Job-Metadaten und Lauf-Status. Landet in einem OpenLineage-kompatiblen Backend. Marquez ist das OSS-Referenz-Backend, und Amazon DataZone akzeptiert OpenLineage-Ereignisse direkt über seinePostLineageEvent-API (angekündigt 2024, seither gereift — prüfen Sie die aktuellen AWS-Docs für den GA-Status und die spezifische Facet-Abdeckung). Auf dem AWS-nativen Stack bedeutet das, dass die OpenLineage-Emitter — das Spark-Plugin für Glue-Jobs,openlineage-airflowfür MWAA, dbt's eingebauter OpenLineage-Adapter — direkt an DataZone posten können, mit einem dünnen Adapter, der ihre Payloads in DataZones Ausprägung übersetzt. Die Projektionen in §§ 2-4 kümmern sich nicht darum, welches Backend die Ereignisse tatsächlich hält; die Ereignis-Senke ist von den Emittern abstrahiert. Wir behandeln diese Pipeline als die Quelle der Wahrheit für welche Daten durch welchen Job geflossen sind. - DataZone-Subscription-Log — jede Asset-Veröffentlichung, jede Abonnement-Anfrage, jede Genehmigung und jeder Widerruf ist ein DataZone-Ereignis. Verfügbar über die DataZone-API und in CloudTrail gespiegelt. Das ist die Quelle der Wahrheit für wer darf welches Datenprodukt sehen.
- CloudTrail-Data-Events — für die Tabellen unter LakeFormation-Verwaltung erfassen CloudTrail-Data-Events das Zugriffs-Log pro Abfrage (
GetTable,GetPartitions, AthenaStartQueryExecution, GlueGetPartitions, etc.). Data-Events müssen explizit aktiviert werden — sie sind nicht standardmäßig an und sind das, was die Audit-Antwort von Vermutung zu Beweis macht.
# Data-plane CloudTrail on the governance account, capturing LakeFormation
# access events across all producer accounts (cross-account CloudTrail).
resource "aws_cloudtrail" "governance_data_events" {
name = "cordata-gov-data-events"
s3_bucket_name = aws_s3_bucket.audit.id
is_organization_trail = true
is_multi_region_trail = true
include_global_service_events = true
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::S3::Object"
# Registered lake buckets — the ones LakeFormation manages
values = ["arn:aws:s3:::cordata-lake-*/*"]
}
data_resource {
type = "AWS::Glue::Table"
values = ["arn:aws:glue:*:*:table/*"]
}
}
}Mit diesen drei Quellen an Ort und Stelle ist alles weiter unten eine Projektion — ein Abfrage-Muster, das Rohereignisse in das Governance-Artefakt verwandelt.
§ 2 — RoPA aus Lineage
Verzeichnis von Verarbeitungstätigkeiten (RoPA, gemäß DSGVO Art. 30) ist das Artefakt, über das die meisten Governance-Programme lügen. Einmal getippt, von Hand gepflegt, binnen eines Quartals falsch. Ihn aus dem Ereignis-Strom der Plattform selbst zu erzeugen entfernt das Lügen.
Kurzer Primer für Leser, die nicht in Art. 30 verwurzelt sind. Der RoPA ist ein Register, das jeder Verantwortliche und Auftragsverarbeiter personenbezogener Daten führen muss — Art. 30(1) für Verantwortliche, Art. 30(2) für Auftragsverarbeiter. Er dokumentiert, pro Verarbeitungstätigkeit: den Zweck der Verarbeitung, die Kategorien der Daten und betroffenen Personen, die Empfängerkategorien, etwaige grenzüberschreitende Übermittlungen, Aufbewahrungsfristen und die eingesetzten technischen und organisatorischen Sicherheitsmaßnahmen. Eine Aufsichtsbehörde (BfDI in Deutschland, die kantonalen Datenschutzbeauftragten in der Schweiz, die DSB in Österreich) kann ihn kurzfristig anfordern. Ein falscher RoPA ist ein dokumentierter Verstoß gegen Art. 30; ein fehlender ist ein erschwerender Faktor bei jedem nachfolgenden Audit nach Art. 83 (Bußgelder bis zu 10 Mio. € oder 2 % des weltweiten Umsatzes, je nachdem, was höher ist). Deshalb ist „wir tippen ihn einmal und pflegen ihn von Hand" eine Form, über die Governance-Programme lügen — die Pflegekosten sind real, die Aktualitätslücke wächst still, und das Risiko ist asymmetrisch, wenn es eintritt.
Der DSGVO-Art.-30-Feldsatz, den ein Datensatz auf Verantwortlicher-Seite tragen muss:
| Art. 30-Feld | Quelle in der Plattform |
|---|---|
| Verarbeitungszweck | Job-Metadaten (dbt-Modell-Tag, Glue-Job-Tag, DataZone-Asset-Annotation) |
| Datenkategorien | Input-Dataset-Schema + LF-Tag sensitivity-Wert |
| Kategorien betroffener Personen | Input-Dataset-Schema + LF-Tag subject_type (Kunde, Mitarbeiter, Auftragnehmer) |
| Empfängerkategorien | Konsumenten des Output-Datasets (über DataZone-Subscription-Log) |
| Drittlandübermittlungen | LF-Tag residency auf Inputs vs. Outputs |
| Aufbewahrungsfristen | Lifecycle-Policy auf Tabellenebene (S3-Lifecycle-Regeln + Glue-Tabellen-Properties) |
| Sicherheitsmaßnahmen | Statisch — aus dem Plattform-Baseline (KMS, LF-verwaltet, VPC-Endpoints) |
| Rechtsgrundlage | Job-Metadaten (Tag auf der Pipeline) |
Bevor die SQL läuft, müssen die Ereignisse irgendwo gemeinsam abfragbar sein. Die vier Quellen — OpenLineage-Ereignisse, Katalog-Metadaten (Glue-Tabellen + LF-Tag-Bindungen), DataZone-Abonnements und CloudTrail-Data-Events — liegen standardmäßig nicht im selben Store. Zwei Wege bringen sie dorthin:
- Zentral materialisieren — ein nächtliches ETL schifft jede Quelle in ein Data-Warehouse im Governance-Konto: Redshift Serverless, Aurora PostgreSQL oder DuckDB-über-S3 via Athena zugegriffen. Operativ einfach (eine Stelle zum Sichern, Auditieren und Sichern), veraltet nach ETL-Kadenz.
- Föderieren mit Trino — Trino-Konnektoren fragen die Quellen an Ort und Stelle ab, ohne Daten zu bewegen. Eine einzige Abfrage joint das OpenLineage-Backend, den Glue-Katalog, das DataZone-Subscription-Log und den CloudTrail-Export direkt. Frischer (auf die Minute aktuell), mehr bewegliche Teile zu pflegen (Konnektor-Configs, Credentials pro Quelle, Kostenzuweisung zur Abfragezeit).
Cordatas Standard-Empfehlung ist zentral materialisieren für das erste Jahr eines Governance-Programms — ein Warehouse, ein Team, eine Grant-Oberfläche, eine Stelle, an die ein Aufseher schauen kann. Trino-Föderation ist die Komplexität wert, wenn der RoPA auf die Minute aktuell sein muss (selten) oder wenn das Quell-Ereignis-Volumen ETL unpraktisch macht.
Die Ereignis-Sammlungs-Verdrahtung selbst — wie die OpenLineage-Senke landet, wie CloudTrail nach Athena exportiert, der Glue-Crawler-Zeitplan — ist außerhalb des Umfangs dieses Beitrags. Das ist konkretes Terrain für den Pipeline-Hälften-Begleitbeitrag in den Folgebeiträgen des Governance-Grundpfeilers.
Wenn die Ereignisse an einem Ort landen, ist die Projektion ein einzelnes dbt-Modell, das OpenLineage plus die LF-Tag-Ontologie plus das DataZone-Subscription-Log liest:
-- models/governance/ropa_current.sql
-- Materialised as a view, refreshed every 6 hours by an EventBridge
-- schedule. Human review + signature happens on the delta since the
-- last signed snapshot (see the HITL step at the end of this section).
WITH lineage_jobs AS (
SELECT
ol.job_namespace,
ol.job_name,
ol.job_facets->'processing'->>'purpose' AS purpose,
ol.job_facets->'processing'->>'legal_basis' AS legal_basis,
ol.inputs,
ol.outputs,
ol.run_id,
ol.event_time
FROM openlineage_events ol
WHERE ol.event_type = 'COMPLETE'
AND ol.event_time > NOW() - INTERVAL '30 days'
),
input_facets AS (
SELECT
j.run_id,
lft.sensitivity,
lft.residency,
lft.subject_type,
d.name AS dataset_name,
d.schema_fields AS fields
FROM lineage_jobs j
CROSS JOIN LATERAL jsonb_array_elements(j.inputs) AS input
JOIN datasets d ON d.namespace = input->>'namespace'
AND d.name = input->>'name'
JOIN lf_tag_bindings lft ON lft.dataset_name = d.name
),
output_facets AS (
SELECT
j.run_id,
d.name AS output_name,
lft.residency AS output_residency
FROM lineage_jobs j
CROSS JOIN LATERAL jsonb_array_elements(j.outputs) AS output
JOIN datasets d ON d.namespace = output->>'namespace'
AND d.name = output->>'name'
JOIN lf_tag_bindings lft ON lft.dataset_name = d.name
),
recipients AS (
-- Downstream consumers via DataZone subscription log
SELECT
of.output_name,
array_agg(DISTINCT dz.subscriber_project) AS consuming_domains
FROM output_facets of
JOIN datazone_subscriptions dz ON dz.asset_name = of.output_name
AND dz.status = 'APPROVED'
GROUP BY of.output_name
)
SELECT
j.purpose AS processing_purpose,
j.legal_basis AS legal_basis,
array_agg(DISTINCT i.subject_type) AS data_subject_categories,
array_agg(DISTINCT i.sensitivity) AS data_categories,
array_agg(DISTINCT i.residency) AS source_residencies,
array_agg(DISTINCT o.output_residency) AS output_residencies,
(SELECT array_agg(consuming_domains)
FROM recipients r
JOIN output_facets o2 ON r.output_name = o2.output_name
WHERE o2.run_id = j.run_id) AS recipient_categories,
j.job_namespace || '.' || j.job_name AS processing_activity_id,
MAX(j.event_time) AS last_seen
FROM lineage_jobs j
JOIN input_facets i ON i.run_id = j.run_id
JOIN output_facets o ON o.run_id = j.run_id
GROUP BY j.job_namespace, j.job_name, j.purpose, j.legal_basis, j.run_id;Beispielausgabe — drei Zeilen aus ropa_current:
| processing_activity_id | legal_basis | data_subject_categories | data_categories | source_residencies | output_residencies | recipient_categories |
|---|---|---|---|---|---|---|
fraud.transactions_scored_daily |
legitimate-interest | {customer} |
{high, confidential} |
{eu} |
{eu} |
{analytics-domain, ml-domain} |
ml.churn_predictions_weekly |
consent | {customer} |
{high} |
{eu} |
{eu} |
{retention-domain} |
finance.bafin_quarterly_extract |
legal-obligation | {customer, contractor} |
{high, confidential} |
{eu} |
{eu, ch} |
{finance-domain, regulator-portal} |
Die Erkennung grenzüberschreitender Übermittlung ist erwähnenswert. Wenn source_residencies und output_residencies sich unterscheiden — z. B. {eu} rein, {eu, us} raus —, ist diese Zeile ein Kandidat für eine grenzüberschreitende Übermittlung nach DSGVO Art. 44-49 und braucht eine dokumentierte Schutzmaßnahme (SCCs, Angemessenheitsbeschluss oder ausdrückliche Einwilligung). Die dritte Zeile oben ist genau dieses Muster ({eu} → {eu, ch}). Die Projektion macht es sichtbar; das Governance-Council entscheidet, ob die Übermittlung autorisiert ist.
Wo die menschliche Verantwortung sitzt. Die Projektion erzeugt den Zustand des RoPA zu jedem Moment; DSGVO Art. 30 verlangt einen signierten Datensatz mit einer benannten Verantwortlichen-Seite-Autorität. Diese Signatur ist die des Datenschutzbeauftragten (oder des in der Governance-RACI benannten Datenschutz-Leads) — niemand sonst in der Organisation ist zur Signatur autorisiert, und diese Identität ist die Verantwortungskette, die DSGVO Art. 30 verlangt. Die HITL-Kadenz: wöchentlich bei unwesentlichen Änderungen, am nächsten Werktag bei wesentlichen (Datenkategorie hinzugefügt, Empfänger hinzugefügt, Flag für grenzüberschreitende Übermittlung umgelegt), sodass der RoPA nie mehr als sieben Tage veraltet ist.
Das vollständige, einsetzbare Framework — EventBridge-Zeitplan, ropa_snapshots-Warehouse-Tabelle, Diff-und-Post-Lambda, GPG-signiertes Audit-Repo, Frische-Trigger, echte Frische-Metriken nach dem Betrieb — ist Terrain für den Folgebeitrag: Wie wir das RoPA-Erzeugungs-Framework gebaut haben (geplant). Die Aufgabe dieses Beitrags ist das Muster; die Aufgabe jenes Beitrags ist der einsetzbare Beweis.
Die Form ist, was ein DSGVO-Auditor akzeptiert: keine von Hand gepflegte Wiki-Seite, sondern ein erzeugter Datensatz mit einem benannten Unterzeichner, einer signierten Nachweiskette und einer Prüfkadenz, die ein Aufseher einsehen kann.
§ 3 — RACI aus Katalog-Eigentum
Die RACI-Matrix — Responsible, Accountable, Consulted, Informed — ist ein Governance-Artefakt, das binnen eines Quartals nach dem Schreiben veraltet. Der Grund ist banal: die Organisation ändert sich, das Papier nicht. Der Behaviour-First-Zug: leiten Sie die RACI aus den Eigentümer-Metadaten des Katalogs selbst ab, die die Fachbereiche aktuell halten, weil ihr eigener Abonnement-Workflow davon abhängt.
Die Zuordnung auf dem AWS-nativen Stack:
| RACI-Rolle | Abgeleitet aus |
|---|---|
| Accountable (A) | DataZone-Projekt-Eigentümer für das Datenprodukt |
| Responsible (R) | Fachbereichsmitglieder mit SELECT + INSERT + UPDATE-Grants über LF-Tag-Policy |
| Consulted (C) | Nachgelagerte Konsumenten via DataZone-Abonnement (Status APPROVED) |
| Informed (I) | Governance-Council-Rolle + Audit-Log-Abonnenten |
Eine einzige Abfrage macht die vollständige Matrix für jedes Datenprodukt sichtbar:
-- models/governance/raci_derived.sql
-- Executes against the catalog + subscription log. Runs on-demand
-- (governance dashboard) or nightly (reconciliation against paper-RACI).
SELECT
ap.asset_name,
ap.data_product,
-- Accountable: the DataZone project owner
ap.owning_project_owner_email AS accountable,
-- Responsible: domain members with write-grants
(SELECT array_agg(DISTINCT lfp.principal_arn)
FROM lf_permissions lfp
WHERE lfp.resource_expression @> ap.lf_tag_expression
AND lfp.permissions && ARRAY['SELECT', 'INSERT', 'UPDATE']
AND lfp.principal_arn LIKE '%' || ap.owning_domain || '%'
) AS responsible,
-- Consulted: approved downstream subscribers
(SELECT array_agg(DISTINCT dz.subscriber_project)
FROM datazone_subscriptions dz
WHERE dz.asset_name = ap.asset_name
AND dz.status = 'APPROVED'
) AS consulted,
-- Informed: governance council + audit subscribers (static config)
ARRAY['governance-council@cordata', 'audit-log@cordata'] AS informed
FROM asset_publications ap;Beispielausgabe — zwei Zeilen aus raci_derived:
| asset_name | accountable | responsible | consulted | informed |
|---|---|---|---|---|
fraud.transactions_curated |
fraud-lead@cordata |
{fraud-eng-1@, fraud-eng-2@} |
{analytics-domain, ml-domain} |
{governance-council, audit-log} |
policy.customer |
policy-lead@cordata |
{policy-eng-1@} |
{claims-domain, marketing-domain} |
{governance-council, audit-log} |
Die interessante Ausgabe ist nicht die Projektion selbst — es ist die Rückkopplung. Wöchentlich vergleicht ein Job die abgeleitete RACI mit der Papier-RACI (typischerweise eine Tabelle, die Legal pflegt) und macht Abweichungen sichtbar:
-- models/governance/raci_divergence.sql
SELECT
derived.asset_name,
derived.accountable AS derived_accountable,
paper.accountable AS paper_accountable,
CASE
WHEN derived.accountable != paper.accountable THEN 'accountable_mismatch'
WHEN derived.responsible != paper.responsible THEN 'responsible_drift'
WHEN cardinality(derived.consulted) != cardinality(paper.consulted) THEN 'subscriber_drift'
ELSE 'aligned'
END AS divergence_type
FROM raci_derived derived
LEFT JOIN raci_paper paper ON paper.asset_name = derived.asset_name
WHERE derived.accountable != paper.accountable
OR derived.responsible != paper.responsible
OR cardinality(derived.consulted) != cardinality(paper.consulted);Beispielausgabe — eine Governance-Council-Prüfung würde auf Zeilen wie diese treffen:
| asset_name | derived_accountable | paper_accountable | divergence_type |
|---|---|---|---|
policy.customer |
policy-lead@cordata |
old-owner@cordata |
accountable_mismatch |
claims.claim_events |
claims-lead@cordata |
claims-lead@cordata |
subscriber_drift |
Abweichung als Signal. Wenn die abgeleitete RACI von der Papier-RACI abweicht, ist das an sich ein Datenqualitäts-Ereignis. Zwei Möglichkeiten:
- Das Papier ist veraltet — die abgeleitete Version ist Wahrheit, und das Papier muss aktualisiert werden (oder, besser, zugunsten der abgeleiteten Ansicht in Rente geschickt).
- Die Katalog-Metadaten sind falsch — ein Fachbereich hat sein Eigentum nicht aktualisiert; die Plattform setzt veraltete Grants gegen veraltete Metadaten durch. So oder so hat die Abweichung einen spezifischen Eigentümer und einen spezifischen Fix.
Das Governance-Council prüft Abweichungen monatlich. Die Papier-RACI hört mit der Zeit auf, die Quelle der Wahrheit zu sein — Teams hören auf, sie zu pflegen, sobald sie der abgeleiteten Ansicht vertrauen. Das ist nicht die Aufgabe der Governance; es ist ihre Reife.
§ 4 — Audit-Bericht aus Subscription-Log
Die Audit-Frage, die ein DACH-Aufseher stellt — BaFin, FINMA oder eine interne Prüfung —, hat fast immer dieselbe Form:
„Zeigen Sie mir, wer in den letzten 90 Tagen auf die Kunden-Tabelle zugegriffen hat, mit Query-Text, anfragender Rolle und der Richtlinie, die den Zugriff gewährt hat."
Diese Antwort aus CloudTrail + DataZone-Ereignissen zu bekommen erfordert echtes Engineering, aber keine exotischen Kunststücke. Der Haken ist, dass die Ereignisse über Konten verteilt sind (Producer-Konten pro Fachbereich für die Storage-Layer-Ereignisse, das Governance-Konto für die Grant-Layer-Ereignisse), sodass die kontoübergreifende Ereignis-Verknüpfung die eigentliche Arbeit ist.
Das Ereignis-Verknüpfungs-Muster:
Diagramm-Erläuterung. Die Athena-Abfrage eines Konsumenten in einem Konto startet eine Session (A1). Die Session assumiert eine kontoübergreifende Rolle im Producer-Konto (A2). LakeFormation vergibt temporäre Credentials für die Glue-Tabelle (P1), die die LF-Tag-Policy im Governance-Konto (G1) auswertet — diese Policy ist ein materialisierter Grant aus einem DataZone-Abonnement (G2). Der S3-Read (P2) findet unter den temporären Credentials statt. Um „wer hat auf die Kunden-Tabelle zugegriffen" zu beantworten, muss die Audit-Abfrage diese Kette rückwärts vom S3-Zugriff zur menschlichen Identität laufen, die die Session gestartet hat.
Die Abfrage, die die Frage des Aufsehers beantwortet, läuft gegen einen CloudTrail-Data-Lake (z. B. Athena über den CloudTrail-S3-Export):
-- Last-90-day audit query for the customer table
WITH s3_reads AS (
SELECT
event_time,
user_identity.session_context.session_issuer.arn AS assumed_role,
request_parameters.bucket_name AS bucket,
request_parameters.key AS object_key,
recipient_account_id AS producer_account
FROM cloudtrail_data_events
WHERE event_source = 's3.amazonaws.com'
AND event_name = 'GetObject'
AND request_parameters.bucket_name = 'cordata-lake-customer'
AND event_time > NOW() - INTERVAL '90' DAY
),
role_sessions AS (
SELECT
event_time AS assume_time,
user_identity.arn AS human_identity,
request_parameters.role_arn AS assumed_role,
response_elements.assumed_role_user.arn AS session_arn
FROM cloudtrail_events
WHERE event_source = 'sts.amazonaws.com'
AND event_name = 'AssumeRole'
AND event_time > NOW() - INTERVAL '90' DAY
),
grant_context AS (
SELECT
dz.approval_time,
dz.subscriber_project,
dz.asset_name,
dz.approved_by,
dz.lf_tag_expression AS granting_policy
FROM datazone_subscription_events dz
WHERE dz.status = 'APPROVED'
AND dz.asset_name LIKE '%customer%'
)
SELECT
r.event_time,
s.human_identity,
r.assumed_role,
r.object_key,
g.granting_policy,
g.approved_by AS grant_authorised_by
FROM s3_reads r
JOIN role_sessions s ON s.session_arn = r.assumed_role
AND r.event_time > s.assume_time
LEFT JOIN grant_context g ON r.object_key LIKE '%' || g.asset_name || '%'
ORDER BY r.event_time DESC;Beispielausgabe — zwei Zeilen aus den letzten 90 Tagen, ARNs zur Lesbarkeit abgekürzt:
| event_time | human_identity | assumed_role | object_key | granting_policy | grant_authorised_by |
|---|---|---|---|---|---|
| 2026-08-01 14:22 | iam::123…:user/anna.mueller |
iam::987…:role/analytics-reader |
customer/2026/07/day=28/part-000.parquet |
domain=customer AND sensitivity<=confidential AND residency=eu |
policy-lead@cordata |
| 2026-08-01 09:15 | iam::123…:user/marc.dubois |
iam::987…:role/ml-training-role |
customer/2026/07/day=27/part-001.parquet |
domain=customer AND sensitivity<=confidential AND residency=eu |
policy-lead@cordata |
Jeder Read eines cordata-lake-customer-Objekts ist jetzt zurückverbunden zu (a) der menschlichen Identität hinter der Session, (b) der assumierten Rolle, (c) der LF-Tag-Policy, die ihn erlaubt hat, und (d) der DataZone-Abonnement-Genehmigung, die diese Policy materialisierte — mit dem Namen des Fachbereichs-Eigentümers, der genehmigte.
Das ist das Format, das ein BaFin-Auditor akzeptiert. Keine aus dem Gedächtnis rekonstruierte Tabelle.
§ 5 — Änderungs-Autorisierungs-Workflow
Der zweite Zug aus § 5 des Grundpfeilers — „ein Änderungs-Autorisierungs-Workflow, der selbst audit-belegt ist" — erweist sich als der am wenigsten mysteriöse der vier Mechanismen, sobald man das Betriebs-Muster akzeptiert: GitOps für Policy-as-Code, mit signierten Commits als Änderungs-Protokoll.
Das ist keine Governance-spezifische Erfindung. Es ist dieselbe Policy-as-Code-Access-Fähigkeit, die als einer der fünf Fabric-Züge im Data-Engineering-Grundpfeiler benannt wird, auf demselben IaC-First-Fundament, das die Fabric + Mesh auf AWS-Referenz in ihrem § 2 als Voraussetzung behandelt. Governance läuft auf denselben Schienen wie alles andere, das die Plattform ausliefert — das ist der Punkt von Behaviour-First als Betriebs-Haltung, nicht nur als Phrase.
Die Form:
- Die LF-Tag-Ontologie, die Grant-Ausdrücke, die DataZone-Projekt-Konfiguration und die Konfiguration des Abonnement-Genehmigungs-Workflows liegen alle in einem Terraform-Repo unter
terraform/governance/. - Jede vorgeschlagene Änderung ist ein Pull Request. Der PR trägt die Ausgabe von
terraform planals angehängtes Text-Artefakt — der Reviewer sieht genau, was sich ändern wird, bevor er genehmigt. - Merged-PRs sind GPG-signierte Commits (
git commit -S), bei denen der Schlüssel des Reviewers seiner Identität im internen Identity Provider zugeordnet ist. - Ein GitHub-Actions-Workflow wendet den Plan beim Merge an, und die CloudTrail-Ereignisse
PutResourcePolicy/CreateLFTag, die daraus entstehen, tragen den Git-SHA als Request-Metadaten-Tag.
# .github/workflows/apply-governance.yml
name: Apply governance changes
on:
push:
branches: [main]
paths:
- "terraform/governance/**"
jobs:
apply:
runs-on: ubuntu-latest
environment:
name: production
# Environment requires 2 approvers from the governance CODEOWNERS
# (defined in .github/CODEOWNERS below). No lone-wolf changes.
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Verify signed commit
run: |
if [ "$(git log -1 --pretty=format:%G?)" != "G" ]; then
echo "Commit is not GPG-verified — refusing to apply."
exit 1
fi
- name: Apply with SHA tag
env:
COMMIT_SHA: ${{ github.sha }}
run: |
cd terraform/governance
terraform init
terraform apply -auto-approve \
-var="change_reference=${COMMIT_SHA}"# .github/CODEOWNERS
terraform/governance/lf-tags/** @governance-council
terraform/governance/grants/** @platform-team @governance-council
terraform/governance/datazone/** @platform-team
CODEOWNERS setzt den föderierten Entscheidungs-Vertrag aus § 5 des Grundpfeilers durch: jede Änderung an der LF-Tag-Ontologie (dem gemeinsamen Vokabular) erfordert Genehmigung durch das Governance-Council; Grants erfordern Plattformteam UND Governance-Council; die DataZone-Projekt-Konfiguration gehört dem Plattformteam.
Das Änderungs-Protokoll IST der Audit-Nachweis. Wenn ein Aufseher fragt „wann wurde der Tag sensitivity=regulator_only hinzugefügt und wer hat ihn genehmigt?", ist die Antwort eine Git-Log-Abfrage, keine Wiki-Archäologie-Grabung:
git log --show-signature --patch \
--follow terraform/governance/lf-tags/sensitivity.tfDie Ausgabe enthält die Commit-Nachricht, den Diff, die GPG-Signatur, die Signierer-Identität und die Zeitstempel. Alles unveränderlich. Alles Beweis.
Betriebsrats-Meldungs-Integration
Der Betriebsrats-Rhythmus aus § 8 des Grundpfeilers wird zu einem konkreten Teil dieses Workflows. Jeder PR, der terraform/governance/lf-tags/** (RBAC-relevante Änderungen) berührt, erzeugt automatisch einen Betriebsrats-Meldungsentwurf:
# .github/workflows/works-council-notification.yml
on:
pull_request:
paths:
- "terraform/governance/lf-tags/**"
jobs:
draft-notification:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate works-council notification draft
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
python .github/scripts/draft_notification.py \
--pr "$PR_NUMBER" \
--title "$PR_TITLE" \
--diff "$(git diff origin/main -- terraform/governance/lf-tags/)" \
--template docs/works-council/notification-template.de.md \
--out /tmp/notification.md
- name: Attach to PR
uses: mshick/add-pr-comment@v2
with:
message-path: /tmp/notification.mdDer erzeugte Entwurf ist eine ausgefüllte Kopie der Betriebsrats-Meldungs-Vorlage (die Vorlage-Datei ist auf Deutsch — .de.md — weil der Betriebsrat ein deutschsprachiges Dokument erhält) — automatisch befüllt mit dem Änderungsumfang (welche Tag-Werte hinzugefügt/entfernt/neu definiert werden), der betroffenen Datenprodukt-Oberfläche, der geschätzten Anzahl betroffener Grants und dem Rollback-Plan des Plattformteams. Der Betriebsrat erhält einen echten Entwurf, den er prüfen kann; das Plattformteam bezahlt die Prüfzyklus-Zeit vorab, statt drei Sprints später ein Rollback zu essen.
§ 6 — Was das nicht abdeckt
Die vier Mechanismen oben sind die Artefakt-Erzeugungs-Seite der Behaviour-First-Governance. Drei spezifische Dinge tun sie bewusst nicht:
- Sie entscheiden keine Richtlinien. Die Projektionen erzeugen das Protokoll darüber, welche Richtlinien existieren und was sie durchsetzen. Das Governance-Council muss die Entscheidungen weiterhin selbst treffen — föderierte computationale Governance bedeutet computationale Durchsetzung, keine computationale Entscheidung.
- Sie versöhnen nicht über Rechtsordnungen hinweg. Ein Multi-Jurisdiktions-Haus (DE + CH + AT mit separaten Rechtsgrundlagen) braucht einen pro-Jurisdiktion-RoPA und einen pro-Jurisdiktion-Audit-Trail. Die Projektion oben ist einheits-jurisdiktional; sie zu erweitern ist ein spezifischer Mandats-Umfang, keine generische Vorlage.
- Sie migrieren keine Alt-Artefakte. Wenn ein Haus bereits einen manuell gepflegten RoPA und eine Papier-RACI hat, ist die Migration eine separate Anstrengung — Papier mit abgeleiteter Ansicht abgleichen, entscheiden, welche historischen Einträge übernommen werden, und mit Legal über den Übergang verhandeln. Die abgeleitete Ansicht ist stabil; die Migration ist der Ort, an dem die politische Arbeit lebt.
Wer erzeugte Governance als vollständige Antwort auf ein Governance-Programm verkauft, verpasst alle drei.
Wo das hineinpasst
Der Data-Governance-Grundpfeiler etablierte die Theorie — Governance als Verhalten mit Nachweis. Die Fabric + Mesh auf AWS-Referenz etablierte das technische Fundament. Dieser Beitrag ist die Verdrahtung zwischen ihnen.
Für ein Haus, das das ernsthaft baut, ist das zweiwöchige Discovery aus dem Engagement-Modell des Grundpfeilers der Ausgangspunkt: aktuelle Governance-Oberfläche prüfen, die bestehende Papier-RACI gegen die abgeleitete laufen lassen, Abweichungen zählen, entscheiden, welcher der vier Mechanismen die meiste Compliance-Wirkung zuerst liefert. RoPA-Erzeugung ist die übliche Antwort — sie spart dem DSB eine halbe Woche manueller Pflege pro Monat, und die Frische-Verbesserung ist das Artefakt, das ein Aufseher zuerst bemerkt.
Wenn Sie einen der vier Mechanismen selbst verdrahtet haben — oder auf ein spezifisches Projektions-Problem gestoßen sind, das die obige SQL nicht abdeckt — würde ich wirklich gerne hören, wo die Nähte sichtbar wurden. Die Tür steht offen unter cordata.tech/contact. Weiterlesen: der Data-Governance-Grundpfeiler für das Betriebs-Modell-Argument, das dieser Walkthrough konkretisiert; die Fabric + Mesh auf AWS-Referenz für das LakeFormation + DataZone-Fundament, aus dem die Projektionen lesen; und der kommende Beitrag DSGVO Art. 17 über ein Mesh hinweg zur Löschungs-Mechanik, die dieser Walkthrough nicht abdeckt.