public/maester/aiagent/Test-MtAIAgentOrphaned.ps1
|
<# .SYNOPSIS Tests if AI agents are orphaned. .DESCRIPTION Checks all Copilot Studio agents for those whose owner accounts no longer exist or are disabled in Entra ID. Orphaned agents lack active governance and may drift from security policies without anyone responsible for maintaining them. .OUTPUTS [bool] - Returns $true if all agents have active owners, $false if any agent has disabled or missing owners, $null if data is unavailable. .EXAMPLE Test-MtAIAgentOrphaned .LINK https://maester.dev/docs/commands/Test-MtAIAgentOrphaned #> function Test-MtAIAgentOrphaned { [CmdletBinding()] [OutputType([bool])] param() $agents = Get-MtAIAgentInfo if ($null -eq $agents) { Add-MtTestResultDetail -SkippedBecause 'Custom' -SkippedCustomReason 'No Copilot Studio agent data available. Ensure DataverseEnvironmentUrl is configured in maester-config.json and Connect-Maester -Service Dataverse has been run. See https://maester.dev/docs/tests/MT.1122 for prerequisites.' return $null } $failedAgents = @() foreach ($agent in $agents) { if ([string]::IsNullOrEmpty($agent.OwnerAccountUpns)) { $failedAgents += $agent | Select-Object *, @{N='OrphanReason';E={'No owner assigned'}} continue } # Check each owner UPN against Entra ID $ownerUpns = @($agent.OwnerAccountUpns -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } $allOwnersInvalid = $true foreach ($upn in $ownerUpns) { try { $user = Invoke-MtGraphRequest -RelativeUri "users/$upn" -Select 'id,accountEnabled' -ApiVersion 'v1.0' if ($user -and $user.accountEnabled -eq $true) { $allOwnersInvalid = $false break } } catch { # User not found or access denied - treat as invalid Write-Verbose "Could not resolve owner $upn for agent $($agent.AIAgentName): $_" } } if ($allOwnersInvalid) { $reason = if ([string]::IsNullOrEmpty($ownerUpns)) { "No owner assigned" } else { "All owners disabled or not found" } $failedAgents += $agent | Select-Object *, @{N='OrphanReason';E={$reason}} } } if ($failedAgents.Count -eq 0) { $testResultMarkdown = "Well done. All AI agents have active owners." } else { $testResultMarkdown = "Found $($failedAgents.Count) AI agent(s) with no active owner.`n`n%TestResult%" $result = "| Agent Name | Environment | Owner UPNs | Reason |`n" $result += "| --- | --- | --- | --- |`n" foreach ($agent in $failedAgents) { $owners = if ($agent.OwnerAccountUpns) { $agent.OwnerAccountUpns } else { "None" } $result += "| $($agent.AIAgentName) | $($agent.EnvironmentId) | $owners | $($agent.OrphanReason) |`n" } $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result } Add-MtTestResultDetail -Result $testResultMarkdown -Severity "Medium" return ($failedAgents.Count -eq 0) } |