Today’s post will cover the basics of using PowerShell to send Teams messages to users (One to One). This is through utilizing Microsoft Graph APIs.
Table of Contents
Prerequisites
You need to download PowerShell Graph SDK from Microsoft PowerShell Gallery.
Required permission scope:
- Chat.ReadWrite
- Chat.Create
If you are new to Microsoft Graph, then you can read more about Microsoft Graph and the scope here Understanding Microsoft Graph SDK PowerShell.
To download the Password Expiery Notifier using Teams PS Script, you can download it from my GitHub, Also you can read more about it in my blog on Microsoft Community DevBlog Password Expiry Notification Using Teams and Graph API
Starting Teams Chat session using PowerShell.
Before sending a message to a Teams user using PowerShell, we must create the chat session to get a ChatID and then use the ChatId with each message sent.
Each chat session with a Teams user has its unique chat ID, so a chat session with UserA has a different ID when sending a chat message to UserB.
Please note that you cannot use application authentication to send a Teams chat message. If you use Get-MgChat or New-MgChatMessage, you gets the following error message:
Requested API is not supported in application-only context
To create the Teams chat using PowerShell session, you need to use the New-MgChat
.
Using New-MgChat to Initiate a Chat Session
Graph Explorer is one of the best ways to start as it helps find information about the needed endpoint and permissions.
The Endpoint used to create chat is https://graph.microsoft.com/v1.0/chats
If you are not familiar with Microsoft Graph, I have already covered the basics you need to know in Using Graph Explorer To Find the Scope of a Cmdlet.
To start, we need to create a chat session using New-MgChat
, then use the ID from New-MgChat to send messages. Please note that only one chat session can be created. If there was a previous chat session, then PowerShell returned that chat session ID.
You can use the Get-MgChat cmdlet to get a list of all the previous chats. Using the Get-MgChat, return a list of all previous chat sessions, including meetings and OneOnOne.
PS C:\Users\rescu> Get-MgChat
Id ChatType CreatedDateTime LastUpdatedDateTime TenantId Topic WebUrl
-- -------- --------------- ------------------- -------- ----- ------
19:19f3febd1-4e69-4e69-4e69-19f3febd16f6_febd16f6-4e69-4e69-4e69-19f3febd1@unq.gbl.spaces oneOnOne 20-Dec-20 10:02:59 AM 20-Dec-20 10:02:59 AM e55eec26-4d5a-4d5a-4d5a-e55eec26bf5a https://teams.microsoft.com/l/chat/19%…
19:meeting_MTJhOWYzNGMtMDFhYS0MTJhOWYzNGMtOWYzNGMtMDFhYS@thread.v2 meeting 20-Jun-22 10:18:24 AM 13-Jul-22 8:01:01 AM e55eec26-4d5a-4d5a-4d5a-e55eec26bf5a New Meeting RFP https://teams.microsoft.com/l/chat/19%…
The New-MgChat requires some parameters defined in a collection of Hashtables and arrays. Feel free to copy it as is but just make sure to change the ID of the users in lines 11 and 18
To Get a user ID using Graph, you can use the
Get-MgUser -Userid UserA@Domain.com
Import-Module Microsoft.Graph.Teams
$params = @{
chatType = "oneOnOne"
members = @(
@{
"@odata.type" = "#microsoft.graph.aadUserConversationMember"
roles = @(
"owner"
)
"user@odata.bind" = "https://graph.microsoft.com/v1.0/users('xxxxxxxx-xxxx-4xxd-xxxd-d72xxxxxxx1')"
}
@{
"@odata.type" = "#microsoft.graph.aadUserConversationMember"
roles = @(
"owner"
)
"user@odata.bind" = "https://graph.microsoft.com/v1.0/users('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx')"
}
)
}
$myChatSession=New-MgChat -BodyParameter $params
Update 8 March 2024, I don’t know what did Microsoft do, but if you got the following error
New-MgChat : Provided payload is invalid. Errors –
Key – ” , Error – ‘An undeclared property ‘User’ which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and
declared named streams can be represented as properties without values.’
At line:21 char:1
FullyQualifiedErrorId : BadRequest,Microsoft.Graph.PowerShell.Cmdlets.NewMgChat_Create1
Make sure that the word User@odata is small letter, nothing start with capital letter
The Param has the following values in its hashtable:
- ChatType: Define the chat type. Is it one-to-one or a group? So the possible values are oneOnOne or Group.
- Members: The type is an array. This array contains a hashtable of the possible members, including the member who initiated the chat session.
- @odata.type: This is the type of a JSON object or name/value pair. Use the value as is.
- Roles: all users are owners except for the guest. The possible values are Owner or Guest.
- User@odata.bind is the URI for the user id participating in the chat. Replace the ID with the user’s ID, you can get this value by running.
(get-mgUser -UserId test@mydomain.com).id
The New-MgChat
uses the BodyParameter
parameter and the $params as the argument to execute the operating
To see the result of the command, call the $myChatSession
, the result looks like the following. We need the Id. The Id is assigned to New-MgChatMessage to send a message to a Teams user.
PS C:\Faris> $myChatSession | fl
ChatType : oneOnOne
CreatedDateTime : 20-Dec-20 10:02:59 AM
Id : 19:8ef78f89-b72e-b72e-b72e-19f3febd16f6_8ef78f89-b72e-433d-8287-19f3febd16f6@unq.gbl.spaces
InstalledApps :
LastUpdatedDateTime : 20-Dec-20 10:02:59 AM
Members :
Messages :
OnlineMeetingInfo : Microsoft.Graph.PowerShell.Models.MicrosoftGraphTeamworkOnlineMeetingInfo
Tabs :
TenantId : e55eec26-86c7-86c7-86c7-e55eec26bf5a
Topic :
WebUrl : https://teams.microsoft.com/l/chat/19%19:8ef78f89-b72e-b72e-b72e-19f3febd16f6_8ef78f89-b72e-433d-8287-19f3febd16f6%40unq.gbl.spaces/0?te
nantId=e55eec26-86c7-86c7-86c7-e55eec26bf5a
AdditionalProperties : {[@odata.context, https://graph.microsoft.com/v1.0/$metadata#chats/$entity]}
Send Teams Message Using PowerShell and New-MgChatMessage
As explained in the previous paragraph, we used the New-MgChat to get a chat Id. The Id includes information about all the participants in the chat.
To send a Teams message, we use the cmdlet New-MgChatMessage
to send the message.
Explaining the New-MgChatMessage Parameter.
The New-MgChatMessage accepts many parameters, but in this post, I will focus on the main ones.
- ChatId: is the chat id returned from the
New-MgChat
cmdlet. - Body: a hashtable contains the message content. It contains the following keys:
- Content: the message you want to send. The type is a string.
- ContentType: Message content type. Keep this key to HTML to send an HTML formatted message.
- HostedContents: You can include pictures and other inline content to include in the chat message. The hosted content is an array of hashtable, each one should include the following:
- “@microsoft.graph.temporaryId”: This is a temp ID. It can be whatever the value you want, but just make sure it matches with the Body
- ContentBytes: It’s the binary string of the file you need to include. use the
[System.IO.File]::ReadAllBytes('Path_To_Your_Image')
to do the conversion. - ContentType: The type of the attached content. For example, “image/png”
- Importance: Support three values Normal, High and Urgent.
- Mentions: To mention a user in the sent message. The type of this object is an array of hashtables. Each hashtable is a single user and should include the following information.
- ID: a serial number given to a user, start it from 0, and each added user to the mention should have a unique number.
- MentionText: How the recipient sees his mention can be anything. For example, Mr. Faris Malaeb.
- Mentioned: The list of entities that are part of the mention, the type of this object is a hashtable. The possible values are user, bot, team, channel, and tag. I will explain the User mention.
- User: A hashtable of the user details which include:
- ID: The ID of the user. Use Get-MgUser to find the user id.
- UserIdentityType: the user directory location, usually, its aadUser.
Using New-MgChatMessage To Send Inline Image.
Some elements must be specified in the Body \ Content otherwise, the message won’t be delivered, and you will get an error.
Recall the HostedContent
and how it has a unique ID, this ID must be passed inside the message Body \ Content.
– Dont forget, its an inline item not an attachment, so if you try to pass a PDF file it wont work and you will get an error about an unsupported content type.
New-MgChatMessage_CreateExpanded: Unsupported hosted content type
– I assume you already have the ChatID fromGet-MgChat
, and the value is stored in a$myChatSession.Id
variable
See how the image from the HostedContents is presented in the Body in line 4. The img src
presented as the following “../hostedContents/1/$value”. Keep it as is no need to change anything except the number “1”. So, in the following example, the temporaryId in the HostedContents is 1. All you need to do in the img src
is use this number.. keep the rest the same.
$Body = @{
ContentType = 'html'
Content = @'
<img height="200" src="../hostedContents/1/$value" width="200" style="vertical-align:bottom; width:700px; height:700px">
<Strong>I am a nice bot</Strong>
'@
}
$HostedContents = @(
@{
"@microsoft.graph.temporaryId" = "1"
ContentBytes = [System.IO.File]::ReadAllBytes("C:\Users\f.malaeb\Pictures\ShellBot.png")
ContentType = "image/png"
}
)
New-MgChatMessage -ChatId $myChatSession.id -Body $Body -HostedContents $HostedContents
The result looks like this.
Using New-MgChatMessage To Mention a User.
The following example sends an HTML message with a user named VDI One is mentioned.
See the Body \ Content how the mention is presented. You must use the following tag <at id="X">YYY YYY</at>
. But you need to replace the X with the Id from the Mentions \ id.
Also, you need to replace the YYY YYY with the exact value from Mentions \ MentionText.
$Body = @{
ContentType = 'html'
Content = @'
Hello Mr/Mis <at id="0">Mr. VDI One</at> and <at id="0">Mis. VDI Two</at><br> Thanks for your Message
'@
}
$Mentions = @(
@{
Id = 0
MentionText = "Mr. VDI One"
Mentioned = @{
User = @{
Id = "5e65eafa-b164-b164-b164-5e65eafab164"
UserIdentityType = "aadUser"
}
}
}
)
New-MgChatMessage -ChatId $myChatSession.id -Body $Body -Mentions $Mentions
The results look like
Sending Teams Chat Using PowerShell (Muiltiple Mentions and Inline Images)
The following example puts everything together and shows how to send multiple inline images with multiple mentions. This helps you understand how to add more and more elements to the chat message.
## Start a Chat Session using the New-MgChat to create a session ID
$params = @{
ChatType = "oneOnOne"
Members = @(
@{
"@odata.type" = "#microsoft.graph.aadUserConversationMember"
Roles = @(
"owner"
)
"User@odata.bind" = "https://graph.microsoft.com/v1.0/users('8f89cb19-6e65-6e65-6e65-8ef78f89cb19')"
}
@{
"@odata.type" = "#microsoft.graph.aadUserConversationMember"
Roles = @(
"owner"
)
"User@odata.bind" = "https://graph.microsoft.com/v1.0/users('eafa16f6-b72e-b72e-b72e-5e65eafa16f6')"
}
)
}
$myChatSession=New-MgChat -BodyParameter $params
##### Sending Teams Message using New-MgChatMessage
$Body = @{
ContentType = 'html'
Content = @'
Hello Mr/Mis <at id="0">Mr. VDI One</at> and <at id="1">Mis. VDI Two</at>
<img height="200" src="../hostedContents/1/$value" width="200" style="vertical-align:bottom; width:700px; height:700px">
<Strong>Thanks for your attention</Strong>
<img height="200" src="../hostedContents/2/$value" width="200" style="vertical-align:bottom; width:700px; height:700px">
'@
}
$HostedContents = @(
@{
"@microsoft.graph.temporaryId" = "1"
ContentBytes = [System.IO.File]::ReadAllBytes("C:\Users\f.malaeb\Pictures\ShellBot.png")
ContentType = "image/png"
}
@{
"@microsoft.graph.temporaryId" = "2"
ContentBytes = [System.IO.File]::ReadAllBytes("C:\Users\f.malaeb\Pictures\Thanks.jpg")
ContentType = "image/png"
}
)
$Mentions = @(
@{
Id = 0
MentionText = "Mr. VDI One"
Mentioned = @{
User = @{
Id = "5e65eafa-b164-b164-b164-5e65eafa16f6"
UserIdentityType = "aadUser"
}
}
}
@{
Id = 1
MentionText = "Mis. VDI Two"
Mentioned = @{
User = @{
Id = "53a804223-b535-b535-b535-60c7653a8047"
UserIdentityType = "aadUser"
}
}
}
)
New-MgChatMessage -ChatId $myChatSession.id -Body $Body -HostedContents $HostedContents -Mentions $Mentions
The result looks like the following.
Related: Did you know you can also configure Azure AD conditional access using Microsoft Graph PowerShell module, Read more about it from here
How to automate if we can’t use Application method as authentication?
This is the challenge… for now, this won’t be fully automated as the delegate auth is used due to the endpoint support.
This is not working if I use mentions get the below error
PS C:\Windows\system32> New-MgChatMessage -ChatId $myChatSession.id -Body $Body -Mentions $Mentions
New-MgChatMessage : Invalid request body was sent.
Status: 400 (BadRequest)
ErrorCode: BadRequest
Date: 2023-11-24T18:19:52
Headers:
Transfer-Encoding : chunked
Vary : Accept-Encoding
Strict-Transport-Security : max-age=31536000
request-id : 7e4f32b6-e018-4f03-926c-3fd3b25de694
client-request-id : 6bc248c0-528a-4abd-aba1-ed5c2c715574
x-ms-ags-diagnostic : {“ServerInfo”:{“DataCenter”:”South
India”,”Slice”:”E”,”Ring”:”2″,”ScaleUnit”:”001″,”RoleInstance”:”MA1PEPF00002517″}}
Date : Fri, 24 Nov 2023 18:19:51 GMT
At line:1 char:1
+ New-MgChatMessage -ChatId $myChatSession.id -Body $Body -Mentions $Me …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({ ChatId = 19:3…phChatMessage }:f__AnonymousType0`2) [New-MgChatM
essage_CreateExpanded], Exception
+ FullyQualifiedErrorId : BadRequest,Microsoft.Graph.PowerShell.Cmdlets.NewMgChatMessage_CreateExpanded
Will go through this one again,
I think Microsoft update something from their end.
You can use a Graph ROPC flow for non-channel based chat messages. This auth flow requires an AAD app and user. The user can be a dummy service user email/password or an actual user/password, however, the user must be licensed. The app will have a Client Id and Client Secret, the user will have an email address and password. The permissions are delegated from the user to the app. The process I wrote only needed to send a chat to any user, not receive a chat. The permissions I needed were: Chat.Create Chat.ReadWrite ChatMessage.Send User.Read User.ReadBasic.All
Hope this helps…
Hi Peter,
Thanks for the great addition. I will update the post soon to reflect the notes your specified
Hi is it possible to mention everyone in the teams group chat? using the graph (not mentioning them individually)
Did you try to use @ sign.
for example, if your teams name is MyTeams call it through @MyTeams
Let me know if it work
hi peter I’ve created aan aad app and when I log in with my token i get the token responce,
with valid tokens and everything. but my ‘account’ of the mgcontext is blank.
when i try to send a chat I receive;
New-MgChatMessage : Message POST is allowed in application-only context only for import purposes
this is al pretty fresh, but you wouldn’t have some more examples ?
I used:
$Body = @{
Grant_Type = “client_credentials”
Scope = $Scope
client_Id = $clientID
Client_Secret = $clientSecret
}
$authUri = “https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token”
$TokenResponse = Invoke-RestMethod -Uri $authUri -Method POST -Body $Body
Connect-MgGraph -AccessToken $TokenResponse.access_token
————-
to connect
Hi Faris, I’m struggling to receive the ChatID. The request looks fine, but still it gets refused
Name Value
—- —–
User@odata.bind https://graph.microsoft.com/v1.0/users('5afc298f-xxx-xxxxx-xxxxxx‘)
Roles {owner}
@odata.type #microsoft.graph.aadUserConversationMember
New-MgChat : Provided payload is invalid. Errors –
Key – ” , Error – ‘An undeclared property ‘User’ which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and
declared named streams can be represented as properties without values.’
Status: 400 (BadRequest)
No worry, I think Microsoft did some update to the endpoint.
I will check it and update it soon.
Hi Faris,
I struggle at the start of the script. The request for chats was refused, because of mal formatted payload.
This is the content of my @params
Name Value
—- —–
User@odata.bind https://graph.microsoft.com/v1.0/users('5afc298f-xxxx-xxxx-xxxxx‘)
Roles {owner}
@odata.type #microsoft.graph.aadUserConversationMember
New-MgChat : Provided payload is invalid. Errors –
Key – ” , Error – ‘An undeclared property ‘User’ which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and
declared named streams can be represented as properties without values.’
Status: 400 (BadRequest)
ErrorCode: BadRequest
Hi Gaultier,
Here is the fix, it seems that Microsoft did some update on thire endpoint
I update the script, and the problem is that the word user@odata.bind should all be in small letter.
just copy and paste the updated code and things should be fine.