Live training: next batches coming soon. Join the waitlist:Waitlists open:Developer Training →Developer →Copilot Studio Training →Copilot Studio →

How to Export Microsoft Teams Chat History Using PowerShell

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&amp;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?
PowerShell convert Teams message HTML to text
Tags removed, & decoded and the @mention kept as a name (PowerShell 7)

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
PowerShell ConvertFrom-Json date stays a string in 5.1
The same value is a String in Windows PowerShell 5.1 (Windows PowerShell 5.1)

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!
PowerShell export Teams chat history to CSV
Four messages in reading order, times in Eastern (PowerShell 7)

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 &amp; 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: