PowerShell Setup: Control 3.1 - Agent Inventory and Metadata Management
Last Updated: January 2026 Modules Required: Microsoft.PowerApps.Administration.PowerShell
Prerequisites
# Install Power Platform Admin modules
Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Scope CurrentUser -Force
Install-Module -Name Microsoft.PowerApps.PowerShell -Scope CurrentUser -Force
# Connect to Power Platform (interactive authentication)
Add-PowerAppsAccount
# For automated/unattended scenarios, use service principal authentication:
# $appId = "<Application-Client-ID>"
# $secret = "<Client-Secret>"
# $tenantId = "<Tenant-ID>"
# Add-PowerAppsAccount -ApplicationId $appId -ClientSecret $secret -TenantID $tenantId
Configuration Scripts
Get All Environments
# List all environments with details
$environments = Get-AdminPowerAppEnvironment
$environments | Select-Object DisplayName, EnvironmentName, EnvironmentType, CreatedTime, SecurityGroupId |
Format-Table -AutoSize
# Export environment list to CSV
$environments | Export-Csv -Path "C:\Governance\Environments_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
Get Apps and Flows Across Environments
# Get all Power Apps across all environments
$allApps = Get-AdminPowerApp
Write-Host "Total Power Apps found: $($allApps.Count)" -ForegroundColor Cyan
$allApps | Select-Object DisplayName, EnvironmentName, Owner, CreatedTime, LastModifiedTime |
Format-Table -AutoSize
# Get all Power Automate flows across all environments
$allFlows = Get-AdminFlow
Write-Host "Total Power Automate flows found: $($allFlows.Count)" -ForegroundColor Cyan
$allFlows | Select-Object DisplayName, EnvironmentName, Enabled, CreatedTime, LastModifiedTime |
Format-Table -AutoSize
Export Comprehensive Agent Inventory
# Comprehensive Agent Inventory Export Script
# Run this script weekly for compliance documentation
$reportDate = Get-Date -Format "yyyyMMdd_HHmmss"
$exportPath = "C:\Governance\AgentInventory"
# Create export directory if not exists
if (-not (Test-Path $exportPath)) {
New-Item -ItemType Directory -Path $exportPath -Force
}
# Get all environments
$environments = Get-AdminPowerAppEnvironment
# Initialize inventory collection
$inventoryReport = @()
foreach ($env in $environments) {
Write-Host "Processing environment: $($env.DisplayName)" -ForegroundColor Yellow
# Get apps in this environment
$apps = Get-AdminPowerApp -EnvironmentName $env.EnvironmentName -ErrorAction SilentlyContinue
foreach ($app in $apps) {
$inventoryReport += [PSCustomObject]@{
ItemName = $app.DisplayName
ItemType = "PowerApp"
Owner = $app.Owner.displayName
OwnerEmail = $app.Owner.email
EnvironmentName = $env.DisplayName
EnvironmentType = $env.EnvironmentType
CreatedDate = $app.CreatedTime
ModifiedDate = $app.LastModifiedTime
AppType = $app.AppType
InventoryDate = Get-Date
}
}
# Get flows in this environment
$flows = Get-AdminFlow -EnvironmentName $env.EnvironmentName -ErrorAction SilentlyContinue
foreach ($flow in $flows) {
$inventoryReport += [PSCustomObject]@{
ItemName = $flow.DisplayName
ItemType = "Flow"
Owner = $flow.Owner.displayName
OwnerEmail = $flow.Owner.email
EnvironmentName = $env.DisplayName
EnvironmentType = $env.EnvironmentType
CreatedDate = $flow.CreatedTime
ModifiedDate = $flow.LastModifiedTime
AppType = "N/A"
InventoryDate = Get-Date
}
}
}
# Export inventory to CSV
$inventoryReport | Export-Csv -Path "$exportPath\AgentInventory_$reportDate.csv" -NoTypeInformation
Write-Host "Inventory exported to: $exportPath\AgentInventory_$reportDate.csv" -ForegroundColor Green
Write-Host "Total items inventoried: $($inventoryReport.Count)" -ForegroundColor Cyan
# Evidence integrity: compute and record a SHA-256 hash for the export
$exportFile = "$exportPath\AgentInventory_$reportDate.csv"
$hash = (Get-FileHash -Path $exportFile -Algorithm SHA256).Hash
"$reportDate,AgentInventory,$exportFile,SHA256,$hash" | Out-File -FilePath "$exportPath\AgentInventory_Hashes.csv" -Append -Encoding utf8
Write-Host "Export SHA-256: $hash" -ForegroundColor Cyan
Identify Orphaned Agents
# Identify Orphaned Agents Script
# Finds agents with missing or invalid owners
$orphanedAgents = @()
# Get all apps
$allApps = Get-AdminPowerApp
foreach ($app in $allApps) {
# Check if owner email is empty or contains system account indicators
if ([string]::IsNullOrEmpty($app.Owner.email) -or
$app.Owner.email -like "*system*" -or
$app.Owner.email -like "*deleted*") {
$orphanedAgents += [PSCustomObject]@{
ItemName = $app.DisplayName
ItemType = "PowerApp"
OwnerInfo = $app.Owner.displayName
EnvironmentName = $app.EnvironmentName
CreatedDate = $app.CreatedTime
LastModified = $app.LastModifiedTime
Status = "Orphaned - No Valid Owner"
}
}
}
# Display orphaned agents
if ($orphanedAgents.Count -gt 0) {
Write-Host "Found $($orphanedAgents.Count) orphaned agents requiring remediation:" -ForegroundColor Red
$orphanedAgents | Format-Table -AutoSize
# Export for remediation
$orphanedAgents | Export-Csv -Path "C:\Governance\OrphanedAgents_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
} else {
Write-Host "No orphaned agents found." -ForegroundColor Green
}
Create Inventory Summary Report
# Generate Inventory Summary Report
# Provides executive-level summary for governance committee
$environments = Get-AdminPowerAppEnvironment
$summaryReport = @()
foreach ($env in $environments) {
$apps = Get-AdminPowerApp -EnvironmentName $env.EnvironmentName -ErrorAction SilentlyContinue
$flows = Get-AdminFlow -EnvironmentName $env.EnvironmentName -ErrorAction SilentlyContinue
$summaryReport += [PSCustomObject]@{
EnvironmentName = $env.DisplayName
EnvironmentType = $env.EnvironmentType
TotalApps = $apps.Count
TotalFlows = $flows.Count
TotalItems = $apps.Count + $flows.Count
Region = $env.Location
SecurityGroupEnabled = if ($env.SecurityGroupId) { "Yes" } else { "No" }
ReportDate = Get-Date
}
}
# Display summary
Write-Host "`n=== AGENT INVENTORY SUMMARY REPORT ===" -ForegroundColor Cyan
$summaryReport | Format-Table -AutoSize
# Total counts
$totalApps = ($summaryReport | Measure-Object -Property TotalApps -Sum).Sum
$totalFlows = ($summaryReport | Measure-Object -Property TotalFlows -Sum).Sum
Write-Host "`nTotal Apps Across All Environments: $totalApps" -ForegroundColor Green
Write-Host "Total Flows Across All Environments: $totalFlows" -ForegroundColor Green
Write-Host "Total Environments: $($environments.Count)" -ForegroundColor Green
# Export summary
$summaryReport | Export-Csv -Path "C:\Governance\InventorySummary_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
Validation Script
# Validation: Check inventory access and export capability
Write-Host "`n=== Control 3.1 Validation ===" -ForegroundColor Cyan
# Check 1: Can access environments
$environments = Get-AdminPowerAppEnvironment
if ($environments.Count -gt 0) {
Write-Host "[PASS] Can access environments: $($environments.Count) found" -ForegroundColor Green
} else {
Write-Host "[FAIL] No environments accessible - check permissions" -ForegroundColor Red
}
# Check 2: Can retrieve apps
$allApps = Get-AdminPowerApp
if ($allApps -ne $null) {
Write-Host "[PASS] Can retrieve Power Apps: $($allApps.Count) found" -ForegroundColor Green
} else {
Write-Host "[WARN] No Power Apps found or access issue" -ForegroundColor Yellow
}
# Check 3: Can retrieve flows
$allFlows = Get-AdminFlow
if ($allFlows -ne $null) {
Write-Host "[PASS] Can retrieve Power Automate flows: $($allFlows.Count) found" -ForegroundColor Green
} else {
Write-Host "[WARN] No flows found or access issue" -ForegroundColor Yellow
}
# Check 4: Verify export location exists
$exportPath = "C:\Governance\AgentInventory"
if (Test-Path $exportPath) {
Write-Host "[PASS] Export directory exists: $exportPath" -ForegroundColor Green
} else {
Write-Host "[WARN] Export directory does not exist - will be created on first export" -ForegroundColor Yellow
}
Complete Configuration Script
<#
.SYNOPSIS
Configures Control 3.1 - Agent Inventory and Metadata Management
.DESCRIPTION
This script performs comprehensive agent inventory by:
1. Enumerating all environments
2. Collecting all apps and flows
3. Identifying orphaned agents
4. Exporting to CSV with SHA-256 hash
5. Generating summary report
.PARAMETER ExportPath
The directory path for inventory exports
.EXAMPLE
.\Export-AgentInventory.ps1 -ExportPath "C:\Governance\AgentInventory"
.NOTES
Last Updated: January 2026
Related Control: Control 3.1 - Agent Inventory and Metadata Management
#>
param(
[Parameter(Mandatory=$false)]
[string]$ExportPath = "C:\Governance\AgentInventory"
)
try {
# Connect to Power Platform
Add-PowerAppsAccount
Write-Host "Executing Control 3.1 Agent Inventory Export" -ForegroundColor Cyan
# Create export directory
if (-not (Test-Path $ExportPath)) {
New-Item -ItemType Directory -Path $ExportPath -Force | Out-Null
}
$reportDate = Get-Date -Format "yyyyMMdd_HHmmss"
$environments = Get-AdminPowerAppEnvironment
$inventoryReport = @()
$orphanedAgents = @()
foreach ($env in $environments) {
Write-Host " Processing: $($env.DisplayName)" -ForegroundColor Yellow
$apps = Get-AdminPowerApp -EnvironmentName $env.EnvironmentName -ErrorAction SilentlyContinue
foreach ($app in $apps) {
$isOrphaned = [string]::IsNullOrEmpty($app.Owner.email) -or
$app.Owner.email -like "*system*" -or
$app.Owner.email -like "*deleted*"
$record = [PSCustomObject]@{
ItemName = $app.DisplayName
ItemType = "PowerApp"
Owner = $app.Owner.displayName
OwnerEmail = $app.Owner.email
EnvironmentName = $env.DisplayName
EnvironmentType = $env.EnvironmentType
CreatedDate = $app.CreatedTime
ModifiedDate = $app.LastModifiedTime
IsOrphaned = $isOrphaned
InventoryDate = Get-Date
}
$inventoryReport += $record
if ($isOrphaned) { $orphanedAgents += $record }
}
}
# Export full inventory
$fullExport = "$ExportPath\AgentInventory_$reportDate.csv"
$inventoryReport | Export-Csv -Path $fullExport -NoTypeInformation
$hash = (Get-FileHash -Path $fullExport -Algorithm SHA256).Hash
"$reportDate,AgentInventory,$fullExport,SHA256,$hash" | Out-File -FilePath "$ExportPath\Hashes.csv" -Append
# Export orphaned agents
if ($orphanedAgents.Count -gt 0) {
$orphanedAgents | Export-Csv -Path "$ExportPath\OrphanedAgents_$reportDate.csv" -NoTypeInformation
Write-Host " [ALERT] $($orphanedAgents.Count) orphaned agents found" -ForegroundColor Red
}
Write-Host "`nInventory complete!" -ForegroundColor Cyan
Write-Host " Total items: $($inventoryReport.Count)" -ForegroundColor Green
Write-Host " Export file: $fullExport" -ForegroundColor Green
Write-Host " SHA-256: $hash" -ForegroundColor Green
Write-Host "`n[PASS] Control 3.1 configuration completed successfully" -ForegroundColor Green
}
catch {
Write-Host "[FAIL] Error: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[INFO] Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Yellow
exit 1
}
Back to Control 3.1 | Portal Walkthrough | Verification Testing | Troubleshooting