ModernMailTools.psm1
# $global: function Register-ModernMailMessageEntraIDApp { <# .SYNOPSIS Registers an Entra ID App. .DESCRIPTION Registers an app in Entra ID, assigning necessary permissions and outputting relevant information. .PARAMETER ApplicationName The name of the application to register. .EXAMPLE Register-ModernMailMessageEntraIDApp -ApplicationName "M365 ModernMailTools" .INPUTS None .OUTPUTS .NOTES #> param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ApplicationName #[string]$Tenant, #[string]$TenantId, #[string]$TenantName ) if (-not $DisableTelemetry) { Write-Telemetry -EventName "RegisterModernMailMessageEntraIDApp" } # Reference: # https://pnp.github.io/powershell/cmdlets/Register-PnPAzureADApp.html #Import-Module Microsoft.Graph.Authentication #Import-Module Microsoft.Entra.Authentication #$ApplicationName = "Test M365 ModernMailTools" ##$ApplicationName = "Test PS ModernMailTools" ##$ApplicationName = "Test M365 SendMail" ##$ApplicationName = "Test PS SendMail" #Connect-MgGraph if (-not (Get-EntraContext)) { Connect-Entra -Scopes 'Application.ReadWrite.All' -NoWelcome #Connect-Entra -Scopes 'Application.ReadWrite.All' -NoWelcome #-TenantId } #$application = Get-EntraApplication -Filter "DisplayName eq 'My new application'" $application = Get-EntraApplication -Filter "DisplayName eq '$ApplicationName'" Write-Verbose "VAR: $($application.DisplayName)" -Verbose if(!$application) { #$application = New-EntraApplication -DisplayName 'My new application' $application = New-EntraApplication -DisplayName $ApplicationName #New-EntraServicePrincipal -AppId $myApp.AppId -DisplayName 'My new service principal' Write-Verbose "APP: $($application.DisplayName)" -Verbose } $requiredResourceAccess = @( @{resourceAppId = '00000003-0000-0000-c000-000000000000' # Microsoft Graph resourceAccess = @( #@{ # id = 'c79f8feb-a9db-4090-85f9-90d820caa0eb' # Application.Read.All (Delegate) - Read applications # type = 'Scope' #} #@{ # id = '9a5d68dd-52b0-4cc2-bd40-abcf44ac3a30' # Application.Read.All (Application) - Read all applications # type = 'Role' #} @{ id = 'e383f46e-2787-4529-855e-0e479a3ffac0' # Mail.Send (Delegate) - Send mail as a user type = 'Scope' # Role - AADSTS650051: Claim is invalid: Mail.Send does not exist in client application's RequiredResourceAccess. } #@{ # id = 'a367ab51-6b49-43bf-a716-a1fb06d2a174' # Mail.Send.Shared (Delegate) - Send mail on behalf of others # type = 'Scope' #} @{ id = 'b633e1c5-b582-4048-a93e-9f11b44c7e96' # Mail.Send (Application) - Send mail as any user type = 'Role' } @{ id = '258f6531-6087-4cc4-bb90-092c5fb3ed3f' # SMTP.Send (Delegate) - Send emails from mailboxes using SMTP AUTH type = 'Scope' # Role - AADSTS650051: Claim is invalid: SMTP.Send does not exist on resource application 00000003-0000-0000-c000-000000000000. } ) } @{resourceAppId = '00000002-0000-0ff1-ce00-000000000000' # Office 365 Exchange Online resourceAccess = @( @{ id = 'b633e1c5-b582-4048-a93e-9f11b44c7e96' # Mail.Send (Application) - Send mail as any user type = 'Role' } ) } ) $applicationEdited = Set-EntraApplication -ApplicationId $application.Id -RequiredResourceAccess $requiredResourceAccess # AADSTS500113: No reply address is registered for the application. #Set-EntraApplication -ApplicationId $application.Id -ReplyUrls "" # N/A # https://login.microsoftonline.com/common/oauth2/nativeclient + http://localhost # Ensure you have a 'Mobile and desktop applications' platform with redirect to 'http://localhost' configured (and not a 'Web' Platform). # Grant-EntraAdminConsent -AppId "your-application-id" # N/A # https://login.microsoftonline.com/{organization}/adminconsent?client_id={client-id} if($applicationEdited){ $context = Get-EntraContext $clientId = $application.AppId $tenantId = $context.TenantId $consentUrl = "https://login.microsoftonline.com/$tenantId/adminconsent?client_id=$clientId" #Write-host -ForegroundColor Gray "Opening: $consentUrl" Write-Output "Opening: $consentUrl" #Write-Information "Opening: $consentUrl" #Write-Verbose "Opening: $consentUrl" Start-Process $consentUrl } Write-Verbose "EDIT: $($application.DisplayName)" -Verbose } function Send-ModernMailMessage { <# .SYNOPSIS Sends an email message. .DESCRIPTION The Send-ModernMailMessage cmdlet sends an email message from within PowerShell. .EXAMPLE Send-ModernMailMessage -From "user01@fabrikam.com" -To "user02@fabrikam.com" -Subject "Test mail" Send-ModernMailMessage -From "user01@fabrikam.com" -To "user02@fabrikam.com" -Subject "Test mail" -SmtpServer smtp.contoso.com -UseSsl -Port 587 .INPUTS None .OUTPUTS .NOTES Use "Enable-MailMessageAlias" to enable the command "Send-MailMessage". #> param ( [Parameter(Position = 0)] # The path and file names of files to be attached to the email message. [alias("Attachments")] [String[]]$Attachment, [Parameter(Position = 1)] # Email addresses that receive a copy of the mail but are not listed as recipients of the message. #[Array] $Bcc, [String[]]$Bcc, [Parameter(Position = 2)] # The body (content) of the email message. [alias("Message")] #[string[]] $Text, [String]$Body, [Parameter(Position = 3)] # Indicates that the value of the Body parameter contains HTML. #[alias("Body")] #[string[]] $HTML, [Switch]$BodyAsHtml, [Parameter(Position = 4)] # The encoding used for the body and subject. # Explicitly reference System.Text.Encoding [System.Text.Encoding]$Encoding = [System.Text.Encoding]::Default, [Parameter(Position = 5)] # Email addresses to which a carbon copy (CC) of the email message is sent. #[Array] $Cc, [String[]]$Cc, [Parameter(Position = 6)] # Delivery notifications (if accepted by the recipient) [alias("Dno")] #[System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOption, # Explicitly reference System.Net.Mail [ValidateSet('None', 'OnSuccess', 'OnFailure', 'Delay', 'Never')] # Delivery notification options with validation [String[]]$DeliveryNotificationOption, [Parameter(Position = 7, Mandatory = $true)] # The address from which the mail is sent. #[Parameter(Mandatory = $true)] #[object] $From, [alias("UserId")] [ValidateNotNullOrEmpty()] [String]$From, [Parameter(Position = 8)] # Deprecated?? [alias('Host')] [alias('Server')] [String]$SmtpServer = "smtp.office365.com", [Parameter(Position = 9)] # The priority of the email message. #[System.Net.Mail.MailPriority]$Priority, # Explicitly reference System.Net.Mail.MailPriority [alias('Importance')] [ValidateSet('Low', 'Normal', 'High')] [string] $Priority, [Parameter(Position = 10)] # Specifies additional email addresses (other than the From address) to use to reply to this message #[string] $ReplyTo, [String[]]$ReplyTo, [Parameter(Position = 11)] # The subject of the email message. #[string] $Subject, [String]$Subject, [Parameter(Position = 12, Mandatory = $true)] # The addresses to which the mail is sent #[Array] $To, #[Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String[]]$To, [Parameter(Position = 13)] # Deprecated? [PSCredential]$Credential, [Parameter(Position = 14)] # Deprecated? #[Bool]$UseSsl = $true, [Switch]$UseSsl, [Parameter(Position = 15, Mandatory = $false)] # Deprecated? #[Parameter(Position = 15)] #[Parameter(Mandatory = $false)] [Int32]$Port = 587, # Indicates whether to save the message in Sent Items. [Switch]$SaveToSentItems, # Indicates whether a read receipt is requested for the message. #ToDevelop:IsReadReceiptRequested [Switch]$RequestReadReceipt, # Indicates whether a delivery receipt is requested for the message. #ToDevelop: IsDeliveryReceiptRequested [Switch]$RequestDeliveryReceipt, #ToDevelop: Message #ToDevelop: BodyParameter #-DisableTelemetry #-DisableTelemetry $false #[Parameter(Mandatory = $false, HelpMessage = 'Disable telemetry')] #[bool]$DisableTelemetry, # Disable Telemetry # If set, telemetry information will not be logged. [Parameter(Mandatory = $false, HelpMessage = 'Disable telemetry')] [switch] $DisableTelemetry, # Modus defines the mode of operation for sending notifications. # Default: None # Fallback: Graph with Delegation (user-based interaction). # Fallback: User-based delegation for Microsoft Graph (interactive). # Recommended: # - 'GRAPH': App-only authentication (certificate-based) for Graph API. # - 'SMTP': SMTP-based email sending (e.g., OAuth2 authentication). # - 'TEAMS': Teams webhook notification (send notifications to a Teams channel). [ValidateSet('GRAPH', 'SMTP', 'TEAMS')] [String]$Modus = 'GRAPH', # Specifies the application ID of the service principal that is used in application-based authentication. [alias('$ApplicationId')] [string]$ClientId, # Specifies the ID of a tenant. [string]$TenantId, # Specifies the certificate thumbprint of a digital public key X.509 certificate of an application that has permission to perform this action. [string]$CertificateThumbprint ) <# Reference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage?view=powershell-7.5 https://ss64.com/ps/send-mailmessage.html https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users.actions/send-mgusermail?view=graph-powershell-1.0 Send-MailMessage -From "User01 <user01@fabrikam.com>"" -To "User02 <user02@fabrikam.com>" -Subject "Test mail" Send-MailMessage -To "User01 <user01@contoso.com>" -From "User02 <user02@contoso.com>" -Subject "Test mail" -SmtpServer smtp.contoso.com -UseSsl -Port 587 #> ## Defensive handling for Encoding parameter #if ($null -eq $Encoding) { # $Encoding = [System.Text.Encoding]::Default #} elseif ($Encoding -is [string]) { # $Encoding = [System.Text.Encoding]::GetEncoding($Encoding) #} if (-not $From) { throw "Parameter 'From' is required." } if (-not $To) { throw "Parameter 'To' is required." } # Deprecated values if ($SmtpServer) { Write-Verbose "Deprecated: $($SmtpServer)" } if ($Credential) { Write-Verbose "Deprecated: $($Credential)" } if ($UseSsl) { Write-Verbose "Deprecated: $($UseSsl)" } if ($Port) { Write-Verbose "Deprecated: $($Port)" } # Development values #if ($Attachments) { Write-Verbose "Development: $($Attachments)" } if ($Cc) { Write-Verbose "Development: $($Cc)" } if ($Bcc) { Write-Verbose "Development: $($Bcc)" } if ($DeliveryNotificationOption) { Write-Verbose "Development: $($DeliveryNotificationOption)" } # DNO if ($Priority) { Write-Verbose "Development: $($Priority)" } if ($ReplyTo) { Write-Verbose "Development: $($ReplyTo)" } if ($SaveToSentItems) { Write-Verbose "Development: $($SaveToSentItems)" } if ($RequestReadReceipt) { Write-Verbose "Development: $($RequestReadReceipt)" } # DNO if ($RequestDeliveryReceipt) { Write-Verbose "Development: $($RequestDeliveryReceipt)" } # DNO if ($Encoding) { Write-Verbose "Development: $($Encoding)" } if (-not $DisableTelemetry) { Write-Telemetry -EventName "SendModernMailMessage" } # -- Authentication #Get-MgContext #Get-EntraContext switch ($Modus) { 'GRAPH' { # Graph - Application # Implement Microsoft Graph API logic using certificate for authentication (app-based) Write-Debug "Sending email via Microsoft Graph Application (certificate-based) to $Recipient" # Implement Microsoft Graph API logic using certificate-based authentication (app-only) Write-Debug "Sending email via Microsoft Graph Application (app-only authentication) to $Recipient" if ($ClientId -and $TenantId -and $CertificateThumbprint) { Connect-MgGraph ` -ClientId $ClientId ` -TenantId $TenantId ` -CertificateThumbprint $CertificateThumbprint } #try { # $user = Get-MgUser -UserId $From -ErrorAction Stop #} catch { # throw "The user specified in -From ('$From') does not exist in Microsoft 365." #} } 'SMTP' { # Implement SMTP with OAuth2 authentication here # Write-Debug "Sending email via SMTP to $Recipient" # Implement SMTP with OAuth2 authentication here # Write-Debug "Sending email via SMTP to $Recipient" # SMTP Import-Module EntraAuth ## During the Connectiong #$clientId = $application.AppId #$tenantId = $context.TenantId #$token = Connect-EntraService -ClientID $clientId -TenantID $tenantId -Service GraphBeta -PassThru #$token.Scopes Connect-EntraService -ClientID Graph -Scopes "SMTP.Send" -Service GraphBeta #Get-EntraService # After already being connected $token = Get-EntraToken -Service GraphBeta #$token.Scopes # SMTP.Send #$token | fl * #$xauth2 = $token.AccessToken #$secure_xauth2 = ConvertTo-SecureString -AsPlainText $xauth2 -Force # Token #$secure_xauth2 = ConvertTo-SecureString -AsPlainText "[Password]" -Force # Pw (with Enabled MFA) ##[pscredential]$credential = New-Object System.Management.Automation.PSCredential("AutomateB@contoso.onmicrosoft.com", $secure_xauth2) } 'TEAMS' { # Implement Teams webhook notification logic here Write-Debug "Sending Teams notification to $Recipient" Connect-MicrosoftTeams` -CertificateThumbprint $CertificateThumbprint ` -ApplicationId $ClientId ` -TenantId $TenantId } default { # Handle invalid modus, if necessary Write-Debug "Invalid modus specified: $Modus" # You can also add additional handling for invalid modus, like logging or exit # Graph - Delegated (Fallback) # Implement Microsoft Graph API logic using delegated user consent (interactive) Write-Debug "Sending email via Microsoft Graph Delegation (user-based) to $Recipient" # Implement Microsoft Graph API logic using delegated user consent (interactive) Write-Debug "Sending email via Microsoft Graph Delegation (user-based authentication) to $Recipient" # Only connect if not already connected if (-not (Get-MgContext)) { Connect-MgGraph -Scopes "Mail.Send" -NoWelcome } $IsDelegated = $true } } # -- Settings $From = if ($IsDelegated) {(Get-MgContext).Account} else {$From} #$From = if ($Modus -eq 'GRAPH_DELEGATION') {(Get-MgContext).Account} else {$From} #$To = if ($To.Count -gt 1) {} else { $To[0] } # Handle Array $MessageBody = @{ contentType = if ($BodyAsHtml) { "HTML" } else { "Text" } #content = if ($Body) { $Body -join [System.Environment]::NewLine } else { "" } content = if ($Body) { $Body -join [System.Environment]::NewLine } else { "This email is sent via Microsoft Graph." } } if (!$Subject) { $Subject = "Test message from ModernMailTools (Modus: $Modus)" } if ($Attachment){ try { #$Attachment = "..\_readme.md" #Test-Path $Attachment # True #(Get-Item -Path $Attachment).Length -lt 3000000 # 3191 | True if ((Test-Path $Attachment) -and ((Get-Item -Path $Attachment).Length -lt 3000000)) { # Attachments are under 4MB or empty #Get File Name and Base64 string $FileName = (Get-Item -Path $Attachment).Name $FileBytes = [Convert]::ToBase64String([IO.File]::ReadAllBytes($Attachment)) Write-Verbose "Name: $($FileName)" Write-Verbose "Length: $($FileBytes.Length)" } else { <#Do this if attachments are over 4MB#> } } catch { Write-Verbose $_ #Write-Error $_.Exception.Message } } # -- Send if ($From) { Write-Verbose "Send v1: $($From)"} if ($MessageBody) { Write-Verbose "Send v1: $($MessageBody | Out-String)"} if ($Subject) { Write-Verbose "Send v1: $($Subject)"} if ($To) { Write-Verbose "Send v1: $($To)"} Write-Debug "Modus: $($Modus)" #-Debug #If ($null -eq $Modus){$Modus = 'GRAPH'} switch ($Modus) { 'GRAPH' { # Graph $params = [ordered] @{ # https://docs.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0 message = [ordered] @{ subject = $Subject body = $MessageBody #from = $From toRecipients = @( @{ emailAddress = @{ address = $To[0] } } ) #toRecipients = @( # @{ # emailAddress = @{ # address = "meganb@contoso.com" # } # } #) #ccRecipients = @() #bccRecipients = @() #replyTo = @() #importance = $Priority #isReadReceiptRequested = $RequestReadReceipt.IsPresent #isDeliveryReceiptRequested = $RequestDeliveryReceipt.IsPresent } #saveToSentItems = -not $DoNotSaveToSentItems.IsPresent #saveToSentItems = $false saveToSentItems = $SaveToSentItems.IsPresent } Write-Debug ($params.Values | Out-String) #Send-MgUserMail -UserId $From -Message $params try { Send-MgUserMail -UserId $From -BodyParameter $params } catch { throw "Failed to send mail: $($_.Exception.Message)" } } 'SMTP' { # --- SMTP #Send-MKMailMessage -To "admin@contoso.onmicrosoft.com" -From "AutomateB@contoso.onmicrosoft.com" -Subject "Test" -SmtpServer "smtp.office365.com" -Credential $credential -Port 25 # > Send-MKMailMessage: as8758.net SMTPBLOCKER ESMTP Service not available #Send-MKMailMessage -To "admin@contoso.onmicrosoft.com" -From "AutomateB@contoso.onmicrosoft.com" -Subject "Test" -SmtpServer "smtp.office365.com" -Credential $credential -Port 587 # > Send-MKMailMessage: 535: 5.7.139 Authentication unsuccessful, the request did not meet the criteria to be authenticated successfully # > Send-MKMailMessage: 535: 5.7.139 Authentication unsuccessful, the request did not meet the criteria to be authenticated successfully. If (!(Get-Package MailKit)){ # Find-Package -Name 'MailKit' -Source 'https://www.nuget.org/api/v2' | Install-Package #-Verbose } #Get-Package MailKit #(Get-Package -ProviderName NuGet -Name 'MailKit') | fl * $folderNameMail = Split-Path (Get-Package -ProviderName NuGet -Name 'MailKit').Source -Parent $folderNameMime = Split-Path (Get-Package -ProviderName NuGet -Name 'MimeKit').Source -Parent #C:\Program Files\PackageManagement\NuGet\Packages\MailKit.4.11.0\MailKit.4.11.0.nupkg # C:\Program Files\PackageManagement\NuGet\Packages\MailKit.4.11.0 [System.Reflection.Assembly]::LoadFile("$($folderNameMail)\lib\net48\MailKit.dll") > $null [System.Reflection.Assembly]::LoadFile("$($folderNameMime)\lib\net48\MimeKit.dll") > $null Add-Type -Path "$($folderNameMail)\lib\net48\MailKit.dll" Add-Type -Path "$($folderNameMime)\lib\net48\MimeKit.dll" #$SMTP = New-Object MailKit.Net.Smtp.SmtpClient #[MailKit.Net.Smtp.SmtpClient] $smtpClient = [MailKit.Net.Smtp.SmtpClient]::new() # Set TLS to automatically negotiate security #$UseSSL = $false if ($UseSSL -and $Port -ne "587"){ $SSLAuto = [MailKit.Security.SecureSocketOptions]::SslOnConnect } else { $SSLAuto = [MailKit.Security.SecureSocketOptions]::StartTlsWhenAvailable #$SSLAuto = [MailKit.Security.SecureSocketOptions]::Auto # https://mimekit.net/docs/html/T_MailKit_Security_SecureSocketOptions.htm } #$smtpClient.Connect($channel.Definition.host, $channel.Definition.port, $channel.Definition.ssl) # $SMTP.Connect('smtp.gmail.com', 587, $False) #$smtpClient.Connect($SmtpServer, $Port, $UseSSL) # $SMTP.Connect('smtp.gmail.com', 587, $False) $smtpClient.Connect($SmtpServer, $Port, $SSLAuto) #$smtpClient.Authenticate($channel.Definition.username, ( Convert-SecureToPlaintext -String $channel.Definition.password)) # $SMTP.Authenticate('myemail1@gmail.com', 'appspecificpassword' ) #$smtpClient.Authenticate($Username, $Password) # $SMTP.Authenticate('myemail1@gmail.com', 'appspecificpassword' ) # $token.AccessToken = OAuth2 Token (replace this with your actual OAuth2 token) #$username = "test" #$username = $token.TokenData.unique_name $username = $token.TokenData.upn # Create an OAuth2 mechanism for authentication ##$oAuth2 = [MailKit.Security.SaslMechanismOAuth2]::new($AuthToken.Account.Username, $AuthToken.AccessToken) #$oAuth2 = New-Object MailKit.Security.SaslMechanismOAuth2 -ArgumentList $channel.Definition.username, $token.AccessToken $oAuth2 = New-Object MailKit.Security.SaslMechanismOAuth2 -ArgumentList $username, $token.AccessToken #$oAuth2 = New-Object MailKit.Security.SaslMechanismOAuthBearer -ArgumentList $username, $token.AccessToken Write-Verbose ($oAuth2.Credentials | Out-String) -Verbose #Write-Verbose ($oAuth2.Credentials | fl * | Out-String) -Verbose #$smtpClient.Authenticate($oauth2Mechanism) $smtpClient.Authenticate($oAuth2) #MethodInvocationException: Exception calling "Authenticate" with "1" argument(s): "451: 4.7.0 Temporary server error. Please try again later. PRX5 [ZR2P278CA0086.CHEP278.PROD.OUTLOOK.COM 2025-03-31T01:06:59.921Z 08DD6E37F3A552D5]" # Create the email message (MimeMessage) $message = [MimeKit.MimeMessage]::new() # Set the 'From' address $from = [MimeKit.MailboxAddress]::new('Your Name', $From) $message.From.Add($from) # Set the 'To' address $to = [MimeKit.MailboxAddress]::new('Recipient Name', $To) $message.To.Add($to) # Set the subject and body of the email #$message.Subject = "Your Email Subject" $message.Subject = $Subject $messageBody = [MimeKit.TextPart]::new("plain") #$messageBody.Text = "This is the email body content." $messageBody.Text = $Body $message.Body = $messageBody # Proceed with sending email $smtpClient.Send($message) # MethodInvocationException: Exception calling "Send" with "1" argument(s): "5.7.57 Client not authenticated to send mail. [ZR0P278CA0195.CHEP278.PROD.OUTLOOK.COM 2025-03-31T00:59:51.228Z 08DD6FAFE0C04FAE]" # Disconnect after sending the email $smtpClient.Disconnect($True) $smtpClient.Dispose() } 'TEAMS' { Write-Verbose "Development: ToDo" } default { Write-Debug "Invalid modus specified" } } } function Enable-MailMessageAlias { <# .SYNOPSIS Enables alias for Send-MailMessage command. .DESCRIPTION Enables Send-MailMessage command alias in the current PowerShell session. .EXAMPLE Enable-MailMessageAlias .INPUTS None .OUTPUTS .NOTES #> if (-not $DisableTelemetry) { Write-Telemetry -EventName "EnableMailMessageAlias" } #Reference: # https://learn.microsoft.com/en-us/powershell/module/microsoft.entra/enable-entraazureadalias?view=entra-powershell Set-Alias -Name Send-MailMessage -Value Send-ModernMailMessage -Scope Global -Force Set-Alias -Name Register-MailMessageEntraIDApp -Value Register-ModernMailMessageEntraIDApp -Scope Global -Force Set-Alias -Name Register-MailMessageApp -Value Register-ModernMailMessageEntraIDApp -Scope Global -Force } function Write-Telemetry { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet("RegisterModernMailMessageEntraIDApp","SendModernMailMessage","EnableMailMessageAlias")] [string] $EventName ) #$EventName = "SendModernMailMessage" Write-Verbose "Sending telemetry event: $EventName" $tenantId = Get-MgContext | Select-Object -ExpandProperty TenantId if (-not $tenantId) { $tenantId = "unknown" } $jsonData = @{ "api_key" = "c0cd5aa997bde6dd291d06729c7548a7" "events" = @{ "user_id" = $tenantId #"device_id" = "$($env:ComputerName)" "device_id" = $([BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($env:ComputerName))).Replace('-', '').ToLower()) "event_type"= $EventName "app_version" = "0.1.1" "platform" = "PowerShell $($PSVersionTable.PSVersion)" "os_name" = (Get-CimInstance Win32_OperatingSystem).Caption "language" = (Get-Culture).Name "ip" = '$remote' } } # Convert the data to JSON format $jsonBody = $jsonData | ConvertTo-Json # Define the URL $url = "https://api.eu.amplitude.com/2/httpapi" # Send the POST request try { Invoke-RestMethod -Uri $url -Method Post -ContentType "application/json" -Body $jsonBody | Out-Null } catch { Write-Verbose $_ } } Export-ModuleMember -Function @("Send-ModernMailMessage", "Register-ModernMailMessageEntraIDApp","Enable-MailMessageAlias") |