Graph API Send Mail with PowerShell

Hallo zusammen,

Mit den Informationen kann man nun mit dem MSAL.PS Modul ein Token von Azure AD

Import-Module MSAL.PS
$TenantId = "icewolfch.onmicrosoft.com"
$AppID = "c1a5903b-cd73-48fe-ac1f-e71bde968412" #DelegatedMail
$Scope = "https://graph.microsoft.com/.default" 
$Token = Get-MsalToken -ClientId $AppID -TenantId $tenantID -Scope $Scope -Interactive

Aus der Token Variable hole ich mir den AccessToken, lasse mir den AccessToken anzeigen und kopiere ihn mit dem "clip" Befehl in die Zwischenablage

$AccessToken = $Token.AccessToken
$AccessToken
$AccessToken | clip

Nachdem die Untenstehende PowerShell Funktion geladen ist, analysiere ich den JWT AccessToken

Parse-JWTtoken $AccessToken

###############################################################################
# Parse JWT Token
# https://www.michev.info/Blog/Post/2140/decode-jwt-access-and-id-tokens-via-powershell
###############################################################################
function Parse-JWTtoken {
 
    [cmdletbinding()]
    param([Parameter(Mandatory=$true)][string]$token)
 
    #Validate as per https://tools.ietf.org/html/rfc7519
    #Access and ID tokens are fine, Refresh tokens will not work
    if (!$token.Contains(".") -or !$token.StartsWith("eyJ")) { Write-Error "Invalid token" -ErrorAction Stop }
 
    #Header
    $tokenheader = $token.Split(".")[0].Replace('-', '+').Replace('_', '/')
    #Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0
    while ($tokenheader.Length % 4) { Write-Verbose "Invalid length for a Base-64 char array or string, adding ="; $tokenheader += "=" }
    Write-Verbose "Base64 encoded (padded) header:"
    Write-Verbose $tokenheader
    #Convert from Base64 encoded string to PSObject all at once
    Write-Verbose "Decoded header:"
    [System.Text.Encoding]::ASCII.GetString([system.convert]::FromBase64String($tokenheader)) | ConvertFrom-Json | fl | Out-Default
 
    #Payload
    $tokenPayload = $token.Split(".")[1].Replace('-', '+').Replace('_', '/')
    #Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0
    while ($tokenPayload.Length % 4) { Write-Verbose "Invalid length for a Base-64 char array or string, adding ="; $tokenPayload += "=" }
    Write-Verbose "Base64 encoded (padded) payoad:"
    Write-Verbose $tokenPayload
    #Convert to Byte array
    $tokenByteArray = [System.Convert]::FromBase64String($tokenPayload)
    #Convert to string array
    $tokenArray = [System.Text.Encoding]::ASCII.GetString($tokenByteArray)
    Write-Verbose "Decoded array in JSON format:"
    Write-Verbose $tokenArray
    #Convert from JSON to PSObject
    $tokobj = $tokenArray | ConvertFrom-Json
    Write-Verbose "Decoded Payload:"
   
    return $tokobj
}

Oder man benutzt https://jwt.ms/

Wie man nun über die Graph API ein Mail sendet ist in untenstehender API Dokumentation zu finden. 

Send mail

https://docs.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http

Häufig nutze ich dazu auch den Graph Explorer https://developer.microsoft.com/en-us/graph/graph-explorer

Auch hier müssen natürlich die Permissions vorhanden sein.

Mit den folgenden Befehlen wird das Mail nun versendet.

$URI = "https://graph.microsoft.com/v1.0/me/sendMail"
$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}

$Body = @"
{
    "message": {
        "subject": "Test mit Powershell",
        "body": {
            "contentType": "Text",
            "content": "Send a Mail with Powershell via Graph API."
        },
        "toRecipients": [
            {
                "emailAddress": {
                    "address": "postmaster@icewolf.ch"
                }
            }
        ]
    }
}
"@
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Body $Body -Headers $Headers -ContentType $ContentType

In den gesendeten Objekten ist das Mail zu finden

Man kann den eingeloggten User (Delegated User) auch mit folgender URI angeben

$URI = https://graph.microsoft.com/v1.0/users/a.bohren@icewolf.ch/sendMail
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Body $Body -Headers $Headers -ContentType $ContentType

Was natürlich nicht geht, ist ein Mail für einen anderen Benutzer zu versenden

$URI = https://graph.microsoft.com/v1.0/users/m.bohren@icewolf.ch/sendMail
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Body $Body -Headers $Headers -ContentType $ContentType

Application Permission

Bei einem Script das unbeaufsichtigt läuft, kann man sich nicht anmelden. Dafür braucht man dann die Application Permissions.

Um sich anzumelden braucht es dann dafür einen ClientSecret oder ein Zertifikat

Anmelden mit ClientSecret

Mit ClientSecret sieht das Script dann so aus

Import-Module MSAL.PS
$TenantId = "icewolfch.onmicrosoft.com"
$AppID = "c1a5903b-cd73-48fe-ac1f-e71bde968412" #DelegatedMail
$ClientSecret = ConvertTo-SecureString "YourClientSecret" -AsPlainText -Force
$Scope = "https://graph.microsoft.com/.default" 
$Token = Get-MsalToken -ClientId $AppID -ClientSecret $ClientSecret -TenantId $TenantID -Scope $Scope
$AccessToken = $Token.AccessToken
$URI = "https://graph.microsoft.com/v1.0/users/a.bohren@icewolf.ch/sendMail"
$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Body = @"
{
    "message": {
        "subject": "Test mit Powershell",
        "body": {
            "contentType": "Text",
            "content": "Send a Mail with Powershell via Graph API."
        },
        "toRecipients": [
            {
                "emailAddress": {
                    "address": "postmaster@icewolf.ch"
                }
            }
        ]
    }
}
"@
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Body $Body -Headers $Headers -ContentType $ContentType

Hier kann ich allerdings Mails als alle Benutzer versenden. Um das auf bestimmte Mailboxen einzuschränken benötigt man die ApplicationAccessPolicies

$URI = "https://graph.microsoft.com/v1.0/users/m.bohren@icewolf.ch/sendMail"
$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Body = @"
{
    "message": {
        "subject": "Test mit Powershell",
        "body": {
            "contentType": "Text",
            "content": "Send a Mail with Powershell via Graph API."
        },
        "toRecipients": [
            {
                "emailAddress": {
                    "address": "a.bohren@icewolf.ch"
                }
            }
        ]
    }
}
"@
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Body $Body -Headers $Headers -ContentType $ContentType

Wie man sieht, kann man damit von jedem Benutzerpostfach aus Mails versenden.

Anmelden mit Zertifikat

Mit Zertifikat sieht das Script dann so aus. Auch hier kann man als jede Mailbox senden, wenn man das nicht mit ApplicationAccessPolicies einschränkt.

Import-Module MSAL.PS
$TenantId = "icewolfch.onmicrosoft.com"
$AppID = "c1a5903b-cd73-48fe-ac1f-e71bde968412" #DelegatedMail
$CertificateThumbprint = "3B42F63204F0F54F6E4CB9CF96E365513C913C2B"
$Scope = "https://graph.microsoft.com/.default" 
$ClientCertificate = Get-Item Cert:\CurrentUser\My\$CertificateThumbprint
$Token = Get-MsalToken -clientID $AppID -ClientCertificate $ClientCertificate -tenantID $tenantID -Scope $Scope
$AccessToken = $Token.AccessToken
$URI = "https://graph.microsoft.com/v1.0/users/a.bohren@icewolf.ch/sendMail"
$ContentType = "application/json"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$Body = @"
{
    "message": {
        "subject": "Test mit Powershell",
        "body": {
            "contentType": "Text",
            "content": "Send a Mail with Powershell via Graph API."
        },
        "toRecipients": [
            {
                "emailAddress": {
                    "address": "a.bohren@icewolf.ch"
                }
            }
        ]
    }
}
"@
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Body $Body -Headers $Headers -ContentType $ContentType

Grüsse
Andres Bohren