├── CODE_OF_CONDUCT.md ├── Get-AzureADGraphApps.ps1 ├── LICENSE ├── README.md ├── SECURITY.md └── SUPPORT.md /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /Get-AzureADGraphApps.ps1: -------------------------------------------------------------------------------- 1 | #Requires -PSEdition Desktop 2 | <# 3 | .SYNOPSIS 4 | Returns all the apps and service principles that currently use Azure AD Graph 5 | 6 | .EXAMPLE 7 | PS C:\> .\Get-AzureADGraphApps.ps1 8 | Returns a collection of all the apps that have Azure AD Graph permissions assigned to them 9 | #> 10 | 11 | [cmdletbinding()] 12 | param() 13 | 14 | function Load-Module ($m) { 15 | Write-Progress -Activity "Loading dependencies..." 16 | # If module is imported say that and do nothing 17 | if (Get-Module | Where-Object { $_.Name -eq $m }) { 18 | Write-Verbose "Module $m is already imported." 19 | } 20 | else { 21 | 22 | # If module is not imported, but available on disk then import 23 | if (Get-Module -ListAvailable | Where-Object { $_.Name -eq $m }) { 24 | Import-Module $m 25 | } 26 | else { 27 | 28 | # If module is not imported, not available on disk, but is in online gallery then install and import 29 | if (Find-Module -Name $m | Where-Object { $_.Name -eq $m }) { 30 | Install-Module -Name $m -Force -Scope CurrentUser 31 | Import-Module $m 32 | } 33 | else { 34 | 35 | # If module is not imported, not available and not in online gallery then abort 36 | Write-Host "Module $m not imported, not available and not in online gallery, exiting." 37 | EXIT 1 38 | } 39 | } 40 | } 41 | } 42 | 43 | function Get-MSCloudIdConsentGrantList { 44 | # An in-memory cache of objects by {object ID} andy by {object class, object ID} 45 | $script:ObjectByObjectId = @{} 46 | $script:ObjectByObjectClassId = @{} 47 | 48 | # Function to add an object to the cache 49 | function CacheObject($Object) { 50 | if ($Object) { 51 | if (-not $script:ObjectByObjectClassId.ContainsKey($Object.ObjectType)) { 52 | $script:ObjectByObjectClassId[$Object.ObjectType] = @{} 53 | } 54 | $script:ObjectByObjectClassId[$Object.ObjectType][$Object.ObjectId] = $Object 55 | $script:ObjectByObjectId[$Object.ObjectId] = $Object 56 | } 57 | } 58 | 59 | # Function to retrieve an object from the cache (if it's there), or from Azure AD (if not). 60 | function GetObjectByObjectId($ObjectId) { 61 | if (-not $script:ObjectByObjectId.ContainsKey($ObjectId)) { 62 | Write-Verbose ("Querying Azure AD for object '{0}'" -f $ObjectId) 63 | try { 64 | $object = Get-AzureADObjectByObjectId -ObjectId $ObjectId 65 | CacheObject -Object $object 66 | } 67 | catch { 68 | Write-Verbose "Object not found." 69 | } 70 | } 71 | return $script:ObjectByObjectId[$ObjectId] 72 | } 73 | 74 | # Get all ServicePrincipal objects and add to the cache 75 | Write-Progress -Activity "Retrieving Service Principal objects. Please wait..." 76 | $servicePrincipals = Get-AzureADServicePrincipal -All $true 77 | $tenantId = (Get-AzureADTenantDetail).ObjectId 78 | 79 | $Oauth2PermGrants = @() 80 | 81 | $count = 0 82 | foreach ($sp in $servicePrincipals) { 83 | CacheObject -Object $sp 84 | $spPermGrants = Get-AzureADServicePrincipalOAuth2PermissionGrant -ObjectId $sp.ObjectId -All $true 85 | $Oauth2PermGrants += $spPermGrants 86 | $count++ 87 | Write-Progress -Activity "Getting Service Principal Delegate Permissions..." -Status "$count of $($servicePrincipals.Count) - $($sp.DisplayName)" -percentComplete (($count / $servicePrincipals.Count) * 100) 88 | 89 | if ($sp.AppId -eq "00000002-0000-0000-c000-000000000000") { 90 | #Azure Active Directory Graph API app 91 | $aadGraphSp = $sp 92 | } 93 | } 94 | 95 | # Get all existing OAuth2 permission grants, get the client, resource and scope details 96 | Write-Progress -Activity "Checking Delegated Permission Grants..." 97 | foreach ($grant in $Oauth2PermGrants) { 98 | if ($grant.ResourceId -eq $aadGraphSp.ObjectId -and $grant.Scope) { 99 | $grant.Scope.Split(" ") | Where-Object { $_ } | ForEach-Object { 100 | $scope = $_ 101 | $client = GetObjectByObjectId -ObjectId $grant.ClientId 102 | 103 | $isAppProxyApp = $false 104 | if ($client.Tags -contains "WindowsAzureActiveDirectoryOnPremApp") { 105 | $isAppProxyApp = $true 106 | } 107 | if(-not $isAppProxyApp) 108 | { 109 | $ownerUPN = (Get-AzureADServicePrincipalOwner -ObjectId $client.ObjectId -Top 1).UserPrincipalName 110 | Write-Progress -Activity "Checking Delegate Permissions - $($client.DisplayName)" 111 | 112 | # Determine if the object comes from the Microsoft Services tenant, and flag it if true 113 | $MicrosoftRegisteredClientApp = @() 114 | if ($client.AppOwnerTenantId -eq "f8cdef31-a31e-4b4a-93e4-5f571e91255a" -or $client.AppOwnerTenantId -eq "72f988bf-86f1-41af-91ab-2d7cd011db47") { 115 | $MicrosoftRegisteredClientApp = $true 116 | } 117 | else { 118 | $MicrosoftRegisteredClientApp = $false 119 | } 120 | 121 | if(-not $MicrosoftRegisteredClientApp) { 122 | $isTenantOwnedApp = $false 123 | if ($client.AppOwnerTenantId -eq $tenantId) { 124 | $isTenantOwnedApp = $true 125 | } 126 | 127 | $resource = GetObjectByObjectId -ObjectId $grant.ResourceId 128 | 129 | if ($grant.ConsentType -eq "AllPrincipals") { 130 | $simplifiedgranttype = "Delegated-AllPrincipals" 131 | } 132 | elseif ($grant.ConsentType -eq "Principal") { 133 | $simplifiedgranttype = "Delegated-Principal" 134 | } 135 | $global:hasData = $true 136 | New-Object PSObject -Property ([ordered]@{ 137 | "ObjectId" = $grant.ClientId 138 | "DisplayName" = $client.DisplayName 139 | "ApplicationId" = $client.AppId 140 | "PermissionType" = $simplifiedgranttype 141 | "Resource" = $resource.DisplayName 142 | "Permission" = $scope 143 | "Owner" = $ownerUPN 144 | "TenantOwned" = $isTenantOwnedApp 145 | }) 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | # Iterate over all ServicePrincipal objects and get app permissions 153 | Write-Progress -Activity "Getting Application Permission Grants..." 154 | $script:ObjectByObjectClassId['ServicePrincipal'].GetEnumerator() | ForEach-Object { 155 | $sp = $_.Value 156 | Write-Progress -Activity "Checking Application Permissions - $($sp.DisplayName)" 157 | 158 | Get-AzureADServiceAppRoleAssignedTo -ObjectId $sp.ObjectId -All $true ` 159 | | Where-Object { $_.PrincipalType -eq "ServicePrincipal" -and $_.ResourceId -eq $aadGraphSp.ObjectId } | ForEach-Object { 160 | $assignment = $_ 161 | 162 | $client = GetObjectByObjectId -ObjectId $assignment.PrincipalId 163 | 164 | $isAppProxyApp = $false 165 | if ($client.Tags -contains "WindowsAzureActiveDirectoryOnPremApp") { 166 | $isAppProxyApp = $true 167 | } 168 | if(-not $isAppProxyApp) 169 | { 170 | $ownerUPN = (Get-AzureADServicePrincipalOwner -ObjectId $client.ObjectId -Top 1).UserPrincipalName 171 | # Determine if the object comes from the Microsoft Services tenant, and flag it if true 172 | $MicrosoftRegisteredClientApp = @() 173 | if ($client.AppOwnerTenantId -eq "f8cdef31-a31e-4b4a-93e4-5f571e91255a" -or $client.AppOwnerTenantId -eq "72f988bf-86f1-41af-91ab-2d7cd011db47") { 174 | $MicrosoftRegisteredClientApp = $true 175 | } 176 | else { 177 | $MicrosoftRegisteredClientApp = $false 178 | } 179 | 180 | if(-not $MicrosoftRegisteredClientApp) { 181 | $isTenantOwnedApp = $false 182 | if ($client.AppOwnerTenantId -eq $tenantId) { 183 | $isTenantOwnedApp = $true 184 | } 185 | $resource = GetObjectByObjectId -ObjectId $assignment.ResourceId 186 | $appRole = $resource.AppRoles | Where-Object { $_.Id -eq $assignment.Id } 187 | 188 | $global:hasData = $true 189 | New-Object PSObject -Property ([ordered]@{ 190 | "ObjectId" = $assignment.PrincipalId 191 | "DisplayName" = $client.DisplayName 192 | "ApplicationId" = $client.AppId 193 | "PermissionType" = "Application" 194 | "Resource" = $resource.DisplayName 195 | "Permission" = $appRole.Value 196 | "Owner" = $ownerUPN 197 | "TenantOwned" = $isTenantOwnedApp 198 | }) 199 | } 200 | } 201 | } 202 | } 203 | } 204 | 205 | Load-Module "AzureAD" 206 | $global:hasData = $false 207 | Get-MSCloudIdConsentGrantList 208 | if($global:hasData -eq $false){ 209 | Write-Warning "This tenant does not have any apps using Azure AD Graph" 210 | } 211 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure AD Graph 2 | 3 | This PowerShell script lists applications in your tenant that use permissions for Azure AD Graph. [Azure AD Graph will be retired soon](https://techcommunity.microsoft.com/t5/azure-active-directory-identity/update-your-applications-to-use-microsoft-authentication-library/ba-p/1257363). 4 | 5 | If you have applications that use Azure AD Graph permissions and that actively call Azure AD Graph, follow the steps in the [App migration planning checklist](https://docs.microsoft.com/graph/migrate-azure-ad-graph-planning-checklist) to migrate your applications using Azure AD Graph to Microsoft Graph. 6 | 7 | > :warning: **Azure AD Graph is deprecated**. To avoid loss of functionality, [migrate your applications to Microsoft Graph](https://docs.microsoft.com/graph/migrate-azure-ad-graph-planning-checklist) as soon as possible. 8 | > 9 | 10 | ## Prerequisites 11 | 12 | You will need PowerShell Desktop edition to run this script. If you're on a Mac or if you use Azure Cloud Shell, you can obtain the same list of applications using [the Azure portal by following these steps](https://docs.microsoft.com/en-us/graph/migrate-azure-ad-graph-faq#method-2-use-the-app-registrations-menu-of-the-azure-portal). 13 | 14 | > :warning: This script does not capture all instances of Azure AD Graph usage. If you have applications that have been granted permissions or app role assignments using the methods listed below they will not be included in the generated report. 15 | > * App has not been granted delegated permissions for Azure AD Graph, but calls Azure AD Graph (e.g. relying on delegated permissions granted for Microsoft Graph for authorization). 16 | > * App has not been granted app role assignments for Azure AD Graph, but calls Azure AD Graph (e.g. relying on directory role assignments and/or ownership for direct authorization). 17 | > * App has been granted delegated permissions for Azure AD Graph but has already migrated to Microsoft Graph (e.g. relying on delegated permissions granted previously for Azure AD Graph, which are considered granted for Microsoft Graph as well). 18 | 19 | Download and save the [Get-AzureADGraphApps.ps1](https://github.com/microsoft/AzureADGraphApps/blob/main/Get-AzureADGraphApps.ps1) script file to your device. 20 | 21 | Note: 22 | This script has a dependency on the Azure AD PowerShell module. When the script is run it will automatically install the dependant module if it is not already installed. 23 | 24 | ## Usage 25 | The command below will create a csv of all the apps in the tenant that rely on the Azure AD Graph. 26 | 27 | ```powershell 28 | Connect-AzureAD 29 | .\Get-AzureADGraphApps.ps1 | Export-Csv .\aadgraphapps.csv -NoTypeInformation 30 | ``` 31 | 32 | 33 | 34 | ## FAQs 35 | **Q: I use a Mac/Azure Cloud Shell. Can I run this script?** 36 | 37 | **A:** No, but you can fetch the same list of applications (that use Azure Active Directory Graph permissions) using the [Azure portal by following these steps](https://docs.microsoft.com/en-us/graph/migrate-azure-ad-graph-faq#method-2-use-the-app-registrations-menu-of-the-azure-portal). 38 | 39 | **Q: What permission do I need to run this script?** 40 | 41 | **A:** This script can be run by any user in the tenant and does not require a privileged Azure AD role. 42 | 43 | **Q: How long will the script take to complete?** 44 | 45 | **A:** The duration depends on the number of service principals in the tenant. A small tenant with less than 1000 service principals will usually complete in a few minutes. Larger tenants can take up to 1-2 hours and very large tenants that have more than 100,000 service principals can take 10-24 hours to run. 46 | 47 | **Q: Can I use Azure AD Graph permissions to call Microsoft Graph?** 48 | 49 | **A:** No, use the corresponding Microsoft Graph permissions. For more information, see [Review app registration, permissions, and consent](https://docs.microsoft.com/graph/migrate-azure-ad-graph-app-registration). 50 | 51 | **Q: Does this script automatically remove my Azure AD Graph permissions in favor of Microsoft Graph permissions?** 52 | 53 | **A:** No, this script gives you a list of applications that have Azure AD Graph permissions. You should review these applications, grant them the corresponding Microsoft Graph permissions, migrate their Azure AD Graph API calls to Microsoft Graph, and then remove these Azure AD Graph permissions. Our [App migration planning checklist](https://docs.microsoft.com/graph/migrate-azure-ad-graph-planning-checklist) can help you with this process. 54 | 55 | 56 | ## Support 57 | 58 | Please see [SUPPORT.md](SUPPORT.md) for support options. 59 | 60 | ## Contributing 61 | 62 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 63 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 64 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 65 | 66 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 67 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 68 | provided by the bot. You will only need to do this once across all repos using our CLA. 69 | 70 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 71 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 72 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 73 | 74 | ## Trademarks 75 | 76 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 77 | trademarks or logos is subject to and must follow 78 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 79 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 80 | Any use of third-party trademarks or logos are subject to those third-party's policies. 81 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and request help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests: 6 | 7 | 1. Search the [existing issues](https://github.com/microsoft/AzureADGraphApps/issues) before filing new issues to avoid duplicates. 8 | 2. If you don't find an issue that matches yours, file your bug or feature request as a new Issue. 9 | 10 | ## Microsoft Support Policy 11 | 12 | Support for the the resources in this repository is limited to the resources listed above. 13 | 14 | For help and questions about Microsoft Graph, please see https://developer.microsoft.com/graph/support. --------------------------------------------------------------------------------