← Files Power BI DesktopARCHIVED FILE
scripts/Import-PowerBIUsageSignals.ps1
6.51 KB · Oct 2, 2026 · 00:32 UTC
param(
[string]$Path = ".",
[string]$UsagePath,
[string]$OutputPath,
[switch]$Json
)
$ErrorActionPreference = 'Stop'
$resolved = (Resolve-Path -LiteralPath $Path).Path
$expectedColumns = @(
'ReportName',
'DatasetName',
'WorkspaceName',
'UserPrincipalName',
'Activity',
'Operation',
'CreationTime',
'ViewCount'
)
function Get-RelativePath {
param([string]$BasePath, [string]$TargetPath)
if ([System.IO.Path].GetMethod('GetRelativePath', [type[]]@([string], [string]))) {
return [System.IO.Path]::GetRelativePath($BasePath, $TargetPath)
}
$baseUri = [Uri]((Join-Path $BasePath '.') + [System.IO.Path]::DirectorySeparatorChar)
$targetUri = [Uri]$TargetPath
[Uri]::UnescapeDataString($baseUri.MakeRelativeUri($targetUri).ToString()).Replace('/', [System.IO.Path]::DirectorySeparatorChar)
}
function Get-PropertyValue {
param([object]$Item, [string[]]$Names)
foreach ($name in $Names) {
if ($null -ne $Item.PSObject.Properties[$name]) {
return $Item.PSObject.Properties[$name].Value
}
}
$null
}
function ConvertTo-Number {
param([object]$Value)
if ($null -eq $Value -or "$Value" -eq '') { return 0 }
$number = 0
if ([int]::TryParse("$Value", [ref]$number)) { return $number }
0
}
function Read-UsageFile {
param([System.IO.FileInfo]$File)
$extension = $File.Extension.ToLowerInvariant()
if ($extension -eq '.csv') {
return @(Import-Csv -LiteralPath $File.FullName)
}
if ($extension -eq '.json') {
$raw = Get-Content -Raw -LiteralPath $File.FullName
if (-not $raw.Trim()) { return @() }
$json = $raw | ConvertFrom-Json
if ($json.value) { return @($json.value) }
if ($json.records) { return @($json.records) }
if ($json.activities) { return @($json.activities) }
if ($json.usage) { return @($json.usage) }
return @($json)
}
@()
}
function New-UsageSignal {
param([object]$Row, [string]$Source)
$report = Get-PropertyValue $Row @('ReportName', 'Report', 'ItemName', 'ArtifactName', 'ObjectName')
$dataset = Get-PropertyValue $Row @('DatasetName', 'SemanticModelName', 'ModelName', 'Dataset')
$workspace = Get-PropertyValue $Row @('WorkspaceName', 'WorkSpaceName', 'Workspace', 'GroupName')
$user = Get-PropertyValue $Row @('UserPrincipalName', 'UserId', 'User', 'Actor', 'ActorUserPrincipalName')
$activity = Get-PropertyValue $Row @('Activity', 'Operation', 'Action', 'EventName', 'RecordType')
$time = Get-PropertyValue $Row @('CreationTime', 'TimeGenerated', 'Timestamp', 'Date', 'LastActivity')
$views = ConvertTo-Number (Get-PropertyValue $Row @('ViewCount', 'Views', 'ReportViews', 'Count'))
[pscustomobject]@{
reportName = if ($report) { "$report" } else { '[unknown report]' }
datasetName = if ($dataset) { "$dataset" } else { '[unknown dataset]' }
workspaceName = if ($workspace) { "$workspace" } else { '[unknown workspace]' }
userPrincipalName = if ($user) { "$user" } else { '[unknown user]' }
activity = if ($activity) { "$activity" } else { '[unknown activity]' }
activityTime = if ($time) { "$time" } else { $null }
viewCount = $views
source = $Source
}
}
$usageFiles = @()
if ($UsagePath) {
if (Test-Path -LiteralPath $UsagePath -PathType Leaf) {
$usageFiles = @(Get-Item -LiteralPath $UsagePath)
}
elseif (Test-Path -LiteralPath $UsagePath -PathType Container) {
$usageFiles = @(Get-ChildItem -LiteralPath $UsagePath -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Extension.ToLowerInvariant() -in @('.csv', '.json') })
}
}
else {
$usageFiles = @(Get-ChildItem -LiteralPath $resolved -Recurse -File -ErrorAction SilentlyContinue |
Where-Object {
$_.Extension.ToLowerInvariant() -in @('.csv', '.json') -and
$_.Name -match '(?i)usage|activity|audit|view|report.*metric|metrics'
})
}
$signals = New-Object System.Collections.Generic.List[object]
foreach ($file in $usageFiles) {
$source = Get-RelativePath -BasePath $resolved -TargetPath $file.FullName
foreach ($row in @(Read-UsageFile -File $file)) {
$signals.Add((New-UsageSignal -Row $row -Source $source))
}
}
$reportUsage = @($signals |
Group-Object reportName |
ForEach-Object {
$rows = @($_.Group)
[pscustomobject]@{
reportName = $_.Name
activityCount = $rows.Count
viewCount = [int](($rows | Measure-Object -Property viewCount -Sum).Sum)
distinctUserCount = @($rows | Where-Object { $_.userPrincipalName -ne '[unknown user]' } | Select-Object -ExpandProperty userPrincipalName -Unique).Count
lastActivity = @($rows | Where-Object activityTime | Sort-Object activityTime -Descending | Select-Object -First 1 -ExpandProperty activityTime)
sources = @($rows | Select-Object -ExpandProperty source -Unique)
}
} | Sort-Object @{ Expression = 'viewCount'; Descending = $true }, @{ Expression = 'activityCount'; Descending = $true })
$status = if ($signals.Count -gt 0) { 'Imported' } else { 'EmptyImport' }
$result = [pscustomobject]@{
schema = 'codex.powerbi.usageSignals.v1'
generated = (Get-Date).ToString('s')
source = $resolved
usagePath = $UsagePath
status = $status
expectedColumns = $expectedColumns
fileCount = @($usageFiles).Count
signalCount = $signals.Count
reportUsage = @($reportUsage)
signals = @($signals.ToArray())
}
if ($Json) {
$text = $result | ConvertTo-Json -Depth 8
if ($OutputPath) { Set-Content -LiteralPath $OutputPath -Value $text -Encoding UTF8 }
$text
return
}
$lines = @(
'# Power BI Usage Signals',
'',
"Status: $status",
"Signals: $($signals.Count)",
"Files: $(@($usageFiles).Count)",
'',
'## Expected columns',
($expectedColumns -join ', '),
'',
'## Report usage'
) + @($reportUsage | ForEach-Object { "- $($_.reportName): activities=$($_.activityCount), views=$($_.viewCount), users=$($_.distinctUserCount)" })
if ($reportUsage.Count -eq 0) {
$lines += '- No usage export found. Provide Usage Metrics, Activity Events, or audit CSV/JSON exports.'
}
$content = ($lines -join [Environment]::NewLine) + [Environment]::NewLine
if ($OutputPath) { Set-Content -LiteralPath $OutputPath -Value $content -Encoding UTF8 }
$content
SHA-256: f5a9612b412135df5cf41031ca7d89e5437c5e8afee3ed107c6320dfb77ae460