Achter de schermen van de Data Privacy Firewall

Notitie

Privacyniveaus zijn momenteel niet beschikbaar in Power Platform-gegevensstromen, maar het productteam werkt aan het inschakelen van deze functionaliteit.

Als u Power Query al enige tijd hebt gebruikt, hebt u deze waarschijnlijk ervaren. Als u plotseling een foutmelding krijgt dat er geen enkele hoeveelheid online zoeken, query's aanpassen of toetsenbordbashing kan worden opgelost. Een fout zoals:

Formula.Firewall: Query 'Query1' (step 'Source') references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.

Of misschien:

Formula.Firewall: Query 'Query1' (step 'Source') is accessing data sources that have privacy levels which cannot be used together. Please rebuild this data combination.

Deze Formula.Firewall fouten zijn het resultaat van de Data Privacy Firewall van Power Query (ook wel bekend als de firewall), die soms lijkt alsof het alleen bestaat om gegevensanalisten over de hele wereld te frustreren. Geloof het of niet, de firewall dient echter een belangrijk doel. In dit artikel gaan we onder de motorkap duiken om beter te begrijpen hoe het werkt. Bewapend met meer begrip, kunt u hopelijk in de toekomst beter firewallfouten vaststellen en oplossen.

Wat is het?

Het doel van de Data Privacy Firewall is eenvoudig: het bestaat om te voorkomen dat Power Query onbedoeld gegevens uitlekt tussen bronnen.

Waarom is dit nodig? Ik bedoel, u kunt zeker een aantal M maken die een SQL-waarde zou doorgeven aan een OData-feed. Maar dit zou opzettelijk gegevenslekken zijn. De mashup-auteur zou (of tenminste moeten) weten dat ze dit deden. Waarom dan de noodzaak van bescherming tegen onbedoelde gegevenslekken?

Het antwoord? Vouwen.

Vouwen?

Vouwen is een term die verwijst naar het converteren van expressies in M (zoals filters, hernoemingen, joins, enzovoort) in bewerkingen op basis van een onbewerkte gegevensbron (zoals SQL, OData, enzovoort). Een groot deel van de kracht van Power Query is afkomstig van het feit dat PQ de bewerkingen die een gebruiker uitvoert via de gebruikersinterface kan converteren naar complexe SQL- of andere back-endgegevensbrontalen, zonder dat de gebruiker deze talen hoeft te kennen. Gebruikers krijgen het prestatievoordeel van systeemeigen gegevensbronbewerkingen, met het gebruiksgemak van een gebruikersinterface waarin alle gegevensbronnen kunnen worden getransformeerd met behulp van een gemeenschappelijke set opdrachten.

Als onderdeel van het vouwen kan PQ soms bepalen dat de meest efficiënte manier om een bepaalde mashup uit te voeren gegevens uit de ene bron moet halen en doorgeven aan een andere. Als u bijvoorbeeld een klein CSV-bestand koppelt aan een enorme SQL-tabel, wilt u waarschijnlijk niet dat PQ het CSV-bestand leest, de hele SQL-tabel leest en deze vervolgens samenvoegt op uw lokale computer. U wilt waarschijnlijk dat PQ de CSV-gegevens in een SQL-instructie inline zet en de SQL-database vraagt om de join uit te voeren.

Zo kan onbedoeld gegevenslekken optreden.

Stel dat u deelneemt aan SQL-gegevens met burgerservicenummers van werknemers met de resultaten van een externe OData-feed en u plotseling hebt ontdekt dat de burgerservicenummers van SQL naar de OData-service werden verzonden. Slecht nieuws, toch?

Dit is het soort scenario dat de firewall bedoeld is om te voorkomen.

Hoe werkt het?

De firewall bestaat om te voorkomen dat gegevens van de ene bron onbedoeld naar een andere bron worden verzonden. Eenvoudig genoeg.

Hoe bereikt het deze missie?

Dit doet u door uw M-query's te verdelen in partities en vervolgens de volgende regel af te dwingen:

  • Een partitie heeft mogelijk toegang tot compatibele gegevensbronnen of verwijst naar andere partities, maar niet naar beide.

Eenvoudige... maar verwarrend. Wat is een partitie? Wat maakt twee gegevensbronnen 'compatibel'? En waarom moet de firewall zorgen als een partitie toegang wil krijgen tot een gegevensbron en naar een partitie wil verwijzen?

Laten we dit opsplitsen en kijken naar de bovenstaande regel één stuk tegelijk.

Wat is een partitie?

Op het meest eenvoudige niveau is een partitie slechts een verzameling van een of meer querystappen. De meest gedetailleerde partitie die mogelijk is (ten minste in de huidige implementatie) is één stap. De grootste partities kunnen soms meerdere query's omvatten. (Meer informatie hierover later.)

Als u niet bekend bent met stappen, kunt u deze rechts van het Power Query-editor venster bekijken nadat u een query hebt geselecteerd, in het deelvenster Toegepaste stappen. Stappen houden alles bij wat u hebt gedaan om uw gegevens te transformeren in de uiteindelijke vorm.

Partities die verwijzen naar andere partities

Wanneer een query wordt geëvalueerd met de firewall ingeschakeld, verdeelt de firewall de query en alle bijbehorende afhankelijkheden in partities (dat wil gezegd groepen stappen). Telkens wanneer een partitie verwijst naar iets in een andere partitie, vervangt de firewall de verwijzing door een aanroep naar een speciale functie die wordt aangeroepen Value.Firewall. Met andere woorden, de firewall staat partities niet rechtstreeks toegang tot elkaar toe. Alle verwijzingen worden gewijzigd om via de firewall te gaan. Denk aan de firewall als gatekeeper. Een partitie die verwijst naar een andere partitie, moet hiervoor de machtiging van de firewall krijgen en de firewall bepaalt of de gegevens waarnaar wordt verwezen wel of niet worden toegestaan in de partitie.

Dit lijkt misschien nogal abstract, dus laten we eens kijken naar een voorbeeld.

Stel dat u een query hebt met de naam Werknemers, waarmee enkele gegevens uit een SQL-database worden opgehaald. Stel dat u ook een andere query hebt (EmployeesReference), die simpelweg verwijst naar Werknemers.

shared Employees = let
    Source = Sql.Database(…),
    EmployeesTable = …
in
    EmployeesTable;

shared EmployeesReference = let
    Source = Employees
in
    Source;

Deze query's worden onderverdeeld in twee partities: één voor de query Werknemers en één voor de EmployeesReference-query (die verwijst naar de partitie Werknemers). Wanneer de firewall wordt geëvalueerd, worden deze query's als volgt herschreven:

shared Employees = let
    Source = Sql.Database(…),
    EmployeesTable = …
in
    EmployeesTable;

shared EmployeesReference = let
    Source = Value.Firewall("Section1/Employees")
in
    Source;

U ziet dat de eenvoudige verwijzing naar de query Werknemers is vervangen door een aanroep naar Value.Firewall, die de volledige naam van de query Werknemers heeft opgegeven.

Wanneer EmployeesReference wordt geëvalueerd, wordt de aanroep onderschept Value.Firewall("Section1/Employees") door de firewall, die nu de mogelijkheid heeft om te bepalen of (en hoe) de aangevraagde gegevens naar de partitie EmployeesReference stromen. Het kan een willekeurig aantal dingen doen: de aanvraag weigeren, de aangevraagde gegevens bufferen (waardoor verdere vouwen naar de oorspronkelijke gegevensbron niet meer voorkomen), enzovoort.

Dit is de wijze waarop de firewall controle houdt over de gegevens die tussen partities stromen.

Partities die rechtstreeks toegang hebben tot gegevensbronnen

Stel dat u een queryquery1 met één stap definieert (houd er rekening mee dat deze query met één stap overeenkomt met één firewallpartitie) en dat deze ene stap toegang heeft tot twee gegevensbronnen: een SQL-databasetabel en een CSV-bestand. Hoe gaat de firewall hiermee om, omdat er geen partitiereferentie is en dus geen aanroep om deze te Value.Firewall onderscheppen? Laten we eens kijken naar de regel die eerder is aangegeven:

  • Een partitie heeft mogelijk toegang tot compatibele gegevensbronnen of verwijst naar andere partities, maar niet naar beide.

Om ervoor te zorgen dat uw query met één partitie,maar twee gegevensbronnen mag worden uitgevoerd, moeten de twee gegevensbronnen compatibel zijn. Met andere woorden, het moet geen probleem zijn dat gegevens bidirectioneel tussen de gegevens worden gedeeld. Dit betekent dat de privacyniveaus van beide bronnen openbaar moeten zijn, of beide organisatie, omdat dit de enige twee combinaties zijn die het delen in beide richtingen mogelijk maken. Als beide bronnen zijn gemarkeerd als Privé of als een bron openbaar is gemarkeerd en een bron is gemarkeerd als Organisatie, of als ze zijn gemarkeerd met een andere combinatie van privacyniveaus, is bidirectioneel delen niet toegestaan en is het dus niet veilig dat ze beide in dezelfde partitie worden geëvalueerd. Dit zou betekenen dat onveilige gegevens kunnen worden gelekt (vanwege vouwen) en dat de firewall dit niet kan voorkomen.

Wat gebeurt er als u toegang probeert te krijgen tot incompatibele gegevensbronnen in dezelfde partitie?

Formula.Firewall: Query 'Query1' (step 'Source') is accessing data sources that have privacy levels which cannot be used together. Please rebuild this data combination.

Hopelijk begrijpt u nu een van de foutberichten aan het begin van dit artikel.

Houd er rekening mee dat deze compatibiliteitsvereiste alleen van toepassing is binnen een bepaalde partitie. Als een partitie verwijst naar andere partities, hoeven de gegevensbronnen van de partities waarnaar wordt verwezen, niet compatibel met elkaar te zijn. Dit komt doordat de firewall de gegevens kan bufferen, waardoor het vouwen van de oorspronkelijke gegevensbron wordt voorkomen. De gegevens worden in het geheugen geladen en behandeld alsof ze nergens vandaan komen.

Waarom niet beide?

Stel dat u een query met één stap definieert (die opnieuw overeenkomt met één partitie) die toegang heeft tot twee andere query's (dat wil zeggen twee andere partities). Wat moet u doen als u in dezelfde stap ook rechtstreeks toegang wilt krijgen tot een SQL-database? Waarom kan een partitie niet verwijzen naar andere partities en rechtstreeks toegang krijgen tot compatibele gegevensbronnen?

Zoals u eerder hebt gezien, fungeert de firewall als gatekeeper voor alle gegevens die naar de partitie stromen, wanneer de ene partitie verwijst naar een andere partitie. Hiervoor moet het kunnen bepalen in welke gegevens zijn toegestaan. Als er gegevensbronnen worden geopend binnen de partitie en gegevens die vanuit andere partities binnenkomen, verliest het de mogelijkheid om de poortwachter te zijn, omdat de gegevens die binnenkomen, kunnen worden gelekt naar een van de intern geopende gegevensbronnen zonder dat ze hiervan op de hoogte zijn. De firewall voorkomt dus dat een partitie die toegang heeft tot andere partities, rechtstreeks toegang heeft tot gegevensbronnen.

Wat gebeurt er als een partitie probeert te verwijzen naar andere partities en ook rechtstreeks toegang probeert te krijgen tot gegevensbronnen?

Formula.Firewall: Query 'Query1' (step 'Source') references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.

Nu krijgt u hopelijk meer inzicht in het andere foutbericht dat aan het begin van dit artikel wordt vermeld.

Partities uitgebreid

Zoals u waarschijnlijk kunt raden uit de bovenstaande informatie, is hoe query's worden gepartitioneerd, ongelooflijk belangrijk. Als u enkele stappen hebt die verwijzen naar andere query's en andere stappen die toegang hebben tot gegevensbronnen, herkent u nu hopelijk dat het tekenen van de partitiegrenzen op bepaalde plaatsen firewallfouten veroorzaakt, terwijl u ze op andere plaatsen tekent, uw query prima kan worden uitgevoerd.

Hoe worden query's dan precies gepartitioneerd?

Deze sectie is waarschijnlijk het belangrijkst om te begrijpen waarom u firewallfouten ziet en hoe u deze waar mogelijk kunt oplossen.

Hier volgt een samenvatting op hoog niveau van de partitioneringslogica.

  • Eerste partitionering
    • Hiermee maakt u een partitie voor elke stap in elke query
  • Statische fase
    • Deze fase is niet afhankelijk van evaluatieresultaten. In plaats daarvan is het afhankelijk van de structuur van de query's.
      • Parameter bijsnijden
        • Trimt parameter-esque partities, dat wil gezegd, elke partitie die:
          • Verwijst niet naar andere partities
          • Bevat geen functie-aanroepen
          • Is niet cyclisch (dat wil gezegd, het verwijst niet naar zichzelf)
        • Houd er rekening mee dat het verwijderen van een partitie deze in feite omvat in alle andere partities waarnaar wordt verwezen.
        • Door parameterpartities te knippen, kunnen parameterverwijzingen die worden gebruikt in aanroepen van de gegevensbronfunctie (bijvoorbeeld Web.Contents(myUrl)) werken, in plaats van fouten 'partitie kan niet verwijzen naar gegevensbronnen en andere stappen'.
      • Groeperen (statisch)
        • Partities worden samengevoegd in volgorde van onderste afhankelijkheid. In de resulterende samengevoegde partities is het volgende gescheiden:
          • Partities in verschillende query's
          • Partities die niet verwijzen naar andere partities (en die dus toegang hebben tot een gegevensbron)
          • Partities die verwijzen naar andere partities (en die dus geen toegang hebben tot een gegevensbron)
  • Dynamische fase
    • Deze fase is afhankelijk van evaluatieresultaten, waaronder informatie over gegevensbronnen die worden geopend door verschillende partities.
    • Trimmen
      • Knipt partities die voldoen aan alle volgende vereisten:
        • Heeft geen toegang tot gegevensbronnen
        • Verwijst niet naar partities die toegang hebben tot gegevensbronnen
        • Is geen cyclische
    • Groeperen (dynamisch)
      • Nu onnodige partities zijn ingekort, probeert u bronpartities te maken die zo groot mogelijk zijn. Dit wordt gedaan door de partities samen te voegen met behulp van dezelfde regels die in de bovenstaande statische groeperingsfase worden beschreven.

Wat betekent dit allemaal?

Laten we een voorbeeld bekijken om te laten zien hoe de bovenstaande complexe logica werkt.

Hier volgt een voorbeeldscenario. Het is een vrij eenvoudige samenvoeging van een tekstbestand (Contactpersonen) met een SQL-database (Werknemers), waarbij de SQL-server een parameter (DbServer) is.

De drie query's

Hier volgt de M-code voor de drie query's die in dit voorbeeld worden gebruikt.

shared DbServer = "montegoref6" meta [IsParameterQuery=true, Type="Text", IsParameterQueryRequired=true];
shared Contacts = let

    Source = Csv.Document(File.Contents("C:\contacts.txt"),[Delimiter="   ", Columns=15, Encoding=1252, QuoteStyle=QuoteStyle.None]),

    #"Promoted Headers" = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),

    #"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"ContactID", Int64.Type}, {"NameStyle", type logical}, {"Title", type text}, {"FirstName", type text}, {"MiddleName", type text}, {"LastName", type text}, {"Suffix", type text}, {"EmailAddress", type text}, {"EmailPromotion", Int64.Type}, {"Phone", type text}, {"PasswordHash", type text}, {"PasswordSalt", type text}, {"AdditionalContactInfo", type text}, {"rowguid", type text}, {"ModifiedDate", type datetime}})

in

    #"Changed Type";
shared Employees = let

    Source = Sql.Databases(DbServer),

    AdventureWorks = Source{[Name="AdventureWorks"]}[Data],

    HumanResources_Employee = AdventureWorks{[Schema="HumanResources",Item="Employee"]}[Data],

    #"Removed Columns" = Table.RemoveColumns(HumanResources_Employee,{"HumanResources.Employee(EmployeeID)", "HumanResources.Employee(ManagerID)", "HumanResources.EmployeeAddress", "HumanResources.EmployeeDepartmentHistory", "HumanResources.EmployeePayHistory", "HumanResources.JobCandidate", "Person.Contact", "Purchasing.PurchaseOrderHeader", "Sales.SalesPerson"}),

    #"Merged Queries" = Table.NestedJoin(#"Removed Columns",{"ContactID"},Contacts,{"ContactID"},"Contacts",JoinKind.LeftOuter),

    #"Expanded Contacts" = Table.ExpandTableColumn(#"Merged Queries", "Contacts", {"EmailAddress"}, {"EmailAddress"})

in

    #"Expanded Contacts";

Hier ziet u een weergave op een hoger niveau met de afhankelijkheden.

Dialoogvenster Queryafhankelijkheden.

Laten we een partitie maken

Laten we even inzoomen en stappen in de afbeelding opnemen en beginnen met het doorlopen van de partitioneringslogica. Hier volgt een diagram van de drie query's, waarin de eerste firewallpartities groen worden weergegeven. U ziet dat elke stap in een eigen partitie begint.

Initiële firewallpartities.

Vervolgens knippen we parameterpartities. DbServer wordt dus impliciet opgenomen in de bronpartitie.

Bijgesneden firewallpartities.

Nu voeren we de statische groepering uit. Hierdoor wordt de scheiding tussen partities in afzonderlijke query's gehandhaafd (houd er rekening mee dat de laatste twee stappen van werknemers niet worden gegroepeerd met de stappen van contactpersonen) en tussen partities die verwijzen naar andere partities (zoals de laatste twee stappen van Werknemers) en de partities die niet (zoals de eerste drie stappen van Werknemers) niet worden gegroepeerd.

Post static-grouping firewall partities.

Nu gaan we de dynamische fase in. In deze fase worden de bovenstaande statische partities geëvalueerd. Partities die geen toegang hebben tot gegevensbronnen, worden ingekort. Partities worden vervolgens gegroepeerd om bronpartities te maken die zo groot mogelijk zijn. In dit voorbeeldscenario hebben alle resterende partities echter toegang tot gegevensbronnen en er is geen verdere groepering die kan worden uitgevoerd. De partities in ons voorbeeld veranderen dus niet tijdens deze fase.

Laten we doen alsof

Laten we echter eens kijken wat er zou gebeuren als de contactpersonenquery, in plaats van afkomstig uit een tekstbestand, in M is vastgelegd (mogelijk via het dialoogvenster Gegevens invoeren).

In dit geval heeft de query Contactpersonen geen toegang tot gegevensbronnen. Het zou dus worden ingekort tijdens het eerste deel van de dynamische fase.

Firewallpartitie na dynamische fase bijsnijden.

Als de partitie Contactpersonen is verwijderd, verwijzen de laatste twee stappen van Werknemers niet meer naar partities, behalve naar de partitie die de eerste drie stappen van Werknemers bevat. De twee partities worden dus gegroepeerd.

De resulterende partitie ziet er als volgt uit.

Definitieve firewallpartities.

Voorbeeld: Gegevens van de ene gegevensbron doorgeven aan een andere

Oké, genoeg abstracte uitleg. Laten we eens kijken naar een veelvoorkomend scenario waarin u waarschijnlijk een firewallfout tegenkomt en de stappen om dit op te lossen.

Stel dat u een bedrijfsnaam wilt opzoeken uit de Northwind OData-service en vervolgens de bedrijfsnaam gebruikt om een Bing-zoekopdracht uit te voeren.

Eerst maakt u een bedrijfsquery om de bedrijfsnaam op te halen.

let
    Source = OData.Feed("https://services.odata.org/V4/Northwind/Northwind.svc/", null, [Implementation="2.0"]),
    Customers_table = Source{[Name="Customers",Signature="table"]}[Data],
    CHOPS = Customers_table{[CustomerID="CHOPS"]}[CompanyName]
in
    CHOPS

Vervolgens maakt u een zoekquery die verwijst naar Bedrijf en doorgeeft aan Bing.

let
    Source = Text.FromBinary(Web.Contents("https://www.bing.com/search?q=" & Company))
in
    Source

Op dit moment ondervindt u problemen. Bij het evalueren van Search wordt een firewallfout gegenereerd.

Formula.Firewall: Query 'Search' (step 'Source') references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.

Dit komt doordat de bronstap van zoeken verwijst naar een gegevensbron (bing.com) en ook verwijst naar een andere query/partitie (bedrijf). Het schendt de bovenstaande regel ('een partitie heeft mogelijk toegang tot compatibele gegevensbronnen of verwijst naar andere partities, maar niet naar beide').

Wat u moet doen? Een optie is om de firewall helemaal uit te schakelen (via de privacyoptie Negeren van de privacyniveaus en mogelijk betere prestaties). Maar wat als u de firewall ingeschakeld wilt laten?

Als u de fout wilt oplossen zonder de firewall uit te schakelen, kunt u Bedrijf en Zoeken combineren in één query, zoals:

let
    Source = OData.Feed("https://services.odata.org/V4/Northwind/Northwind.svc/", null, [Implementation="2.0"]),
    Customers_table = Source{[Name="Customers",Signature="table"]}[Data],
    CHOPS = Customers_table{[CustomerID="CHOPS"]}[CompanyName],
    Search = Text.FromBinary(Web.Contents("https://www.bing.com/search?q=" & CHOPS))
in
    Search

Alles gebeurt nu binnen één partitie. Ervan uitgaande dat de privacyniveaus voor de twee gegevensbronnen compatibel zijn, zou de firewall nu blij moeten zijn en krijgt u geen foutmelding meer.

Dat is een wikkeling

Hoewel er nog veel meer over dit onderwerp kan worden gezegd, is dit inleidende artikel al lang genoeg. Hopelijk krijgt u een beter inzicht in de firewall en kunt u firewallfouten begrijpen en oplossen wanneer u deze in de toekomst tegenkomt.