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¶
- Sign in to the Azure portal.
- Navigate to Microsoft Entra ID (or search for "Entra ID" in the top search bar).
- In the left sidebar, select App registrations.
- Click + New registration.
- Fill in:
- Name:
RP-PAM - Supported account types: Accounts in this organisational directory only (Single tenant)
- Redirect URI: Leave blank (RP-PAM uses client credentials, not interactive login)
- Click Register.
- On the overview page, copy and save:
- Application (client) ID -- you will need this for RP-PAM configuration
- 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¶
- From your app registration, select API permissions in the left sidebar.
- Click + Add a permission.
- Select Microsoft Graph.
- Select Application permissions (not "Delegated permissions").
- Search for and check each permission listed above:
User.Read.AllGroup.Read.AllGroup.ReadWrite.AllGroupMember.ReadWrite.AllAuditLog.Read.AllDirectory.Read.All- 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
}
)
Step 3: Grant Admin Consent¶
Application permissions require admin consent before they take effect. Without admin consent, the permissions are listed but not active.
Using the Azure Portal¶
- From your app registration, go to API permissions.
- Click Grant admin consent for [Your Tenant Name].
- Confirm by clicking Yes.
- 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¶
- From your app registration, select Certificates & secrets in the left sidebar.
- Under Client secrets, click + New client secret.
- Fill in:
- Description:
RP-PAM Service - Expires: Choose an appropriate expiration (e.g., 12 months or 24 months)
- Click Add.
- 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¶
- In the Azure portal, go to your app registration.
- Select Certificates & secrets > Certificates tab.
- Click Upload certificate.
- Upload the
.cerfile (public key only). - 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:
- Create a new client secret in the Azure portal (Step 4).
- Update RP-PAM:
PowerShell:
& "C:\Program Files\Ravenphyre\RP-PAM\tools\rppam-migrate.exe" rotate-entraid-secret `
--client-secret "NEW_CLIENT_SECRET"
Linux:
- 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
- [ ]
managedGroupsscoped to only the groups RP-PAM should manage - [ ] App registration does NOT have
RoleManagement.ReadWrite.DirectoryorApplication.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¶
- Break-Glass Emergency Access -- Configure emergency access procedures
- MFA and TOTP Enrolment -- Set up multi-factor authentication
- Entra ID Module Configuration -- Detailed module configuration reference
RP-PAM v1.0.0 -- Copyright 2026 Ravenphyre. All rights reserved.