Cuatro formas de enviar correo electrónico PowerShell y Microsoft Graph
Microsoft ofrece API sin formato y dos métodos SDK
SMTP AUTH sigue siendo un protocolo de conexión de Exchange Online que admite la autenticación básica. Microsoft se vio obligada a excluirlo del conjunto de protocolos que se encuentra en las últimas etapas de su retirada. Demasiadas organizaciones tienen demasiados dispositivos y aplicaciones que envían correo electrónico a través de Exchange Online para que Microsoft desconecte SMTP AUTH. Sin embargo, no hay duda de que esto sucederá. La única pregunta es cuándo.
El reemplazo consiste en utilizar las API de Microsoft Graph para crear y enviar correos electrónicos. Durante el último año, he estado investigando los métodos disponibles. Microsoft ofrece las siguientes opciones basadas en gráficos:
- La API Graph sendMail.
- El cmdlet Send-MgUserMessage en el SDK de Microsoft Graph PowerShell.
- El cmdlet Send-MgUserMail en el SDK de Microsoft Graph PowerShell.
¿Por qué el SDK de Microsoft Graph PowerShell incluye dos métodos para realizar el mismo trabajo? Una respuesta está en la forma en que Microsoft genera cmdlets SDK automáticamente mediante el proceso AutoRest. Si existe una API, el SDK obtiene un cmdlet. Y debido a que Graph incluye la API mensaje: enviar y la API usuario: sendMail, el SDK se enorgullece de dos cmdlets que envían correo electrónico a través de Exchange Online. Pero lo más importante es que cada cmdlet tiene un propósito diferente. Llegaremos a eso pronto.
El cmdlet independiente Send-EmailMessage
Si no le gustan los cmdlets de Microsoft, Przemyslaw Klys, el desarrollador del respetado módulo PSWriteHTML PowerShell, ofrece su propia visión del tema en el cmdlet Send-EmailMessage. Este cmdlet se encuentra en el módulo Mailozaurr, cuyo nombre es espléndido.
A diferencia de los cmdlets del SDK de Microsoft Graph PowerShell, el trabajo de Przemyslaw exhibe las características de una programación bien pensada para que crear y enviar mensajes sea lo más fácil posible. Por ejemplo, los cmdlets de Microsoft requieren un procesamiento especial para archivos adjuntos grandes (cualquier cosa que supere los 3 MB), lo que puede ser un verdadero obstáculo (consulte este artículo del MVP Glen Scales). El cmdlet Send-EmailMessage maneja archivos adjuntos grandes automáticamente, que es exactamente la forma en que deberían hacerlo los cmdlets de Microsoft.
La razón por la que la gente usa las API de Microsoft es porque son las API de Microsoft. Aunque podría existir una alternativa independiente mejor, las organizaciones podrían ser reacias a escribir código basado en un módulo que no cuenta con soporte formal. Por supuesto, los desarrolladores internos podrían hacer exactamente lo que hizo Przemyslaw y crear un mejor contenedor alrededor de las API Graph, pero no veo que muchos se apresuren a hacerlo. En cualquier caso, echa un vistazo al módulo de Mailozaurr y toma tu propia decisión.
Decidir entre los dos cmdlets del SDK
Si tuviera que elegir entre los dos cmdlets del SDK, ¿cuál debería utilizar? Anteriormente, cubrí cómo enviar correos electrónicos utilizando el cmdlet Send-MgUserMessage. El cmdlet Send-MgUserMail es más fácil de usar porque puede crear y enviar un mensaje con un solo comando en lugar de crear un borrador de mensaje primero (con New-MgUserMessage) y luego enviándolo.
Por sí solo, crear y enviar con un solo movimiento probablemente hace que Send-MgUserMail sea una buena opción para la mayoría de los escenarios en los que las aplicaciones necesitan enviar correo electrónico. Normalmente, estos mensajes son algo así como una notificación para informar a alguien sobre cierta información, como la finalización de un trabajo o algo que verificar (como aplicaciones de Azure AD con permisos elevados).
Send-MgUserMessage tiene algunos trucos bajo la manga. Debido a que envía un mensaje ya creado, puede manejar escenarios como respuestas y reenvíos de mensajes. Sin embargo, creo que relativamente pocas aplicaciones necesitan procesar borradores de mensajes o responder al correo entrante; esta capacidad puede no ser tan útil como parece.
Prueba de Send-MgUserMail
Para probar qué tan fácil es usar el cmdlet Send-MgUserMail, convertí el script de muestra que usé con Send-MgUserMessage. Este script envía un mensaje de bienvenida a los nuevos empleados. Utiliza el cmdlet Get-ExoMailbox para buscar buzones de correo creados en la última semana. Esto es lógico porque si vamos a enviar un mensaje de bienvenida es necesario que vaya a un buzón. Si no quisieras cargar el módulo de administración de Exchange Online, podrías hacer algo similar con:
$CheckDate = (Get-Date).AddDays(-7)
[array]$Users = Get-MgUser -All -Filter "userType eq 'Member'"
$Users = $Users | Where-Object {$_.CreatedDateTime -gt $CheckDate}
Este comando funciona perfectamente, pero su rendimiento no será brillante en inquilinos más grandes. El cmdlet Get-MgUser no admite el filtrado según la fecha de creación de una cuenta, por lo que para encontrar el conjunto de cuentas creadas recientemente, debemos buscar todas las cuentas y luego aplicar un filtro del lado del cliente. En comparación, el cmdlet Get-ExoMailbox puede utilizar un filtro del lado del servidor, lo que significa que Exchange Online solo devuelve el conjunto de buzones de correo de destino.
[array]$Users = (Get-ExoMailbox -Filter "WhenMailboxCreated -gt '$CheckDate'" -RecipientTypeDetails UserMailbox -ResultSize Unlimited -Properties WhenMailboxCreated | Select WhenMailboxCreated, DisplayName, UserPrincipalName, PrimarySmtpAddress)Cuando escribí sobre Send-MgUserMessage, noté que el cmdlet usa matrices para pasar los destinatarios del mensaje y dije que los desarrolladores del SDK podrían hacernos un favor a todos al facilitar esta parte de la creación de propiedades del mensaje. . Una función de PowerShell funciona y puede manejar destinatarios Para, CC y CCO.
Function Populate-MessageRecipient { # Build a list of recipients for a message
[cmdletbinding()]
Param(
[array]$ListOfAddresses )
ForEach ($SMTPAddress in $ListOfAddresses) {
@{
emailAddress = @{address = $SMTPAddress}
}
}
}
Para agregar destinatarios, creamos una matriz que contiene la dirección SMTP de cada destinatario y la pasamos por la función para crear la matriz para agregar a las propiedades del mensaje. Por ejemplo, este código agrega dos destinatarios (un grupo de distribución y un buzón) como destinatarios de CC.
$CcRecipientList = @(
'Tenant.Admins@office365itpros.com'
'Kim.Akers@Office365itpros.com' )
[array]$MsgCcRecipients = Populate-MessageRecipient -ListOfAddresses $CcRecipientList
El resto del guión:
- Prepara un archivo adjunto codificado en Base64.
- Personaliza el contenido del mensaje para el destinatario y crea un cuerpo de mensaje HTML.
- Rellena el conjunto de propiedades del mensaje (asunto, destinatarios, destinatarios CC, datos adjuntos y cuerpo del mensaje).
- Completa dos parámetros de mensaje (guarda el mensaje en los elementos enviados y solicita un recibo de entrega).
- Envía el mensaje con Send-MgUserMail.
Aquí está el código (el script completo está disponible en GitHub):
$MsgAttachment = @(
@{
"@odata.type" = "#microsoft.graph.fileAttachment"
Name = ($AttachmentFile -split '\\')[-1]
ContentType = "text/plain"
ContentBytes = $EncodedAttachmentFile
}
)
ForEach ($User in $Users) {
$ToRecipientList = @( $User.PrimarySmtpAddress )
[array]$MsgToRecipients = Populate-MessageRecipient -ListOfAddresses $ToRecipientList
Write-Host "Sending welcome email to" $User.DisplayName
# Customize the message
$htmlHeaderUser = "<h2>New User " + $User.DisplayName + "</h2>"
$HtmlMsg = "</body></html>" + $HtmlHead + $htmlheaderuser + $htmlbody + "<p>"
# Construct the message body
$MsgBody = @{
Content = "$($HtmlMsg)"
ContentType = 'html' }
$Message = @{subject = $MsgSubject}
$Message += @{toRecipients = $MsgToRecipients}
$Message += @{ccRecipients = $MsgCcRecipients}
$Message += @{attachments = $MsgAttachment}
$Message += @{body = $MsgBody}
$Params = @{'message' = $Message}
$Params += @{'saveToSentItems' = $True}
$Params += @{'isDeliveryReceiptRequested' = $True}
Send-MgUserMail -UserId $MsgFrom -BodyParameter $Params
}
Write-Host "All done. Messages sent!"
Todo funciona y nuestros nuevos empleados reciben un bonito mensaje de bienvenida (Figura 1).
Tratar con permisos
El uso de los cmdlets Send-MgUserMessage y Send-MgUserMail requiere el permiso de gráfico Mail.Send. La creación del borrador del mensaje para que se envíe Send-MgUserMessage requiere el permiso Mail.ReadWrite. Estos permisos se aplican a todos los buzones del inquilino. En otras palabras, una aplicación en segundo plano equipada con estos permisos que utiliza autenticación basada en certificados puede escribir en cada buzón y enviar correo electrónico desde cada buzón. Con ese tipo de acceso, se debe tener cuidado de restringir la capacidad de los scripts que se ejecutan con aplicaciones o que usan runbooks de Azure Automation (con o sin una identidad administrada) para acceder a los buzones.
La forma correcta de hacerlo es con una política de acceso a la aplicación. Estas políticas dictan los buzones a los que puede acceder una aplicación, lo que significa que puede limitar las aplicaciones a algunos buzones específicos y mantenerlas alejadas de los buzones de los usuarios.
Tiempo de decisión
Es bueno tener una opción. Los desarrolladores que actualizan scripts para eliminar la dependencia de SMTP AUTH tienen al menos cuatro opciones basadas en Graph. Para mí, creo que utilizaré Send-MgUserMail siempre que sea posible, aunque sólo sea porque es la forma más sencilla de enviar un mensaje. Al menos, el método más sencillo basado en Microsoft. ¡El cmdlet independiente Send-EmailMessage también parece una opción atractiva!
