Skip to content

Implement retry logic for service principal retrieval in group assignment #18

@andres-canello

Description

@andres-canello

Service Principal Retrieval Retry Logic Implementation

Overview

Implement exponential backoff retry logic when retrieving service principals after application creation, similar to the existing retry pattern used for retrieving newly created Private Access applications.

Issue Type

Enhancement

Priority

Medium

Background

Currently, the Set-ApplicationGroupAssignments function retrieves the service principal immediately after an application is created. Due to eventual consistency in Microsoft Entra ID, the service principal may not be immediately available, causing group assignment operations to fail.

This is similar to the issue already addressed for retrieving newly created applications in the New-PrivateAccessApplication function, which uses exponential backoff retry logic (lines 748-783).

Current Implementation

Location

  • File: Migrate2GSA/functions/GSA/Start-EntraPrivateAccessProvisioning.ps1
  • Function: Set-ApplicationGroupAssignments
  • Lines: ~979-1012

Current Code

if (-not $WhatIfPreference) {
    try {
        # Get the service principal for the application
        $servicePrincipalParams = @{
            Filter = "appId eq '$AppId'"
            ErrorAction = 'Stop'
        }
        if ($DebugPreference -eq 'Continue') {
            $servicePrincipalParams['Debug'] = $true
        }
        $servicePrincipal = Get-EntraBetaServicePrincipal @servicePrincipalParams
        
        if (-not $servicePrincipal) {
            Write-LogMessage "Service principal not found for application ID: $AppId" -Level ERROR -Component "GroupAssignment"
            $result.Failed = $GroupNames.Count
            $result.FailedGroups = $GroupNames
            return $result
        }
        
        # ... rest of the code
    }
    catch {
        Write-LogMessage "Failed to retrieve service principal: $_" -Level ERROR -Component "GroupAssignment"
        $result.Failed = $GroupNames.Count
        $result.FailedGroups = $GroupNames
        return $result
    }
}

Proposed Solution

Implement exponential backoff retry logic similar to the pattern in New-PrivateAccessApplication:

# Retry logic to retrieve the service principal with exponential backoff
$maxRetries = 5
$baseDelay = 2  # seconds
$servicePrincipal = $null

for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
    try {
        Write-LogMessage "Attempting to retrieve service principal for AppId '$AppId' (attempt $attempt/$maxRetries)" -Level INFO -Component "GroupAssignment"
        
        $servicePrincipalParams = @{
            Filter = "appId eq '$AppId'"
            ErrorAction = 'Stop'
        }
        if ($DebugPreference -eq 'Continue') {
            $servicePrincipalParams['Debug'] = $true
        }
        $servicePrincipal = Get-EntraBetaServicePrincipal @servicePrincipalParams
        
        if ($servicePrincipal) {
            Write-LogMessage "Successfully retrieved service principal for AppId '$AppId' on attempt $attempt" -Level SUCCESS -Component "GroupAssignment"
            break
        }
        
        # If no service principal found and not the last attempt, wait before retrying
        if ($attempt -lt $maxRetries) {
            $delay = $baseDelay * [math]::Pow(2, $attempt - 1)  # Exponential backoff: 2, 4, 8, 16 seconds
            Write-LogMessage "Service principal for AppId '$AppId' not found on attempt $attempt. Retrying in $delay seconds..." -Level WARN -Component "GroupAssignment"
            Start-Sleep -Seconds $delay
        }
    }
    catch {
        $delay = $baseDelay * [math]::Pow(2, $attempt - 1)
        
        if ($attempt -eq $maxRetries) {
            Write-LogMessage "Failed to retrieve service principal for AppId '$AppId' after $maxRetries attempts. Final error: $_" -Level ERROR -Component "GroupAssignment"
            $result.Failed = $GroupNames.Count
            $result.FailedGroups = $GroupNames
            return $result
        }
        
        Write-LogMessage "Failed to retrieve service principal for AppId '$AppId' on attempt $attempt. Retrying in $delay seconds... Error: $_" -Level WARN -Component "GroupAssignment"
        Start-Sleep -Seconds $delay
    }
}

if (-not $servicePrincipal) {
    Write-LogMessage "Service principal not found for application ID: $AppId after $maxRetries retry attempts" -Level ERROR -Component "GroupAssignment"
    $result.Failed = $GroupNames.Count
    $result.FailedGroups = $GroupNames
    return $result
}

Benefits

  1. Increased Reliability: Handle eventual consistency delays in Microsoft Entra ID
  2. Better Error Messages: Clear logging of retry attempts and reasons for delays
  3. Consistent Pattern: Matches existing retry logic used elsewhere in the codebase
  4. Graceful Degradation: Multiple attempts before final failure

Retry Strategy Details

  • Maximum Retries: 5 attempts (configurable)
  • Base Delay: 2 seconds
  • Backoff Schedule: 2s, 4s, 8s, 16s (exponential)
  • Total Max Wait Time: ~30 seconds across all retries
  • Retry Triggers:
    • Service principal not found (null result)
    • Exception during retrieval

Testing Considerations

  1. Test with newly created applications to ensure service principal is retrieved successfully
  2. Verify proper logging at each retry attempt
  3. Confirm graceful failure after max retries exceeded
  4. Test with existing applications (should succeed on first attempt)

Related Code

  • Reference Implementation: New-PrivateAccessApplication function (lines 748-783)
  • Affected Function: Set-ApplicationGroupAssignments
  • Dependencies: Write-LogMessage, Get-EntraBetaServicePrincipal

Acceptance Criteria

  • Implement retry logic with exponential backoff matching the pattern in New-PrivateAccessApplication
  • Log retry attempts with clear messages (INFO level for attempts, WARN for retries, SUCCESS for completion)
  • Handle both null results and exceptions during retrieval
  • Maintain existing functionality for successful first-attempt retrievals
  • Update function documentation if needed
  • Test with newly created applications to verify retry logic works

Additional Notes

This enhancement improves the reliability of group assignment operations, especially in environments with high latency or eventual consistency delays. The retry pattern is already proven effective in the application creation flow.

Related Specification

See detailed specification document: Specs/20251012-ServicePrincipalRetryLogic.md

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions