Skip to content

Service Account Setup — Entra ID

Section: Security | Article 21
Audience: Azure / Entra ID Administrators
Last Updated: 2026-04-07


Overview

RP-PAM integrates with Microsoft Entra ID (formerly Azure Active Directory) through an app registration. The app registration acts as RP-PAM's identity in your Entra ID tenant, allowing it to read users and groups, manage group memberships, and query audit logs via the Microsoft Graph API.

How It Maps to the Three-Account Model

RP-PAM uses the same three-role separation as Active Directory (see Article 20), but in Entra ID all three roles are represented by a single app registration with scoped permissions:

Role Entra ID Equivalent Graph API Permissions
DRA (Directory Reader) Application permission: read users and groups User.Read.All, Group.Read.All
PA (Provisioning Account) Application permission: manage group members Group.ReadWrite.All, GroupMember.ReadWrite.All
ARA (Audit & Reporting) Application permission: read audit logs AuditLog.Read.All, Directory.Read.All

Note: You may choose to create separate app registrations for each role to achieve the same credential separation as AD. This guide covers the single-registration approach. If you prefer separate registrations, repeat the process three times with the appropriate subset of permissions.


Prerequisites

Requirement Detail
Azure portal access An account with the Application Administrator or Global Administrator role
Entra ID tenant The tenant where your users and groups reside
Admin consent authority Ability to grant admin consent for application permissions
RP-PAM installed At least one node running (see installation guides)

Step 1: Create the App Registration

Using the Azure Portal

  1. Sign in to the Azure portal.
  2. Navigate to Microsoft Entra ID (or search for "Entra ID" in the top search bar).
  3. In the left sidebar, select App registrations.
  4. Click + New registration.
  5. Fill in:
  6. Name: RP-PAM
  7. Supported account types: Accounts in this organisational directory only (Single tenant)
  8. Redirect URI: Leave blank (RP-PAM uses client credentials, not interactive login)
  9. Click Register.
  10. On the overview page, copy and save:
  11. Application (client) ID -- you will need this for RP-PAM configuration
  12. Directory (tenant) ID -- you will need this for RP-PAM configuration

Using PowerShell (Microsoft Graph Module)

# Install the Microsoft Graph module if not already installed
Install-Module Microsoft.Graph -Scope CurrentUser -Force

# Connect to your tenant
Connect-MgGraph -Scopes "Application.ReadWrite.All"

# Create the app registration
$app = New-MgApplication -DisplayName "RP-PAM" `
  -SignInAudience "AzureADMyOrg"

# Display the important IDs
Write-Host "Application (client) ID: $($app.AppId)"
Write-Host "Object ID: $($app.Id)"

# Get the tenant ID
$tenant = Get-MgOrganization
Write-Host "Tenant ID: $($tenant.Id)"

Using Azure CLI

# Login to Azure
az login

# Create the app registration
az ad app create --display-name "RP-PAM" --sign-in-audience "AzureADMyOrg"

# Note the "appId" and "id" from the output
# Get the tenant ID
az account show --query tenantId -o tsv

Step 2: Configure API Permissions

The app registration needs the following Microsoft Graph application permissions (not delegated permissions):

Permission Type Purpose
User.Read.All Application Read all user profiles
Group.Read.All Application Read all groups and their members
Group.ReadWrite.All Application Add/remove group members (provisioning)
GroupMember.ReadWrite.All Application Manage group membership
AuditLog.Read.All Application Read Entra ID audit and sign-in logs
Directory.Read.All Application Read directory data (roles, service principals)

Using the Azure Portal

  1. From your app registration, select API permissions in the left sidebar.
  2. Click + Add a permission.
  3. Select Microsoft Graph.
  4. Select Application permissions (not "Delegated permissions").
  5. Search for and check each permission listed above:
  6. User.Read.All
  7. Group.Read.All
  8. Group.ReadWrite.All
  9. GroupMember.ReadWrite.All
  10. AuditLog.Read.All
  11. Directory.Read.All
  12. Click Add permissions.

Using PowerShell

# Get the Microsoft Graph service principal
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"

# Define required permissions
$permissions = @(
    "User.Read.All",
    "Group.Read.All",
    "Group.ReadWrite.All",
    "GroupMember.ReadWrite.All",
    "AuditLog.Read.All",
    "Directory.Read.All"
)

# Find the permission IDs
$requiredPermissions = @()
foreach ($perm in $permissions) {
    $role = $graphSp.AppRoles | Where-Object { $_.Value -eq $perm }
    $requiredPermissions += @{
        Id   = $role.Id
        Type = "Role"
    }
}

# Add permissions to the app
Update-MgApplication -ApplicationId $app.Id -RequiredResourceAccess @(
    @{
        ResourceAppId  = "00000003-0000-0000-c000-000000000000"  # Microsoft Graph
        ResourceAccess = $requiredPermissions
    }
)

Application permissions require admin consent before they take effect. Without admin consent, the permissions are listed but not active.

Using the Azure Portal

  1. From your app registration, go to API permissions.
  2. Click Grant admin consent for [Your Tenant Name].
  3. Confirm by clicking Yes.
  4. Verify that each permission shows a green checkmark under the "Status" column with the text "Granted for [Your Tenant]".

Using PowerShell

# Create a service principal for the app (if it doesn't exist)
$sp = New-MgServicePrincipal -AppId $app.AppId

# Grant admin consent for each permission
foreach ($perm in $permissions) {
    $role = $graphSp.AppRoles | Where-Object { $_.Value -eq $perm }
    New-MgServicePrincipalAppRoleAssignment `
        -ServicePrincipalId $sp.Id `
        -PrincipalId $sp.Id `
        -ResourceId $graphSp.Id `
        -AppRoleId $role.Id
}

Using Azure CLI

# Get the app's object ID
APP_ID=$(az ad app list --display-name "RP-PAM" --query "[0].appId" -o tsv)

# Create a service principal
az ad sp create --id "$APP_ID"

# Grant admin consent (requires Global Admin or Privileged Role Admin)
az ad app permission admin-consent --id "$APP_ID"

Step 4: Create a Client Secret

RP-PAM uses a client secret to authenticate as the app registration. In production, consider using a certificate instead for stronger security (see Using a Certificate below).

Using the Azure Portal

  1. From your app registration, select Certificates & secrets in the left sidebar.
  2. Under Client secrets, click + New client secret.
  3. Fill in:
  4. Description: RP-PAM Service
  5. Expires: Choose an appropriate expiration (e.g., 12 months or 24 months)
  6. Click Add.
  7. Immediately copy the secret value. It is only displayed once. If you navigate away, you cannot retrieve it.

Using PowerShell

$secret = Add-MgApplicationPassword -ApplicationId $app.Id -PasswordCredential @{
    DisplayName = "RP-PAM Service"
    EndDateTime = (Get-Date).AddMonths(12)
}

Write-Host "Client Secret: $($secret.SecretText)"
Write-Host "Expires: $($secret.EndDateTime)"
Write-Host "COPY THIS SECRET NOW — it cannot be retrieved later."

Using Azure CLI

az ad app credential reset \
  --id "$APP_ID" \
  --display-name "RP-PAM Service" \
  --years 1 \
  --query password -o tsv

Critical: Copy the secret immediately and store it securely. You will enter it into RP-PAM in the next step.


Step 5: Store the Client Secret in RP-PAM

Store the Entra ID credentials in RP-PAM's encrypted vault.

Windows PowerShell:

& "C:\Program Files\Ravenphyre\RP-PAM\tools\rppam-migrate.exe" setup-entraid `
  --tenant-id "YOUR_TENANT_ID" `
  --client-id "YOUR_CLIENT_ID" `
  --client-secret "YOUR_CLIENT_SECRET"

Linux:

sudo /opt/rppam/tools/rppam-migrate setup-entraid \
  --tenant-id "YOUR_TENANT_ID" \
  --client-id "YOUR_CLIENT_ID" \
  --client-secret "YOUR_CLIENT_SECRET"

The tool encrypts the client secret with AES-256-GCM and stores it in the vault. The plaintext secret is not written to disk.


Step 6: Configure the Entra ID Module

Add the Entra ID module configuration to rppam.config:

Windows path: C:\ProgramData\Ravenphyre\RP-PAM\rppam.config
Linux path: /etc/rppam/rppam.config

{
  "modules": {
    "entraId": {
      "enabled": true,
      "tenantId": "YOUR_TENANT_ID",
      "clientId": "YOUR_CLIENT_ID",
      "clientSecretVaultKey": "entraid-client-secret",
      "graphBaseUrl": "https://graph.microsoft.com/v1.0",
      "syncIntervalMinutes": 15,
      "managedGroups": [
        "sg-pam-admins",
        "sg-pam-helpdesk",
        "sg-database-access"
      ]
    }
  }
}
Field Description
tenantId Your Entra ID tenant ID
clientId The app registration's Application (client) ID
clientSecretVaultKey The vault key where the encrypted client secret is stored (set automatically by setup-entraid)
graphBaseUrl Microsoft Graph API endpoint; use https://graph.microsoft.com/v1.0 for production
syncIntervalMinutes How often RP-PAM syncs users and groups from Entra ID
managedGroups List of group display names that RP-PAM is allowed to manage

Step 7: Verify the Integration

Restart RP-PAM and verify the Entra ID connection:

Windows PowerShell:

Restart-Service RpPam

# Check module status
$modules = Invoke-RestMethod -Uri "http://localhost:7101/api/v1/modules" `
  -Headers @{ Authorization = "Bearer $adminJwt" }
$modules.items | Where-Object { $_.moduleName -eq "entraId" } | ConvertTo-Json

Linux:

sudo systemctl restart rppam

curl -s http://localhost:7101/api/v1/modules \
  -H "Authorization: Bearer $ADMIN_JWT" | jq '.items[] | select(.moduleName == "entraId")'

Expected output:

{
  "moduleName": "entraId",
  "status": "healthy",
  "lastSync": "2026-04-07T10:00:00Z",
  "usersDiscovered": 150,
  "groupsManaged": 3
}

Test a User Lookup

curl -s http://localhost:7101/api/v1/users?source=entraId&limit=5 \
  -H "Authorization: Bearer $ADMIN_JWT" | jq '.items[].displayName'
$users = Invoke-RestMethod -Uri "http://localhost:7101/api/v1/users?source=entraId&limit=5" `
  -Headers @{ Authorization = "Bearer $adminJwt" }
$users.items | Select-Object displayName, userPrincipalName | Format-Table

Using a Certificate (Optional)

For higher security, use a certificate instead of a client secret.

Generate a Self-Signed Certificate

PowerShell:

$cert = New-SelfSignedCertificate `
  -Subject "CN=RP-PAM Entra ID" `
  -CertStoreLocation "Cert:\CurrentUser\My" `
  -KeyExportPolicy Exportable `
  -KeySpec Signature `
  -KeyLength 2048 `
  -NotAfter (Get-Date).AddYears(2)

# Export the public key (.cer) for upload to Azure
Export-Certificate -Cert $cert -FilePath "C:\temp\rppam-entraid.cer"

# Export the private key (.pfx) for RP-PAM
$pfxPassword = ConvertTo-SecureString -String "YOUR_PFX_PASSWORD" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\rppam-entraid.pfx" -Password $pfxPassword

Linux (OpenSSL):

openssl req -x509 -newkey rsa:2048 \
  -keyout /etc/rppam/certs/entraid.key \
  -out /etc/rppam/certs/entraid.crt \
  -days 730 -nodes \
  -subj "/CN=RP-PAM Entra ID"

# Create a PFX for RP-PAM
openssl pkcs12 -export \
  -out /etc/rppam/certs/entraid.pfx \
  -inkey /etc/rppam/certs/entraid.key \
  -in /etc/rppam/certs/entraid.crt \
  -passout pass:YOUR_PFX_PASSWORD

Upload the Certificate to Azure

  1. In the Azure portal, go to your app registration.
  2. Select Certificates & secrets > Certificates tab.
  3. Click Upload certificate.
  4. Upload the .cer file (public key only).
  5. Click Add.

Update rppam.config for Certificate Auth

{
  "modules": {
    "entraId": {
      "enabled": true,
      "tenantId": "YOUR_TENANT_ID",
      "clientId": "YOUR_CLIENT_ID",
      "authMode": "certificate",
      "certificatePath": "/etc/rppam/certs/entraid.pfx",
      "certificatePasswordVaultKey": "entraid-cert-password",
      "graphBaseUrl": "https://graph.microsoft.com/v1.0",
      "syncIntervalMinutes": 15
    }
  }
}

Secret Rotation

Client secrets have expiration dates. RP-PAM displays a warning in the dashboard 30 days before expiration. To rotate:

  1. Create a new client secret in the Azure portal (Step 4).
  2. Update RP-PAM:

PowerShell:

& "C:\Program Files\Ravenphyre\RP-PAM\tools\rppam-migrate.exe" rotate-entraid-secret `
  --client-secret "NEW_CLIENT_SECRET"

Linux:

sudo /opt/rppam/tools/rppam-migrate rotate-entraid-secret \
  --client-secret "NEW_CLIENT_SECRET"

  1. Delete the old secret from the Azure portal.

Security Checklist

  • [ ] App registration created with single-tenant audience
  • [ ] All required API permissions added (6 permissions)
  • [ ] Admin consent granted (green checkmarks for all permissions)
  • [ ] Client secret created and stored in RP-PAM vault
  • [ ] Secret expiration date documented and calendar reminder set
  • [ ] managedGroups scoped to only the groups RP-PAM should manage
  • [ ] App registration does NOT have RoleManagement.ReadWrite.Directory or Application.ReadWrite.All
  • [ ] Test: RP-PAM can list users from Entra ID
  • [ ] Test: RP-PAM can list managed groups
  • [ ] Test: RP-PAM can add/remove a test user from a managed group

Troubleshooting

Problem Cause Solution
"status": "unhealthy" for Entra ID module Client secret expired or invalid Rotate the secret (see Secret Rotation above)
"Insufficient privileges" in logs Admin consent not granted Go to API permissions in Azure portal and click "Grant admin consent"
"AADSTS7000215: Invalid client secret" Wrong secret or secret has been deleted Create a new client secret and update RP-PAM
"AADSTS700016: Application not found" Wrong tenant ID or client ID Verify tenantId and clientId in rppam.config match the Azure portal
Users not syncing syncIntervalMinutes too high, or Graph API throttled Reduce interval; check Graph API rate limits in Azure portal > Monitoring
"Authorization_RequestDenied" Missing a required permission Add the missing permission and grant admin consent again
Cannot manage a group Group not in managedGroups list Add the group's display name to the managedGroups array

Next Steps


RP-PAM v1.0.0 -- Copyright 2026 Ravenphyre. All rights reserved.