Applies to BloodHound Enterprise only

Create the AzureHound Enterprise app

  1. Log into the Microsoft Entra admin center as a user with the Global Administrator role, or the following less privileged roles:
  1. In the left menu, select App registrations.
  2. Click New registration.
  3. In the Name field, give the application an identifying name in your organization. Make sure the supported account type is set to the “Accounts in this organizational directory only (Single tenant)” option. A URI is not required. Then click Register.
  4. In the Overview menu, copy the Application (client) ID and Directory (tenant) ID to be used later in AzureHound Enterprise Local Configuration.
  5. Continue to the next section: “Grant Microsoft Graph Permissions”.

Grant Microsoft Graph Permissions

  1. In the AzureHound application, select API Permissions.
  2. Remove the default User.Read delegated permission from the application, as it is not needed for data collection. Confirm the operation in the dialog window that appears after selecting the Remove permission option from the context menu.
  3. Select Add a permission.
  4. Click on Microsoft Graph.
  5. Select Application permissions.
  6. Search for the Directory.Read.All permission and check the box next to it.
  7. Search for the RoleManagement.Read.All permission and check the box next to it.
  8. In the bottom of the window, select Add permissions.
  9. Click on Grant admin consent for <your_tenant_name>.
  10. Click Yes on the confirmation dialog.
  1. After being redirected to API Permissions again, you should see both permissions as Granted.
  1. Continue to the next section: “Add application authentication certificate”.

Add application authentication certificate

This section requires you have authentication material. We highly recommend using certificate-based authentication. If you do not already have a certificate created, follow the article AzureHound Enterprise Local Configuration and then return back here.
  1. Select the Certificates & secrets section on the left.
  2. Click on Certificates.
  3. Click Upload certificate.
  4. Locate the cert.pem file created during AzureHound setup (either on your own, or utilizing the instructions at AzureHound Enterprise Local Configuration).
  5. Click the folder icon and locate the “cert.pem” file. Add a description if desired.
  6. In the bottom of the window, select Add.
  7. Continue to the next section to optionally configure application branding.

Configure application branding (optional)

Note: All steps in this section are optional and do not affect the functionality of the collector.
  1. Download the AzureHound Enterprise icon to your computer.
  2. Still on the AzureHound application in the Entra ID admin center, open the Branding & properties section.
  3. Click the Select a file button and browse to the file you previously downloaded.
  4. Provide a human-readable Name for the application, e.g., BloodHound Enterprise Collector (AzureHound).
  5. Type your BloodHound Enterprise tenant URL into the Home page URL field. This is for record keeping only.
  6. Click the Save button and review the results.
  7. Continue to the next section to assign the Directory Readers role on your Entra ID tenant.

Assign “Directory Readers” role on the Entra ID tenant

  1. In the left menu, select Roles & admins.
  1. Search for the role Directory Readers and click the role name or description. Note: Clicking the checkbox sometimes prevents clicking on the role itself.
  2. In the “Directory Readers” role, select Add assignments.
  3. Click No member selected to open the search window.
  4. Search for the previously created service principal with either its name, application ID, or object ID. Select it by clicking on it.
  5. Click Select.
  6. Validate that your principal is displayed and click Next.
  7. Ensure that the Assignment type is Active and the Permanently assigned box is checked. Provide a justification and click Assign.
  8. Confirm the service principal is a Directory Reader by refreshing this view.
  9. Continue to the next section to assign the Reader role on your Azure subscriptions.

Grant “Reader” role on all Azure subscriptions

Note: If you do not have any management groups, you may either create your Tenant Root Group following the prompts in the middle of the screen to ensure future visibility if another administrator begins use of subscriptions, or you may skip this section altogether. If you skip this section, you will see a warning in the logs for each collection indicating the lack of ability to collect this data accordingly.
  1. Log into the Azure portal as a user with the User Access Administrator role.
  2. Search for and select the Management groups item in the top search bar.
  3. Select Tenant Root Group.
  4. Select Access control (IAM).
  5. Select Role assignments.
  6. Click Add, then Add role assignment.
  7. Find the Reader role and select it.
  8. Click Members.
  9. Click Select members.
  10. Search for and click on your previously created service principal.
  1. Validate the principal selected, then click Select.
  2. Click the tab Review + Assign.
  3. Click Review + Assign at the bottom of the page.
  4. Confirm the role is present by refreshing this view. You may need to alter the filter to see this role.
  5. Continue to Run and Upgrade AzureHound (Windows, Docker, or Kubernetes)

Scripted app registration

As an alternative to the manual app registration process described in this document, you can use PowerShell to achieve the same. Below is a sample PowerShell script that performs all the required steps, with the exception of configuring the authentication certificate.
<#
.SYNOPSIS
Registers the Azure Data Exporter for BloodHound Enterprise (AzureHound)
as an application in Entra ID.

.DESCRIPTION
This script registers the Azure Data Exporter for BloodHound Enterprise (AzureHound)
as an on-prem application in Microsoft Entra ID and Azure,
including all the necessary read permissions.

The required PowerShell modules can be installed
from the PowerShell Gallery using the following command:

Install-Module -Scope AllUsers -Repository PSGallery -Force -Name @(
    Microsoft.Graph.Applications,
    Microsoft.Graph.Authentication,
    Microsoft.Graph.Identity.DirectoryManagement,
    Az.Resources,
    Az.Accounts
)

.NOTES
Version: 1.1
#>

#Requires -Version 5
#Requires -Modules Microsoft.Graph.Applications,Microsoft.Graph.Authentication
#Requires -Modules Microsoft.Graph.Identity.DirectoryManagement
#Requires -Modules Az.Resources,Az.Accounts

#region Entra ID

# Connect to Microsoft Entra ID through the Microsoft Graph API
# Note: The -TenantId parameter is also required when using an External ID.
Connect-MgGraph -NoWelcome -ContextScope Process -Scopes @(
   'User.Read',
   'Application.ReadWrite.All',
   'AppRoleAssignment.ReadWrite.All',
   'RoleManagement.ReadWrite.Directory'
)

# Register the AzureHound application
[string] $appName = 'BloodHound Enterprise Collector'
[string] $appDescription =
  'Azure Data Exporter for BloodHound Enterprise (AzureHound)'
# TODO: Optionally provide the actual URL your BloodHound Enterprise tenant
[string] $homePage = 'https://specterops.io/bloodhound-enterprise'
[hashtable] $infoUrls = @{
    MarketingUrl      = 'https://specterops.io/bloodhound-enterprise'
    TermsOfServiceUrl = 'https://specterops.io/terms-of-service'
    PrivacyStatementUrl = 'https://specterops.io/privacy-policy'
    SupportUrl = 'https://support.bloodhoundenterprise.io/'
}
[hashtable] $webUrls = @{
    HomePageUrl = $homePage
}

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphApplication] $registeredApp =
   New-MgApplication -DisplayName $appName `
                     -Description $appDescription `
                     -Info $infoUrls `
                     -Web $webUrls `
                     -SignInAudience 'AzureADMyOrg'

# Configure the application logo
[string] $logoUrl =
  'https://bloodhound.specterops.io/assets/icons/entra-bhe-app-icon.png'
[string] $tempLogoPath = New-TemporaryFile

Invoke-WebRequest -Uri $logoUrl `
                  -OutFile $tempLogoPath `
                  -UseBasicParsing `
                  -ErrorAction Stop
try {
    Set-MgApplicationLogo -ApplicationId $registeredApp.Id `
                          -ContentType 'image/png' `
                          -InFile $tempLogoPath
}
finally {
    # Delete the local copy of the logo from temp
    Remove-Item -Path $tempLogoPath
}

# Make sure the app instance property lock is enabled
Update-MgApplication `
  -ApplicationId $registeredApp.Id `
  -ServicePrincipalLockConfiguration @{
    IsEnabled = $true
    AllProperties = $true
}

# Create the associated service principal object
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $servicePrincipal =
   New-MgServicePrincipal -DisplayName $appName `
                          -AppId $registeredApp.AppId `
                          -AccountEnabled `
                          -ServicePrincipalType Application `
                          -Notes $appDescription `
                          -Homepage $homePage `
                          -Tags 'WindowsAzureActiveDirectoryIntegratedApp','HideApp'

# Fetch the Microsoft Graph applicaton ID,
# which should be 00000003-0000-0000-c000-000000000000
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $microsoftGraph =
    Get-MgServicePrincipal -Filter "DisplayName eq 'Microsoft Graph'"

# Fetch the Directory.Read.All scope ID,
# which should be 7ab1d382-f21e-4acd-a863-ba3e13f7da61
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRole] $readDirectoryScope =
    $microsoftGraph.AppRoles | Where-Object Value -eq 'Directory.Read.All'

# Fetch the RoleManagement.Read.All scope ID,
# which should be c7fbd983-d9aa-4fa7-84b8-17382c103bc4
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRole] $readRolesScope =
    $microsoftGraph.AppRoles | Where-Object Value -eq 'RoleManagement.Read.All'

# Delegate the required API permissions
Update-MgApplication -ApplicationId $registeredApp.Id -RequiredResourceAccess @{
    ResourceAppId = $microsoftGraph.AppId # 00000003-0000-0000-c000-000000000000
    ResourceAccess = @(@{
        id = $readDirectoryScope.Id       # 7ab1d382-f21e-4acd-a863-ba3e13f7da61
        type = 'Role'
    },@{
        id = $readRolesScope.Id           # c7fbd983-d9aa-4fa7-84b8-17382c103bc4
        type = 'Role'
    })
}

# Approve the permissions on the tenant
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRoleAssignment] $readRolesAdminConsent =
    New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $servicePrincipal.Id `
                                            -PrincipalId $servicePrincipal.Id `
                                            -ResourceId $microsoftGraph.Id `
                                            -AppRoleId $readRolesScope.Id

[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRoleAssignment] $readDirectoryAdminConsent =
    New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $servicePrincipal.Id `
                                            -PrincipalId $servicePrincipal.Id `
                                            -ResourceId $microsoftGraph.Id `
                                            -AppRoleId $readDirectoryScope.Id

# Fetch the template ID of the Directory Readers role,
# which should be 88d8e3e3-8f55-4a1e-953a-9b9898b8876b
[Microsoft.Graph.PowerShell.Models.MicrosoftGraphDirectoryRole] $directoryReadersRole =
    Get-MgDirectoryRole -Filter "displayName eq 'Directory Readers'"

# Get the environment-specific Microsoft Graph API endpoint
# Azure Global: https://graph.microsoft.com
# Azure USGov:  https://graph.microsoft.us
[string] $graphEndpoint =
    (Get-MgEnvironment -Name (Get-MgContext).Environment).GraphEndpoint

# OData IDs need to be used when assigning role membership,
# e.g., https://graph.microsoft.com/v1.0/serviceprincipals/{46615ae4-da39-4403-8de2-606e10774ae0}
[string] $servicePrincipalOdataId =
  "$graphEndpoint/v1.0/serviceprincipals/{$($servicePrincipal.Id)}"

# Assign the Directory Readers Entra ID Role to the service principal
New-MgDirectoryRoleMemberByRef -DirectoryRoleId $directoryReadersRole.Id `
                               -OdataId $servicePrincipalOdataId

# Fetch the info about the current user
[Microsoft.Graph.PowerShell.Models.IMicrosoftGraphDirectoryObject] $currentUser =
    Invoke-MgGraphRequest -Method GET -Uri '/v1.0/me'

# OData IDs need to be used when assigning application ownership,
# e.g., https://graph.microsoft.com/v1.0/users/{bca3617a-4c54-45eb-9a32-744c1938242e}
[string] $currentUserOdataId = "$graphEndpoint/v1.0/users/{$($currentUser.Id)}"

# Assign the current user as the application object owner
New-MgApplicationOwnerByRef -ApplicationId $registeredApp.Id `
                            -OdataId $currentUserOdataId

# Assign the current user as the service principal owner
New-MgServicePrincipalOwnerByRef -ServicePrincipalId $servicePrincipal.Id `
                                 -OdataId $currentUserOdataId

# Sign out from Microsoft Graph
Disconnect-MgGraph | Out-Null

#endregion Entra ID

#region Azure (Optional)

# Optionally enable browser-based login on Windows 10 and later
Update-AzConfig -EnableLoginByWam $false

# Authenticate against Azure Resource Manager
Connect-AzAccount -Environment AzureCloud -Scope Process

# Fetch the identifier of the Reader role
[string] $rootScope = '/'
[Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition] $azureReaderRole =
    Get-AzRoleDefinition -Scope $rootScope -Name 'Reader'

if (-not (Test-Path -Path 'variable:servicePrincipal')) {
    # Fetch the service principal if the Azure part of the script is executed independently
    [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.IMicrosoftGraphServicePrincipal] $servicePrincipal =
        Get-AzADServicePrincipal -DisplayName 'BloodHound Enterprise Collector'
}

# Fetch the Tenant Root Group
[guid] $currentTenantId = (Get-AzContext).Tenant.Id
[Microsoft.Azure.Commands.Resources.Models.ManagementGroups.PSManagementGroup] $rootManagementGroup =
    Get-AzManagementGroup -GroupName $currentTenantId

# Assign the Reader role on all Azure subscriptions to AzureHound
[Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleAssignment] $readerRoleAssignment =
    New-AzRoleAssignment -ObjectId $servicePrincipal.Id `
                         -Scope $rootManagementGroup.Id `
                         -RoleDefinitionId $azureReaderRole.Id

# Sign out from Azure Resource Manager
Disconnect-AzAccount -Scope Process

#endregion Azure (Optional)