Dynamics 365 Massendaten löschen und anonymisieren

Eine gängige Anforderung ist es, Daten im Sinne der DSGVO sauber zu halten. Das kann beispielsweise der Fall sein, wenn ein On-Premise Dynamics 365 Entwicklungssystem extern gehostet ist und eine alte bestehende Datenbasis rechtskonform aufbereitet werden soll. Dieser Artikel zeigt, wie diese Anforderung in Dynamics 365 umgesetzt werden kann und auf welche technischen Gefahren der Entwickler gewappnet sein sollte.

Was sind anonymisierte Daten?

Laut DSGVO beziehen sich “Anonymisierte Daten” auf alle Daten und Informationen, die auf eine natürliche Person zugeordnet werden können. Der Begriff “Anonymisierung” beschreibt nun den Vorgang, die Daten so aufzubereiten, dass eine Person nicht mehr dadurch identifiziert werden kann. Für die Anonymisierung können generalisierte Pseudodaten oder Null Werte verwendet werden.

Vorgehensweise in Dynamics 365

Zunächst muss geprüft werden, welche Daten gelöscht werden und welche im Anschluss anonymisiert werden können. Dieses Feststellen kann in Absprache mit den Business Analysten erfolgen, wichtig ist aber, dass die Daten kontrolliert werden. Möglichkeiten dazu sind das Durchklicken der CRM Anwendung, um löschbare Daten zu verifizieren und Abhängigkeiten zu finden. Ferner eignet sich die Verwendung von Tools wie der XRM Toolbox zum Abfragen der Daten mittels FetchXML.

Speziell bei Massendaten, muss vor dem Löschen oder Anonymisieren geprüft werden, ob auf den betroffenen Entitäten Workflows, Plugins oder Business Regeln hängen. Diese Prozesse sollten jedenfalls deaktiviert werden, da es sonst zu zeitintensiven Hintergrundabläufen kommen kann, die das Löschen oder Anonymisieren verzögern. Außerdem sollte der Überwachungsverlauf deaktiviert werden.

Der Überwachungsverlauf kann global unter Einstellungen => Überwachung deaktiviert werden.

Die Prozesse können global Einstellungen => Prozesse deaktiviert werden.

Business Regeln könne auf der jeweiligen Entität deaktiviert werden, z.B. über Einstellungen => Lösungen => jeweilige Entität.

Plugins können mittels der XRM Toolbox deaktiviert werden.

Löschen der Daten

Wenn möglich, sollten Daten über den CRM Client gelöscht werden. Dies kann über die Listenansicht der jeweiligen Entität erfolgen oder über den Menüpunkt Einstellungen => Datenverwaltung => Massenlöschung von Datensätzen. Unter diesem Menüpunkt, sollte auch der Löschvorgang überwacht werden.

Ein gängiges Problem beim Löschvorgang ist, dass der Vorgang hängen bleibt und z.B. längere Zeit im gleichen Status bleibt oder Fehler verursacht. Eine Lösung dafür ist es, den Asynchronen CRM Service neu zu starten. Dafür in Windows unter Services den “Microsoft Dynamics Asynchronous Processing Service” neu starten – nähere Infos hier von Microsoft. Wenn das nichts hilft, könnte auch die AsyncOperationBase Tabelle überfüllt sein.

Anonymisieren der Daten

Massendaten können mithilfe einer aktuellen Version der XRM Toolbox und dem Bulk Data Update Werkzeug geändert werden. Dafür muss zunächst ein FetchXML erstellt werden, mit dem die benötigten Daten selektiert werden können. Der Bulk Data Updater ist leider nicht die performanteste Lösung und gibt kaum Auskunft über Fehler, die beim ändern der Daten auftreten können. Das Werkzeug erfüllt aber seinen Job, speziell bei kleineren Tabellen.

Die effizienteste und effektivste Lösung ist allerdings ein Update mittels einer C# Konsolenanwendung. Nähere Infos gibt es hier. Der folgende Code soll eine richtungsweisende Idee geben:

OrganizationServiceProxy orgProxy = GetProxy();

var fetchXML = $@"<fetch>
                     // ...
                  </fetch>";

var multipleRequest = new ExecuteMultipleRequest()
{
    Settings = new ExecuteMultipleSettings
    {
        ContinueOnError = true,
        ReturnResponses = true
    },

    Requests = new OrganizationRequestCollection()
};

List<Entity> totalRecords = GetFetchXML(orgProxy, fetchXML);

var lstlstEntity = CreateBatches(totalRecords, 500);

foreach (var lstEntity in lstlstEntity)
{
    multipleRequest.Requests.Clear();
    foreach (var entity in lstEntity)
    {
        UpdateRequest request = new UpdateRequest { Target = entity };

        entity.Attributes["..."] = null;

        multipleRequest.Requests.Add(request);
    }

    ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)orgProxy.Execute(multipleRequest);

    if (multipleResponse.IsFaulted)
    {
        foreach (var item in multipleResponse.Responses)
        {
            if (item.Fault is null)
                continue;

            throw new Exception($"Fault: {item.Fault}");
            
        }
    }
}