Forum Discussion

AshMSport's avatar
AshMSport
Copper Contributor
May 18, 2022

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 Bhatia's avatar
    Vinay Bhatia
    Brass 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)

     

    • AshMSport's avatar
      AshMSport
      Copper Contributor
      It worked like a charm. Thank you so much.

Resources