Forum Discussion
AshMSport
May 18, 2022Copper Contributor
Unable to Export All Channels Conversations from a Team using Graph-PowerShell
I'm using the below script to export channel conversations. It works fine per channel. But I want to be able to export all channel conversations in a Team. I tried using ForEach ($Channel in $Channels) but it's not working. Can someone help please. Credit to PSGuy for the original script: https://www.psguy.eu/how-to-export-ms-teams-chat-to-html-file-for-backup/
[CmdletBinding(DefaultParameterSetName='default')]
param
(
[Parameter(ParameterSetName='Channel')]
$Team,
[Parameter(Mandatory=$false,ParameterSetName='default')]
[Parameter(Mandatory=$true,ParameterSetName='Channel')]
$Channel
)
Write-Host "Exporting Team Chats Homie"
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$Date = Get-Date -Format "MM-dd-yyyy-HHmm"
$clientId = "YourClientID"
$tenantName = "YourTenantName"
$clientSecret = "YourClientSecret"
$resource = "https://graph.microsoft.com/"
$ReqTokenBody = @{
Grant_Type = "Password"
client_Id = $clientID
Client_Secret = $clientSecret
Username = 'YourTeamsAdmUserName'
Password = 'YourTeamsAdmPassword'
Scope = "https://graph.microsoft.com/.default"
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
#Getting all Groups
$apiUrl = "https://graph.microsoft.com/beta/groups"
$Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get -ErrorVariable RespErr
$Groups = ($Data | Select-Object Value).Value
if ($Team -eq $NULL){
Write-Host "You have" -NoNewline
Write-Host " $($Groups.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " teams."
Write-Host ""
Write-Host "Messages from which Team do you want to export to the HTML format?" -ForegroundColor Yellow
$Groups | FT DisplayName,Description
$Team = Read-Host "Type one of the Team (DisplayName)"
}
$TeamID = ($Groups | Where-Object {$_.displayname -eq "$($Team)"}).id
$apiUrl = "https://graph.microsoft.com/v1.0/teams/$TeamID/Channels"
$Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
if ($Channel -eq $NULL){
Write-Host "You choose" -NoNewline
Write-Host " $($Team)" -ForegroundColor Yellow -NoNewline
Write-Host " Team."
Write-Host ""
$Channels = ($Data | Select-Object Value).Value
Write-Host "Messages from which Channel do you want to export to the HTML format?" -ForegroundColor Yellow
$Channels | FT DisplayName,Description
$Channel = Read-Host "Type one of the Channel(DisplayName)"
}
$ChannelID = (($Data | Select-Object Value).Value | Where-Object {$_.displayName -eq "$($Channel)"}).ID
$apiUrl = "https://graph.microsoft.com/beta/groups/$TeamID/members"
$Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
class messageData
{
[string]$dateTime
[string]$from
[string]$body
messageData()
{
$this.dateTime = ""
$this.from = ""
$this.body = ""
}
}
function parseMessage($Data) #returns resultset
{
$messages = ($Data | Select-Object Value).Value
foreach ($message in $Messages)
{
$messageID = $message.id
$messageSet = New-Object System.Collections.ArrayList;
$result = New-object messageData
#parse message
if ($NULL -eq $message.from.user.displayName) {
$result.dateTime = $message.createdDateTime
$result.from = $message.from.application.displayName
}
else {
$result.dateTime = $message.createdDateTime
$result.from = $message.from.user.displayName
}
$bodyOut = ""
if ($NULL -eq $message.summary)
{
foreach ($attachment in $message.attachments)
{
$output = $attachment.content
$output = $output.substring(14)
$output = $output.substring(0,$output.length-4)
$bodyOut = $bodyOut + $output
}
}
else {
$bodyOut = $message.summary;
}
$bodyOut = $bodyOut + $message.body.content
$result.body = $bodyOut;
$messageSet.Add($result)
#parse replies
$repliesURI = "https://graph.microsoft.com/beta/teams/" + $TeamID + "/channels/" + $ChannelID + "/messages/" + $messageID + "/replies?`$top100"
$repliesResponse = Invoke-RestMethod -Method Get -Uri $repliesURI -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"}
foreach ($reply in $repliesResponse.value )
{
$replyData = New-Object messageData
if ($NULL -eq $reply.from.user.displayName) {
$replyData.dateTime = $reply.createdDateTime
$replyData.from = $reply.from.application.displayName
}
else {
$replyData.dateTime = $reply.createdDateTime
$replyData.from = $reply.from.user.displayName
}
$bodyOut = ""
if ($NULL -eq $message.summary)
{
foreach ($attachment in $reply.attachments)
{
$output = $attachment.content
$output = $output.substring(14)
$output = $output.substring(0,$output.length-4)
$bodyOut = $bodyOut + $output
}
}
else {
$bodyOut = $message.summary
}
$replyData.body = $bodyOut + $reply.body.content
$messageSet.Add($replyData)
}
$resultList.Add($messageSet)
}
return
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
$resultList = New-Object System.Collections.ArrayList;
$apiUrl = "https://graph.microsoft.com/beta/teams/$TeamID/channels/$ChannelID/messages?`$top=100"
$sourceData = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
parseMessage($sourceData)
$nextLink = $sourceData.'@Odata.NextLink'
while ($NULL -ne $nextLink)
{
$nextURL = $nextLink;
$sourceData = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $nextURL -Method Get
parseMessage($sourceData)
$nextLink = $sourceData.'@Odata.NextLink'
}
$resultFieldSet = New-Object System.Collections.ArrayList
foreach($resultData in $resultList) {
$resultFields = $resultData | Select-Object @{Name = 'DateTime'; Expression = {Get-Date -Date (($_).dateTime) -Format 'MM/dd/yyyy hh:mm:ss.fff tt'}}, @{Name = 'From'; Expression = {((($_).from))}}, @{Name = 'Message'; Expression = {(($_).body) -replace '<.*?>',''}}| Sort-Object DateTime
$resultFieldSet.Add($resultFields)
}
$Header = @"
<style>
h1, h5, th { text-align: center; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
</style>
"@
$count = 0
foreach ($resultCount in $resultList){
$count = $count + $resultCount.Count
}
$body = "<body><b>Generated:</b> $(Get-Date -Format 'MM/dd/yyyy hh:mm tt') <br><br> <b>Team Name:</b> $($Team) <br> <b>Channel Name:</b> $($Channel) <br><br>" + "<b>number of messages:</b> " + $count + " <br><br>"
$body = $body + "</head>"
$resultHtml = ""
foreach ($resultFields in $resultFieldSet){
$tempHtml = $resultFields | ConvertTo-Html -Head $header
$resultHtml = $tempHtml + "<br>" + $resultHtml
}
$resultHtml = $body + "<br>" + $resultHtml
$Export = "$dir\TeamsHistory\$Team-$Channel"
New-Item -ItemType Directory -Path $Export -ErrorAction Ignore
$resultHtml | Out-File $Export\$Team-$Channel-$Date.html
Write-Host "
"
Write-Host "Messages from the" -NoNewline
Write-Host " $($Team)" -NoNewline -ForegroundColor Yellow
Write-Host " team and" -NoNewline
Write-Host " $($Channel)" -NoNewline -ForegroundColor Yellow
Write-Host " channel were generated and saved to the" -NoNewline
Write-Host " $($Export)" -NoNewline -ForegroundColor Yellow
Write-Host " as a" -NoNewline
Write-Host " $($Team)-$($Channel)-$($Date).html" -NoNewline -ForegroundColor Yellow
Write-Host " file."
Write-Host "
"
- Vinay BhatiaBrass Contributor
AshMSport To export messages from all Channels within a team, you need to loop through the Channels. Here is a slightly modified script (All credit to original author you mentioned in your post).
Ensure your app has the required permissions. For my test, I gave the following permissions. It may be more permissions than what is really needed. You can remove permissions one by one and test to determine the minimal permissions required.
[CmdletBinding(DefaultParameterSetName='default')] param ( [Parameter(ParameterSetName='Channel')] $Team, [Parameter(Mandatory=$false,ParameterSetName='default')] [Parameter(Mandatory=$true,ParameterSetName='Channel')] $Channel ) Write-Host "Exporting Team Chats Homie" $scriptpath = $MyInvocation.MyCommand.Path $dir = Split-Path $scriptpath $Date = Get-Date -Format "MM-dd-yyyy-HHmm" #TODO: Update for your tenant $clientId = "XXXXXXX" $tenantName = "YYYYYY" $clientSecret = "ZZZZZZZ" $resource = "https://graph.microsoft.com/" class messageData { [string]$dateTime [string]$from [string]$body messageData() { $this.dateTime = "" $this.from = "" $this.body = "" } } function parseMessage($Data) #returns resultset { $messages = ($Data | Select-Object Value).Value foreach ($message in $Messages) { $messageID = $message.id $messageSet = New-Object System.Collections.ArrayList; $result = New-object messageData #parse message if ($NULL -eq $message.from.user.displayName) { $result.dateTime = $message.createdDateTime $result.from = $message.from.application.displayName } else { $result.dateTime = $message.createdDateTime $result.from = $message.from.user.displayName } $bodyOut = "" if ($NULL -eq $message.summary) { foreach ($attachment in $message.attachments) { $output = $attachment.content if ($output) { $output = $output.substring(14) $output = $output.substring(0,$output.length-4) } $bodyOut = $bodyOut + $output } } else { $bodyOut = $message.summary; } $bodyOut = $bodyOut + $message.body.content $result.body = $bodyOut; $messageSet.Add($result) #parse replies $repliesURI = "https://graph.microsoft.com/beta/teams/" + $TeamID + "/channels/" + $ChannelID + "/messages/" + $messageID + "/replies?`$top100" $repliesResponse = Invoke-RestMethod -Method Get -Uri $repliesURI -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} foreach ($reply in $repliesResponse.value ) { $replyData = New-Object messageData if ($NULL -eq $reply.from.user.displayName) { $replyData.dateTime = $reply.createdDateTime $replyData.from = $reply.from.application.displayName } else { $replyData.dateTime = $reply.createdDateTime $replyData.from = $reply.from.user.displayName } $bodyOut = "" if ($NULL -eq $message.summary) { foreach ($attachment in $reply.attachments) { $output = $attachment.content if ($output) { $output = $output.substring(14) $output = $output.substring(0,$output.length-4) } $bodyOut = $bodyOut + $output } } else { $bodyOut = $message.summary } $replyData.body = $bodyOut + $reply.body.content $messageSet.Add($replyData) } $resultList.Add($messageSet) } return } $ReqTokenBody = @{ Grant_Type = "Password" client_Id = $clientID Client_Secret = $clientSecret #TODO: Update for your tenant Username = 'admin@XXXXXXXX.onmicrosoft.com' Password = 'YYYYYYYYYY' Scope = "https://graph.microsoft.com/.default" } $TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody #Getting all Groups $apiUrl = "https://graph.microsoft.com/beta/groups" $Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get -ErrorVariable RespErr $Groups = ($Data | Select-Object Value).Value if ($Team -eq $NULL){ Write-Host "You have" -NoNewline Write-Host " $($Groups.Count)" -ForegroundColor Yellow -NoNewline Write-Host " teams." Write-Host "" Write-Host "Messages from which Team do you want to export to the HTML format?" -ForegroundColor Yellow $Groups | FT DisplayName,Description $Team = Read-Host "Type one of the Team (DisplayName)" } $TeamID = ($Groups | Where-Object {$_.displayname -eq "$($Team)"}).id $apiUrl = "https://graph.microsoft.com/v1.0/teams/$TeamID/Channels" $Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get if ($Channel -eq $NULL){ Write-Host "You choose" -NoNewline Write-Host " $($Team)" -ForegroundColor Yellow -NoNewline Write-Host " Team." Write-Host "" $Channels = ($Data | Select-Object Value).Value #Write-Host "Messages from which Channel do you want to export to the HTML format?" -ForegroundColor Yellow #$Channels | FT DisplayName,Description #$Channel = Read-Host "Type one of the Channel(DisplayName)" } foreach ($Channel in $Channels) { #$ChannelID = (($Data | Select-Object Value).Value | Where-Object {$_.displayName -eq "$($Channel)"}).ID $ChannelID = $Channel.id Write-Host "Channel ID: " + $ChannelID #$apiUrl = "https://graph.microsoft.com/beta/groups/$TeamID/members" $apiUrl = "https://graph.microsoft.com/v1.0/groups/$TeamID/members" $Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get $TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody $resultList = New-Object System.Collections.ArrayList; #Correct URL with escape $apiUrl = "https://graph.microsoft.com/beta/teams/$TeamID/channels/$ChannelID/messages?`$top=100" Write-Host $apiUrl $sourceData = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get parseMessage($sourceData) $nextLink = $sourceData.'@Odata.NextLink' while ($NULL -ne $nextLink) { $nextURL = $nextLink; $sourceData = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $nextURL -Method Get parseMessage($sourceData) $nextLink = $sourceData.'@Odata.NextLink' } $resultFieldSet = New-Object System.Collections.ArrayList foreach($resultData in $resultList) { $resultFields = $resultData | Select-Object @{Name = 'DateTime'; Expression = {Get-Date -Date (($_).dateTime) -Format 'MM/dd/yyyy hh:mm:ss.fff tt'}}, @{Name = 'From'; Expression = {((($_).from))}}, @{Name = 'Message'; Expression = {(($_).body) -replace '<.*?>',''}}| Sort-Object DateTime $resultFieldSet.Add($resultFields) } $Header = @" <style> h1, h5, th { text-align: center; } table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; } th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; } td { font-size: 11px; padding: 5px 20px; color: #000; } tr { background: #b8d1f3; } tr:nth-child(even) { background: #dae5f4; } tr:nth-child(odd) { background: #b8d1f3; } </style> "@ $count = 0 foreach ($resultCount in $resultList){ $count = $count + $resultCount.Count } $body = "<body><b>Generated:</b> $(Get-Date -Format 'MM/dd/yyyy hh:mm tt') <br><br> <b>Team Name:</b> $($Team) <br> <b>Channel Name:</b> $($Channel) <br><br>" + "<b>number of messages:</b> " + $count + " <br><br>" $body = $body + "</head>" $resultHtml = "" foreach ($resultFields in $resultFieldSet){ $tempHtml = $resultFields | ConvertTo-Html -Head $header $resultHtml = $tempHtml + "<br>" + $resultHtml } $resultHtml = $body + "<br>" + $resultHtml $channelDisplayName = $Channel.displayName $Export = "$dir\TeamsHistory\$Team-$channelDisplayName" New-Item -ItemType Directory -Path $Export -ErrorAction Ignore $resultHtml | Out-File $Export\$Team-$channelDisplayName-$Date.html Write-Host " " Write-Host "Messages from the" -NoNewline Write-Host " $($Team)" -NoNewline -ForegroundColor Yellow Write-Host " team and" -NoNewline Write-Host " $($channelDisplayName)" -NoNewline -ForegroundColor Yellow Write-Host " channel were generated and saved to the" -NoNewline Write-Host " $($Export)" -NoNewline -ForegroundColor Yellow Write-Host " as a" -NoNewline Write-Host " $($Team)-$($channelDisplayName)-$($Date).html" -NoNewline -ForegroundColor Yellow Write-Host " file." Write-Host "" }#end foreach ($Channel in $Channels)
- AshMSportCopper ContributorIt worked like a charm. Thank you so much.