How to Generate an Activity Report for Microsoft 365 Groups and Teams

Find Obsolete Groups and Teams

May 11: Microsoft is deprecating the TechNet Gallery in June 2020. The script can now be downloaded from GitHub.

Oct 14: Microsoft updated the location of the Teams compliance records used to check how active a team is. Version 4.7 or later of the script handles this issue.

I wrote the first version of a script to analyze the activity in Office 365 Groups and Teams to identify obsolete groups in 2017. The script is described in this Petri.com article and is reasonably popular. I keep an eye on the feedback from people who run the script and update the script as time goes by. You can download the latest version from GitHub. The latest version is V4.8 dated 16 December 2020.

Update: A Graph-based version of the script is available in GitHub (5.1). This version is much faster at processing Microsoft 365 Groups and Teams and is now the recommended code and the base for future development.

The basic idea is to analyze the Microsoft 365 Groups in a tenant to find underused groups or simply understand the level of activity across groups. The script looks at the level of activity in:

  • Conversations stored in the Inbox of group mailboxes (for Outlook Groups).
  • Documents (in SharePoint document libraries belonging to the groups)
  • Chats (for Teams-enabled Groups). In fact, the script checks the compliance records logged in the group mailbox for conversations in channels belonging to the Teams.

Checking different aspects of individual groups isn’t fast. One tenant tells me that it takes 17 hours to process 5,300 groups… but this isn’t a report that you’ll run daily. It’s more like a monthly or quarterly activity.

Script Outputs

The script records the data for each group in a PowerShell list. Eventually, after processing all the groups, the script outputs an HTML report and a CSV file. The script also assigns a status of Pass, Warning, or Fail to each group depending on the level of detected activity. The determination of the status is entirely arbitrary and should be adjusted to meet the needs of your organization.

The output from the Microsoft 365 Groups and Teams activity report
Figure 1: The output from the Microsoft 365 Groups and Teams activity report

At the bottom of the report you’ll see a summary like this:

Report created for: tenant.onmicrosoft.com
Number of groups scanned: 182
Number of potentially obsolete groups (based on document library activity): 120
Number of potentially obsolete groups (based on conversation activity): 129
Number of Teams-enabled groups : 65
Percentage of Teams-enabled groups: 35.71%

----------------------------------------------------------------------------

Reviewing the report should help you find the Microsoft 365 Groups and Teams that are not being used. These groups are candidates for removal or archival.

You can view a screen capture video showing how to run the script here.

Recent Improvements

Scripts that evolve over time can often do with a rewrite from scratch. However, I don’t have the time for that. but I do make changes that I think are useful. Here are some recent changes.

  • New tests to see if the SharePoint Online and Teams modules are loaded. In particular, the Teams check took far too long because the cmdlets in this module are slow. In fact, the Get-Team module is so slow that we don’t use it from V4.3 onwards. Using Get-UnifiedGroup with a filter is about twice as fast. We might revisit this point as new versions of the Teams PowerShell module appear. See note above about the Graph-based version of the script.
  • Use a PowerShell list object to store the report data. This is much faster than an array, especially when you might want to store data for thousands of groups. This was one of the performance tips received after publishing a post about how we write PowerShell and it makes a real difference.
  • Use Get-Recipient instead of Get-UnifiedGroup to create a set of group objects to process. Get-Recipient is much faster than Get-UnifiedGroup when you need to create a list of mail-enabled objects like Office 365 Groups. V5.0 and later replaces these calls with Graph API commands.
  • Output the storage consumed by the SharePoint site belonging to each group.
  • Handle groups that have no conversations in the group mailbox more elegantly. Groups used by Teams are often in this situation.

Test Before Deployment

As always, test any PowerShell code downloaded from the web, even from sources like GitHub, before introducing it to a production system. The code as written needs some extra error handling to make it as robust as it could be, but I’ve left that to the professionals as people tend to have their own way of approaching this issue.


The Office 365 for IT Pros eBook includes many valuable tips for writing PowerShell scripts to interact with Office 365 Groups and Teams. Subscribe to gain benefit from all that knowledge!

60 Replies to “How to Generate an Activity Report for Microsoft 365 Groups and Teams”

  1. Hello all

    am getting the below error and CSV and HTML file is null

    Results
    ——-
    Number of groups scanned : 0
    Potentially obsolete groups (based on document library activity): 0
    Potentially obsolete groups (based on conversation activity) : 0
    Number of Teams-enabled groups : 9
    You cannot call a method on a null-valued expression.
    At C:\Users\raarumug\Desktop\MS Teams\Scripts\TeamsGroupsActivityReport4.3.PS1:216 char:1
    + Write-Host “Percentage of Teams-enabled groups …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Summary report in c:\temp\GroupsActivityReport.html and CSV in c:\temp\GroupsActivityReport.csv
    PS C:\Users\raarumug\Desktop\MS Teams\Scripts>

    1. Well, the script hasn’t processed any groups for some reason. What’s in the $TeamList and $Groups variables?

      And to make sure that we don’t run into the same problem, I updated the script to V4.4 to include a check that some groups are found before we proceed.

  2. Great script! Have used it regularly to get an idea of the current status of our 3,500+ groups.

    I noticed though in the last report that I had a handful of groups reporting SPOStatus as ‘Document library never created’ but SPOActivity was ‘Document library in use’ – which didn’t make a lot of sense!

    I found that the $AuditRecs parameter is not set if the $G.SharePointSiteURL does not exist. Thus when it gets set in a loop iteration, on the next iteration the previous value is carried over if $G.SharePointSiteURL does not exist.

    I’ve fixed this in my copy of the script, just thought you’d like to know!

  3. Tank you for that Script it worked Great,i have just on Question i have some Groups that Date of chat return null i try to write something but always no informations.

      1. Iám not realy at home in the scripting scene. How could I do that by adjusting the script?

      2. You’d be creating a new script to remove the calls which form the collection of groups and replacing those commands with Graph API calls as described in the link I referenced. You’d also need to register an app with Azure AD. That process is described in https://petri.com/exploiting-graph-when-powershell-teams. This isn’t probably something to plunge into if you don’t know PowerShell. Is there anyone local within the organization who can help?

      3. I can’t reply on youre last post. But it is a bit over my head that is true. And no there isn’t someone in our organisation with this kind of expertise.

  4. This is a script that is very usefull. But when I run it it keeps failing after a long time. I needs to go true about 11000 groups. Could that be the problem? It takes me about 2 days before it reaches about 3300 groups.

    Am I doing something wrong maybe? Tips and tricks are welcome :-).

  5. Hi Tony !

    Thank’s for the script.
    I want to use the V5 version. I created an app (Azure AD > App registration), but what Microsoft Graph API access does the script need?
    Thank you !

    1. I believe the following permissions are used: Group.Read.All, Reports.Read.All

      I have some extra permissions assigned to the app I use for testing purposes, but a check of all the Graph calls used indicate that these are the two required.

      1. Thank’s Tony.
        Now, I have an issue with the script : Get-GraphData is not recognise (line 119 and 176). Any suggestion ??

      2. Get-GraphData is a function. Did you load the whole script (the function is at the bottom of the code)?

    1. Problem solved :
      The Get-GraphData must be in first position (Powershell is not a compiled language).
      And i’ve found another “bug”. I’m in a french tenant, so “Shared%documents” is not valid. It’s “Documents%partages” (lines 212 and 213)
      I will open a pull to suggest a var named “shareddocs” where users can put there localize “shared%documents”.

      1. Thanks. We write code in the expectation that people take it and adapt for their own use. I’ve moved the function up. I should have done that before. I’ll also fix the shared document issue.

      2. Added update for the URL I18N issue. Here’s the text:

        Added the two variables $SharedDocFolder and $SharedDocFolder2

        $SharedDocFolder is used to locate the position of the folder for the doclib in a team site. This is language dependent. For example, English language sites have values like:
        https://office365itpros.sharepoint.com/sites/euroOffice365Engage/Shared%20Documents, so we look for “/Shared%20Documents”. In French, this is “/Documents%20Partages”
        $SharedDocFolder2 holds a value without the %20 (space character) that we append to the site URL to get a well-formed URL to the doclib. For English, this value is “/Shared Documents”. In French, it would be “/Documents Partages”

  6. Hello Tony, Thanx for yoyr work for this script. I tried to get this working for me. Registereing an app is no issue also the permissions are ok. But after running the latest version I still get this error

    Extracting list of Microsoft 365 Groups for checking…
    Fetching data about Microsoft 365 Groups…
    Exception calling “Substring” with “2” argument(s): “Lengte kan niet minder dan nul zijn.
    Parameternaam: length”
    At C:\tools\TacTix_TeamsGroupActivity_GraphV2.ps1:214 char:8
    + $SPOUrl = $SPOData.WebUrl.SubString(0,$SpoData.weburl.IndexOf( …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

    I can’t find where the problem is with this error.

    Lengte kan niet minder dan nul zijn = Length cannot be less than zero

    I hope you can give me a clue.

    1. Looks like it is failing to get any SharePoint data for the call $Uri = “https://graph.microsoft.com/v1.0/groups/” + $Group.Id + “/drive”

      When the script fails, what’s the value of the $SPOData variable?

  7. Hello Tony,

    I am not a programmer and do not have vast knowledge of PowerShell. I saw this script while searching for a better way to weed out teams that are no longer in use, but am running into some error messages. I hope you can point me in the right direction as, like I stated before, I am not a programmer. It seems that I have to enter a value for the AccessToken before running the script? Below is the output after running the script.

    Continuing to fetch information about Microsoft 365 Groups…
    Get-GraphData : Cannot bind argument to parameter ‘AccessToken’ because it is null.
    At C:\Users\tvanelli\Documents\Office365itpros-master\TeamsGroupsActivityReportV5.PS1:219 char:51
    + $GuestMemberCount = Get-GraphData -AccessToken $Token -Uri $uri
    + ~~~~~~
    + CategoryInfo : InvalidData: (:) [Get-GraphData], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-GraphData

    Get-GraphData : Cannot bind argument to parameter ‘AccessToken’ because it is null.
    At C:\Users\tvanelli\Documents\Office365itpros-master\TeamsGroupsActivityReportV5.PS1:221 char:51
    + $GroupMemberCount = Get-GraphData -AccessToken $Token -Uri $uri
    + ~~~~~~
    + CategoryInfo : InvalidData: (:) [Get-GraphData], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-GraphData

    Get-Date : Cannot bind parameter ‘Date’ to the target. Exception setting “Date”: “Cannot convert null to type “System.DateTime”.”
    At C:\Users\tvanelli\Documents\Office365itpros-master\TeamsGroupsActivityReportV5.PS1:232 char:36
    + WhenCreated = Get-Date ($Group.createdDateTime) -format g
    + ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : WriteError: (:) [Get-Date], ParameterBindingException
    + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.GetDateCommand

    Get-GraphData : Cannot bind argument to parameter ‘AccessToken’ because it is null.
    At C:\Users\tvanelli\Documents\Office365itpros-master\TeamsGroupsActivityReportV5.PS1:242 char:37
    + $Teams = Get-GraphData -AccessToken $Token -Uri $uri
    + ~~~~~~
    + CategoryInfo : InvalidData: (:) [Get-GraphData], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-GraphData

    No Microsoft 365 Groups can be found – exiting

      1. Ahhh. Thanks very much. I was wondering if that was the case after reading some of the other comments here, but I wasn’t sure I even understood what I was reading, haha. I would eventually like to learn how to create apps in Azure as it seems like a good skill to have if I’m going to be our Teams admin.

        The script is running like a champ right now. Thanks again.

  8. The script ran great and output the information needed.
    Are the .html and .csv file saved at the end? I get the message saying that the summary report and CSV are in c:\temp, but there is no such directory.
    I’m sure that I’m missing something again. If these files are not saved automatically, is there a way of saving the output file that is generated?

    1. You’ll need to either create the c:\temp directory or change the script to point to another directory. If the directory isn’t available, the files aren’t created. However, you should have two PowerShell objects that can be exported (if the session is still available). Try:

      $htmlreport | Out-File c:\htmlreport.html-Encoding UTF8
      $Report | Export-CSV -NoTypeInformation c:\report.csv

      Replace the file names I use with your preference and you should be able to generate the files.

  9. pulling down the latest version of the script, when I run it, it comes up saying that there is a error on line 237 with an extra } barket. once this is remvoed, and I have added my Application registration details, the script runs but comes back with “Fetching data about Microsoft 365 Groups…
    No Microsoft 365 Groups can be found – exiting”

    1. There was an extra bracket, which I removed.

      It was after this section:

      $Uri = “https://graph.microsoft.com/v1.0/groups/” + $Group.Id + “/owners?”
      $GroupData = Invoke-RestMethod -Headers $Headers -Uri $Uri -UseBasicParsing -Method “GET”
      If ($GroupData.”@odata.count” -eq 0) {
      $OwnerNames = “No owners found” }
      Else { # Extract owner names
      $OwnerNames = $GroupData.Value.DisplayName -join “, ” }

      Did you remove the same bracket? The code now posted in GitHub works.

      1. Yes, I have downloaded the latest version. I don’t have the error anymore from the bracket.

  10. Thanks for all the great help, Tony. I got the script to run and save the files where I wanted. I’m happy I came across this when I did because it was much needed.

    Out of curiosity, is this supposed to be able to run from PowerShell ISE? I tried to run it 4 times on two different machines yesterday. Three times on my local PC while working from home connected to VPN and once from a PC at work over an RDP session. Each time PowerShell ISE froze (at different times in the script) and I received a generic “…encountered a problem” or something, error message behind the PowerShell window. I then ran it from regular PowerShell at home over VPN today and it worked fine.

    Just wondering…

  11. Tony, Thank you for this script, and for your frequent updates!
    Now running v5.1 of the Graph-based script, I noticed a couple of differences in the results from when I was running v4.8 of the orginal script, and wanted to share them with you.
    The first is that for my >3700 Groups, the report lists 0 for “number of Teams-enabled groups”, while the v4.8 script shows 327 teams across a small sample of 375 groups I let that script process.
    The second item is that the report returns no results for SPOStorageGB (empty or null) while the v4.8 script returns data for each row.

    Again, thank you for providing these scripts!!

    1. Can you run the start of the script to the point where it fetches the set of Teams to build the Teams hash table and see what’s returned when $TeamsCount = $TeamsHash.count. That’s how the number of team-enabled groups is reported. As to SharePoint, the storage data comes from the call: $SPOUsage = (Invoke-RestMethod -Uri $SPOUsageReportsURI -Headers $Headers -Method Get -ContentType “application/json”) -Replace “…Report Refresh Date”, “Report Refresh Date” | ConvertFrom-Csv

      Check what comes back to $SPOUsage.

      I don’t see a problem in either place here. It all works as expected.

      1. Tony, The first time I ran the code the CLS near the end masked me seeing the errors. I set a breakpoint on line 249. I can see that $GroupsList contains 3,740 entries while $Teams contains a single record.

        I am logged in as a global admin, but from the errors shown it appears there are some other permission issues. I can confirm I have granted the permissions you noted in an earlier comment to my app (Group.Read.All, Reports.Read.All).
        I would welcome any feedback you may have. I renamed your script, as noted in the errors, with “(tony)” simply so I would know I was running your original unaltered version.

        Continuing to fetch information about Microsoft 365 Groups…
        Get-GraphData : System.Net.WebException: The remote server returned an error: (401) Unauthorized.
        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
        At C:\Users\michael.sevast\Desktop\PowerShell\TeamsGroupsActivityReportV5(tony).ps1:218 char:24
        + $GuestMemberCount = Get-GraphData -AccessToken $Token -Uri $uri
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
        + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-GraphData

        Get-GraphData : System.Net.WebException: The remote server returned an error: (401) Unauthorized.
        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
        At C:\Users\michael.sevast\Desktop\PowerShell\TeamsGroupsActivityReportV5(tony).ps1:220 char:24
        + $GroupMemberCount = Get-GraphData -AccessToken $Token -Uri $uri
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
        + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-GraphData

        Get-GraphData : System.Net.WebException: The remote server returned an error: (401) Unauthorized.
        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
        At C:\Users\michael.sevast\Desktop\PowerShell\TeamsGroupsActivityReportV5(tony).ps1:241 char:10
        + $Teams = Get-GraphData -AccessToken $Token -Uri $uri
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
        + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-GraphData

        Exception calling “Add” with “2” argument(s): “Key cannot be null.
        Parameter name: key”
        At C:\Users\michael.sevast\Desktop\PowerShell\TeamsGroupsActivityReportV5(tony).ps1:244 char:4
        + $TeamsHash.Add($_.Id, $_.DisplayName) } )
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : ArgumentNullException

      2. Can you try adding the GroupMember.Read.All permission? I see that it’s failing to return the counts for guest and tenant users. That could be it.

      3. Tony,
        I forgot I had already added GroupMember.Read.All during my testing. I deleted and readded it just to be sure. To confirm, Group.Read.All, Reports.Read.All, and GroupMember.Read.All are the only permissions assigned to my app. Unfortunately, my results are the same as I showed previously.

      4. Do you have User,Read.All? I have that and User.Read in the app I am using. I can’t recall if this was done for a reason. It’s a while since I set the app up.

      5. Tony, I’m baffled as I’ve deleted all permissions and then added these permissions; Group.Read.All, GroupMember.Read.All, Reports.Read.All, Sites.Read.All, Team.ReadBasic.All, TeamMember.Read.All), but the same errors continue to be displayed. I’m about ready to grant the app full “kitchen sink” permissions 😉 Seriously, I appreciate your feedback so far. This could be a “between the ears” issue on my end. I’m just not seeing it.

      6. Shorted my list a bit. The full permissions granted are Group.Read.All, GroupMember.Read.All, Reports.Read.All, Sites.Read.All, Team.ReadBasic.All, TeamMember.Read.All, User.Read.All and User.Readwrite.All.

      7. It’s odd… You seem to have the right permissions but you don’t… Never mind, we shall work the issue through and get a successful conclusion.

    2. I am also having SPOStorageGB return 0GB but it tracks further back to this loop:

      ForEach ($Site in $SPOUsage) {
      If ($Site.”Root Web Template” -eq “Group”) {
      If ([string]::IsNullOrEmpty($Site.”Last Activity Date”)) { # No activity for this site
      $LastActivityDate = $Null }
      Else {
      $LastActivityDate = Get-Date($Site.”Last Activity Date”) -format g
      $LastActivityDate = $LastActivityDate.Split(” “)[0] }
      $SiteDisplayName = $Site.”Owner Display Name”.IndexOf(“Owners”) # Extract site name
      If ($SiteDisplayName -ge 0) {
      $SiteDisplayName = $Site.”Owner Display Name”.SubString(0,$SiteDisplayName) }
      Else {
      $SiteDisplayName = $Site.”Owner Display Name” }
      $StorageUsed = [string]([math]::round($Site.”Storage Used (Byte)”/1GB,2)) + ” GB”
      $SingleSiteData = @{
      ‘DisplayName’ = $SiteDisplayName
      ‘LastActivityDate’ = $LastActivityDate
      ‘FileCount’ = $Site.”File Count”
      ‘StorageUsed’ = $StorageUsed }
      $DataTable.Add([String]$Site.”Site URL”,$SingleSiteData)
      }
      }

      It looks like the issue is the first If statement, “If ($Site.”Root Web Template” -eq “Group”) {”
      As a lot of the Root Web templates are not Group for mine, but rather “Team Site” so this ends up with no information in the datatable
      Changing it to “If ($Site.”Root Web Template” -eq “Group” -Or “Team Site”) {” enabled this to then get most sites/teams data correctly, removing this If completely has meant that all SPO sites have complete data in the datatable and then only gets used if needed later on in the script, and dosnt seem to add any running time to the script.

      Thanks

      1. On the script finishing running it seems that it still has not output the SPOStorageGB but it is listed in the Data table correctly now
        the issue looks to be on line 413: SPOStorageGB = $SPOStorage should be SPOStorageGB = $SPOStorageUsed

      2. Here’s how it should work:

        SPO data is collected into a hash table, including the storage used

        $SingleSiteData = @{
        ‘DisplayName’ = $SiteDisplayName
        ‘LastActivityDate’ = $LastActivityDate
        ‘FileCount’ = $Site.”File Count”
        ‘StorageUsed’ = $StorageUsed }

        The $SPOStorageUsed variable is filled using a lookup against the hash table containing SPO site information.

        $ThisSiteData = $Datatable[$G.SharePointURL]
        $SPOFileCount = $ThisSiteData.FileCount
        $SPOStorageUsed = $ThisSiteData.StorageUsed

        And is later written out into the report:

        “SPO Storage Used (GB)” = $SPOStorageUsed

        But I notice that my copy of the script has this code and the version on GitHub uses what you report, so that’s a bug (now fixed). Thanks for letting me know.

  12. Hi,

    that’s a great script.
    I add a new Application (App Registration) give them a lot of permission. (Group.Read.All, GroupMember.Read.All, Reports.Read.All, Sites.Read.All, Team.ReadBasic.All, TeamMember.Read.All, User.Read.All and User.Readwrite.All.)

    But when i run your script i get error:

    Checking Microsoft 365 Groups and Teams in the tenant: xxxxx.onmicrosoft.com
    Invoke-RestMethod : The remote server returned an error: (403) Forbidden.
    At C:\Users\a-00114099\Desktop\test.ps1:164 char:17
    + … SPOUsage = (Invoke-RestMethod -Uri $SPOUsageReportsURI -Headers $Head …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebExc
    eption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

    Fetching data about Microsoft 365 Groups…
    Get-GraphData : System.Net.WebException: The remote server returned an error: (403) Forbidden.
    at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
    at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
    At C:\Users\a-00114099\Desktop\test.ps1:192 char:11
    + $Groups = Get-GraphData -AccessToken $Token -Uri $uri
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-GraphData

    Get-GraphData : System.Net.WebException: The remote server returned an error: (400) Bad Request.
    at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
    at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
    At C:\Users\a-00114099\Desktop\test.ps1:197 char:17
    + $GroupData = Get-GraphData -AccessToken $Token -Uri $Uri
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Do you know what’s the problem?

    1. You’re hitting an error when fetching the SharePoint usage data and then when fetching Groups. Has consent been given by an admin to allow Reports.Read.All and Groups.Read.All and GroupsMember.Read.All to be used? Is this a test tenant where report data isn’t available by any chance?

  13. Ahhh, now it’s working. Took some time after adding the permissions. But now i get another error 🙂

    Do you know what the problem is?

    Checking Microsoft 365 Groups and Teams in the tenant: xxxx.onmicrosoft.com
    Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary:
    ‘https://xxx.sharepoint.com/sites/WO-Bereich-V’ Key being added:
    ‘https://xxx.sharepoint.com/sites/WO-Bereich-V'”
    At C:\Users\a-00114099\Desktop\test.ps1:184 char:6
    + $DataTable.Add([String]$Site.”Site URL”,$SingleSiteData)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException

    Fetching data about Microsoft 365 Groups…
    Exception calling “Substring” with “2” argument(s): “Length cannot be less than zero.
    Parameter name: length”
    At C:\Users\a-00114099\Desktop\test.ps1:211 char:8
    + $SPOUrl = $SPOData.WebUrl.SubString(0,$SpoData.weburl.IndexOf( …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

    Exception calling “Substring” with “2” argument(s): “Length cannot be less than zero.
    Parameter name: length”
    At C:\Users\a-00114099\Desktop\test.ps1:211 char:8
    + $SPOUrl = $SPOData.WebUrl.SubString(0,$SpoData.weburl.IndexOf( …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

    Exception calling “Substring” with “2” argument(s): “Length cannot be less than zero.
    Parameter name: length”
    At C:\Users\a-00114099\Desktop\test.ps1:211 char:8
    + $SPOUrl = $SPOData.WebUrl.SubString(0,$SpoData.weburl.IndexOf( …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

    1. Looks like you didn’t initialize all the variables before running the script with the right permissions. When the script attempts to populate hash tables holding data, it finds that the data it wants to add is already there and you get the problem.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.