Skip to content

Control 3.6: Orphaned Agent Detection and Remediation

Overview

Control ID: 3.6 Control Name: Orphaned Agent Detection and Remediation Regulatory Reference: FINRA 4511, SOX 404, GLBA 501(b), OCC 2011-12 Setup Time: 2-3 hours

Purpose

Orphaned Agent Detection and Remediation ensures continuous visibility into agent ownership and operational status across the organization. An orphaned agent is one without an assigned owner (due to employee departure, role change, or oversight) or one that is no longer actively maintained. For financial services organizations, unmanaged agents represent significant security, compliance, and operational risks. This control establishes automated detection, notification, and remediation processes to maintain governance integrity.


Prerequisites

Primary Owner Admin Role: Power Platform Admin Supporting Roles: Dataverse System Admin

Licensing Requirements

Component License Required
Power Platform Admin Center Included with Power Platform
Microsoft Entra ID Microsoft 365 E3/E5
Power Automate Power Platform (included with M365)
SharePoint for Tracking Microsoft 365 E3/E5
Microsoft 365 Audit Logs Microsoft 365 E3/E5

Permissions Required

Task Role/Permission
View Agent Inventory Power Platform Administrator
Query User Status User Administrator (Entra ID)
Reassign Agents Environment Administrator
Delete/Archive Agents System Administrator
Configure Detection Flows Power Automate Administrator
Access HR Termination Data HR Integration Service Account

Dependencies

  • [x] Control 3.1: Agent Inventory and Metadata Management
  • [x] Control 2.3: ALM and Lifecycle Management
  • [x] Control 2.8: Access Control and Segregation of Duties
  • [ ] HR system integration (for termination events)

Pre-Setup Checklist

  • [ ] Agent inventory complete and current
  • [ ] Owner assignment documented for all agents
  • [ ] HR termination feed configured (or manual process defined)
  • [ ] Escalation matrix for unowned agents established
  • [ ] Retention policy for decommissioned agents defined

Governance Levels

Baseline (Level 1)

Identify agents without assigned owner; document remediation plan.

Automated detection; monthly review; email notifications to IT.

Regulated/High-Risk (Level 4)

Real-time detection; automatic suspension until owner assigned.


Setup & Configuration

Step 1: Define Orphaned Agent Criteria

Orphaned Agent Indicators:

Indicator Detection Method Severity
Owner Departed User disabled/deleted in Entra ID Critical
Owner Role Changed User no longer in authorized group High
No Owner Assigned Owner field null in inventory Critical
Inactive Agent No usage in 90+ days Medium
Failed Health Check Connector errors, timeouts High
License Expired Maker license no longer active High
Environment Deleted Parent environment removed Critical

Risk Classification:

Category Definition Auto-Action
Critical Immediate governance risk Suspend agent access
High Action required within 24 hours Alert + suspend after 48h
Medium Action required within 7 days Alert only
Low Review in next governance cycle Include in monthly report

Step 2: Configure Owner Status Monitoring

Portal Path: Microsoft Entra Admin Center → Users → User settings

Set up monitoring for user status changes:

  1. Navigate to Entra Admin Center
  2. Go to MonitoringWorkbooksUser Activity
  3. Create custom workbook to track:
  4. User account disable events
  5. User account delete events
  6. Group membership changes (Power Platform makers)
  7. Configure alerts for relevant changes

Step 3: Create Automated Detection Flow

Portal Path: Power Automate → Create → Scheduled cloud flow

Flow: Daily Orphaned Agent Check

Trigger: Recurrence (Daily at 6 AM)

Actions:

1. Get all agents from Agent Inventory list
2. For each agent:
   ├── Get owner user account from Entra ID
   ├── Check if account is:
   │   ├── Disabled → Flag as Orphaned (Owner Departed)
   │   ├── Deleted → Flag as Orphaned (Owner Departed)
   │   ├── Not in Maker group → Flag as Orphaned (Role Changed)
   │   └── Null → Flag as Orphaned (No Owner)
   │
   ├── If orphaned:
   │   ├── Update Agent Inventory status
   │   ├── Log to Orphaned Agents list
   │   ├── Determine severity
   │   └── Trigger appropriate action
   │
3. If Critical orphans found:
   ├── Email IT Operations immediately
   ├── Create incident ticket
   └── Optionally suspend agent

4. Generate daily summary report

Step 4: Build Orphaned Agent Tracking List

Portal Path: SharePoint → AI Governance Site → Create List

Create SharePoint list with these columns:

Column Name Type Required Description
AgentId Lookup Yes Link to Agent Inventory
AgentName Text Yes Agent display name
OrphanedDate DateTime Yes When detected
OrphanReason Choice Yes Departed/Role Changed/No Owner/Inactive
PreviousOwner Text Yes Last known owner
Severity Choice Yes Critical/High/Medium/Low
Status Choice Yes Detected/Under Review/Reassigned/Suspended/Decommissioned
AssignedTo Person No IT staff responsible
NewOwner Person No Reassigned owner
RemediationDate DateTime No When resolved
Notes MultiLine No Action notes

Step 5: Configure Remediation Workflows

Workflow 1: Owner Reassignment

Trigger: User clicks "Reassign" button
Actions:
├── Prompt for new owner selection
├── Validate new owner:
│   ├── Has active account
│   ├── Has appropriate license
│   └── Is in authorized maker group
├── Update agent ownership in Power Platform
├── Update Agent Inventory
├── Update Orphaned Agents list (Status = Reassigned)
├── Notify new owner
└── Close remediation ticket

Workflow 2: Agent Suspension

Trigger: Severity = Critical AND no action in 24 hours
Actions:
├── Disable agent connections
├── Set agent sharing to "No access"
├── Update status to "Suspended"
├── Email escalation chain
├── Log suspension to audit
└── Create P2 incident

Workflow 3: Decommission

Trigger: IT Admin approves decommission
Actions:
├── Export agent configuration
├── Export agent conversation logs
├── Archive to long-term storage
├── Remove agent from environment
├── Update inventory (Status = Decommissioned)
├── Update cost tracking
└── Close all related tickets

Step 6: Set Up HR Integration

Option A: Direct HR System Integration

HR System (Workday/SAP) → Power Automate → Orphan Detection

Trigger: Employee termination event
Actions:
├── Query Agent Inventory for agents owned by terminated user
├── For each agent found:
│   ├── Update status to Orphaned
│   ├── Set severity to Critical
│   ├── Notify agent's supervisor for reassignment
│   └── Start remediation clock

Option B: Entra ID Change Detection

Microsoft Entra → Logic App → Power Automate

Trigger: User account disabled
Actions:
├── Same as above

Step 7: Configure Inactivity Monitoring

Portal Path: Power Platform Admin Center → Analytics → Usage

Set up usage-based orphan detection:

Inactivity Period Status Action
30 days Warning Email owner
60 days Review Required Email owner + manager
90 days Orphan Candidate Add to remediation queue
180 days Decommission Candidate Initiate decommission review

PowerShell Configuration

# ============================================================
# Control 3.6: Orphaned Agent Detection and Remediation
# PowerShell Configuration Script for FSI Organizations
# ============================================================

# Install required modules
Install-Module -Name Microsoft.Graph -Force -AllowClobber
Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Force -AllowClobber
Install-Module -Name PnP.PowerShell -Force -AllowClobber

# Connect to services
Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All"
Add-PowerAppsAccount
Connect-PnPOnline -Url "https://[tenant].sharepoint.com/sites/AI-Governance" -Interactive

# ============================================================
# SECTION 1: Orphaned Agent Detection
# ============================================================

function Find-OrphanedAgents {
    param(
        [string]$EnvironmentFilter = "*",
        [switch]$IncludeInactive
    )

    Write-Host "Scanning for orphaned agents..." -ForegroundColor Cyan

    # Get all environments
    $environments = Get-AdminPowerAppEnvironment | Where-Object {
        $_.DisplayName -like $EnvironmentFilter
    }

    $orphanedAgents = @()
    $agentCount = 0

    foreach ($env in $environments) {
        Write-Host "  Checking environment: $($env.DisplayName)" -ForegroundColor Gray

        # Get all apps in environment
        $apps = Get-AdminPowerApp -EnvironmentName $env.EnvironmentName

        foreach ($app in $apps) {
            $agentCount++
            $ownerId = $app.Owner.id

            # Check owner status in Entra ID
            try {
                $owner = Get-MgUser -UserId $ownerId -Property "accountEnabled,deletedDateTime" -ErrorAction Stop

                if ($owner.deletedDateTime) {
                    # Owner deleted
                    $orphanedAgents += [PSCustomObject]@{
                        AgentName = $app.DisplayName
                        AgentId = $app.AppName
                        Environment = $env.DisplayName
                        PreviousOwner = $owner.UserPrincipalName
                        OrphanReason = "Owner Deleted"
                        Severity = "Critical"
                        DetectedDate = Get-Date
                    }
                }
                elseif (-not $owner.accountEnabled) {
                    # Owner disabled
                    $orphanedAgents += [PSCustomObject]@{
                        AgentName = $app.DisplayName
                        AgentId = $app.AppName
                        Environment = $env.DisplayName
                        PreviousOwner = $owner.UserPrincipalName
                        OrphanReason = "Owner Disabled"
                        Severity = "Critical"
                        DetectedDate = Get-Date
                    }
                }
            }
            catch {
                # Owner not found
                $orphanedAgents += [PSCustomObject]@{
                    AgentName = $app.DisplayName
                    AgentId = $app.AppName
                    Environment = $env.DisplayName
                    PreviousOwner = $ownerId
                    OrphanReason = "Owner Not Found"
                    Severity = "Critical"
                    DetectedDate = Get-Date
                }
            }
        }
    }

    Write-Host "`nScan Complete:" -ForegroundColor Green
    Write-Host "  Total agents scanned: $agentCount"
    Write-Host "  Orphaned agents found: $($orphanedAgents.Count)"

    if ($orphanedAgents.Count -gt 0) {
        Write-Host "`nOrphaned Agents:" -ForegroundColor Yellow
        $orphanedAgents | Format-Table AgentName, OrphanReason, Severity, PreviousOwner -AutoSize
    }

    return $orphanedAgents
}

# ============================================================
# SECTION 2: Inactive Agent Detection
# ============================================================

function Find-InactiveAgents {
    param(
        [int]$InactiveDays = 90
    )

    Write-Host "Scanning for agents inactive for $InactiveDays+ days..." -ForegroundColor Cyan

    $cutoffDate = (Get-Date).AddDays(-$InactiveDays)
    $inactiveAgents = @()

    $environments = Get-AdminPowerAppEnvironment

    foreach ($env in $environments) {
        $apps = Get-AdminPowerApp -EnvironmentName $env.EnvironmentName

        foreach ($app in $apps) {
            $lastModified = $app.LastModifiedTime

            if ($lastModified -lt $cutoffDate) {
                # Would need usage analytics API for actual last usage
                # This uses last modified as proxy
                $daysSinceActivity = ((Get-Date) - $lastModified).Days

                $inactiveAgents += [PSCustomObject]@{
                    AgentName = $app.DisplayName
                    AgentId = $app.AppName
                    Environment = $env.DisplayName
                    Owner = $app.Owner.displayName
                    LastActivity = $lastModified
                    DaysInactive = $daysSinceActivity
                    Severity = if ($daysSinceActivity -gt 180) { "High" }
                              elseif ($daysSinceActivity -gt 120) { "Medium" }
                              else { "Low" }
                }
            }
        }
    }

    Write-Host "Inactive agents found: $($inactiveAgents.Count)" -ForegroundColor Yellow

    if ($inactiveAgents.Count -gt 0) {
        $inactiveAgents | Sort-Object DaysInactive -Descending | Format-Table AgentName, DaysInactive, Severity, Owner -AutoSize
    }

    return $inactiveAgents
}

# ============================================================
# SECTION 3: Remediation Actions
# ============================================================

function Set-AgentOwner {
    param(
        [Parameter(Mandatory=$true)]
        [string]$AppId,
        [Parameter(Mandatory=$true)]
        [string]$EnvironmentId,
        [Parameter(Mandatory=$true)]
        [string]$NewOwnerEmail
    )

    Write-Host "Reassigning agent ownership..." -ForegroundColor Cyan

    # Validate new owner
    try {
        $newOwner = Get-MgUser -Filter "mail eq '$NewOwnerEmail'" -Property "accountEnabled,id"

        if (-not $newOwner.accountEnabled) {
            Write-Error "New owner account is disabled"
            return $false
        }

        # Add new owner as co-owner first
        Set-AdminPowerAppRoleAssignment `
            -PrincipalType "User" `
            -PrincipalObjectId $newOwner.Id `
            -RoleName "CanEdit" `
            -AppName $AppId `
            -EnvironmentName $EnvironmentId

        Write-Host "Agent ownership updated to: $NewOwnerEmail" -ForegroundColor Green

        # Log the change
        $auditEntry = @{
            Action = "OwnerReassignment"
            AgentId = $AppId
            NewOwner = $NewOwnerEmail
            ChangedBy = (Get-MgContext).Account
            Timestamp = Get-Date
        }

        Write-Host "Audit logged: $($auditEntry | ConvertTo-Json -Compress)"

        return $true
    }
    catch {
        Write-Error "Failed to reassign ownership: $_"
        return $false
    }
}

function Suspend-Agent {
    param(
        [Parameter(Mandatory=$true)]
        [string]$AppId,
        [Parameter(Mandatory=$true)]
        [string]$EnvironmentId,
        [Parameter(Mandatory=$true)]
        [string]$Reason
    )

    Write-Host "Suspending agent: $AppId" -ForegroundColor Yellow

    try {
        # Remove all role assignments except admin
        $roleAssignments = Get-AdminPowerAppRoleAssignment -AppName $AppId -EnvironmentName $EnvironmentId

        foreach ($role in $roleAssignments) {
            if ($role.RoleName -ne "CanViewWithShare") {  # Keep admin access
                Remove-AdminPowerAppRoleAssignment `
                    -RoleId $role.RoleId `
                    -AppName $AppId `
                    -EnvironmentName $EnvironmentId
            }
        }

        Write-Host "Agent suspended - all user access removed" -ForegroundColor Yellow

        # Log suspension
        $suspensionRecord = @{
            AgentId = $AppId
            SuspendedDate = Get-Date
            Reason = $Reason
            SuspendedBy = (Get-MgContext).Account
        }

        # Would log to SharePoint or tracking system

        return $true
    }
    catch {
        Write-Error "Failed to suspend agent: $_"
        return $false
    }
}

function Remove-OrphanedAgent {
    param(
        [Parameter(Mandatory=$true)]
        [string]$AppId,
        [Parameter(Mandatory=$true)]
        [string]$EnvironmentId,
        [switch]$ExportFirst,
        [string]$ExportPath = ".\AgentExports"
    )

    Write-Host "Decommissioning agent: $AppId" -ForegroundColor Red

    if ($ExportFirst) {
        # Export agent package before deletion
        if (-not (Test-Path $ExportPath)) {
            New-Item -ItemType Directory -Path $ExportPath | Out-Null
        }

        Write-Host "Exporting agent configuration..." -ForegroundColor Cyan

        # In production, use proper export cmdlets
        $exportFile = Join-Path $ExportPath "$AppId-export.zip"
        # Export-AdminPowerApp would be used here

        Write-Host "Exported to: $exportFile" -ForegroundColor Green
    }

    # Remove the app
    try {
        Remove-AdminPowerApp -AppName $AppId -EnvironmentName $EnvironmentId

        Write-Host "Agent successfully decommissioned" -ForegroundColor Green

        # Update tracking
        $decommissionRecord = @{
            AgentId = $AppId
            DecommissionedDate = Get-Date
            DecommissionedBy = (Get-MgContext).Account
            ExportLocation = if ($ExportFirst) { $exportFile } else { "Not exported" }
        }

        return $true
    }
    catch {
        Write-Error "Failed to decommission agent: $_"
        return $false
    }
}

# ============================================================
# SECTION 4: Generate Orphan Report
# ============================================================

function New-OrphanedAgentReport {
    param(
        [string]$OutputPath = ".\OrphanedAgentReport.html"
    )

    Write-Host "Generating orphaned agent report..." -ForegroundColor Cyan

    # Gather data
    $orphaned = Find-OrphanedAgents
    $inactive = Find-InactiveAgents -InactiveDays 90

    $html = @"
<!DOCTYPE html>
<html>
<head>
    <title>Orphaned Agent Detection Report</title>
    <style>
        body { font-family: 'Segoe UI', Arial, sans-serif; margin: 40px; background: #f8f9fa; }
        .container { max-width: 1100px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
        h1 { color: #C41E3A; border-bottom: 3px solid #C41E3A; padding-bottom: 15px; }
        h2 { color: #333; margin-top: 30px; }
        .alert-box { padding: 20px; border-radius: 8px; margin: 20px 0; }
        .alert-critical { background: #F8D7DA; border-left: 4px solid #DC3545; }
        .alert-warning { background: #FFF3CD; border-left: 4px solid #FFC107; }
        .alert-info { background: #D1ECF1; border-left: 4px solid #17A2B8; }
        .metric-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin: 20px 0; }
        .metric-card { background: #f5f5f5; padding: 20px; border-radius: 8px; text-align: center; }
        .metric-value { font-size: 36px; font-weight: bold; }
        .critical { color: #DC3545; }
        .warning { color: #FFC107; }
        .success { color: #28A745; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th { background: #0078D4; color: white; padding: 12px; text-align: left; }
        td { border: 1px solid #ddd; padding: 10px; }
        tr:nth-child(even) { background: #f9f9f9; }
        .severity-critical { background: #DC3545; color: white; padding: 4px 8px; border-radius: 4px; }
        .severity-high { background: #FFC107; padding: 4px 8px; border-radius: 4px; }
        .severity-medium { background: #17A2B8; color: white; padding: 4px 8px; border-radius: 4px; }
        .footer { text-align: center; color: #666; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; }
    </style>
</head>
<body>
    <div class="container">
        <h1>⚠️ Orphaned Agent Detection Report</h1>
        <p><strong>Generated:</strong> $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")</p>

        <div class="metric-row">
            <div class="metric-card">
                <div class="metric-value critical">$($orphaned.Count)</div>
                <div>Orphaned Agents</div>
            </div>
            <div class="metric-card">
                <div class="metric-value warning">$($inactive.Count)</div>
                <div>Inactive Agents (90+ days)</div>
            </div>
            <div class="metric-card">
                <div class="metric-value">$(($orphaned | Where-Object { $_.Severity -eq "Critical" }).Count)</div>
                <div>Critical Priority</div>
            </div>
            <div class="metric-card">
                <div class="metric-value success">0</div>
                <div>Remediated This Week</div>
            </div>
        </div>

        $(if ($orphaned.Count -gt 0) {
            "<div class='alert-box alert-critical'>
                <strong>⚠️ Immediate Action Required:</strong> $($orphaned.Count) agents detected without valid owners.
                Critical agents should be reassigned or suspended within 24 hours.
            </div>"
        })

        <h2>Orphaned Agents</h2>
        $(if ($orphaned.Count -gt 0) {
            "<table>
                <tr><th>Agent Name</th><th>Environment</th><th>Reason</th><th>Severity</th><th>Previous Owner</th></tr>
                $($orphaned | ForEach-Object {
                    $severityClass = "severity-$($_.Severity.ToLower())"
                    "<tr>
                        <td>$($_.AgentName)</td>
                        <td>$($_.Environment)</td>
                        <td>$($_.OrphanReason)</td>
                        <td><span class='$severityClass'>$($_.Severity)</span></td>
                        <td>$($_.PreviousOwner)</td>
                    </tr>"
                })
            </table>"
        } else {
            "<div class='alert-box alert-info'>✅ No orphaned agents detected</div>"
        })

        <h2>Inactive Agents (90+ days)</h2>
        $(if ($inactive.Count -gt 0) {
            "<table>
                <tr><th>Agent Name</th><th>Environment</th><th>Days Inactive</th><th>Owner</th><th>Recommendation</th></tr>
                $($inactive | Sort-Object DaysInactive -Descending | Select-Object -First 10 | ForEach-Object {
                    $recommendation = if ($_.DaysInactive -gt 180) { "Recommend Decommission" }
                                     elseif ($_.DaysInactive -gt 120) { "Review with Owner" }
                                     else { "Monitor" }
                    "<tr>
                        <td>$($_.AgentName)</td>
                        <td>$($_.Environment)</td>
                        <td>$($_.DaysInactive)</td>
                        <td>$($_.Owner)</td>
                        <td>$recommendation</td>
                    </tr>"
                })
            </table>"
        } else {
            "<div class='alert-box alert-info'>✅ No inactive agents detected</div>"
        })

        <h2>Remediation Actions</h2>
        <ol>
            <li><strong>Critical orphans:</strong> Reassign to appropriate owner or suspend within 24 hours</li>
            <li><strong>High severity:</strong> Contact department manager for ownership decision within 48 hours</li>
            <li><strong>Inactive agents:</strong> Verify with owners if agents are still needed</li>
            <li><strong>Decommission candidates:</strong> Archive and remove after 180 days inactivity</li>
        </ol>

        <div class="footer">
            <p>Generated by FSI-AgentGov Orphan Detection System</p>
            <p>For assistance: ai-governance@company.com</p>
        </div>
    </div>
</body>
</html>
"@

    $html | Out-File -FilePath $OutputPath -Encoding UTF8
    Write-Host "Report generated: $OutputPath" -ForegroundColor Green

    return $OutputPath
}

# ============================================================
# SECTION 5: HR Termination Integration
# ============================================================

function Watch-EmployeeTerminations {
    param(
        [int]$DaysBack = 1
    )

    Write-Host "Checking for recent employee terminations..." -ForegroundColor Cyan

    $cutoffDate = (Get-Date).AddDays(-$DaysBack)

    # Query Entra ID for recently disabled users
    $disabledUsers = Get-MgUser -Filter "accountEnabled eq false" -Property "id,displayName,userPrincipalName,signInActivity" |
        Where-Object {
            # Would need more sophisticated date checking in production
            $true
        }

    Write-Host "Found $($disabledUsers.Count) disabled users to check" -ForegroundColor Yellow

    foreach ($user in $disabledUsers) {
        # Check if user owns any agents
        $userApps = Get-AdminPowerApp | Where-Object { $_.Owner.id -eq $user.Id }

        if ($userApps.Count -gt 0) {
            Write-Host "⚠️ Terminated user $($user.DisplayName) owns $($userApps.Count) agents" -ForegroundColor Red

            foreach ($app in $userApps) {
                Write-Host "  - $($app.DisplayName)" -ForegroundColor Yellow
            }
        }
    }
}

# ============================================================
# EXAMPLE USAGE
# ============================================================

Write-Host "=== Control 3.6: Orphaned Agent Detection and Remediation ===" -ForegroundColor Magenta

# Find orphaned agents
# $orphans = Find-OrphanedAgents

# Find inactive agents
# $inactive = Find-InactiveAgents -InactiveDays 90

# Reassign agent ownership
# Set-AgentOwner -AppId "guid" -EnvironmentId "env-guid" -NewOwnerEmail "newowner@company.com"

# Suspend an agent
# Suspend-Agent -AppId "guid" -EnvironmentId "env-guid" -Reason "Owner departed - pending reassignment"

# Decommission an agent
# Remove-OrphanedAgent -AppId "guid" -EnvironmentId "env-guid" -ExportFirst

# Generate orphan report
# New-OrphanedAgentReport -OutputPath ".\WeeklyOrphanReport.html"

# Check for employee terminations
# Watch-EmployeeTerminations -DaysBack 1

Write-Host "`nConfiguration script ready. Uncomment and run desired functions." -ForegroundColor Green

Financial Sector Considerations

Regulatory Requirements

Regulation Requirement Implementation
FINRA 4511 Maintain books and records Document all agent ownership changes
SOX 404 IT access controls Prevent unauthorized agent access
GLBA 501(b) Safeguard customer data Suspend agents accessing NPI immediately
OCC 2011-12 Third-party oversight Track vendor-built orphaned agents
SEC 17a-4 Preserve records Archive decommissioned agent data

Zone-Specific Configuration

Zone Detection Frequency Auto-Suspend Grace Period
Zone 1 (Personal Productivity) Weekly No 30 days
Zone 2 (Team Collaboration) Daily After 7 days 7 days
Zone 3 (Enterprise Managed) Real-time Immediate 24 hours

FSI Example: Critical Orphan Remediation

SCENARIO: Account Manager terminated - owns 3 agents

Agent Portfolio:
├── Customer Portfolio Analyzer (Tier 3) - CRITICAL
│   └── Accesses customer account data
├── Trade Blotter Bot (Tier 3) - CRITICAL
│   └── Integrates with trading systems
└── Meeting Scheduler (Tier 1) - LOW
    └── Calendar access only

Remediation Timeline:
├── T+0:00 - HR termination event received
├── T+0:05 - Automated orphan detection triggered
├── T+0:10 - Tier 3 agents auto-suspended
├── T+0:30 - Incident ticket created
├── T+1:00 - Manager contacted for reassignment
├── T+4:00 - New owners identified and assigned
├── T+8:00 - Agents restored with new ownership
├── T+24:00 - Audit review completed
└── T+48:00 - Compliance sign-off

Documentation Required:
├── Termination date/time
├── Agents affected
├── Suspension timestamp
├── New owner assignment
├── Testing verification
└── Compliance approval

Verification & Testing

Verification Steps

  1. Detection Accuracy
  2. Disable test user account
  3. Verify agent flagged as orphaned within SLA
  4. Confirm severity classification correct

  5. Automated Workflows

  6. Test notification flow triggers
  7. Verify escalation timing

    • Confirm auto-suspend (Tier 3) works
  8. Remediation Process

  9. Test owner reassignment
  10. Verify permissions transfer correctly
  11. Confirm audit trail created

  12. Reporting

  13. Generate orphan report
  14. Verify counts match manual check
  15. Confirm all data fields populated

Compliance Checklist

Item Required For Status
Daily orphan detection running SOX 404
Auto-suspend for Tier 3 agents GLBA 501(b)
HR integration configured Best practice
Ownership change audit trail FINRA 4511
Decommissioned agent archival SEC 17a-4
Weekly orphan report generated Internal governance
Remediation SLAs defined Operational efficiency

Troubleshooting & Validation

Issue: Detection Not Finding Orphans

Symptoms: Known orphaned agents not appearing in scan

Resolution:

  1. Verify Entra ID connection permissions
  2. Check user account truly disabled (not just expired)
  3. Confirm agent inventory is complete
  4. Review filter logic in detection script

Issue: Auto-Suspend Not Triggering

Symptoms: Critical orphans remain active

Resolution:

  1. Check Power Automate flow is enabled
  2. Verify severity classification correct
  3. Confirm flow has admin permissions
  4. Test with manual trigger first

Issue: HR Integration Lag

Symptoms: Terminations not detected for 24+ hours

Resolution:

  1. Check HR system sync frequency
  2. Verify Entra ID provisioning settings
  3. Consider real-time webhook from HR
  4. Implement scheduled gap checking

Issue: False Positives

Symptoms: Active users flagged as departed

Resolution:

  1. Check for account sync delays
  2. Verify filter logic accuracy
  3. Add grace period before flagging
  4. Implement manual review for edge cases

Issue: Reassignment Failures

Symptoms: New owner cannot access agent

Resolution:

  1. Verify new owner has appropriate license
  2. Check environment permissions
  3. Confirm security role assignment
  4. Test with different permission levels

Additional Resources

Control Relationship
3.1 Agent Inventory Source of agent ownership data
3.5 Cost Allocation Cost savings from decommissioning
2.3 Change Management Lifecycle state management
2.8 Access Control Permission management
1.2 Agent Registry Registry cleanup

Support & Questions

For implementation support or questions about this control, contact:

  • AI Governance Lead (governance direction)
  • Compliance Officer (regulatory requirements)
  • Technical Implementation Team (platform setup)

Updated: Dec 2025
Version: v1.0 Beta (Dec 2025)
UI Verification Status: ❌ Needs verification