Last active
November 8, 2024 11:18
-
-
Save AlexanderHolmeset/6d06ca40b9982613173ed3abf76e8f83 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### Team Owner Routine | |
### Version 1.0 | |
### Author: Alexander Holmeset | |
### Email: [email protected] | |
### Twitter: twitter.com/alexholmeset | |
### Blog: alexholmeset.blog | |
$TenantId = "Populate variable" | |
$ClientID = "Populate variable" | |
$ClientSecret = Get-AutomationVariable -Name 'secret' | |
$SharePointSiteID = "Populate variable" | |
$SharePointListID = "Populate variable" | |
$SharePointListURL = "https://graph.microsoft.com/v1.0/sites/$($SharePointSiteID)/lists/$($SharePointListID)" | |
$EmailUPN = "Populate variable" | |
$TeamsWebhookURL = "Populate variable" | |
#Number of days between warnings. | |
$DaysBewteenReminders = 7 | |
$WarningAdminDays = 14 | |
function Get-MSGraphAppToken{ | |
<# .SYNOPSIS | |
Get an app based authentication token required for interacting with Microsoft Graph API | |
.PARAMETER TenantID | |
A tenant ID should be provided. | |
.PARAMETER ClientID | |
Application ID for an Azure AD application. Uses by default the Microsoft Intune PowerShell application ID. | |
.PARAMETER ClientSecret | |
Web application client secret. | |
.EXAMPLE | |
# Manually specify username and password to acquire an authentication token: | |
Get-MSGraphAppToken -TenantID $TenantID -ClientID $ClientID -ClientSecert = $ClientSecret | |
.NOTES | |
Author: Jan Ketil Skanke | |
Contact: @JankeSkanke | |
Created: 2020-15-03 | |
Updated: 2020-15-03 | |
Version history: | |
1.0.0 - (2020-03-15) Function created | |
#>[CmdletBinding()] | |
param ( | |
[parameter(Mandatory = $true, HelpMessage = "Your Azure AD Directory ID should be provided")] | |
[ValidateNotNullOrEmpty()] | |
[string]$TenantID, | |
[parameter(Mandatory = $true, HelpMessage = "Application ID for an Azure AD application")] | |
[ValidateNotNullOrEmpty()] | |
[string]$ClientID, | |
[parameter(Mandatory = $true, HelpMessage = "Azure AD Application Client Secret.")] | |
[ValidateNotNullOrEmpty()] | |
[string]$ClientSecret | |
) | |
Process { | |
$ErrorActionPreference = "Stop" | |
# Construct URI | |
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" | |
# Construct Body | |
$body = @{ | |
client_id = $clientId | |
scope = "https://graph.microsoft.com/.default" | |
client_secret = $clientSecret | |
grant_type = "client_credentials" | |
} | |
try { | |
$MyTokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing | |
$MyToken =($MyTokenRequest.Content | ConvertFrom-Json).access_token | |
If(!$MyToken){ | |
Write-Warning "Failed to get Graph API access token!" | |
Exit 1 | |
} | |
$MyHeader = @{"Authorization" = "Bearer $MyToken" } | |
} | |
catch [System.Exception] { | |
Write-Warning "Failed to get Access Token, Error message: $($_.Exception.Message)"; break | |
} | |
return $MyHeader | |
} | |
} | |
$Count=0 | |
#Generate Graph API access token | |
$global:Header = Get-MSGraphAppToken -TenantID $TenantId -ClientID $ClientID -ClientSecret $ClientSecret | |
#URL to SharePoint list with entries of sent reminders. | |
$currentUriSharePoint = "$($SharePointListURL)/items?expand=fields" | |
#Get all entries in SharePoint list. | |
$ListItems = while (-not [string]::IsNullOrEmpty($currentUriSharePoint)) { | |
# API Call | |
# Write-Host "`r`nQuerying $currentUri..." -ForegroundColor Yellow | |
$apiCall = Invoke-WebRequest -Method "GET" -Uri $currentUriSharePoint -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing | |
$nextLink = $null | |
$currentUriSharePoint = $null | |
if ($apiCall.Content) { | |
# Check if any data is left | |
$nextLink = $apiCall.Content | ConvertFrom-Json | Select-Object '@odata.nextLink' | |
$currentUriSharePoint = $nextLink.'@odata.nextLink' | |
$apiCall.Content | ConvertFrom-Json | |
} | |
} | |
$ListItems = ($ListItems.value).fields | |
#Checks if Team in SharePoint list has 1 or 2 owners. If team has 2 owners, then the entry is deleted from the list. | |
foreach($item in $ListItems){ | |
#Get list of owners of team. | |
$TeamOwners = @() | |
try{$TeamOwners = (Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/beta/groups/$($item.TeamID)/owners" -Headers $global:Header).value} | |
catch{$deleteUri = '$($SharePointListURL)/items/' + $($item.id) | |
$deleteApiCall = Invoke-WebRequest -Method "DELETE" -Uri $deleteUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing} | |
if($TeamOwners.Count -gt 1){ | |
$deleteUri = "$($SharePointListURL)/items/$($item.id)" | |
$deleteApiCall = Invoke-WebRequest -Method "DELETE" -Uri $deleteUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing | |
} | |
} | |
$currentUri = 'https://graph.microsoft.com/beta/groups?$select=id,resourceProvisioningOptions,Displayname,mail' | |
$Teams = while (-not [string]::IsNullOrEmpty($currentUri)) { | |
# API Call | |
# Write-Host "`r`nQuerying $currentUri..." -ForegroundColor Yellow | |
$apiCall = Invoke-WebRequest -Method "GET" -Uri $currentUri -ContentType "application/json" -Headers $global:Header -ErrorAction Stop -UseBasicParsing | |
$nextLink = $null | |
$currentUri = $null | |
if ($apiCall.Content) { | |
# Check if any data is left | |
$nextLink = $apiCall.Content | ConvertFrom-Json | Select-Object '@odata.nextLink' | |
$currentUri = $nextLink.'@odata.nextLink' | |
$apiCall.Content | ConvertFrom-Json | |
} | |
} | |
$Teams = $Teams.value | |
#Filters out all groups that are teams. | |
$Teams = $Teams | Where-Object { $_.resourceProvisioningOptions -eq 'Team' } | |
$count2 = 0 | |
foreach($team in $teams){ | |
$count2++ | |
"Team $count2" | |
$TeamDisplayName = @() | |
$TeamDisplayName = ($($Team.displayname).replace("'","")).replace('"','') | |
#Get list of owners of team. | |
$TeamOwners = @() | |
$TeamOwners = (Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/beta/groups/$($team.id)/owners" -Headers $global:Header).value | |
#If team has only 1 owner. | |
If($TeamOwners.Count -eq 1){ | |
$Title="Action Required – Your team is breaking our guidelines" | |
$bodyHTML ='<doctype html><html><head><title>' + $Title +' </title></head><body><font face="Calibri" size="3">' | |
$bodyHTML+="<p>Hi $($TeamOwners.givenname)!</p><p>the team<b><i> $TeamDisplayName</i></b>," | |
$bodyHTML+=' that you are the owner of is breaking our policy for two owners or more for a team. Please add another owner to the team. | |
<ul><li>More info here: <a href=https://alexholmeset.blog#for-eiere-og-superbrukere-av-teams>Info about team ownership</a> <br /> <p>Best regards <br /> <b><i> IT Team</i></b></p></font></body></html>' | |
$BodyEmail = @" | |
{ | |
"message": { | |
"subject": "$Title", | |
"body": { | |
"contentType": "HTML", | |
"content": '$bodyHTML' | |
}, | |
"toRecipients": [ | |
{ | |
"emailAddress": { | |
"address": "$($teamowners.userPrincipalName)" | |
} | |
} | |
] | |
}, | |
"saveToSentItems": "false" | |
} | |
"@ | |
#Check if team already has an entry in the SharePoint list. | |
$TeamListItem = @() | |
$TeamListItem = $ListItems | Where-Object { $_.TeamID -eq $team.id } | |
#If entry exists in SharePoint list, go on and send new reminder. | |
If($TeamListItem.TeamID -eq $Team.ID){ | |
#Send 2. warning. | |
if ($TeamListItem.Warning1 -and !$TeamListItem.Warning2) { | |
$Days =@() | |
$Days = ($TeamListItem.Warning1 | New-TimeSpan).Days | |
#If first warning has been sent, send second warning if enough time has passed. | |
If($Days -eq $DaysBewteenReminders){ | |
#Send email to team owner. | |
Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/v1.0/users/$($EmailUPN)/sendMail" -Headers $global:Header -body $BodyEmail -ContentType "application/json;charset=utf-8" | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'Warning2': '$(Get-Date)' | |
} | |
"@ | |
Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/v1.0/sites/a5c40278-b214-4a00-8a9c-49cfa85e1ce8/lists/e5d86ef4-22e8-46a9-9bc2-901a322cf38b/items/$($TeamListItem.id)/fields" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json" | |
$teamowners.userPrincipalName | |
/} | |
} | |
#Send 3. warning. | |
if ($TeamListItem.Warning2 -and !$TeamListItem.Warning3) { | |
$Days =@() | |
$Days = ($TeamListItem.Warning2 | New-TimeSpan).Days | |
#If second warning has been sent, send third warning if enough time has passed. | |
If($Days -eq $DaysBewteenReminders){ | |
#Send email to team owner. | |
Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/v1.0/users/$($EmailUPN)/sendMail" -Headers $global:Header -body $BodyEmail -ContentType "application/json;charset=utf-8" | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'Warning3': '$(Get-Date)' | |
} | |
"@ | |
Invoke-RestMethod -Method Patch -Uri "$($SharePointListURL)/items/$($TeamListItem.id)/fields" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json" | |
$teamowners.userPrincipalName | |
} | |
} | |
#Send 4. warning. | |
if ($TeamListItem.Warning3 -and !$TeamListItem.Warning4) { | |
$Days =@() | |
$Days = ($TeamListItem.Warning3 | New-TimeSpan).Days | |
#If third warning has been sent, send fourth warning if enough time has passed. | |
If($Days -eq $DaysBewteenReminders){ | |
#Send email to team owner. | |
Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/v1.0/users/$($EmailUPN)/sendMail" -Headers $global:Header -body $BodyEmail -ContentType "application/json;charset=utf-8" | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'Warning4': '$(Get-Date)' | |
} | |
"@ | |
Invoke-RestMethod -Method Patch -Uri "$($SharePointListURL)/items/$($TeamListItem.id)/fields" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json" | |
$teamowners.userPrincipalName | |
} | |
} | |
#Send 5. warning. | |
if ($TeamListItem.Warning4 -and !$TeamListItem.Warning5) { | |
$Days =@() | |
$Days = ($TeamListItem.Warning4 | New-TimeSpan).Days | |
#Om fjerde varsel er sendt, send femte varsel om det har gått lang nokk tid. | |
#If fourth warning has been sent, send fifth warning if enough time has passed. | |
If($Days -eq $DaysBewteenReminders){ | |
#Siste epost varsling til eier av Team. | |
#Final email warning to team owner. | |
$Title="Last Warning – Your team is breaking our guidelines" | |
$bodyHTMLsiste ='<doctype html><html><head><title>' + $Title +' </title></head><body><font face="Calibri" size="3">' | |
$bodyHTML+="<p>Hi $($TeamOwners.givenname)!</p><p>the team<b><i> $TeamDisplayName</i></b>," | |
$bodyHTML+=' that you are the owner of is breaking our policy for two owners or more for a team. Please add another owner to the team, or the team will be deleted in 14 days. | |
<ul><li>More info here: <a href=https://alexholmeset.blog#for-eiere-og-superbrukere-av-teams>Info about team ownership</a> <br /> <p>Best regards <br /> <b><i> IT Team</i></b></p></font></body></html>' | |
$BodyEmailsiste = @" | |
{ | |
"message": { | |
"subject": "$Title", | |
"body": { | |
"contentType": "HTML", | |
"content": '$bodyHTMLsiste' | |
}, | |
"toRecipients": [ | |
{ | |
"emailAddress": { | |
"address": "$($teamowners.userPrincipalName)" | |
} | |
} | |
] | |
}, | |
"saveToSentItems": "false" | |
} | |
"@ | |
Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/v1.0/users/$($EmailUPN)/sendMail" -Headers $global:Header -body $BodyEmailSiste -ContentType "application/json;charset=utf-8" | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'Warning5': '$(Get-Date)' | |
} | |
"@ | |
Invoke-RestMethod -Method Patch -Uri "$($SharePointListURL)/items/$($TeamListItem.id)/fields" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json" | |
$teamowners.userPrincipalName | |
} | |
} | |
if ($TeamListItem.Warning5 -and !$TeamListItem.WarningAdmin) { | |
$Days =@() | |
$Days = ($TeamListItem.Warning5 | New-TimeSpan).Days | |
#Om femte varsel er sendt, send IT varsel om det har gått lang nokk tid. | |
If($Days -ge $WarningAdminDays){ | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'WarningAdmin': '$(Get-Date)' | |
} | |
"@ | |
Invoke-RestMethod -Method Patch -Uri "$($SharePointListURL)/items/$($TeamListItem.id)/fields" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json" | |
#Team still has 1 owner after 14 days, send message to IT in Teams. | |
$body = @" | |
{"text": "$($team.displayname) / $($team.id) has no owner!"} | |
"@ | |
Invoke-RestMethod -Method POST -Body $body -Uri $TeamsWebhookURL -ContentType "application/json;charset=utf-8" | |
} | |
} | |
} | |
Else{ | |
$Count++ | |
$Count | |
#If not warning has been sent, send first warning and create entry in SharePoint list. | |
Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/v1.0/users/$($EmailUPN)/sendMail" -Headers $global:Header -body $BodyEmail -ContentType "application/json;charset=utf-8" | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'fields': { | |
'Title': "$TeamDisplayName", | |
'TeamID': '$($Team.ID)', | |
'UPN': '$($teamowners.userPrincipalName)', | |
'Warning1': '$(Get-Date)' | |
} | |
} | |
"@ | |
$TeamOwners | |
$BodyNewListItem | |
Invoke-RestMethod -Method POST -Uri "$($SharePointListURL)/items" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json;charset=utf-8" | |
$teamowners.userPrincipalName | |
} | |
} | |
#If the team has now owner, send message to IT in Teams. | |
If($TeamOwners.Count -eq 0){ | |
$TeamListItem = @() | |
$TeamListItem = $ListItems | Where-Object { $_.TeamID -eq $team.id } | |
#If entry does not exist in SharePoint list, create a entry and warning to IT in Teams. | |
If($TeamListItem.TeamID -ne $Team.ID){ | |
$body = @" | |
{"text": "$($team.displayname) / $($team.id) can be deleted today!"} | |
"@ | |
Invoke-RestMethod -Method POST -Body $body -Uri $TeamsWebhookURL -ContentType "application/json;charset=utf-8" | |
#Create entry in SharePoint list. | |
$BodyNewListItem = @() | |
$BodyNewListItem = @" | |
{ | |
'fields': { | |
'Title': "$($team.displayName)", | |
'TeamID': '$($Team.ID)', | |
'WarningAdmin': '$(Get-Date)' | |
} | |
} | |
"@ | |
Invoke-RestMethod -Method POST -Uri "$($SharePointListURL)/items" -Headers $global:Header -body $BodyNewListItem -ContentType "application/json;charset=utf-8" | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment