UPDATED January 2026 -- Added a note about ConsentFix hardening and how it affects the Microsoft Graph Command Line Tools default app. If your tenant has locked down consent attacks, you'll need to register your own app instead.
Exporting Teams Chats: Why You Might Need It, and How I Built a Tool for It
If you've spent more than five minutes in Microsoft Teams, you've probably hit a wall trying to do something that should be incredibly simple: getting a copy of your own damn chat out of the system.
Maybe HR asked for a copy of a conversation. Maybe you're trying to feed a massive technical thread into an LLM so it can write the documentation you've been avoiding. Or maybe you're just paranoid that the "temporary" project team is going to be deleted tomorrow and take all your architectural decisions with it.
Whatever the reason, Microsoft in their infinite wisdom decided that "Export Chat" shouldn't be a button you can just click. Your options are essentially: screenshot everything like it's 2012 (terrible), blindly trust that Teams will keep it forever (risky), or try to convince your compliance team to run an eDiscovery search for you (expensive and usually requires filling out three forms).
So, naturally, I got annoyed and built a PowerShell script to do it properly: Export-TeamsChat.
Disclaimer: This is a proof of concept. I've tested it on a few different tenants and it works great, but it's not an officially supported enterprise tool. Use it for what it is—a handy utility—and always test in a non-production environment first before you point it at the CEO's chat history.
Why Export Teams Chats?
Before we look at the code, let's talk about why you'd bother doing this.
Feeding the AI overlords. I've been experimenting with taking messy, rambling technical chats and throwing them into LLMs to extract action items or summarize decisions. For that to actually work, you need structured data—JSON or CSV—not a bunch of PNGs you took with Snipping Tool.
Archiving that doesn't suck. Some of us still maintain knowledge bases in flat files or markdown. If critical decisions live in Teams, exporting them to TXT or HTML lets you archive them in a format you control, safely away from any Microsoft 365 licensing disputes.
The "Just in Case" folder. Not an eDiscovery replacement, but useful for internal reviews or just making sure you have a paper trail of that one vendor who keeps changing their story.
Which brings me to the obvious warning: this is not a forensic or legal eDiscovery solution. There are no cryptographic checksums here. No chain-of-custody mechanism. I can export a chat to JSON and change what my boss said. If you need tamper-evident records that hold up in court, use Microsoft 365 Purview eDiscovery. If you just want to read your chat history without opening the bloated Teams client? Keep reading.
Two Ways to Authenticate (Pick Your Poison)
The script supports two very different authentication modes because "personal use" and "enterprise automation" are two very different beasts.
Delegated Mode: The "I Just Want My Own Stuff" Option
You sign in with your own account. The script only touches chats you're already a participant in. No client secrets to manage, no begging the global admins for consent.
pwsh ./Export-TeamsChat.ps1 -Interactive
When you run this, it kicks off a device code flow:
- Enter your tenant ID (find it in the Entra admin center, you know the drill).
- Press Enter to use the default Microsoft Graph Command Line Tools app.
- Open
https://microsoft.com/devicelogin, punch in the code, and sign in. - Paste your Teams chat URL when prompted.
- Pick your format and let it run.
That's it. It's the safest option by far because it literally cannot access anything your account can't already see.
A note on the Microsoft Graph Command Line Tools app: This default app (14d82eec-204b-4c2f-b7e8-296a70dab67e) is fantastic because it requires zero registration. You just pull the chat. The catch? If your security team is actually paying attention—perhaps they've read my ConsentFix article—they might have locked down token refresh or disabled this app entirely to prevent consent attacks. If your tenant is locked down, you'll need to register your own app with the Chat.Read delegated permission instead. It's the same process, just one extra hoop to jump through.
App-Only Mode: The "I Am The Admin" Option
You register an app in Entra ID with Chat.Read.All application permission and admin consent. The script can now read any chat in the entire tenant.
Use this when you need to:
- Automate bulk exports across the tenant.
- Export chats for users who have already left the company.
- Run unattended scripts on a server somewhere.
The trade-off? You're managing a client secret. Protect it like the keys to the kingdom, because it essentially is.
Setting Up App-Only Mode
If you actually need tenant-wide access, here's the setup:
- Head to Microsoft Entra admin center → App registrations.
- Create a new app registration.
- Go to API permissions -> Add a permission -> Microsoft Graph.
- Select Application permissions (NOT delegated) and check off:
Chat.Read.All(required)ChatMessage.Read.All(optional, but a good idea)
- Smash that Grant admin consent button.
- Generate a client secret under Certificates & secrets.
- Write down the Tenant ID, Client ID, and the Secret.
Then you either pass them all as parameters:
pwsh ./Export-TeamsChat.ps1 -TenantId "..." -ClientId "..." -ClientSecret "..." -TeamsUrl "..."
Or you save them in a TeamsExportConfig.json file next to the script. If you choose the JSON route, for the love of all that is holy, do not commit that file to git. Add it to .gitignore before you even create it.
The Output Formats
The script spits out data in four formats because everyone's downstream process is different.
TXT — Plain text. Good for reading, printing, or pasting into an email when you need to prove a point quickly. Includes timestamps and who sent what.
JSON — Structured data. Full message metadata, exact timestamps, and raw reaction data. Perfect if you're feeding this into a Python script or an LLM.
HTML — A self-contained web page. It actually looks pretty decent. Open it in Edge, print to PDF, file it away.
CSV — For the spreadsheet warriors. Every message gets a row. Pull it into Excel, filter by sender, and pivot-table your heart out.
How It Works (The Short Version)
It's just PowerShell and the Microsoft Graph API doing a little dance.
- Auth: Gets a token using either device code or client credentials.
- Parse: Rips the actual chat ID out of the Teams URL (whether it's
@thread.v2or@unq). You can even give it a link to a specific message and it figures it out. - Loop: The Graph API only gives you 50 messages at a time. The script patiently paginates through until it hits the end.
- Format: Transforms the raw Graph JSON into whatever format you asked for.
- Save: Drops the file on your disk and spits out the path.
Things to Keep in Mind
Delegated mode means your chats only. You cannot use the -Interactive flag to read the C-suite's private channel. The Graph API blocks it, not me.
App-only mode is dangerous. Don't hand out Chat.Read.All to a service account unless you absolutely trust the person holding the secret.
Emojis are weird. The HTML output usually renders emojis fine. TXT might spit out Unicode blocks depending on your console font. JSON will give you the raw data.
No files attached. It exports the text of the chat. If someone dropped a 40MB PowerPoint in the thread, the script will tell you a file was attached, but it won't download the actual file.
It can't read deleted messages. If your company has a 30-day retention policy that actively purges Teams messages, the script can't magically recover them. It only sees what you can see.
Go Get It
I built this because I was tired of doing things the hard way. It's open-source (Prosperity Public License 3.0.0, non-commercial + 30-day commercial trial), and it works.
Grab the script over at github.com/mardahl/Export-TeamsChat.
You need PowerShell 7+ and an Entra ID tenant. Download it, run it, and finally get your data out of the walled garden.