To export Microsoft Teams chat history with PowerShell, connect to Microsoft Graph with the Chat.Read permission, get the messages with Get-MgChatMessage, turn the HTML bodies into text and save them with Export-Csv:
Connect-MgGraph -Scopes 'Chat.Read'
$messages = Get-MgChatMessage -ChatId $chatId -All
The raw messages need some cleanup before they’re readable. Below I cover finding the chat, skipping system events, converting HTML to text, fixing the time zone and writing CSV and HTML files.
I didn’t sign in to a real tenant for this post. The processing steps ran on a sample file shaped exactly like the Graph response, in PowerShell 7 and Windows PowerShell 5.1.
Connect and find the chat
Install the Teams part of the Microsoft Graph PowerShell SDK, sign in, and list your chats to get the chat ID:
Install-Module -Name Microsoft.Graph.Teams -Scope CurrentUser
Connect-MgGraph -Scopes 'Chat.Read'
Get-MgChat -All | Select-Object -Property Id, Topic, ChatType
Group chats show their Topic, while one-on-one chats usually have none. If you also manage Teams settings from the shell, see how to connect to Microsoft Teams using PowerShell.
Get the messages
Graph returns at most 50 messages per page. -All follows the next-page links for you. Save the result as JSON so you can rerun the cleanup without calling Graph again:
$messages = Get-MgChatMessage -ChatId $chatId -All
@{ value = $messages } | ConvertTo-Json -Depth 10 | Set-Content -Path .\austin-launch-messages.json
My sample file has five messages from a store launch chat, including one system event. Grouping by messageType shows what’s inside:
$response = Get-Content -Path .\Teams\austin-launch-messages.json -Raw | ConvertFrom-Json
$response.value | Group-Object -Property messageType | Select-Object -Property Name, Count
Output:
Name Count
---- -----
message 4
systemEventMessage 1
System events record things like a renamed chat or an added member. They have no sender, so I skip them. If you need to clean up chats after the export, see how to delete chats in Microsoft Teams.
Turn message HTML into plain text
Most message bodies are HTML, with paragraphs, bold text, line breaks and @mention tags. These two helpers clean the text and fix the time zone, and I saved them as ChatHelpers.ps1:
function ConvertTo-ChatText ([string]$Html) {
$text = $Html -replace '<br\s*/?>|</p>', ' ' -replace '<[^>]+>', ''
[System.Net.WebUtility]::HtmlDecode($text).Trim() -replace '\s{2,}', ' '
}
function ConvertTo-Eastern ($Value) {
$utc = if ($Value -is [datetime]) { $Value.ToUniversalTime() }
else { [datetimeoffset]::Parse($Value, [cultureinfo]::InvariantCulture).UtcDateTime }
[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($utc, 'Eastern Standard Time')
}
. .\ChatHelpers.ps1
ConvertTo-ChatText '<p>Shelves arrive Thursday.<br>Q&A sheet is in the channel.</p>'
ConvertTo-ChatText '<p><at id="0">Tom</at> can you confirm the shelf delivery?</p>'
Output:
Shelves arrive Thursday. Q&A sheet is in the channel.
Tom can you confirm the shelf delivery?

To control how chats look before you export them, see changing the font style in Microsoft Teams.
Watch the dates: PowerShell 7 vs 5.1
createdDateTime is in UTC. PowerShell 7’s ConvertFrom-Json turns it into a DateTime, while Windows PowerShell 5.1 leaves it as text:
$response = Get-Content -Path .\Teams\austin-launch-messages.json -Raw | ConvertFrom-Json
$created = $response.value[0].createdDateTime
"createdDateTime is a: $($created.GetType().Name)"
Output:
createdDateTime is a: DateTime

ConvertTo-Eastern handles both, then converts to US Eastern time with TimeZoneInfo. Swap in ‘Central Standard Time’ or ‘Pacific Standard Time’ for other offices. To set the time zone Teams shows in the app, see how to change the time zone in Microsoft Teams.
Export the chat to CSV
Put it together: keep real messages, build one row each, and sort by time. Graph only returns newest first, so Sort-Object gives you the normal reading order:
. .\ChatHelpers.ps1
$response = Get-Content -Path .\Teams\austin-launch-messages.json -Raw | ConvertFrom-Json
$transcript = $response.value |
Where-Object messageType -EQ 'message' |
ForEach-Object {
[pscustomobject]@{
TimeET = (ConvertTo-Eastern $_.createdDateTime).ToString('yyyy-MM-dd HH:mm')
From = $_.from.user.displayName
Text = ConvertTo-ChatText $_.body.content
}
} | Sort-Object -Property TimeET
$transcript | Export-Csv -Path .\Teams\austin-launch-chat.csv -NoTypeInformation
"Exported $($transcript.Count) messages"
$transcript | Format-Table -Property TimeET, From, Text -Wrap
Output:
Exported 4 messages
TimeET From Text
------ ---- ----
2026-10-28 10:05 Maria Lopez Kickoff for the Austin store launch on Oct 30, 2026
2026-10-28 10:20 Dana Reyes Tom can you confirm the shelf delivery?
2026-10-28 14:15 Tom Baker Shelves arrive Thursday. Q&A sheet is in the channel.
2026-10-28 15:42 Maria Lopez Perfect. See you all at the Austin store Friday!

The CSV opens in Excel and is easy to search. For another Teams export, see how to download the attendance list from a Teams meeting.
Create an HTML transcript
For a version that’s easy to read or attach to a ticket, convert the CSV rows to an HTML page:
$rows = Import-Csv -Path .\Teams\austin-launch-chat.csv
$rows | ConvertTo-Html -Title 'Austin store launch chat' -PreContent '<h1>Austin store launch chat</h1>' |
Out-File -FilePath .\Teams\austin-launch-chat.html -Encoding utf8
"HTML rows: $(@(Select-String -Path .\Teams\austin-launch-chat.html -Pattern '<tr><td>').Count)"
Output:
HTML rows: 4
ConvertTo-Html encodes the message text, so a message with < or & can’t break the page. For more ready-made admin scripts, see these PnP PowerShell scripts for SharePoint.
Exporting chats for other users
Chat.Read only reaches chats you’re part of. Reading other people’s chats needs an application permission such as Chat.Read.All, which requires admin consent and a registered app.
For legal holds and compliance exports, Microsoft Purview eDiscovery is usually the better tool. It can search and export Teams chats without writing code, and it keeps an audit trail.
Frequently Asked Questions
How do I export Teams chat history with PowerShell?
Run Connect-MgGraph -Scopes 'Chat.Read', get the messages with Get-MgChatMessage -ChatId $id -All, convert the HTML to text and pipe the rows to Export-Csv.
What permission do I need to read Teams chat messages?
Chat.Read for chats you’re in. Application access uses ChatMessage.Read.Chat or Chat.Read.All, as listed in Microsoft’s List messages in a chat reference.
Why are my exported messages in the wrong order?
Graph returns the newest messages first and doesn’t support ascending order. Sort by createdDateTime with Sort-Object.
Why does the message text contain HTML tags?
Teams stores most messages as HTML. Remove the tags and decode entities like & before you export.
Can I export all chats in my organization?
Use Microsoft Purview eDiscovery for organization-wide exports. See searching Teams content in eDiscovery.
Working with Microsoft 365 data in PowerShell? Try these:
- Connect to Microsoft Teams using PowerShell
- Download a recording from Microsoft Teams
- Microsoft Teams vs SharePoint

Hey! I’m Bijay Kumar, founder of SPGuides.com and a Microsoft Business Applications MVP (Power Automate, Power Apps). I launched this site in 2020 because I truly enjoy working with SharePoint, Power Platform, and SharePoint Framework (SPFx), and wanted to share that passion through step-by-step tutorials, guides, and training videos. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.