functions/send-d365broadcastmessage.ps1


<#
    .SYNOPSIS
        Send broadcast message to online users in D365FO
         
    .DESCRIPTION
        Utilize the same messaging framework available from LCS and send a broadcast message to all online users in the environment
         
    .PARAMETER Tenant
        Azure Active Directory (AAD) tenant id (Guid) that the D365FO environment is connected to, that you want to send a message to
         
    .PARAMETER URL
        URL / URI for the D365FO environment you want to send a message to
         
    .PARAMETER ClientId
        The ClientId obtained from the Azure Portal when you created a Registered Application
         
    .PARAMETER ClientSecret
        The ClientSecret obtained from the Azure Portal when you created a Registered Application
         
    .PARAMETER TimeZone
        Id of the Time Zone your environment is running in
         
        You might experience that the local VM running the D365FO is running another Time Zone than the computer you are running this cmdlet from
         
        All available .NET Time Zones can be traversed with tab for this parameter
         
        The default value is "UTC"
         
    .PARAMETER StartTime
        The time and date you want the message to be displayed for the users
         
        Default value is NOW
         
        The specified StartTime will always be based on local Time Zone. If you specify a different Time Zone than the local computer is running, the start and end time will be calculated based on your selection.
         
    .PARAMETER EndingInMinutes
        Specify how many minutes into the future you want this message / maintenance window to last
         
        Default value is 60 minutes
         
        The specified StartTime will always be based on local Time Zone. If you specify a different Time Zone than the local computer is running, the start and end time will be calculated based on your selection.
         
    .PARAMETER OnPremise
        Specify if environnement is an D365 OnPremise
         
        Default value is "Not set" (= Cloud Environnement)
         
    .EXAMPLE
        PS C:\> Send-D365BroadcastMessage
         
        This will send a message to all active users that are working on default D365FO environment.
         
        See the RELATED LINKS section for the supporting cmdlets needed to store a default configuration.
         
    .EXAMPLE
        PS C:\> Send-D365BroadcastMessage -Tenant "e674da86-7ee5-40a7-b777-1111111111111" -URL "https://usnconeboxax1aos.cloud.onebox.dynamics.com" -ClientId "dea8d7a9-1602-4429-b138-111111111111" -ClientSecret "Vja/VmdxaLOPR+alkjfsadffelkjlfw234522"
         
        This will send a message to all active users that are working on the D365FO environment located at "https://usnconeboxax1aos.cloud.onebox.dynamics.com".
        It will authenticate against the Azure Active Directory with the "e674da86-7ee5-40a7-b777-1111111111111" guid.
        It will use the ClientId "dea8d7a9-1602-4429-b138-111111111111" and ClientSecret "Vja/VmdxaLOPR+alkjfsadffelkjlfw234522" go get access to the environment.
        It will use the default value "UTC" Time Zone for converting the different time and dates.
        It will use the default start time which is NOW.
        It will use the default end time which is 60 minutes.
         
    .EXAMPLE
        PS C:\> Send-D365BroadcastMessage -OnPremise -Tenant "https://adfs.local/adfs" -URL "https://ax-sandbox.d365fo.local" -ClientId "dea8d7a9-1602-4429-b138-111111111111" -ClientSecret "Vja/VmdxaLOPR+alkjfsadffelkjlfw234522"
         
        This will send a message to all active users that are working on the D365FO OnPremise environment located at "https://ax-sandbox.d365fo.local".
        It will authenticate against Local ADFS with the "https://adfs.local/adfs" path
        It will use the ClientId "dea8d7a9-1602-4429-b138-111111111111" and ClientSecret "Vja/VmdxaLOPR+alkjfsadffelkjlfw234522" go get access to the environment.
        It will use the default value "UTC" Time Zone for converting the different time and dates.
        It will use the default start time which is NOW.
        It will use the default end time which is 60 minutes.
         
    .NOTES
         
        The specified StartTime will always be based on local Time Zone. If you specify a different Time Zone than the local computer is running, the start and end time will be calculated based on your selection.
         
        For OnPremise environnement use -OnPremise flag to added "namespaces/AXSF" path to D365 URL and allow to get token from local ADFS server
         
        Tags: Servicing, Message, Users, Environment
         
        Author: Mötz Jensen (@Splaxi)
         
    .LINK
        Add-D365BroadcastMessageConfig
         
    .LINK
        Clear-D365ActiveBroadcastMessageConfig
         
    .LINK
        Get-D365ActiveBroadcastMessageConfig
         
    .LINK
        Get-D365BroadcastMessageConfig
         
    .LINK
        Remove-D365BroadcastMessageConfig
         
    .LINK
        Set-D365ActiveBroadcastMessageConfig
         
#>


function Send-D365BroadcastMessage {
    [CmdletBinding()]
    [OutputType()]
    param (
        [Parameter(Mandatory = $false, Position = 1)]
        [Alias('$AADGuid')]
        [string] $Tenant = $Script:BroadcastTenant,

        [Parameter(Mandatory = $false, Position = 2)]
        [Alias('URI')]
        [string] $URL = $Script:BroadcastUrl,

        [Parameter(Mandatory = $false, Position = 3)]
        [string] $ClientId = $Script:BroadcastClientId,

        [Parameter(Mandatory = $false, Position = 4)]
        [string] $ClientSecret = $Script:BroadcastClientSecret,

        [Parameter(Mandatory = $false, Position = 5)]
        [string] $TimeZone = $Script:BroadcastTimeZone,

        [Parameter(Mandatory = $false, Position = 6)]
        [datetime] $StartTime = (Get-Date),

        [Parameter(Mandatory = $false, Position = 7)]
        [int] $EndingInMinutes = $Script:BroadcastEndingInMinutes,

        [Parameter(Mandatory = $false, Position = 8)]
        [switch] $OnPremise = $Script:BroadcastOnPremise
    )

    $URL = $URL -replace "/$", ""

    $bearerParms = @{
        Resource     = $URL
        ClientId     = $ClientId
        ClientSecret = $ClientSecret
    }

    if ($OnPremise) {
        $bearerParms.AuthProviderUri = "$Tenant/oauth2/token"
    }
    else {
        $bearerParms.AuthProviderUri = "https://login.microsoftonline.com/$Tenant/oauth2/token"
    }

    $bearer = Invoke-ClientCredentialsGrant @bearerParms | Get-BearerToken

    $headerParms = @{
        URL         = $URL
        BearerToken = $bearer
    }

    $headers = New-AuthorizationHeaderBearerToken @headerParms

    [System.UriBuilder] $messageEndpoint = $URL

    if ($OnPremise) {
        $messageEndpoint.Path = "namespaces/AXSF/api/services/SysBroadcastMessageServices/SysBroadcastMessageService/AddMessage"
    }
    else {
        $messageEndpoint.Path = "api/services/SysBroadcastMessageServices/SysBroadcastMessageService/AddMessage"
    }

    $endTime = $StartTime.AddMinutes($EndingInMinutes)
    
    $timeZoneFound = Get-TimeZone -InputObject $TimeZone

    if (Test-PSFFunctionInterrupt) { return }
    
    $startTimeConverted = [System.TimeZoneInfo]::ConvertTime($startTime, [System.TimeZoneInfo]::Local, $timeZoneFound)
    $endTimeConverted = [System.TimeZoneInfo]::ConvertTime($endTime, [System.TimeZoneInfo]::Local, $timeZoneFound)

    $body = @"
{
    "request": {
        "FromDateTime": "$($startTimeConverted.ToString("s"))",
        "ToDateTime": "$($endTimeConverted.ToString("s"))"
    }
}
"@


    try {
        [PSCustomObject]@{
            MessageId = Invoke-RestMethod -Method Post -Uri $messageEndpoint.Uri.AbsoluteUri -Headers $headers -ContentType 'application/json' -Body $body
        }
    }
    catch {
        Write-PSFMessage -Level Host -Message "Something went wrong while trying to send a message to the users." -Exception $PSItem.Exception
        Stop-PSFFunction -Message "Stopping because of errors."
        return
    }
}