Dimarts, juny 10, 2025
SCRIPTING

Script per generar un informe dels grups obsolets d’Office 365

Aquest script de Powershell, creat per Tony Redmond per a Office 365 for IT Pros, genera un informe per avaluar els grups d’Office 365 que poden estar obsolets i en desús, en funció de la seva activitat recent.  Aquí presentem una versió parcialment traduïda al català, i amb algun canvi respecte a l’original, com el de generar els arxius de l’informe amb la data al seu nom, per si volem conservar diferents versions.  Abans d’executar-lo, hem d’estar connectats a Exchange Online, Sharepoint Online i Teams.

# TeamsGroupsActivityReport.PS1
# A script to check the activity of Microsoft 365 Groups and Teams and report the groups and teams that might be deleted because they're not used.
# We check the group mailbox to see what the last time a conversation item was added to the Inbox folder. 
# Another check sees whether a low number of items exist in the mailbox, which would show that it's not being used.
# We also check the group document library in SharePoint Online to see whether it exists or has been used in the last 90 days.
# And we check Teams compliance items to figure out if any chatting is happening.

# Created 29-July-2016  Tony Redmond 
# V2.0 5-Jan-2018
# V3.0 17-Dec-2018
# V4.0 11-Jan-2020
# V4.1 15-Jan-2020 Better handling of the Team Chat folder
# V4.2 30-Apr-2020 Replaced $G.Alias with $G.ExternalDirectoryObjectId. Fixed problem with getting last conversation from Groups where no conversations are present.
# V4.3 13-May-2020 Fixed bug and removed the need to load the Teams PowerShell module
# V4.4 14-May-2020 Added check to exit script if no Microsoft 365 Groups are found
# V4.5 15-May-2020 Some people reported that Get-Recipient is unreliable when fetching Groups, so added code to revert to Get-UnifiedGroup if nothing is returned by Get-Recipient
# V4.6 8-Sept-2020 Better handling of groups where the SharePoint team site hasn't been created
# V4.7 13-Oct-2020 Teams compliance records are now in a different location in group mailboxes
# V4.8 16-Dec-2020 Some updates after review of code to create 5.0 (Graph based version)
# 
# https://github.com/12Knocksinna/Office365itpros/blob/master/TeamsGroupsActivityReport.ps1
#
CLS
# Comprovar que estem connectats a Exchange Online, SharePoint Online, i Teams
Write-Host "Comprovant que els moduls de PowerShell necessaris estan carregats..."
$ModulesLoaded = Get-Module | Select Name
If (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Connecteu-vos a Exchange Online i reinicieu aquest script"; break}
If (!($ModulesLoaded -match "Microsoft.Online.SharePoint.PowerShell")) {Write-Host "Connecteu-vos a SharePoint Online i reinicieu aquest script"; break}
   
$OrgName = (Get-OrganizationConfig).Name  
       
# OK, sembla que estem connectats a Exchange Online i SharePoint Online...
Write-Host "Comprovant els grups i els equips de Microsoft 365 al tenant:" $OrgName
# Configurem algunes coses que farem servir
$WarningDate = (Get-Date).AddDays(-90); $WarningEmailDate = (Get-Date).AddDays(-365); $Today = (Get-Date); $Date = $Today.ToShortDateString()
$TeamsGroups = 0;  $TeamsEnabled = $False; $ObsoleteSPOGroups = 0; $ObsoleteEmailGroups = 0
$Report = [System.Collections.Generic.List[Object]]::new(); $ReportFile = "C:\temp\GroupsActivityReport_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm` tt).ToString()).html"
$CSVFile = "C:\temp\GroupsActivityReport_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm` tt).ToString()).csv"
$htmlhead="<html>
	   <style>
	   BODY{font-family: Arial; font-size: 8pt;}
	   H1{font-size: 22px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
	   H2{font-size: 18px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
	   H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
	   TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}
	   TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}
	   TD{border: 1px solid #969595; padding: 5px; }
	   td.pass{background: #B7EB83;}
	   td.warn{background: #FFF275;}
	   td.fail{background: #FF2626; color: #ffffff;}
	   td.info{background: #85D4FF;}
	   </style>
	   <body>
           <div align=center>
           <p><h1>Informe d'activitat dels grups i els equips de Microsoft 365</h1></p>
           <p><h3>Generat el: " + $date + "</h3></p></div>"
		
# Obtenir el llistat de grups en el tenant.
Write-Host "Obtenint el llistat dels grups de Microsoft 365..."
[Int]$GroupsCount = 0; [int]$TeamsCount = 0; $TeamsList = @{}; $UsedGroups = $False
$Groups = Get-Recipient -RecipientTypeDetails GroupMailbox -ResultSize Unlimited | Sort-Object DisplayName
$GroupsCount = $Groups.Count
# Si no trobem cap grup amb Get-Recipient, provem de fer-ho amb Get-UnifiedGroup.
If ($GroupsCount -eq 0) { # 
   Write-Host "Buscant el grups amb Get-UnifiedGroup"
   $Groups = Get-UnifiedGroup -ResultSize Unlimited | Sort-Object DisplayName 
   $GroupsCount = $Groups.Count; $UsedGroups = $True
   If ($GroupsCount -eq 0) {
     Write-Host "No s'han trobat grups de Microsoft 365; sortint de l'script" ; break} 
} 

Write-Host "Completant la llista d'equips..."
If ($UsedGroups -eq $False) { # Completem la llista de hash dels equips amb una crida a Get-UnifiedGroup
   Get-UnifiedGroup -Filter {ResourceProvisioningOptions -eq "Team"} -ResultSize Unlimited | ForEach { $TeamsList.Add($_.ExternalDirectoryObjectId, $_.DisplayName) } }
Else { # Encara tenim les dades a la variable $Groups, traiem el grup d'aquesta dada
   $Groups | ? {$_.ResourceProvisioningOptions -eq "Team"} | ForEach { $TeamsList.Add($_.ExternalDirectoryObjectId, $_.DisplayName) } }
$TeamsCount = $TeamsList.Count

CLS
# Configurem la barra de progrés
$ProgDelta = 100/($GroupsCount); $CheckCount = 0; $GroupNumber = 0

# Bucle principal
ForEach ($Group in $Groups) { #Obtenim les propietats del grup
   $G = Get-UnifiedGroup -Identity $Group.DistinguishedName
   $GroupNumber++
   $GroupStatus = $G.DisplayName + " ["+ $GroupNumber +"/" + $GroupsCount + "]"
   Write-Progress -Activity "Comprovant el grup" -Status $GroupStatus -PercentComplete $CheckCount
   $CheckCount += $ProgDelta;  $ObsoleteReportLine = $G.DisplayName;    $SPOStatus = "Normal"
   $SPOActivity = "Biblioteca de documents activa"; $SPOStorage = 0
   $NumberWarnings = 0;   $NumberofChats = 0;  $TeamChatData = $Null;  $TeamsEnabled = $False;  $LastItemAddedtoTeams = "N/A";  $MailboxStatus = $Null; $ObsoleteReportLine = $Null
# Comprovar els propietaris del grup
  $ManagedBy = $G.ManagedBy
  If ([string]::IsNullOrWhiteSpace($ManagedBy) -and [string]::IsNullOrEmpty($ManagedBy)) {
     $ManagedBy = "Sense propietaris"
     Write-Host $G.DisplayName "No te propietaris!" -ForegroundColor Red}
  Else {
     $ManagedBy = (Get-ExoMailbox -Identity $G.ManagedBy[0]).DisplayName}
# Edat del grup
  $GroupAge = (New-TimeSpan -Start $G.WhenCreated -End $Today).Days
# Busquem informació d'activitat a la bústia del grup  
   $Data = (Get-ExoMailboxFolderStatistics -Identity $G.ExternalDirectoryObjectId -IncludeOldestAndNewestITems -FolderScope Inbox)
   If ([string]::IsNullOrEmpty($Data.NewestItemReceivedDate)) {$LastConversation = "No s'han trobat elements"}           
   Else {$LastConversation = Get-Date ($Data.NewestItemReceivedDate) -Format g }
   $NumberConversations = $Data.ItemsInFolder
   $MailboxStatus = "Normal"
  
   If ($Data.NewestItemReceivedDate -le $WarningEmailDate) {
      Write-Host "Darrera conversa creada: " $G.DisplayName "en data" $Data.NewestItemReceivedDate "-> Obsolet?"
      $ObsoleteReportLine = $ObsoleteReportLine + " Data de la darrera conversa a l'Outlook: " + $LastConversation + "."
      $MailboxStatus = "La safata d'entrada no s'ha fet servir recentment"
      $ObsoleteEmailGroups++
      $NumberWarnings++ }
   Else
      {# Si hi ha menys de 20 converses, ho indiquem
      If ($Data.ItemsInFolder -lt 20) {
           $ObsoleteReportLine = $ObsoleteReportLine + " Nomes s'han trobat " + $Data.ItemsInFolder + " elements de conversa d'Outlook."
           $MailboxStatus = "Numero de converses baix"
           $NumberWarnings++}
      }

# Comprovem l'activitat al Sharepoint
   If ($G.SharePointSiteURL -ne $Null) {
      $SPOStorage = (Get-SPOSite -Identity $G.SharePointSiteUrl).StorageUsageCurrent
      $SPOStorage = [Math]::Round($SpoStorage/1024,2) # Emmagatzematge de SharePoint en GB
      $AuditCheck = $G.SharePointDocumentsUrl + "/*"
      $AuditRecs = $Null
      $AuditRecs = (Search-UnifiedAuditLog -RecordType SharePointFileOperation -StartDate $WarningDate -EndDate $Today -ObjectId $AuditCheck -ResultSize 1)
      If ($AuditRecs -eq $Null) {
         $ObsoleteSPOGroups++   
         $ObsoleteReportLine = $ObsoleteReportLine + "No s'ha detectat activitat al Sharepoint els darrers 90 dies." }          
       }
   Else
       {
# La biblioteca de Sharepoint està en blanc
        $ObsoleteSPOGroups++  
        $AuditRecs = $Null
        $ObsoleteReportLine = $ObsoleteReportLine + "No s'ha creat la llibreria de Sharepoint." 
       }
# Report to the screen what we found - but only if something was found...   
  If ($ObsoleteReportLine -ne $G.DisplayName)
     {
     Write-Host $ObsoleteReportLine 
     }
# Generar els criteris d'obsolescència dels grups...   
  If ($AuditRecs -eq $Null) {
       $SPOActivity = "No s'ha detectat activitat al Sharepoint els darrers 90 dies."
       $NumberWarnings++ }
   If ($G.SharePointDocumentsUrl -eq $Null) {
       $SPOStatus = "No s'ha creat la llibreria de Sharepoint."
       $NumberWarnings++ }
  
    $Status = "Actiu"
    If ($NumberWarnings -eq 1)
       {
       $Status = "No gaire actiu"
    }
    If ($NumberWarnings -gt 1)
       {
       $Status = "Inactiu"
    } 

# Si el grup té l'equip habilitat, trobar la data de la darrera conversa a l'equip
If ($TeamsList.ContainsKey($G.ExternalDirectoryObjectId) -eq $True) {
    $TeamsEnabled = $True
    [datetime]$DateOldTeams = "1-Jun-2021" # Després d’aquesta data, Microsoft hauria d’haver mogut les dades antigues de Teams a la nova ubicació
    $CountOldTeamsData = $False

# Start by looking in the new location (TeamsMessagesData in Non-IPMRoot)
    $TeamsChatData = (Get-ExoMailboxFolderStatistics -Identity $G.ExternalDirectoryObjectId -IncludeOldestAndNewestItems -FolderScope NonIPMRoot | ? {$_.FolderType -eq "TeamsMessagesData" })
    If ($TeamsChatData.ItemsInFolder -gt 0) {$LastItemAddedtoTeams = Get-Date ($TeamsChatData.NewestItemReceivedDate) -Format g}
    $NumberOfChats = $TeamsChatData.ItemsInFolder
    
# If the script is running before 1-Jun-2021, we need to check the old location of the Teams compliance records
If ($Today -lt $DateOldTeams) {
     $CountOldTeamsData = $True
     $OldTeamsChatData = (Get-ExoMailboxFolderStatistics -Identity $G.ExternalDirectoryObjectId -IncludeOldestAndNewestItems -FolderScope ConversationHistory)
     ForEach ($T in $OldTeamsChatData) { # We might have one or two subfolders in Conversation History; find the one for Teams
     If ($T.FolderType -eq "TeamChat") {
        If ($T.ItemsInFolder -gt 0) {$OldLastItemAddedtoTeams = Get-Date ($T.NewestItemReceivedDate) -Format g}
        $OldNumberofChats = $T.ItemsInFolder
}}}

If ($CountOldTeamsData -eq $True) { 
   $NumberOfChats = $NumberOfChats + $OldNumberOfChats
   If (!$LastItemAddedToTeams) { $LastItemAddedToTeams = $OldLastItemAddedToTeams }
} 

If (($TeamsEnabled -eq $True) -and ($NumberOfChats -le 100)) { Write-Host "El grup" $G.DisplayName "te nomes" $NumberOfChats "registres de compliment" }      
}

# Generem la informació que presentarem a l'informe
    $ReportLine = [PSCustomObject][Ordered]@{
          Nom           = $G.DisplayName
          Propietari           = $ManagedBy
          Membres             = $G.GroupMemberCount
          Convidats      = $G.GroupExternalMemberCount
          Descripcio         = $G.Notes
          Activitat_Correu       = $MailboxStatus
          Team_Habilitat         = $TeamsEnabled
          Darrer_Xat            = $LastItemAddedtoTeams
          Numero_Xats         = $NumberofChats
          Darrera_Conversa    = $LastConversation
          Numero_Converses = $NumberConversations
          Activitat_Sharepoint         = $SPOActivity
          Emmagatzematge_SPO_GB        = $SPOStorage
          Estat_SPO           = $SPOStatus
          Data_Creacio         = Get-Date ($G.WhenCreated) -Format g
          Edat_en_Dies             = $GroupAge
          Alertes      = $NumberWarnings
          Estat              = $Status}
   $Report.Add($ReportLine)   
#Fi del bucle principal
}

If ($TeamsCount -gt 0) { 
    $PercentTeams = ($TeamsCount/$GroupsCount)
    $PercentTeams = ($PercentTeams).tostring("P") }
Else {
    $PercentTeams = "No s'han trobat equips" }

# Creem l'informe en HTML
$htmlbody = $Report | ConvertTo-Html -Fragment
$htmltail = "<p>Informe creat per a: " + $OrgName + 
            "</p>" +
             "<p>Numero de grups analitzats: " + $GroupsCount + "</p>" +
             "<p>Numero de grups potencialment obsolets (segons l'activitat al Sharepoint): " + $ObsoleteSPOGroups + "</p>" +
             "<p>Numero de grups potencialment obsolets (segons l'activitat en converses): " + $ObsoleteEmailGroups + "<p>"+
             "<p>Numero de grups amb l'equip activat    : " + $TeamsCount + "</p>" +
             "<p>Percentatge: de grups amb l'equip activat" + $PercentTeams + "</body></html>" +
             "<p>-----------------------------------------------------------------------------------------------------------------------------"
$htmlreport = $htmlhead + $htmlbody + $htmltail
$htmlreport | Out-File $ReportFile  -Encoding UTF8
$Report | Export-CSV -NoTypeInformation $CSVFile
$Report | Out-GridView
# Resum
CLS
Write-Host " "
Write-Host "Resultats"
Write-Host "-------"
Write-Host "Numero de grups analitzats                          :" $GroupsCount
Write-Host "Numero de grups potencialment obsolets (segons l'activitat al Sharepoint):" $ObsoleteSPOGroups
Write-Host "Número de grups potencialment obsolets (segons l'activitat en converses)    :" $ObsoleteEmailGroups
Write-Host "Numero de grups amb l'equip activat                                  :" $TeamsList.Count
Write-Host "Percentatge: de grups amb l'equip activat                              :" $PercentTeams
Write-Host " "
Write-Host "Podeu trobar l'informe en format html a " $ReportFile "i en CSV a" $CSVFile

# An example script used to illustrate a concept. More information about the topic can be found in the Office 365 for IT Pros eBook https://gum.co/O365IT/
# and/or a relevant article on https://office365itpros.com or https://www.petri.com. See our post about the Office 365 for IT Pros repository # https://office365itpros.com/office-365-github-repository/ for information about the scripts we write.

# Do not use our scripts in production until you are satisfied that the code meets the need of your organization. Never run any code downloaded from the Internet without
# first validating the code in a non-production environment.

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *