Skip to content

PowerShell Setup: Control 3.2 - Usage Analytics and Activity Monitoring

Last Updated: January 2026 Modules Required: Microsoft.PowerApps.Administration.PowerShell, ExchangeOnlineManagement

Prerequisites

# Install Power Platform Admin modules
Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Scope CurrentUser -Force
Install-Module -Name ExchangeOnlineManagement -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

# Connect to Exchange Online (for audit logs)
Connect-ExchangeOnline

Configuration Scripts

Get Usage Metrics from PPAC

# Get all environments with usage data
$environments = Get-AdminPowerAppEnvironment

# Retrieve usage metrics for each environment
foreach ($env in $environments) {
    Write-Host "Environment: $($env.DisplayName)" -ForegroundColor Cyan

    # Get apps in the environment for usage overview
    $apps = Get-AdminPowerApp -EnvironmentName $env.EnvironmentName -ErrorAction SilentlyContinue

    if ($apps) {
        Write-Host "  Total Apps: $($apps.Count)"
        Write-Host "  Owners: $(($apps | Select-Object -ExpandProperty Owner -Unique).Count)"
        Write-Host "  Last Modified: $(($apps | Sort-Object -Property LastModifiedTime -Descending | Select-Object -First 1).LastModifiedTime)"
    } else {
        Write-Host "  No apps found in this environment" -ForegroundColor Yellow
    }
}

Export Copilot Studio Analytics

# Get Copilot Studio agent analytics
function Export-CopilotStudioAnalytics {
    param(
        [string]$EnvironmentId,
        [string]$OutputPath = ".\CopilotAnalytics",
        [int]$DaysBack = 30
    )

    # Create output directory
    if (-not (Test-Path $OutputPath)) {
        New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
    }

    # Calculate date range
    $endDate = Get-Date
    $startDate = $endDate.AddDays(-$DaysBack)

    Write-Host "Exporting analytics for environment: $EnvironmentId" -ForegroundColor Cyan
    Write-Host "Date range: $startDate to $endDate"

    # Get agent list
    $agents = Get-AdminPowerApp -EnvironmentName $EnvironmentId -ErrorAction SilentlyContinue

    $analyticsData = @()

    foreach ($agent in $agents) {
        $agentMetrics = @{
            AgentId = $agent.AppName
            AgentName = $agent.DisplayName
            Environment = $EnvironmentId
            Owner = $agent.Owner.displayName
            CreatedTime = $agent.CreatedTime
            LastModifiedTime = $agent.LastModifiedTime
            ExportDate = Get-Date
        }
        $analyticsData += [PSCustomObject]$agentMetrics
    }

    # Export to CSV
    $fileName = "CopilotAnalytics_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    $analyticsData | Export-Csv -Path (Join-Path $OutputPath $fileName) -NoTypeInformation

    Write-Host "Analytics exported to: $OutputPath\$fileName" -ForegroundColor Green
    Write-Host "Total agents: $($analyticsData.Count)"
    return $analyticsData
}

# Usage
# Export-CopilotStudioAnalytics -EnvironmentId "env-id-here" -DaysBack 30

Query Audit Logs for Agent Activity

Pagination

Search-UnifiedAuditLog returns a maximum of 5,000 records per call. Use -SessionId and -SessionCommand ReturnLargeSet for pagination in high-volume environments. See Microsoft documentation.

# Query unified audit log for Copilot Studio activities
function Get-AgentAuditLogs {
    param(
        [string]$AgentName = "*",
        [int]$DaysBack = 7,
        [string]$OutputPath = ".\AuditLogs"
    )

    Write-Host "Querying audit logs for agent activity..." -ForegroundColor Cyan

    $endDate = Get-Date
    $startDate = $endDate.AddDays(-$DaysBack)

    # Search for Copilot Studio activities
    $auditLogs = Search-UnifiedAuditLog `
        -StartDate $startDate `
        -EndDate $endDate `
        -RecordType PowerApps `
        -ResultSize 5000

    Write-Host "Found $($auditLogs.Count) audit log entries" -ForegroundColor Cyan

    # Parse and filter results
    $parsedLogs = $auditLogs | ForEach-Object {
        $auditData = $_.AuditData | ConvertFrom-Json
        [PSCustomObject]@{
            Timestamp = $_.CreationDate
            User = $_.UserIds
            Operation = $_.Operations
            ItemName = $auditData.ItemName
            ItemType = $auditData.ItemType
            EnvironmentName = $auditData.EnvironmentName
            ResultStatus = $auditData.ResultStatus
        }
    }

    # Filter by agent name if specified
    if ($AgentName -ne "*") {
        $parsedLogs = $parsedLogs | Where-Object { $_.ItemName -like "*$AgentName*" }
    }

    # Export results
    if (-not (Test-Path $OutputPath)) {
        New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
    }

    $fileName = "AgentAuditLogs_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
    $parsedLogs | Export-Csv -Path (Join-Path $OutputPath $fileName) -NoTypeInformation

    Write-Host "Audit logs exported to: $OutputPath\$fileName" -ForegroundColor Green
    return $parsedLogs
}

# Usage
# Get-AgentAuditLogs -AgentName "CustomerService" -DaysBack 30

Generate FSI Usage Report

# Generate comprehensive usage report for FSI governance
function New-FSIUsageReport {
    param(
        [string]$ReportPath = ".\Reports",
        [string]$ReportName = "FSI_Agent_Usage_Report",
        [int]$DaysBack = 30
    )

    Write-Host "Generating FSI Usage Report..." -ForegroundColor Cyan

    # Create report directory
    if (-not (Test-Path $ReportPath)) {
        New-Item -ItemType Directory -Path $ReportPath -Force | Out-Null
    }

    # Get all managed environments
    $environments = Get-AdminPowerAppEnvironment

    $reportData = @()

    foreach ($env in $environments) {
        Write-Host "  Processing: $($env.DisplayName)" -ForegroundColor Yellow

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

        foreach ($app in $apps) {
            $reportData += [PSCustomObject]@{
                EnvironmentName = $env.DisplayName
                EnvironmentType = $env.EnvironmentType
                IsManaged = $env.Properties.isManaged
                AppName = $app.DisplayName
                Owner = $app.Owner.displayName
                OwnerEmail = $app.Owner.email
                CreatedTime = $app.CreatedTime
                LastModifiedTime = $app.LastModifiedTime
                ReportDate = Get-Date
            }
        }
    }

    # Export report
    $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
    $csvPath = Join-Path $ReportPath "${ReportName}_${timestamp}.csv"
    $reportData | Export-Csv -Path $csvPath -NoTypeInformation

    # Generate summary
    $summary = @{
        TotalEnvironments = ($environments).Count
        ManagedEnvironments = ($environments | Where-Object { $_.Properties.isManaged -eq $true }).Count
        TotalApps = ($reportData).Count
        ReportDate = Get-Date
    }

    Write-Host "`n=== Report Summary ===" -ForegroundColor Cyan
    Write-Host "Total Environments: $($summary.TotalEnvironments)"
    Write-Host "Managed Environments: $($summary.ManagedEnvironments)"
    Write-Host "Total Apps: $($summary.TotalApps)"
    Write-Host "Report exported to: $csvPath" -ForegroundColor Green

    return $reportData
}

# Usage
# New-FSIUsageReport -DaysBack 30

Validation Script

# Validation: Check monitoring configuration
Write-Host "`n=== Control 3.2 Validation ===" -ForegroundColor Cyan

# Check 1: Verify environment access
$environments = Get-AdminPowerAppEnvironment
if ($environments.Count -gt 0) {
    Write-Host "[PASS] Environment access verified: $($environments.Count) environments" -ForegroundColor Green
} else {
    Write-Host "[FAIL] Cannot access environments" -ForegroundColor Red
}

# Check 2: Verify managed environments exist
$managedEnvs = $environments | Where-Object { $_.Properties.isManaged -eq $true }
if ($managedEnvs.Count -gt 0) {
    Write-Host "[PASS] Managed environments found: $($managedEnvs.Count)" -ForegroundColor Green
} else {
    Write-Host "[WARN] No managed environments - usage insights unavailable" -ForegroundColor Yellow
}

# Check 3: Verify audit log access
try {
    $testAudit = Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date) -ResultSize 1
    Write-Host "[PASS] Audit log access verified" -ForegroundColor Green
} catch {
    Write-Host "[FAIL] Cannot access audit logs: $($_.Exception.Message)" -ForegroundColor Red
}

# Check 4: Verify app visibility
$allApps = Get-AdminPowerApp
if ($allApps -ne $null) {
    Write-Host "[PASS] App visibility verified: $($allApps.Count) apps" -ForegroundColor Green
} else {
    Write-Host "[WARN] No apps found or access issue" -ForegroundColor Yellow
}

Write-Host "`n=== Validation Complete ===" -ForegroundColor Cyan

Complete Configuration Script

<#
.SYNOPSIS
    Configures Control 3.2 - Usage Analytics and Activity Monitoring

.DESCRIPTION
    This script validates and exports usage analytics by:
    1. Checking environment access and managed status
    2. Exporting app inventory with usage data
    3. Querying audit logs for activity monitoring
    4. Generating comprehensive FSI usage report

.PARAMETER ExportPath
    The directory path for analytics exports

.PARAMETER DaysBack
    Number of days of history to include

.EXAMPLE
    .\Export-UsageAnalytics.ps1 -ExportPath "C:\Governance\Analytics" -DaysBack 30

.NOTES
    Last Updated: January 2026
    Related Control: Control 3.2 - Usage Analytics and Activity Monitoring
#>

param(
    [Parameter(Mandatory=$false)]
    [string]$ExportPath = "C:\Governance\UsageAnalytics",

    [Parameter(Mandatory=$false)]
    [int]$DaysBack = 30
)

# 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

Write-Host "Executing Control 3.2 Usage Analytics Export" -ForegroundColor Cyan

# Create export directory
if (-not (Test-Path $ExportPath)) {
    New-Item -ItemType Directory -Path $ExportPath -Force | Out-Null
}

$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"

# Step 1: Get environment summary
Write-Host "`n[Step 1] Collecting environment data..." -ForegroundColor Yellow
$environments = Get-AdminPowerAppEnvironment

$envSummary = $environments | Select-Object DisplayName, EnvironmentType,
    @{N='IsManaged';E={$_.Properties.isManaged}},
    Location, CreatedTime

$envSummary | Export-Csv -Path "$ExportPath\Environments_$timestamp.csv" -NoTypeInformation
Write-Host "  Environments exported: $($environments.Count)"

# Step 2: Get app inventory
Write-Host "`n[Step 2] Collecting app inventory..." -ForegroundColor Yellow
$allApps = Get-AdminPowerApp

$appInventory = $allApps | Select-Object DisplayName, EnvironmentName,
    @{N='Owner';E={$_.Owner.displayName}},
    @{N='OwnerEmail';E={$_.Owner.email}},
    CreatedTime, LastModifiedTime

$appInventory | Export-Csv -Path "$ExportPath\AppInventory_$timestamp.csv" -NoTypeInformation
Write-Host "  Apps exported: $($allApps.Count)"

# Step 3: Summary report
Write-Host "`n[Step 3] Generating summary report..." -ForegroundColor Yellow

$summary = [PSCustomObject]@{
    ReportDate = Get-Date
    DaysBack = $DaysBack
    TotalEnvironments = $environments.Count
    ManagedEnvironments = ($environments | Where-Object { $_.Properties.isManaged -eq $true }).Count
    TotalApps = $allApps.Count
    ExportPath = $ExportPath
}

$summary | ConvertTo-Json | Out-File "$ExportPath\Summary_$timestamp.json"

Write-Host "`n=== Export Complete ===" -ForegroundColor Cyan
Write-Host "Files saved to: $ExportPath" -ForegroundColor Green
$summary

Back to Control 3.2 | Portal Walkthrough | Verification Testing | Troubleshooting


Updated: January 2026 | Version: v1.2