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 Tippfehleruser1@domain.com; user2@domain.com— zwei Adressen, ein Felduser.@domain.com— syntaktisch kaputt, visuell fast unsichtbaruser@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?
| Bedingung | Fängt |
|---|---|
length(split(@)) == 2 | Kein @, oder mehrere @ (Multi-Email, beliebiger Delimiter) |
length(local) > 0 | Leerer 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) > 1 | user@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
| Eingabe | Ergebnis | Grund |
|---|---|---|
user@domain.com | ✅ valide | Alle Checks bestanden |
a@domain.de | ✅ valide | Kurzer Localpart OK |
user+tag@gmail.com | ✅ valide | + ist RFC dot-atom-konform |
+49 123 456789 | ❌ verworfen | Kein @ |
user1@a.com; user2@b.com | ❌ verworfen | Zwei @ |
user1@a.com, user2@b.com | ❌ verworfen | Zwei @ |
user@domain.com, otheruser | ❌ verworfen | Komma im Localpart |
.@domain.com | ❌ verworfen | Localpart startet mit . |
user.@domain.com | ❌ verworfen | Localpart endet mit . |
user..name@domain.com | ❌ verworfen | Doppelpunkt im Localpart |
.user@domain.com | ❌ verworfen | Localpart startet mit . |
user@.domain.com | ❌ verworfen | Domain startet mit . |
user@domain | ❌ verworfen | Kein Punkt in Domain |
@domain.com | ❌ verworfen | Leerer Localpart |
user @domain.com | ❌ verworfen | Leerzeichen |
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.
