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.

