Azure Communication Services (ACS) je cloudová platforma, která umožňuje integrovat e‑mail, SMS, chat, hlas i video do vlastních aplikací přes jednoduchá API a SDK.
Využívá zabezpečení a identitu Microsoft Entra ID (OAuth 2.0), nabízí škálování, telemetrii a řízení nákladů, takže se hodí pro zasílání základních notifikací až po komplexní komunikační kanály.
Pro zasílání e‑mailů je zde komponenta Azure Communication Services Email, která umožňuje programově odesílat například transakční zprávy, maily z multifunkčních zařízení, z internetových formulářů, skriptů, Logic Apps, Microsoft Sentinel a podobně.
Díky tomu může sloužit jako náhrada za končící SMTP Basic Authentication. Klíč je v tom, že namísto běžných přihlašovacích údajů se v zařízení či aplikaci použije moderní ověřování přes Entra ID a zprávy se odesílají přes Communication Srvices Email standardním SMTP protokolem na portu 25 nebo 587.
Výsledkem je spolehlivější doručování, lepší audit a governance a výrazně bezpečnější model než přímé posílání přes SMTP s Basic autentizací ale při zachování naprosto totožné funkcionality.
Obsah
Azure Communication Services - Email Comminication - celý postup
Tento návod je mixem níže uvedených video-návodu, které jsem vydal již dříve. Videa jsem spojil tak, aby dávala ucelený postup od A až po Z.
🎬 Obsah:
00:00 – Úvod – Představení Azure Communication Services
02:50 – Základní limity služby
03:40 – Ceny (Azure Calculator)
06:39 – Důležité úkony, postup nasazení
09:05 – Vytvoření prostředku Azure Communication Service
11:00 – Vytvoření prostředku Email Communication Services
13:25 – Domain provisioning
– Free Azure subdomain
– Custom domain
17:45 – Propojení Azure Communication Service a Email Communication Services
19:00 – TEST: Odeslání e-mailu z Email Communication Services (Azure portál) – Free Azure subdomain
21:35 – Keys & Connection strings – pro použití v Logic Apps a prostředí Azure
22:15 – Přidání vlastní domény do Email Communication Services (DNS: SPF, DKIM)
29:05 – TEST: Odeslání e-mailu z Email Communication Services (Azure portál) – vlastní doména / custom domain
30:10 – SMTP RELAY: Řízení přístupu IAM, Entra ID – Registrovaná aplikace – Communication and Email service Owner
– Udělení oprávnění
– Vytvoření Client secret pro Registrovanou aplikaci
– Vytvoření SMTP Username (uživatelské jméno pro SMTP)
37:24 – TEST: Odeslání emailu pomocí PowerShell scriptu přes Email Communication Services – ověření SMTP jméno + heslo (přes Registrovanou aplikaci a Client Secret / OAuth)
39:37 – TEST: Odeslání emailu pomocí PHP scriptu přes Email Communication Services – ověření SMTP jméno + heslo (přes Registrovanou aplikaci a Client Secret / OAuth)
40:45 – Nastavení vlastní identifikace odesílatele (MailFrom) namísto DoNotReply
46:15 – TEST – Odeslání e-mailu pomocí PowerShell skriptu s vlastní identifikací odesílatele (MailFrom)
46:20 – TEST – Odeslání e-mailu pomocí PHP skriptu s vlastní identifikací odesílatele (MailFrom)
50:08 – Azure Logic App – napojení na Communication Services, odeslání e-mailu
55:50 – Microsoft Sentinel – napojení na Communication Services, odeslání e-mailu
1:09:35 – Závěrečné shrnutí
Azure Communication Services - odesílání e-mailu bez identity uživatele (Sentinel, Logic App)
V tomto videu se dozvíte, jakým způsobem odeslat e-mail ze služby Microsoft Sentinel nebo z jakékoliv Logic App, aniž by bylo potřeba použít identitu uživatele – tedy bez nutnosti použití identity uživatele s licencí Exchange Online. K odeslání e-mailů tedy nebudeme používat napojení na účet uživatele, ale použijeme službu Azure Communication Services a konkrétně komponentu Email Communication Service.
🎬 Obsah:
00:00 – Úvod
01:33 – Základní limity služby
02:30 – Ceny
05:33 – Vytvoření prostředku Azure Communication Service
07:35 – Vytvoření prostředku Email Communication Services
– Free Azure subdomain
– Custom domain
14:30 – Propojení Azure Communication Service a Email Communication Services
18:04 – Odeslání e-mailu z Logic App
24:40 – Nastavení vlastní domény (Custom domain)
32:17 – Azure Sentinel – jak odeslat e-mail s upozorněním na nový incident pomocí Email Communication Services
46:50 – Dodatečné informace:
– User Engagement Tracking
– MailFrom Addresses (vlastní adresy odesílatele)
Odesílání emailů přes SMTP relay pomocí Microsoft Azure Communication Services
ACS Email může sloužit jako náhrada za končící SMTP Basic Authentication. Klíč je v tom, že namísto běžných přihlašovacích údajů se v zařízení či aplikaci použije moderní ověřování přes Entra ID a zprávy se odesílají přes Communication Services Email standardním SMTP protokolem na portu 25 nebo 587.
Výsledkem je spolehlivější doručování, lepší audit a governance a výrazně bezpečnější model než přímé posílání přes SMTP s Basic autentizací ale při zachování naprosto totožné funkcionality.
🎬 Obsah:
00:00 – Úvod
01:50 – Důležité úkony
04:45 – Konfigurace Azure Communication Services && Email Communication Services
06:45 – Entra ID – Registrovaná aplikace
09:05 – Udělení oprávnění
09:45 – Vytvoření Client secret pro Registrovanou aplikaci
12:30 – Vytvoření SMTP Username (uživatelské jméno pro SMTP)
13:50 – TEST – Odeslání e-mailu pomocí PowerShell skriptu
15:47 – TEST – Odeslání e-mailu pomocí PHP skriptu
16:57 – Nastavenní vlastní identifikace odesílatele (MailFrom) namísto DoNotReply
21:53 – TEST – Odeslání e-mailu pomocí PowerShell skriptu s vlastní identifikací odesílatele (MailFrom)
22:32 – TEST – Odeslání e-mailu pomocí PHP skriptu s vlastní identifikací odesílatele (MailFrom)
23:35 – Tipy na závěr, doporučení, ošetření chyb
25:25 – Zjištění aktuálních kvót
26:24 – Shrnutí, závěr
💡Postup:
Microsoft Azure (Subscription)
Azure Communication Service (ACS)
Email Communication Service (ECS)
Internet Domain
Domain verification
Domain connection to ECS
Entra ID Registred App + Generated secret
SMTP Accounts
Mail From addresses
Test
AZURE CLI: Přidání vlastního mailFrom
# az communication email domain sender-username create --domain-name domena.cz --email-service-name 365lab-1-cs-ecs --resource-group "security-rg" --sender-username "UserName" --username "UserName" --display-name "DisplayName"
az communication email domain sender-username create --domain-name domena.cz --email-service-name 365lab-1-cs-ecs --resource-group "security-rg" --sender-username printer --username printer --display-name Printer SCRIPT: Odeslání e-mailu pomocí PowerShell a ACS Email
# SmtpSend.ps1
# PowerShell script to send an email via SMTP
# www.sraga.cz
# Define SMTP server settings and email parameters
$SmtpServer = "smtp.azurecomm.net" # Azure Communication Services Email server
$SmtpPort = 587
$SmtpUser = "userName@domena.cz"
$SmtpPass = "clientSecretValue" # Entra ID Registreed Application
$SmtpFrom = "mailFromAddress@domena.cz"
$SmtpTo = "test@domena.cz"
$SmtpSubject = "Test email from PowerShell"
$SmtpBody = "This is a test email sent from PowerShell script."
# Create secure credentials
$SmtpSecurePwd = ConvertTo-SecureString $SmtpPass -AsPlainText -Force
$SmtpCreds = New-Object System.Management.Automation.PSCredential ($SmtpUser, $SmtpSecurePwd)
# Send the email
Send-MailMessage -From $SmtpFrom -To $SmtpTo -Subject $SmtpSubject -Body $SmtpBody -SmtpServer $SmtpServer -Port $SmtpPort -Credential $SmtpCreds -UseSsl SCRIPT: Odeslání e-mailu pomocí PHP a ACS E-mail
<?php
/**
* Minimal SMTP sender (STARTTLS + AUTH LOGIN) without Composer.
* Tested with Azure Communication Service SMTP: smtp.azurecomm.net:587
* Author: www.sraga.cz
*/
// ---------------- Example usage ----------------
try {
smtp_send([
'host' => 'smtp.azurecomm.net',
'port' => 587,
'username' => 'userName@domena.cz',
'password' => 'clientSecretValue', // ← store securely (env/secret)
'from' => 'mailFromAddress@domena.cz',
'from_name' => 'mailFromAddress@domena.cz',
'to' => 'test@domena.cz',
'subject' => 'Test email from PHP',
'body' => "This is a test email sent from a pure-PHP SMTP client.",
'debug' => true, // set true to see SMTP I/O to STDERR
]);
echo "Email sent OK\n";
} catch (Throwable $e) {
// Print a clear error if something goes wrong
fwrite(STDERR, "SMTP error: " . $e->getMessage() . "\n");
exit(1);
}
// ---------------- Functions ----------------
function smtp_send(array $cfg): void
{
$host = $cfg['host']; // e.g. smtp.azurecomm.net
$port = $cfg['port'] ?? 587; // 587 for STARTTLS
$username = $cfg['username']; // SMTP user (e.g. web@domain.cz)
$password = $cfg['password']; // SMTP password
$from = $cfg['from']; // RFC5322 From: email address
$fromName = $cfg['from_name'] ?? ''; // Display name (optional)
$to = $cfg['to']; // recipient email
$subject = $cfg['subject']; // subject (UTF-8 ok)
$body = $cfg['body']; // plain text body
$timeout = $cfg['timeout'] ?? 30;
$debug = $cfg['debug'] ?? false;
$peerName = $cfg['peer_name'] ?? $host; // SNI name for TLS
$context = stream_context_create([
'ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
'verify_peer' => true,
'verify_peer_name' => true,
'peer_name' => $peerName,
// If your OS trust store is standard, no need to set cafile/capath.
// 'cafile' => '/etc/ssl/certs/ca-certificates.crt',
'SNI_enabled' => true,
'disable_compression' => true,
],
]);
$fp = stream_socket_client(
"tcp://$host:$port",
$errno,
$errstr,
$timeout,
STREAM_CLIENT_CONNECT,
$context
);
if (!$fp) {
throw new RuntimeException("Connect failed: $errno $errstr");
}
stream_set_timeout($fp, $timeout);
// --- Helpers -------------------------------------------------------------
$read = function() use ($fp, $debug): array {
$lines = [];
while (($line = fgets($fp, 2048)) !== false) {
$lines[] = rtrim($line, "\r\n");
if ($debug) { fwrite(STDERR, "S: $line"); }
// Multi-line replies have format "123-..." and end on "123 ..." (space)
if (preg_match('/^\d{3} /', $line)) break;
}
if (empty($lines)) throw new RuntimeException("No response from server");
$code = (int)substr($lines[count($lines)-1], 0, 3);
return [$code, $lines];
};
$send = function(string $cmd) use ($fp, $debug) {
if ($debug) { fwrite(STDERR, "C: $cmd\n"); }
fwrite($fp, $cmd . "\r\n");
};
$expect = function(int $want) use ($read) {
[$code, $lines] = $read();
if ($code !== $want) {
$msg = implode("\n", $lines);
throw new RuntimeException("Expected $want, got $code\n$msg");
}
return $lines;
};
// --- SMTP dialogue -------------------------------------------------------
$expect(220); // 220 greeting
$ehloName = gethostname() ?: 'localhost';
$send("EHLO $ehloName");
$expect(250);
// STARTTLS
$send("STARTTLS");
$expect(220); // Ready to start TLS
if (!stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT)) {
throw new RuntimeException("TLS handshake failed");
}
// Re‑EHLO after STARTTLS
$send("EHLO $ehloName");
$expect(250);
// AUTH LOGIN
$send("AUTH LOGIN");
$expect(334); // "VXNlcm5hbWU6" (Username:)
$send(base64_encode($username));
$expect(334); // "UGFzc3dvcmQ6" (Password:)
$send(base64_encode($password));
$expect(235); // Auth OK
// MAIL/RCPT
$send("MAIL FROM:<$from>");
$expect(250);
$send("RCPT TO:<$to>");
$expect(250);
// DATA – headers + body with dot‑stuffing
$send("DATA");
$expect(354);
// Headers
$headers = [];
$fromHeader = $fromName !== ''
? sprintf('From: %s <%s>', encodeDisplayName($fromName), $from)
: sprintf('From: <%s>', $from);
$headers[] = $fromHeader;
$headers[] = "To: <$to>";
$headers[] = "Subject: " . encodeSubject($subject);
$headers[] = "Date: " . date('r');
$headers[] = "Message-ID: <" . bin2hex(random_bytes(16)) . '@' . (parse_url("https://$peerName", PHP_URL_HOST) ?: 'localhost') . ">";
$headers[] = "MIME-Version: 1.0";
$headers[] = "Content-Type: text/plain; charset=UTF-8";
$headers[] = "Content-Transfer-Encoding: 8bit";
$headers[] = "X-Mailer: PHP-SMTP/1.0";
$data = implode("\r\n", $headers) . "\r\n\r\n" . $body;
// Dot‑stuffing per RFC5321 §4.5.2
$data = preg_replace('/\r\n\./', "\r\n..", $data);
// Ensure ends with CRLF.CRLF
if (!str_ends_with($data, "\r\n")) $data .= "\r\n";
fwrite($fp, $data . ".\r\n");
$expect(250); // accepted
// QUIT
$send("QUIT");
$read(); // 221
fclose($fp);
}
/** Encode UTF‑8 display name safely for headers */
function encodeDisplayName(string $name): string {
// Quote if contains specials; also provide RFC2047 when needed
if (preg_match('/[^\x20-\x7E]/', $name)) {
return '=?UTF-8?B?' . base64_encode($name) . '?=';
}
if (preg_match('/[",]/', $name)) {
return '"' . addcslashes($name, '\\"') . '"';
}
return $name;
}
/** RFC2047 encode UTF‑8 Subject if needed */
function encodeSubject(string $subject): string {
return preg_match('/[^\x20-\x7E]/', $subject)
? '=?UTF-8?B?' . base64_encode($subject) . '?='
: $subject;
}