Last active
September 8, 2021 13:35
-
-
Save darrenjrobinson/9a99c60af53adb1e68fb9821f4c5f9ab to your computer and use it in GitHub Desktop.
Get all AAD B2B Guest Accounts which haven't signed in, in the last XX Days, or haven't accepted a B2B Guest Invitation in last XX Days. Associated Blogpost https://blog.darrenjrobinson.com/finding-stale-azure-ad-b2b-guest-accounts-based-on-lastsignindatetime
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
Function AuthN { | |
<# | |
.SYNOPSIS | |
Authenticate to Azure AD and receieve Access and Refresh Tokens. | |
.DESCRIPTION | |
Authenticate to Azure AD and receieve Access and Refresh Tokens. | |
.PARAMETER tenantID | |
(required) Azure AD TenantID. | |
.PARAMETER credential | |
(required) ClientID and ClientSecret of the Azure AD registered application with the necessary permissions. | |
.EXAMPLE | |
$myCred = Get-Credential | |
AuthN -credential $myCred -tenantID '74ea519d-9792-4aa9-86d9-abcdefgaaa' | |
.LINK | |
http://darrenjrobinson.com/ | |
#> | |
[cmdletbinding()] | |
param( | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[string]$tenantID, | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[System.Management.Automation.PSCredential]$credential | |
) | |
if (!(get-command Get-MsalToken)) { | |
Install-Module -name MSAL.PS -Force -AcceptLicense | |
} | |
try { | |
# Authenticate and Get Tokens | |
$token = Get-MsalToken -ClientId $credential.UserName -ClientSecret $credential.Password -TenantId $tenantID | |
return $token | |
} | |
catch { | |
$_ | |
} | |
} | |
Function GetAADSignIns { | |
<# | |
.SYNOPSIS | |
Get AAD Account SignIn Activity. | |
.DESCRIPTION | |
Get AAD Account SignIn Activity. | |
.PARAMETER date | |
(required) date whereby users haven't signed in since to return objects for | |
.EXAMPLE | |
GetAADSignIns -date "2021-01-01T00:00:00Z" | |
.LINK | |
http://darrenjrobinson.com/ | |
#> | |
[cmdletbinding()] | |
param( | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[string]$date | |
) | |
# Refresh Access Token | |
$global:myToken = AuthN -credential $myCred -tenantID $myTenantId | |
try { | |
# Get AAD B2B Users. | |
$aadSignInActivity = Invoke-RestMethod -Headers @{Authorization = "Bearer $($myToken.AccessToken)" } ` | |
-Uri "https://graph.microsoft.com/beta/users?filter=signInActivity/lastSignInDateTime le $($date)" ` | |
-Method Get | |
return $aadSignInActivity | |
} | |
catch { | |
$_ | |
} | |
} | |
Function GetAADUserSignInActivity { | |
<# | |
.SYNOPSIS | |
Get AAD Account SignIn Activity. | |
.DESCRIPTION | |
Get AAD Account SignIn Activity. | |
.PARAMETER ID | |
(required) ObjectID of the user to get SignIn Activity for | |
.EXAMPLE | |
GetAADUserSignInActivity -ID "feeb81f9-af70-2d5a-aa8c-f035ddaabcde" | |
.LINK | |
http://darrenjrobinson.com/ | |
#> | |
[cmdletbinding()] | |
param( | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[string]$ID | |
) | |
# Refresh Access Token | |
$global:myToken = AuthN -credential $myCred -tenantID $myTenantId | |
try { | |
# Get AAD SignIn Activity. | |
$aadSignInActivity = Invoke-RestMethod -Headers @{Authorization = "Bearer $($myToken.AccessToken)" } ` | |
-Uri "https://graph.microsoft.com/beta/users/$($ID)?`$select=id,displayName,signInActivity" ` | |
-Method Get | |
return $aadSignInActivity | |
} | |
catch { | |
$_ | |
} | |
} | |
Function GetAADPendingGuests { | |
<# | |
.SYNOPSIS | |
Get AAD B2B Accounts where the inviation hasn't been accepted. | |
.DESCRIPTION | |
Get AAD B2B Accounts where the inviation hasn't been accepted. | |
.EXAMPLE | |
GetAADPendingGuests | |
.LINK | |
http://darrenjrobinson.com/ | |
#> | |
[cmdletbinding()] | |
param() | |
# Refresh Access Token | |
$global:myToken = AuthN -credential $myCred -tenantID $myTenantId | |
try { | |
# Get AAD B2B Pending Users. | |
$aadPendingGuests = Invoke-RestMethod -Headers @{Authorization = "Bearer $($myToken.AccessToken)" } ` | |
-Uri "https://graph.microsoft.com/beta/users?filter=externalUserState eq 'PendingAcceptance'&`$top=999" ` | |
-Method Get | |
return $aadPendingGuests.value | |
} | |
catch { | |
$_ | |
} | |
} | |
# Globals | |
# Tenant ID | |
$global:myTenantId = '<YOUR AZURE AD TENANT ID>' | |
# Registered AAD App ID and Secret | |
$global:myCred = [pscredential]::new("<YOUR AAD APP CLIENT ID>", ("<YOUR AAD APP CLIENT SECRET>" | ConvertTo-SecureString -AsPlainText -Force)) | |
Import-Module MSAL.PS | |
<# | |
FIND State/Dormant B2B Accounts and Stale/Dormant B2B Guest Invitations | |
Get all AAD Accounts which haven't signed in, in the last XX Days, or haven't accepted a B2B Guest Invitation in last XX Days. | |
#> | |
# Number of days over which an Azure AD Account that hasn't signed in is considered stale | |
$staleAgeDays = '90' | |
[datetime]$today = (get-date).ToString('yyyy-MM-dd') | |
$staleDate = $today.AddDays( - "$($staleAgeDays)") | |
$strStaleDate = $staleDate.ToString('yyyy-MM-dd') | |
# Get InActivity Accounts | |
$inactiveAccounts = GetAADSignIns -date "$($strStaleDate)T00:00:00Z" | |
$inactiveAccounts.value.Count | |
# Filter Accounts down to Azure AD Guest Accounts | |
$guests = $inactiveAccounts.value | Select-Object | Where-Object { $_.userType -eq 'Guest' } | |
Write-Host -ForegroundColor Green "$($Guests.count) Guest accounts haven't signed in since $($strStaleDate)" | |
# Add lastSignInDateTime to the User PowerShell Object | |
foreach ($b2bUser in $guests) { | |
$signIns = $null | |
$signIns = GetAADUserSignInActivity -ID $b2bUser.id | |
$b2bUser | Add-Member -Type NoteProperty -Name "lastSignInDateTime" -Value $signIns.signInActivity.lastSignInDateTime | |
} | |
# Disabled Guest Accounts | |
$disabledGuests = $guests | Select-Object | Where-Object { $_.accountEnabled -eq $false } | |
Write-Host -ForegroundColor Yellow " $($disabledGuests.count) Guest accounts haven't signed in since $($strStaleDate) and are flagged as 'Account Disabled'." | |
# Guest Accounts inivited but pending acceptance | |
$pendingGuests = GetAADPendingGuests | |
Write-Host -ForegroundColor Green "$($pendingGuests.count) Guest accounts are still 'pending' B2B Guest invitation acceptance." | |
$stalePendingInvites = $pendingGuests | Select-Object | Where-Object {[datetime]$_.externalUserStateChangeDateTime -le [datetime]"$($strStaleDate)T00:00:00Z"} | |
Write-Host -ForegroundColor Yellow " $($stalePendingInvites.count) Guest accounts were invited before '$($strStaleDate)'" | |
# All Stale Accounts | |
$staleGuests = $null | |
$staleGuests += $guests | |
$staleGuests += $stalePendingInvites | |
Write-Host -ForegroundColor Green "$($staleGuests.count) Guest accounts are still 'pending' B2B Guest invitation acceptance or haven't signed in since '$($strStaleDate)'." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment