Office 365 Mail-Integration auf BTP: E-Mails über Microsoft Graph API versenden

Kategorie
Integration
Veröffentlicht
Autor
Johannes

Die Integration von Office 365 E-Mail in SAP BTP ABAP Environment ermöglicht den Versand von E-Mails direkt aus ABAP-Anwendungen über die Microsoft Graph API. In diesem Artikel zeigen wir die komplette Einrichtung von der Azure-Konfiguration bis zum funktionierenden ABAP-Code.

Architektur-Übersicht

┌──────────────────────────────────────────────────────────────────────────┐
│ Office 365 Mail-Integration Architektur │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ BTP ABAP │ │ Microsoft │ │ Office 365 │ │
│ │ Environment │────>│ Entra ID │────>│ Mail Server │ │
│ │ │ │ (Azure AD) │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Communication │ │ OAuth 2.0 │ │
│ │ Arrangement │ │ Access Token │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ Protokoll: HTTPS + OAuth 2.0 Client Credentials Flow │
│ API: Microsoft Graph API (https://graph.microsoft.com) │
│ │
└──────────────────────────────────────────────────────────────────────────┘

Voraussetzungen

KomponenteAnforderung
Azure ADTenant mit Global Admin oder Application Admin Rechten
Office 365Lizenz mit Exchange Online (Business Basic oder höher)
BTPABAP Environment mit ADT-Zugang
MailboxShared Mailbox oder Service Account für den Versand

1. Azure AD App-Registrierung

Zuerst muss eine App-Registrierung in Microsoft Entra ID (ehemals Azure AD) angelegt werden.

Schritt 1: App registrieren

1. Azure Portal öffnen (portal.azure.com)
2. Microsoft Entra ID → App registrations → New registration
Name: SAP-BTP-Mail-Integration
Supported account types: Accounts in this organizational directory only
Redirect URI: (leer lassen)
3. "Register" klicken

Schritt 2: Client Secret erstellen

1. App öffnen → Certificates & secrets
2. Client secrets → New client secret
Description: SAP BTP ABAP Environment
Expires: 24 months (empfohlen)
3. "Add" klicken
4. Secret Value SOFORT kopieren und sicher speichern!
(Wird nur einmal angezeigt)

Schritt 3: API-Berechtigungen konfigurieren

Für den Mail-Versand über Microsoft Graph benötigen wir Application Permissions:

1. API permissions → Add a permission
2. Microsoft Graph → Application permissions
3. Folgende Berechtigungen hinzufügen:
☑ Mail.Send (E-Mails senden)
☑ Mail.ReadWrite (optional: Entwürfe erstellen)
☑ User.Read.All (optional: Benutzer-Info abrufen)
4. "Add permissions" klicken
5. "Grant admin consent for [Tenant]" klicken

Wichtig: Application Permissions erfordern Admin Consent und erlauben den Versand als jeder Benutzer im Tenant.

Schritt 4: Wichtige IDs notieren

Nach der Registrierung benötigst du folgende Werte:

WertWo zu finden
Application (Client) IDApp Overview Seite
Directory (Tenant) IDApp Overview Seite
Client SecretCertificates & secrets (bei Erstellung kopiert)

Beispiel:

Application (Client) ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Directory (Tenant) ID: 12345678-90ab-cdef-1234-567890abcdef
Client Secret: Xyz123~AbCdEfGhIjKlMnOpQrStUvWx.YZ

2. Communication Scenario in ABAP erstellen

Erstelle ein Custom Communication Scenario für die Microsoft Graph Integration.

Scenario-Definition

In ADT: Rechtsklick auf Package → New → Other ABAP Repository Object → Communication Scenario

<?xml version="1.0" encoding="utf-8"?>
<communicationScenario id="Z_MS_GRAPH_MAIL"
scenarioType="customer">
<description>Microsoft Graph API - Mail Integration</description>
<outboundServices>
<service id="Z_MS_GRAPH_API">
<description>Microsoft Graph REST API</description>
<serviceType>http</serviceType>
</service>
</outboundServices>
<supportedAuthenticationMethods>
<method>oauth2_client_credentials</method>
</supportedAuthenticationMethods>
</communicationScenario>

Nach dem Aktivieren ist das Scenario für die Administrator-Konfiguration verfügbar.

3. Communication Arrangement einrichten

Die Einrichtung erfolgt in den Fiori Apps des ABAP Environment.

Communication System anlegen

Fiori App: "Maintain Communication Systems"
System ID: MS_GRAPH_PROD
System Name: Microsoft Graph API
General:
├── Host Name: graph.microsoft.com
├── Port: 443
└── HTTPS: ☑
OAuth 2.0 Settings:
├── Token Service URL Type: Dedicated
├── Token Service URL: https://login.microsoftonline.com/{Tenant-ID}/oauth2/v2.0/token
└── (Ersetze {Tenant-ID} mit deiner Directory/Tenant ID)

Communication Arrangement anlegen

Fiori App: "Maintain Communication Arrangements"
Arrangement ID: Z_MS_GRAPH_MAIL_PROD
Scenario: Z_MS_GRAPH_MAIL
Communication System: MS_GRAPH_PROD
Outbound Services:
├── Z_MS_GRAPH_API: Active
└── Path: /v1.0
Authentication Method: OAuth 2.0 Client Credentials
├── Client ID: [Application (Client) ID aus Azure]
├── Client Secret: [Secret aus Azure]
└── Token Service Scope: https://graph.microsoft.com/.default

Wichtig: Der Scope https://graph.microsoft.com/.default ist erforderlich für Application Permissions.

4. ABAP-Implementierung

Jetzt implementieren wir die Klasse für den E-Mail-Versand über Microsoft Graph.

E-Mail-Versand Klasse

CLASS zcl_ms_graph_mail DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES:
BEGIN OF ty_email_address,
address TYPE string,
name TYPE string,
END OF ty_email_address,
tt_email_addresses TYPE STANDARD TABLE OF ty_email_address WITH EMPTY KEY.
TYPES:
BEGIN OF ty_attachment,
name TYPE string,
content_type TYPE string,
content TYPE xstring,
END OF ty_attachment,
tt_attachments TYPE STANDARD TABLE OF ty_attachment WITH EMPTY KEY.
METHODS send_mail
IMPORTING
iv_sender_mail TYPE string
iv_subject TYPE string
iv_body TYPE string
iv_is_html TYPE abap_bool DEFAULT abap_false
it_to TYPE tt_email_addresses
it_cc TYPE tt_email_addresses OPTIONAL
it_bcc TYPE tt_email_addresses OPTIONAL
it_attachments TYPE tt_attachments OPTIONAL
RAISING
cx_http_dest_provider_error
cx_web_http_client_error.
PRIVATE SECTION.
CONSTANTS:
c_scenario TYPE if_com_scenario_factory=>ty_cscn_id VALUE 'Z_MS_GRAPH_MAIL',
c_service_id TYPE if_com_scenario_factory=>ty_cscn_outb_srv_id VALUE 'Z_MS_GRAPH_API'.
METHODS get_destination
RETURNING VALUE(ro_destination) TYPE REF TO if_http_destination
RAISING cx_http_dest_provider_error.
METHODS build_mail_json
IMPORTING
iv_subject TYPE string
iv_body TYPE string
iv_is_html TYPE abap_bool
it_to TYPE tt_email_addresses
it_cc TYPE tt_email_addresses
it_bcc TYPE tt_email_addresses
it_attachments TYPE tt_attachments
RETURNING VALUE(rv_json) TYPE string.
METHODS build_recipients_json
IMPORTING it_addresses TYPE tt_email_addresses
RETURNING VALUE(rv_json) TYPE string.
METHODS build_attachments_json
IMPORTING it_attachments TYPE tt_attachments
RETURNING VALUE(rv_json) TYPE string.
ENDCLASS.
CLASS zcl_ms_graph_mail IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Test-Mail versenden
TRY.
send_mail(
iv_sender_mail = '[email protected]'
iv_subject = 'Test aus SAP BTP'
iv_body = |<html><body>| &&
|<h1>Willkommen</h1>| &&
|<p>Dies ist eine Test-E-Mail aus dem SAP BTP ABAP Environment.</p>| &&
|<p>Gesendet: { cl_abap_context_info=>get_system_date( ) }</p>| &&
|</body></html>|
iv_is_html = abap_true
it_to = VALUE #( ( address = '[email protected]'
name = 'Max Mustermann' ) ) ).
out->write( 'E-Mail erfolgreich gesendet!' ).
CATCH cx_http_dest_provider_error INTO DATA(lx_dest).
out->write( |Destination-Fehler: { lx_dest->get_text( ) }| ).
CATCH cx_web_http_client_error INTO DATA(lx_http).
out->write( |HTTP-Fehler: { lx_http->get_text( ) }| ).
ENDTRY.
ENDMETHOD.
METHOD get_destination.
ro_destination = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = c_scenario
service_id = c_service_id ).
ENDMETHOD.
METHOD send_mail.
" 1. Destination abrufen
DATA(lo_destination) = get_destination( ).
" 2. HTTP-Client erstellen
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination(
i_destination = lo_destination ).
TRY.
" 3. Request konfigurieren
DATA(lo_request) = lo_client->get_http_request( ).
" Graph API Endpoint: /users/{user-id}/sendMail
lo_request->set_uri_path( |/users/{ iv_sender_mail }/sendMail| ).
" 4. JSON-Body erstellen
DATA(lv_json) = build_mail_json(
iv_subject = iv_subject
iv_body = iv_body
iv_is_html = iv_is_html
it_to = it_to
it_cc = it_cc
it_bcc = it_bcc
it_attachments = it_attachments ).
lo_request->set_text( lv_json ).
lo_request->set_header_field(
i_name = 'Content-Type'
i_value = 'application/json' ).
" 5. POST ausführen
DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
DATA(lv_status) = lo_response->get_status( )-code.
" 202 Accepted = Erfolgreich
IF lv_status <> 202.
DATA(lv_error) = lo_response->get_text( ).
RAISE EXCEPTION TYPE cx_web_http_client_error
EXPORTING
text = |Mail-Versand fehlgeschlagen: { lv_status } - { lv_error }|.
ENDIF.
CLEANUP.
lo_client->close( ).
ENDTRY.
ENDMETHOD.
METHOD build_mail_json.
" Microsoft Graph Mail JSON-Struktur erstellen
DATA(lv_content_type) = COND string( WHEN iv_is_html = abap_true
THEN 'HTML'
ELSE 'Text' ).
rv_json = |\{| &&
| "message": \{| &&
| "subject": "{ escape( val = iv_subject format = cl_abap_format=>e_json_string ) }",| &&
| "body": \{| &&
| "contentType": "{ lv_content_type }",| &&
| "content": "{ escape( val = iv_body format = cl_abap_format=>e_json_string ) }"| &&
| \},| &&
| "toRecipients": { build_recipients_json( it_to ) }|.
" CC hinzufügen (falls vorhanden)
IF it_cc IS NOT INITIAL.
rv_json = rv_json && |,| &&
| "ccRecipients": { build_recipients_json( it_cc ) }|.
ENDIF.
" BCC hinzufügen (falls vorhanden)
IF it_bcc IS NOT INITIAL.
rv_json = rv_json && |,| &&
| "bccRecipients": { build_recipients_json( it_bcc ) }|.
ENDIF.
" Anhänge hinzufügen (falls vorhanden)
IF it_attachments IS NOT INITIAL.
rv_json = rv_json && |,| &&
| "attachments": { build_attachments_json( it_attachments ) }|.
ENDIF.
rv_json = rv_json &&
| \},| &&
| "saveToSentItems": "true"| &&
|\}|.
ENDMETHOD.
METHOD build_recipients_json.
DATA: lt_json TYPE TABLE OF string.
LOOP AT it_addresses INTO DATA(ls_address).
DATA(lv_entry) = |\{| &&
| "emailAddress": \{| &&
| "address": "{ ls_address-address }"|.
IF ls_address-name IS NOT INITIAL.
lv_entry = lv_entry && |,| &&
| "name": "{ escape( val = ls_address-name format = cl_abap_format=>e_json_string ) }"|.
ENDIF.
lv_entry = lv_entry && | \}| &&
|\}|.
APPEND lv_entry TO lt_json.
ENDLOOP.
rv_json = |[{ concat_lines_of( table = lt_json sep = `, ` ) }]|.
ENDMETHOD.
METHOD build_attachments_json.
DATA: lt_json TYPE TABLE OF string.
LOOP AT it_attachments INTO DATA(ls_attachment).
" Base64-Encoding des Inhalts
DATA(lv_base64) = cl_web_http_utility=>encode_x_base64( ls_attachment-content ).
DATA(lv_entry) = |\{| &&
| "@odata.type": "#microsoft.graph.fileAttachment",| &&
| "name": "{ escape( val = ls_attachment-name format = cl_abap_format=>e_json_string ) }",| &&
| "contentType": "{ ls_attachment-content_type }",| &&
| "contentBytes": "{ lv_base64 }"| &&
|\}|.
APPEND lv_entry TO lt_json.
ENDLOOP.
rv_json = |[{ concat_lines_of( table = lt_json sep = `, ` ) }]|.
ENDMETHOD.
ENDCLASS.

5. Praktische Beispiele

Einfache Text-E-Mail

DATA(lo_mail) = NEW zcl_ms_graph_mail( ).
lo_mail->send_mail(
iv_sender_mail = '[email protected]'
iv_subject = 'Bestellung bestätigt'
iv_body = |Sehr geehrter Kunde,\n\n| &&
|Ihre Bestellung wurde erfolgreich erfasst.\n\n| &&
|Mit freundlichen Grüßen\n| &&
|Ihr SAP-System|
it_to = VALUE #( ( address = '[email protected]'
name = 'Max Mustermann' ) ) ).

HTML-E-Mail mit Formatierung

DATA(lo_mail) = NEW zcl_ms_graph_mail( ).
DATA(lv_html) = |<html>| &&
|<head>| &&
| <style>| &&
| body \{ font-family: Arial, sans-serif; \}| &&
| h1 \{ color: #0070c0; \}| &&
| .highlight \{ background-color: #fff3cd; padding: 10px; \}| &&
| </style>| &&
|</head>| &&
|<body>| &&
| <h1>Buchungsbestätigung</h1>| &&
| <p>Sehr geehrter Herr Mustermann,</p>| &&
| <p>Ihre Flugbuchung wurde erfolgreich angelegt:</p>| &&
| <div class="highlight">| &&
| <strong>Buchungsnummer:</strong> FB-2026-001234<br>| &&
| <strong>Flug:</strong> LH 100<br>| &&
| <strong>Route:</strong> Frankfurt → New York<br>| &&
| <strong>Datum:</strong> 15.03.2026| &&
| </div>| &&
| <p>Mit freundlichen Grüßen,<br>Ihr Reisebüro</p>| &&
|</body>| &&
|</html>|.
lo_mail->send_mail(
iv_sender_mail = '[email protected]'
iv_subject = 'Ihre Buchungsbestätigung FB-2026-001234'
iv_body = lv_html
iv_is_html = abap_true
it_to = VALUE #( ( address = '[email protected]'
name = 'Max Mustermann' ) )
it_cc = VALUE #( ( address = '[email protected]' ) ) ).

E-Mail mit Anhängen

DATA(lo_mail) = NEW zcl_ms_graph_mail( ).
" CSV-Daten als Anhang
DATA(lv_csv) = |Buchungsnummer;Kunde;Betrag\n| &&
|FB-001;Müller;1250.00\n| &&
|FB-002;Schmidt;890.50\n| &&
|FB-003;Weber;2100.00|.
" String zu XString konvertieren
DATA(lv_csv_xstring) = cl_abap_codepage=>convert_to(
source = lv_csv
codepage = 'UTF-8' ).
" PDF-Inhalt (z.B. aus RAP Action oder Adobe Forms)
DATA: lv_pdf_xstring TYPE xstring.
" ... PDF generieren ...
lo_mail->send_mail(
iv_sender_mail = '[email protected]'
iv_subject = 'Monatlicher Buchungsbericht'
iv_body = |<html><body>| &&
|<p>Anbei finden Sie den Buchungsbericht für Februar 2026.</p>| &&
|</body></html>|
iv_is_html = abap_true
it_to = VALUE #( ( address = '[email protected]'
name = 'Geschäftsleitung' ) )
it_attachments = VALUE #(
( name = 'buchungen_2026-02.csv'
content_type = 'text/csv'
content = lv_csv_xstring )
( name = 'bericht_2026-02.pdf'
content_type = 'application/pdf'
content = lv_pdf_xstring ) ) ).

E-Mail aus RAP Action versenden

METHOD send_booking_confirmation.
" RAP Action: Buchungsbestätigung per E-Mail senden
READ ENTITIES OF zi_flightbook IN LOCAL MODE
ENTITY FlightBook
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_bookings).
LOOP AT lt_bookings INTO DATA(ls_booking).
" E-Mail-Adresse des Kunden ermitteln
SELECT SINGLE email FROM zcustomer
WHERE customer_id = @ls_booking-CustomerId
INTO @DATA(lv_customer_email).
IF sy-subrc = 0 AND lv_customer_email IS NOT INITIAL.
TRY.
DATA(lo_mail) = NEW zcl_ms_graph_mail( ).
DATA(lv_body) = |<html><body>| &&
|<h2>Buchungsbestätigung</h2>| &&
|<p>Ihre Buchung wurde bestätigt:</p>| &&
|<ul>| &&
| <li><strong>Buchungsnr.:</strong> { ls_booking-BookingId }</li>| &&
| <li><strong>Flug:</strong> { ls_booking-FlightNumber }</li>| &&
| <li><strong>Datum:</strong> { ls_booking-FlightDate }</li>| &&
| <li><strong>Status:</strong> { ls_booking-Status }</li>| &&
|</ul>| &&
|</body></html>|.
lo_mail->send_mail(
iv_sender_mail = '[email protected]'
iv_subject = |Buchungsbestätigung { ls_booking-BookingId }|
iv_body = lv_body
iv_is_html = abap_true
it_to = VALUE #( ( address = lv_customer_email ) ) ).
" Erfolgsmeldung
APPEND VALUE #(
%tky = ls_booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |E-Mail an { lv_customer_email } gesendet| )
) TO reported-flightbook.
CATCH cx_root INTO DATA(lx_error).
" Fehlermeldung
APPEND VALUE #(
%tky = ls_booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Mail-Fehler: { lx_error->get_text( ) }| )
) TO reported-flightbook.
ENDTRY.
ENDIF.
ENDLOOP.
ENDMETHOD.

6. Fehlerbehandlung

Erweiterte Klasse mit Fehlerbehandlung

CLASS zcl_ms_graph_mail_ext DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_mail_result,
success TYPE abap_bool,
message TYPE string,
error_code TYPE string,
END OF ty_mail_result.
METHODS send_mail_safe
IMPORTING
iv_sender_mail TYPE string
iv_subject TYPE string
iv_body TYPE string
iv_is_html TYPE abap_bool DEFAULT abap_false
it_to TYPE zcl_ms_graph_mail=>tt_email_addresses
RETURNING VALUE(rs_result) TYPE ty_mail_result.
PRIVATE SECTION.
METHODS parse_graph_error
IMPORTING iv_json TYPE string
RETURNING VALUE(rv_error) TYPE string.
ENDCLASS.
CLASS zcl_ms_graph_mail_ext IMPLEMENTATION.
METHOD send_mail_safe.
TRY.
DATA(lo_mail) = NEW zcl_ms_graph_mail( ).
lo_mail->send_mail(
iv_sender_mail = iv_sender_mail
iv_subject = iv_subject
iv_body = iv_body
iv_is_html = iv_is_html
it_to = it_to ).
rs_result = VALUE #(
success = abap_true
message = 'E-Mail erfolgreich gesendet' ).
CATCH cx_http_dest_provider_error INTO DATA(lx_dest).
rs_result = VALUE #(
success = abap_false
message = lx_dest->get_text( )
error_code = 'DESTINATION_ERROR' ).
CATCH cx_web_http_client_error INTO DATA(lx_http).
rs_result = VALUE #(
success = abap_false
message = lx_http->get_text( )
error_code = 'HTTP_ERROR' ).
ENDTRY.
ENDMETHOD.
METHOD parse_graph_error.
" Microsoft Graph Fehler-JSON parsen
" Beispiel: {"error":{"code":"InvalidAuthenticationToken","message":"..."}}
TRY.
DATA: BEGIN OF ls_error,
BEGIN OF error,
code TYPE string,
message TYPE string,
END OF error,
END OF ls_error.
/ui2/cl_json=>deserialize(
EXPORTING json = iv_json
CHANGING data = ls_error ).
rv_error = |{ ls_error-error-code }: { ls_error-error-message }|.
CATCH cx_root.
rv_error = iv_json.
ENDTRY.
ENDMETHOD.
ENDCLASS.

Häufige Fehler und Lösungen

FehlercodeBeschreibungLösung
401 UnauthorizedToken ungültig oder abgelaufenClient Secret erneuern, Tenant-ID prüfen
403 ForbiddenKeine BerechtigungAdmin Consent erteilen, Mail.Send Permission prüfen
404 Not FoundSender-Mailbox nicht gefundenE-Mail-Adresse des Senders überprüfen
400 Bad RequestUngültiges JSON oder fehlende FelderJSON-Struktur validieren
429 Too Many RequestsRate LimitingRetry mit Exponential Backoff implementieren

Rate Limiting beachten

Microsoft Graph hat Rate Limits. Für Mail-Versand:

Limit: 10.000 Anfragen pro 10 Minuten pro App
10.000 E-Mails pro Tag pro Mailbox
Best Practice:
- Batch-Versand vermeiden
- Bei 429: Retry-After Header beachten
- Application Logging für Monitoring

7. Shared Mailbox vs. Service Account

Für den produktiven Einsatz empfiehlt sich eine Shared Mailbox:

AspektShared MailboxService Account
LizenzKeine eigene Lizenz nötigBenötigt Exchange-Lizenz
KostenKostenfreiMonatliche Kosten
VerwaltungÜber Exchange Admin CenterWie normaler Benutzer
EmpfehlungFür automatische MailsNur wenn nötig

Shared Mailbox einrichten

1. Microsoft 365 Admin Center → Teams & Groups → Shared Mailboxes
2. "Add a shared mailbox" klicken
3. Name: SAP-Notifications
4. Mailbox erstellen
5. In Azure AD: App-Permission Mail.Send erlaubt Versand über diese Mailbox

8. Sicherheits-Best-Practices

ThemaEmpfehlung
Secret RotationClient Secret alle 6-12 Monate erneuern
Least PrivilegeNur Mail.Send Permission, keine Read-Rechte
Sender-WhitelistNur dedizierte Mailboxen als Sender erlauben
LoggingAlle Mail-Aktionen mit Application Logging protokollieren
MonitoringAzure AD Sign-in Logs überwachen
Error HandlingSensible Daten nicht in Fehlermeldungen ausgeben

Application Logging integrieren

METHOD send_mail_with_logging.
" Logging initialisieren
DATA(lo_log) = cl_bali_log=>create_with_header(
header = cl_bali_header_setter=>create( )->set_object( 'ZMS_GRAPH' )
->set_subobject( 'MAIL' ) ).
TRY.
DATA(lo_mail) = NEW zcl_ms_graph_mail( ).
lo_mail->send_mail(
iv_sender_mail = iv_sender
iv_subject = iv_subject
iv_body = iv_body
it_to = it_recipients ).
" Erfolg loggen
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_severity_status
id = 'ZMS_GRAPH'
number = '001' )->set_text( |Mail an { it_recipients[ 1 ]-address } gesendet| ) ).
CATCH cx_root INTO DATA(lx_error).
" Fehler loggen
lo_log->add_item( cl_bali_message_setter=>create(
severity = if_bali_constants=>c_severity_error
id = 'ZMS_GRAPH'
number = '002' )->set_text( |Fehler: { lx_error->get_text( ) }| ) ).
ENDTRY.
" Log speichern
cl_bali_log_db=>get_instance( )->save_log( lo_log ).
ENDMETHOD.

Fazit

Die Integration von Office 365 Mail in SAP BTP ABAP Environment bietet eine moderne Alternative zu klassischen Mail-Lösungen. Die Kombination aus Microsoft Graph API, OAuth 2.0 und dem Communication Management in ABAP Cloud ermöglicht sichere und gut wartbare E-Mail-Funktionalität.

Wichtige Punkte:

  1. Azure AD App-Registrierung mit Application Permissions (Mail.Send)
  2. Communication Scenario für saubere Trennung von Entwicklung und Konfiguration
  3. OAuth 2.0 Client Credentials für Machine-to-Machine-Authentifizierung
  4. Shared Mailbox als kostengünstige Sender-Lösung
  5. Error Handling und Logging für produktiven Betrieb

Weiterführende Artikel