Count tabs of each type in Teams.

tabs

I have created a script that goes through every tab in every channel in each team and counts how many there are of each type of tab. Of course, I have excluded the conversation and files tab as it’s in every channel. I created this so there’s an easy and fast way to get statistics on what apps/tabs that are in use.  This probably could be something to show in an executive report. The script utilizes Graph API to get the information it needs.  Some tabs you find by text search and others by app id.


### Team tabs/apps counter                      ###
### Version 1.0                                 ###
### Author: Alexander Holmeset                  ###
### Twitter: twitter.com/alexholmeset           ###
### Blog: alexholmeset.blog                     ###

#Description:

#You specify a Group/team object ID, then the script archives all conversations in every channel for this team in a HTML file.

#Have in mind information protection policies like GDPR when working on information like this.

#Parameters
#Create a Azure App with group read and Appcatalog read rights in Graph API
$clientId = "enter app id here"
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceURI = "https://graph.microsoft.com"
$authority = "https://login.microsoftonline.com/common"
#Remove commenting on username and password if you want to run this without a prompt.
#$Office365Username='user@domain'
#$Office365Password='VeryStrongPassword'
#pre requisites
try {
$AadModule = Import-Module -Name AzureAD -ErrorAction Stop -PassThru
}
catch {
throw 'Prerequisites not installed (AzureAD PowerShell module not installed)'
}
$adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
[System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority

##option without user interaction
if (([string]::IsNullOrEmpty($Office365Username) -eq $false) -and ([string]::IsNullOrEmpty($Office365Password) -eq $false))
{
$SecurePassword = ConvertTo-SecureString -AsPlainText $Office365Password -Force
#Build Azure AD credentials object
$AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential" -ArgumentList $Office365Username,$SecurePassword
# Get token without login prompts.
$authResult = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($authContext, $resourceURI, $clientid, $AADCredential);
}
else
{
# Get token by prompting login window.
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Always"
$authResult = $authContext.AcquireTokenAsync($resourceURI, $ClientID, $RedirectUri, $platformParameters)
}
$accessToken = $authResult.result.AccessToken

$apiUrl = 'https://graph.microsoft.com/v1.0/Groups/'
$myProfile = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl -Method Get
$GroupsList = $myprofile | select-object Value
$Groups = $GroupsList.Value | Select-Object Id

$tabscount = @()

$apiurl4 = 'https://graph.microsoft.com/v1.0/appCatalogs/teamsApps'
$AppnameList = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl4 -Method Get
$appnamelist2 = $appnamelist.value

foreach($Group in $Groups){

   $GroupId = $Group.Id

   $apiUrl5 = "https://graph.microsoft.com/beta/Groups/$Groupid/"
   $GroupsData = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl5 -Method Get
   $NameType = $GroupsData | Select-Object resourceProvisioningOptions,DisplayName
   If($nametype.resourceProvisioningOptions -eq 'Team'){

   "Group "+$nameType.displayName+" is Teams enabled and beeing prcessed."

   $apiurl2 = 'https://graph.microsoft.com/v1.0/teams/'+"$Groupid"+'/channels'
   $Teams = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl2 -Method Get

   foreach($channels in $Teams){

        $channelsID = ($channels.value).id

        foreach($channelID in $channelsID){

            $apiurl3 = 'https://graph.microsoft.com/v1.0/teams/'+"$Groupid"+'/channels/'+$channelID+'/tabs'
            $Tabs = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl3 -Method Get
            $appids = $tabs.value

                foreach($appid in $appids){

                    if($appid.webUrl -like '*maps*'){$tabscount+="Maps"}
                    elseif($appid.webUrl -like '*Forms*'){$tabscount+="Forms"}
                    elseif($appid.webUrl -like '*planner*'){$tabscount+="Planner"}
                    elseif($appid.webUrl -like '*vsts*'){$tabscount+="VSTS"}
                    elseif($appid.webUrl -like '*youtube*'){$tabscount+="YouTube"}
                    elseif($appid.webUrl -like '*stream*'){$tabscount+="Stream"}
                    elseif($appid.webUrl -like '*wiki*'){$tabscount+="Wiki"}
                    elseif($appid.webUrl -like '*excel*'){$tabscount+="Excel"}
                    elseif($appid.webUrl -like '*powerpoint*'){$tabscount+="PowerPoint"}
                    elseif($appid.webUrl -like '*word*'){$tabscount+="Word"}
                    elseif($appid.webUrl -like '*onenote*'){$tabscount+="OneNote"}
                    elseif($appid.webUrl -like '*sharepoint*'){$tabscount+="SharePoint"}
                    elseif($appid.webUrl -like '*website*'){$tabscount+="WebSite"}
                    elseif($appid.webUrl -like '*pdf*'){$tabscount+="PDF"}
                    elseif($appid.webUrl -like '*powerbi*'){$tabscount+="PowerBI"}
                    elseif($appid.webUrl -like '*microsoftteamstabs.test*'){$tabscount+="PowerBI"}
                    else{$tabscount+= ($appnamelist2 | Where-Object id -eq $appid).displayname}

 }
 }

   }

 }
    else {
       "Group "+$nameType.displayName+" is not Teams enabled"
   }
 }
$tabscount | Group-Object | select name,count | Sort-Object count -Descending<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0;">&#65279;</span>

2 thoughts on “Count tabs of each type in Teams.

  1. Hi,

    Thank you very much for providing the script!

    I discovered a few things when running the script against our production environment, so I’ll add a couple of tips to make it even more enterprise friendly 🙂 Since the group query against Microsoft Graph will return multiple pages of data due to server-side paging when there exist more than 100 groups in the tenant, you can advantageously add a loop to ensure that all results are returned. A filter can also be added to only return groups of type unified (Office 365 groups). This is all taken care of if you replace line 52-55 with the following:

    $apiUrl = “https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c+eq+’Unified’)”
    $groups = @()
    Do {
    $myProfile = Invoke-RestMethod -Headers @{Authorization = “Bearer $accessToken”} -Uri $apiUrl -Method Get
    $GroupsList = $myprofile | select-object Value
    $Groups += $GroupsList.Value | Select-Object Id
    $apiUrl = $myProfile.’@odata.nextLink’
    }
    While ($apiUrl -ne $null)

    Like

    • Happy you found it usefull 🙂
      Ah, i totaly forgot about that. Remeber reading about it now logn time ago, but got lost somewhere, hehe.
      Thanks for the tip 🙂

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s