Zum Hauptinhalt springen

Oder: Wie du mit drei Variablen, ein paar split()-Aufrufen und einer großen Condition 90% der Falscheingaben draußen hältst.


Das Problem

Du baust einen Flow, der eine E-Mail-Adresse verarbeitet — aus einem Formular, einer manuellen Eingabe, einem Webhook. Die Frage ist nicht ob jemand Unsinn eingibt, sondern wann.

Typische Kandidaten:

  • +49 173 739 09 86 — eine Telefonnummer, kein Tippfehler
  • user1@domain.com; user2@domain.com — zwei Adressen, ein Feld
  • user.@domain.com — syntaktisch kaputt, visuell fast unsichtbar
  • user@domain.com, otheruser — Komma-separierte Eingabe, nur ein @

Power Automate bietet dafür keine native Validierung. Und ja, es gibt externe Services, die das übernehmen. Die kosten Geld, schicken Daten nach draußen, und für einen Newsletter- oder Kontaktformular-Use-Case ist das schlicht überdimensioniert.


Besser — Vermeide das Problem wo möglich

Bevor wir zur Lösung kommen: Das Beste an einer Validierung ist, sie gar nicht zu brauchen.

Wenn du Kontrolle über die Eingabequelle hast, nutze Systeme, die bereits validieren:

Microsoft Forms validiert E-Mail-Felder nativ. Ein Nutzer kann kein ungültiges Format einreichen. Der Flow bekommt saubere Daten — fertig.

Power Apps bringt die IsMatch()-Funktion mit einem eingebauten EmailAddress-Pattern. Validierung auf der Canvas, nicht im Flow.

User-Lookup via Graph / M365 — wenn du im M365-Kontext arbeitest und den Nutzer ohnehin kennst, hole die E-Mail direkt aus Entra ID. Die ist per Definition valide.

Die Regel: Validiere erst im Flow, wenn du die Eingabequelle nicht kontrollierst.


Der Ansatz — Genau ein @ als Ankerpunkt

Der Schlüssel ist denkbar simpel: Eine valide E-Mail-Adresse hat genau ein @. Das allein filtert schon einen Großteil der Falscheingaben heraus:

  • Telefonnummern → kein @
  • Mehrere Adressen getrennt durch ;, , oder Leerzeichen → mehrere @

split(email, '@') ergibt bei einer validen Adresse exakt ein Array mit zwei Elementen. Mehr oder weniger → weg damit.

Wichtige Ausnahme: user@domain.com, otheruser hat nur ein @ — der @-Count-Check allein reicht hier nicht. Deswegen prüfen wir zusätzlich auf Komma und Semikolon im Localpart. Dazu gleich mehr.


Die Implementierung — Schritt für Schritt

Schritt 1 — Zwei Hilfsvariablen anlegen

Vor der eigentlichen Condition initialisieren wir zwei String-Variablen. Das hält die Condition lesbar.

varEmailLocal — der Teil vor dem @:

first(split(trim(variables('email')), '@'))

varEmailDomain — der Teil nach dem @:

last(split(trim(variables('email')), '@'))

trim() entfernt führende und nachfolgende Leerzeichen. Kein optionaler Schritt — Nutzer kopieren gerne Whitespace mit rein.


Schritt 2 — Die Condition

and(
  equals(length(split(trim(variables('email')), '@')), 2),
  greater(length(variables('varEmailLocal')), 0),
  not(startsWith(variables('varEmailLocal'), '.')),
  not(endsWith(variables('varEmailLocal'), '.')),
  not(contains(variables('varEmailLocal'), '..')),
  not(contains(variables('varEmailLocal'), ',')),
  not(contains(variables('varEmailLocal'), ';')),
  not(contains(variables('varEmailLocal'), ':')),
  not(contains(variables('varEmailLocal'), '(')),
  not(contains(variables('varEmailLocal'), ')')),
  not(contains(variables('varEmailLocal'), '[')),
  not(contains(variables('varEmailLocal'), ']')),
  not(startsWith(variables('varEmailDomain'), '.')),
  contains(variables('varEmailDomain'), '.'),
  greater(length(last(split(variables('varEmailDomain'), '.'))), 1),
  not(contains(trim(variables('email')), ' '))
)

If false → Terminate (Cancelled)

Kein Retry, kein Fehler-Handling. Der Flow bricht sauber ab.


Was wird geprüft?

BedingungFängt
length(split(@)) == 2Kein @, oder mehrere @ (Multi-Email, beliebiger Delimiter)
length(local) > 0Leerer Localpart: @domain.com
not(startsWith(local, '.')).user@domain.com
not(endsWith(local, '.'))user.@domain.com
not(contains(local, '..'))user..name@domain.com
not(contains(local, ','))user@domain.com, otheruser
not(contains(local, ';'))Semikolon-separierte Mehrfacheingabe
not(contains(local, ':'))Syntaktisch ungültig im Localpart
not(contains(local, '(')) / ')'Klammern im Localpart
not(contains(local, '[')) / ']'Eckige Klammern im Localpart
not(startsWith(domain, '.'))user@.domain.com
contains(domain, '.')user@domain — keine TLD
length(TLD) > 1user@domain. — leere TLD
not(contains(' '))user @domain.com — Leerzeichen

Flow-Struktur

[Initialize varEmailLocal]
     ↓
[Initialize varEmailDomain]
     ↓
[Condition: Email valide?]
     ├── Yes → weiter mit Verarbeitungslogik
     └── No  → Terminate (Cancelled)

Testergebnisse

EingabeErgebnisGrund
user@domain.com✅ valideAlle Checks bestanden
a@domain.de✅ valideKurzer Localpart OK
user+tag@gmail.com✅ valide+ ist RFC dot-atom-konform
+49 123 456789❌ verworfenKein @
user1@a.com; user2@b.com❌ verworfenZwei @
user1@a.com, user2@b.com❌ verworfenZwei @
user@domain.com, otheruser❌ verworfenKomma im Localpart
.@domain.com❌ verworfenLocalpart startet mit .
user.@domain.com❌ verworfenLocalpart endet mit .
user..name@domain.com❌ verworfenDoppelpunkt im Localpart
.user@domain.com❌ verworfenLocalpart startet mit .
user@.domain.com❌ verworfenDomain startet mit .
user@domain❌ verworfenKein Punkt in Domain
@domain.com❌ verworfenLeerer Localpart
user @domain.com❌ verworfenLeerzeichen

Was diese Lösung nicht kann

Ehrlichkeit ist hier wichtig. Folgendes wird nicht geprüft und kommt durch:

  • Ungültige Sonderzeichen im Localpart jenseits der geprüften Zeichen (z.B. ä, ü, )
  • Nicht-existente Domains: user@thisdoesnotexist123.com
  • Falsch formatierte Domain-Labels: user@domain-.com

Zur RFC-Einordnung: Unsere Lösung deckt den sogenannten dot-atom-Localpart vollständig ab — das ist das Format, das >99% aller realen E-Mail-Adressen verwenden. RFC 5322 erlaubt darüber hinaus sogenannte quoted-string-Localparts wie "user name"@domain.com. Diese verwerfen wir (wegen des Leerzeichen-Checks) — was für praktisch jeden Power-Platform-Use-Case korrekt ist, weil kein gängiger E-Mail-Provider solche Adressen ausgibt.

Eine vollständige Zeichensatzprüfung wäre technisch über split(varEmailLocal, '') und Apply-to-Each gegen ein Whitelist-Array möglich. Für diesen Use-Case ist das jedoch over-engineered: 6–20 Loop-Iterationen für eine Eingabevalidierung, die eigentlich eine einzeilige Condition sein sollte.

Für die meisten Flows — Newsletter, Kontaktformulare, Onboarding-Prozesse — ist diese Lösung ausreichend. Wer eine Adresse eingibt, die alle Checks besteht, aber nicht existiert, bekommt einen Bounce. Das ist kein Datenverlust, kein Sicherheitsproblem.


Wann diese Lösung passt

Diese Implementierung ist die richtige Wahl, wenn:

  • Eingaben aus nicht-validierten Quellen kommen (Webhooks, manuelle Felder, Legacy-Systeme)
  • du keine externen Dienste integrieren willst
  • eine pragmatische, nachvollziehbare Lösung wichtiger ist als RFC-Vollständigkeit

Wenn du wissen willst, wo eure Power Platform insgesamt governance-technisch steht — nicht nur bei Detailfragen wie dieser — macht den Power Platform Governance Quick Check. Zehn Fragen, ehrliche Auswertung, klares Ergebnis.

Governance in Ihrer Umgebung analysieren?

Im kostenlosen Quick Check zeigen wir Ihnen in 30 Minuten Ihre größten Risiken.