-
Notifications
You must be signed in to change notification settings - Fork 133
/
App_Credential_Usage_Report_GraphSDK.ps1
83 lines (69 loc) · 4.47 KB
/
App_Credential_Usage_Report_GraphSDK.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#Requires -Version 3.0
#The script requires the following permissions:
# Application.Read.All (required)
# AuditLog.Read.All (required)
#For details on what the script does and how to run it, check: https://www.michev.info/blog/post/5986/build-your-own-entra-id-application-credential-activity-report
[CmdletBinding(SupportsShouldProcess)] #Make sure we can use -Verbose
Param()
#==========================================================================
#Main script starts here
#==========================================================================
#Determine the required scopes, based on the parameters passed to the script
$RequiredScopes = @("Application.Read.All", "AuditLog.Read.All")
Write-Verbose "Connecting to Graph API..."
Import-Module Microsoft.Graph.Beta.Applications -Verbose:$false -ErrorAction Stop
try {
Connect-MgGraph -Scopes $RequiredScopes -verbose:$false -ErrorAction Stop -NoWelcome
}
catch { throw $_ }
#Check if we have all the required permissions
$CurrentScopes = (Get-MgContext).Scopes
if ($RequiredScopes | ? {$_ -notin $CurrentScopes }) { Write-Error "The access token does not have the required permissions, rerun the script and consent to the missing scopes!" -ErrorAction Stop }
#Get the list of application objects within the tenant.
$Apps = @()
Write-Verbose "Retrieving list of applications..."
$Apps = Get-MgBetaApplication -All -ErrorAction Stop -Verbose:$false
#Prepare variables
$output = [System.Collections.Generic.List[Object]]::new() #output variable
$i=0; $count = 1; $PercentComplete = 0;
#Process the list of applications
foreach ($App in $Apps) {
#Progress message
$ActivityMessage = "Retrieving data for application $($App.DisplayName). Please wait..."
$StatusMessage = ("Processing application {0} of {1}: {2}" -f $count, @($Apps).count, $App.AppId)
$PercentComplete = ($count / @($Apps).count * 100)
Write-Progress -Activity $ActivityMessage -Status $StatusMessage -PercentComplete $PercentComplete
$count++
Write-Verbose "Processing application $($App.id)..."
#Get the service principal sign-in logs for the application
#Filters SUCCESS events only - automatically done when we include the servicePrincipalCredentialKeyId filter. Consider NOT doing it?
foreach ($cred in @($App.KeyCredentials + $App.PasswordCredentials)) {
$KeyLastLogin = $null
try {
$KeyLastLogin = Get-MgBetaAuditLogSignIn -Filter "(signInEventTypes/any(t:t eq 'servicePrincipal')) and appId eq `'$($App.AppId)`' and servicePrincipalCredentialKeyId eq `'$($cred.KeyId)`'" -Top 1 -ErrorAction Stop -Verbose:$false
}
catch { Write-Warning "Failed to retrieve sign-in logs for application $($App.id)"; $_ }
#Prepare the output
$i++;$objPermissions = [PSCustomObject][ordered]@{
"Number" = $i
"AppId" = $app.AppId
"AppObjectId" = $app.Id
"AppDisplayName" = $app.DisplayName
"KeyId" = $cred.KeyId
"KeyDisplayName" = & { if ($cred.DisplayName) { $cred.DisplayName } else { "N/A" } } #can be null. Portal returns Description, but it's not available via Graph?
"KeyType" = & { if ($cred.Type) { $cred.Type } else { "Client secret" } }
"KeyUsage" = & { if ($cred.Usage) { $cred.Usage } else { "N/A" } }
"LastUsed" = & { if ($KeyLastLogin) { Get-Date($KeyLastLogin.CreatedDateTime.DateTime) -Format g } else { "N/A" } }
"KeyExpirationDate" = & { if ($cred.EndDateTime) { Get-Date($cred.EndDateTime) -Format g } else { "N/A" } }
"CredentialOrigin" = "application"
"ServicePrincipalObjectId" = & { if ($KeyLastLogin.ServicePrincipalId) { $KeyLastLogin.ServicePrincipalId } else { "N/A" } } #Can be null, can we use it to differentiate between cert and client secret?
"ServicePrincipalDisplayName" = & { if ($KeyLastLogin.ServicePrincipalName) { $KeyLastLogin.ServicePrincipalName } else { "N/A" } }
"ResourceId" = & { if ($KeyLastLogin.ResourceId) { $KeyLastLogin.ResourceId } else { "N/A" } }
"ResourceDisplayName" = & { if ($KeyLastLogin.ResourceDisplayName) { $KeyLastLogin.ResourceDisplayName } else { "N/A" } }
}
$output.Add($objPermissions)
}
}
#Export the result to CSV file
$output | select * -ExcludeProperty Number | Export-CSV -nti -Path "$((Get-Date).ToString('yyyy-MM-dd_HH-mm-ss'))_GraphAppRegInventory.csv"
Write-Verbose "Output exported to $($PWD)\$((Get-Date).ToString('yyyy-MM-dd_HH-mm-ss'))_GraphAppRegInventory.csv"