Unable to Export All Channels Conversations from a Team using Graph-PowerShell

Occasional Contributor

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 "

"
2 Replies

@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.

VinayBhatia_0-1652991501795.png

 

 

 

 

 

[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)

 

It worked like a charm. Thank you so much.