mcpField
C
connect-pbid

connect-pbid

github.com/data-goblinデータ・データベース1.1k70 回インストール今日更新
リポジトリ

connect-pbid について

connect-pbid は、PowerShell、TOM、ADOMD.NET を使って Power BI Desktop のモデルを操作するためのスキルです。Tabular Editor CLI や Power BI MCP サーバーが使えない場合に、ローカルモデルの調査、DAX クエリ、更新、変更を支援します。

主な機能

  1. 01ローカル Analysis Services モデルへの接続
  2. 02モデルメタデータの列挙
  3. 03DAX クエリとトレースの実行
  4. 04モデルの更新と変更

活用シーン

  1. 01Power BI Desktop で開いたモデルの調査
  2. 02DAX によるローカルモデルデータの照会
  3. 03利用可能な優先ツールがない場合のモデルメタデータ更新

MCP 対応

このスキルに MCP サーバーは必要ありません。Tabular Editor CLI または Power BI MCP サーバーが利用できない場合の代替手段として説明されています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/data-goblin/power-bi-agentic-development --skill connect-pbid

インストール後、Claude Code で/connect-pbidを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

daxlib.sh
references/
annotations.md
calendar-column-groups.md
dax-expressions.md
dax-pitfalls.md
daxlib.md
desktop-bridge.md
evaluateandlog-debugging.md
export-model.md
load-tmdl-files.md
parallels-macos.md
performance-profiling.md
query-listener.md
refresh-model.md
tom-object-types.md
vertipaq-stats.md
scripts/
connect-and-enumerate.ps1
connect-from-mac.sh
create-field-parameter.ps1
debug-dax.ps1
explore-model.ps1
load-tmdl.ps1
modify-tom-objects.ps1
query-dax.ps1
refresh-table.ps1
scripts/daxlib-tom/
.gitignore
daxlib-tom.csproj
Program.cs

詳細

Power BI Desktop に接続する(ローカル Analysis Services)

重要: このスキルの使用中に遭遇した間違い、予想外の動作、モデル固有の注意点を .claude/rules/connect-pbid.md に記録してください。このファイルは「Learnings from Claude about connecting to semantic models via the connect-pbid skill」で始めてください。有効な参照情報(例:「QueryGroup プロパティはオブジェクトを返すため、名前の文字列には .Folder を使用します」)のみを記載し、変更履歴やイベントの履歴は記録しないでください。スキルやその参照資料ですでに説明されている内容は省いてください。ファイルは常に 1500 文字未満に保ち、新しい項目を追加する際は古い項目を整理してください。このファイルを過度に気にかける必要はありません。本当に予想外のことが見つかった場合にのみ更新してください。

注: MCP サーバーは必要ありません。ローカルモデルには PowerShell と TOM/ADOMD.NET を使用してください。 レポートキャンバスも対象となる場合は、レポート操作に pbir を組み合わせてください。レポートの JSON を直接編集してはいけません。

Tabular Object Model(TOM)と PowerShell の ADOMD.NET を使用して、Power BI Desktop のローカルな表形式モデルに接続するための専門的なガイダンスです。接続、列挙、DAX クエリ、クエリトレース、モデル全体の変更について説明します。

このスキルを使用するタイミング

Tabular Editor CLI または Power BI MCP サーバーを利用できない場合にのみ有効にしてください。TOM はエンジンに対して変更を検証し、アトミックに適用するため、TMDL を直接編集するよりも信頼性があります。

警告: このスキルは XMLA エンドポイント経由のリモートモデルをサポートしていません。Direct Lake モデルまたは Fabric でホストされているモデルには、代わりに Tabular Editor CLI または Power BI MCP サーバーを使用してください。ローカルの Analysis Services プロキシは、外部の TOM/ADOMD.NET 接続に Direct Lake データベースを公開しません。

モデルとレポート:振り分け

Power BI Desktop は、モデルとレポートを別々のローカルインターフェイスとして公開します。このスキルはモデルのインターフェイスとレポートキャンバスの検証を担当し、レポートの作成作業は適切なスキルに振り分けます。

  • モデル(テーブル、列、メジャー、リレーションシップ、ロール、計算グループ、更新):ローカルの Analysis Services インスタンス経由で TOM/ADOMD を使用するこのスキルです。モデルを編集する場合は、利用可能であれば te CLI またはモデル MCP を優先してください。利用できない場合は、このスキルの TOM にフォールバックします(「このスキルを使用するタイミング」を参照)。
  • レポートキャンバスの検証(編集後の再読み込み、ページのスクリーンショット):このスキルが、Desktop Bridge の名前付きパイプ API を直接使用します(セクション 13)。
  • レポートの作成(ビジュアル、ページ、書式設定、フィルター、ブックマーク、テーマ):reports プラグインの pbir-cli スキル(pbir CLI を操作します)。ここでの Desktop Bridge は再読み込みとスクリーンショットのみを行い、ビジュアルは編集しません。ビジュアルまたはページの変更はすべて pbir-cli に振り分けてください。
  • レポート JSON を直接編集する場合(pbir を利用できない場合のみ):pbip プラグインの pbir-format スキルです。

開いている PBIP での一連の作業:ここで TOM を使用してモデルを変更し、pbir-cli でビジュアルを変更した後、ここで Desktop Bridge を使用して再読み込みとスクリーンショットを行い、確認してから反復します。

重大な注意事項

  • 接続する前に、Power BI Desktop を開き、モデルを読み込んでおく必要があります。エラーが発生する場合は、リモートモデルに接続された「シンレポート」か、外部接続をブロックするリモートプロキシを使用する Direct Lake モデルが原因である可能性が高いです。
  • ローカルの Analysis Services インスタンスが受け付ける接続は、localhost からのものに限られます。
  • PBI Desktop のファイルを複数開いている場合、ポートの異なる複数の msmdsrv.exe プロセスが起動します。それぞれのポートに接続し、$server.Databases[0].Name を読み取って、モデルが複数見つかった場合は、どのモデルを使用するかユーザーに確認してください。pbir CLI がインストールされている場合は、pbir desktop list を使って各 Desktop PID と、開いているファイルを正確に対応付けることを推奨します(セクション 2a を参照)。
  • ワークスペースエンジンの Databases: 0 は、シンレポート(リモートモデルへのライブ接続)を示しています。接続できるローカルモデルはありません。代わりに、リモートモデルを通じてシンレポートにクエリを実行してください(pbir model -q が自動的にリモートモデルへルーティングします)。
  • Bash 経由で PowerShell コマンドを実行する場合は、常に 60000ms 以上のタイムアウトを指定してください。
  • シェルのエスケープ処理:Bash は PowerShell の $ 変数($env:TEMP、$server など)を黙って削除します。対処方法は 2 つあります。(1)-Command 引数をシングルクォートで囲み、$ をそのまま PowerShell に渡します。(2)ヒアドキュメント(シングルクォートの区切り文字を使うと $ が保持されます)で .ps1 ファイルを作成し、-File を使います。macOS から Parallels を介して実行する場合、prlctl -> cmd.exe -> powershell.exe の連鎖でエスケープ処理の層が増えます。複雑なスクリプトには .ps1 ファイルのほうが確実ですが、短いコマンドならシングルクォートで囲んだインラインの -Command が使えます。
  • PowerShell コマンドまたはスクリプトの実行時は、必ず -ExecutionPolicy Bypass を指定してください。 Windows では、署名されていないスクリプトはデフォルトでブロックされます。
  • スクリプトファイルの保存場所――永続的に使用するスクリプトは、プロジェクトのエージェントハーネスの scripts ディレクトリ(ハーネスに応じて .claude/scripts/、.github/scripts/、.cursor/scripts/、.gemini/scripts/ など)に保存してください。一時的なスクリプトや使い捨てのスクリプトは、プロジェクトの tmp/ ディレクトリ(.gitignored に追加してください)に保存してください。./ のルートや /tmp/ にはスクリプトを作成しないでください。
  • ユーザーの明示的な指示なしに、モデルのメタデータを変更しないでください。
  • 変更を永続化するには、必ず $model.SaveChanges() を呼び出してください。呼び出さない場合、変更は破棄されます。
  • Parallels で PBI Desktop を実行している macOS ユーザーは、parallels-macos.md を参照してください。
  • このプラグインでは検証フックが有効になっています。DAX の参照を検証し、メジャーのメタデータを強制し、参照整合性を確認して、互換性レベルのアップグレード候補を報告します。チェックは hooks/config.yaml で切り替えられます。

1. 前提条件

要件説明
Power BI Desktopモデル(.pbix または .pbip)を読み込んだ状態で開いていること
PowerShellPBI Desktop を実行しているコンピューターで利用できること
NuGet CLIパッケージのインストール用(winget install Microsoft.NuGet)
TOM NuGet PackageMicrosoft.AnalysisServices.retail.amd64 — モデルのメタデータ用
ADOMD.NET PackageMicrosoft.AnalysisServices.AdomdClient.retail.amd64 — DAX クエリ用

パッケージがまだ存在しない場合に限り、両方をインストールしてください。

$pkgDir = "$env:TEMP\tom_nuget"
if (-not (Test-Path "$pkgDir\Microsoft.AnalysisServices.retail.amd64")) {
    nuget install Microsoft.AnalysisServices.retail.amd64 -OutputDirectory $pkgDir -ExcludeVersion
}
if (-not (Test-Path "$pkgDir\Microsoft.AnalysisServices.AdomdClient.retail.amd64")) {
    nuget install Microsoft.AnalysisServices.AdomdClient.retail.amd64 -OutputDirectory $pkgDir -ExcludeVersion
}

パッケージは lib\net45\ の下に DLL をインストールします。Add-Type -Path で読み込んでください。

TOM の操作が互換性レベルのエラーまたは型が見つからないエラーで失敗する場合、.retail.amd64 パッケージが古すぎる可能性があります。より新しいパッケージ(Microsoft.AnalysisServices、.NET 8 以降)には、より新しい TOM 機能が含まれています。パッケージの違いについて詳しくは、daxlib.md を参照してください。

2. クイックスタート

ポートを見つけ、TOM を読み込み、接続して列挙するスクリプトです。

# Find ports (deduped; netstat lists IPv4 and IPv6 entries per port)
$pids = (Get-Process msmdsrv -ErrorAction SilentlyContinue).Id
$ports = netstat -ano | Select-String "LISTENING" |
    Where-Object { $pids -contains ($_ -split "\s+")[-1] } |
    ForEach-Object { ($_ -split "\s+")[2] -replace ".*:" } |
    Select-Object -Unique

# Load TOM
$basePath = "$env:TEMP\tom_nuget\Microsoft.AnalysisServices.retail.amd64\lib\net45"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Core.dll"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Tabular.dll"

# Connect to the first port that hosts a model; skip thin-report engines (0 databases)
$server = New-Object Microsoft.AnalysisServices.Tabular.Server
foreach ($p in $ports) {
    $server.Connect("Data Source=localhost:$p")
    if ($server.Databases.Count -eq 0) {
        Write-Output "localhost:$p hosts no model (thin report); trying next port"
        $server.Disconnect()
        continue
    }
    break
}
$model = $server.Databases[0].Model

# Enumerate
foreach ($table in $model.Tables) {
    Write-Output "TABLE: [$($table.Name)] ($($table.Columns.Count) cols, $($table.Measures.Count) measures)"
}
Write-Output "Relationships: $($model.Relationships.Count)"

$server.Disconnect()

ポートの検出方法:

方法インストールの種類コマンド
ポートファイルStore 版以外の PBI DesktopGet-Content "$env:LOCALAPPDATA\Microsoft\Power BI Desktop\AnalysisServicesWorkspaces\*\Data\msmdsrv.port.txt"
ポートファイルStore 版 PBI DesktopGet-Content "$env:LOCALAPPDATA\Packages\Microsoft.MicrosoftPowerBIDesktop_*\LocalState\AnalysisServicesWorkspaces\*\Data\msmdsrv.port.txt"
netstatすべてnetstat -ano | findstr LISTENING | findstr <PID>

2a. ポートとレポートの対応付け(複数のインスタンス)

ポートだけでは、そのポートがどのレポートを提供しているか特定できません。誤ったモデルを変更しないよう、接続前に対応付けてください。pbir CLI を使用し、Desktop の「外部ツールへのアクセス」プレビュー機能を有効にすると、pbir desktop list で、各 Desktop PID と、それが開いている正確なファイルを確認できます。プロセスツリーを通じてポートをそれらの PID に対応付けます(各 msmdsrv.exe は PBIDesktop.exe の子プロセスです)。

$conns = Get-NetTCPConnection -State Listen
foreach ($proc in Get-Process msmdsrv -ErrorAction SilentlyContinue) {
    $port = ($conns | Where-Object OwningProcess -eq $proc.Id | Select-Object -First 1).LocalPort
    $parent = (Get-WmiObject Win32_Process -Filter "ProcessId=$($proc.Id)").ParentProcessId
    Write-Output "port $port -> msmdsrv $($proc.Id) -> PBIDesktop $parent"
}

Databases: 0 を報告するエンジンは、シンレポートのワークスペースです。ローカルモデルは存在しません。代わりにリモートモデルをクエリしてください(pbir model -q は自動的にそちらにルーティングします)。

3. TOM の読み込み、接続、変更の保存

アセンブリの読み込み
$basePath = "$env:TEMP\tom_nuget\Microsoft.AnalysisServices.retail.amd64\lib\net45"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Core.dll"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Tabular.dll"
Add-Type -Path "$basePath\Microsoft.AnalysisServices.Tabular.Json.dll"
接続
$server = New-Object Microsoft.AnalysisServices.Tabular.Server
$server.Connect("Data Source=localhost:<PORT>")

# PBI Desktop always has exactly one database
$db = $server.Databases[0]
$model = $db.Model
変更の保存

すべての変更を行った後にのみ保存してください。変更後、次のコマンドで永続化します。

$model.SaveChanges()

変更は PBI Desktop にすぐ反映されます。この方法の欠点は、Power BI では Ctrl+Z で元に戻せないことです。

切断

重要: 変更が完了したら、必ず切断してください。孤立したプロセスが発生する可能性があるため、接続したままにしないでください。

$server.Disconnect()
接続プロパティ
Write-Output "Server: $($server.Name)"
Write-Output "Version: $($server.Version)"
Write-Output "Database: $($db.Name)"
Write-Output "Compatibility: $($db.CompatibilityLevel)"

4. モデルの更新

TMSL(Tabular Model Scripting Language)または TOM の RequestRefresh API を使用してデータ更新を実行します。これにより Power Query/M 式が再実行され、VertiPaq エンジンにデータが再読み込みされます。

# Full refresh of a single table via TMSL
$dbName = $server.Databases[0].Name
$tmsl = '{ "refresh": { "type": "full", "objects": [{ "database": "' + $dbName + '", "table": "Sales" }] } }'
$server.Execute($tmsl)

# Or via TOM RequestRefresh API
$model.Tables["Sales"].RequestRefresh([Microsoft.AnalysisServices.Tabular.RefreshType]::Full)
$model.SaveChanges()
更新の種類動作
fullデータを削除し、ソースに再クエリを行って、DAX を再計算します
calculateDAX のみを再計算します(ソースへのクエリは行いません)
automaticパーティションごとに必要な処理をエンジンが判断します
dataOnlyソースに再クエリを行いますが、DAX の再計算は省略します

詳しい例とすべての更新方法については、refresh-model.md を参照してください。

5. DAX を使用したクエリ

ADOMD.NET の読み込み
Add-Type -Path "$env:TEMP\tom_nuget\Microsoft.AnalysisServices.AdomdClient.retail.amd64\lib\net45\Microsoft.AnalysisServices.AdomdClient.dll"
接続を開く
$conn = New-Object Microsoft.AnalysisServices.AdomdClient.AdomdConnection
$conn.ConnectionString = "Data Source=localhost:<PORT>"
$conn.Open()
クエリを実行する

すべてのクエリでは、できる限り SUMMARIZECOLUMNS を使用してください。 必要に応じて、DAX 関数についてはオンラインの dax.guide を確認してください。

重要: ADOMD.NET は、テーブル名を囲む引用符を付けずに、完全修飾列名を返します(例:Brand Class ではなく Brands[Brand Class]。メジャーの射影は [@Alias] として返されます)。短い名前で列にアクセスしないでください($reader["Brand Class"])。これはエラーを出さずに失敗し、空の値を返します。$reader.GetName($i) で列名を確認してから、インデックスでアクセスしてください。

$cmd = $conn.CreateCommand()
$cmd.CommandText = "EVALUATE SUMMARIZECOLUMNS('Table'[Column], ""@MeasureName"", [Measure])"

$reader = $cmd.ExecuteReader()

# Always iterate by index and use GetName() to map columns
while ($reader.Read()) {
    for ($i = 0; $i -lt $reader.FieldCount; $i++) {
        Write-Output "$($reader.GetName($i)): $($reader.GetValue($i))"
    }
    Write-Output "---"
}
$reader.Close()
DAX のルール
  • 列参照は必ず完全修飾してください。 テーブル名を単一引用符で囲みます:'Sales'[Amount]。[Amount] のようにはしません。これはメジャー、計算列、クエリのすべてに適用されます。列を修飾しないと、曖昧さによるエラーが発生します。
  • DAX ではテーブル名を必ず単一引用符で囲みます:'Sales'[Amount]、'D&D 5E Monsters'[CR]。一貫性を保つため、Sales のような単純な名前も 'Sales' として引用符で囲んでください。
  • 例外はメジャー参照のみです。メジャー参照には常に修飾子を付けません:[Total Revenue]
  • DAX の文字列リテラルには二重引用符を使います。PowerShell のヒア文字列内では "" としてエスケープします。
クエリのパターン
# Full table scan
$cmd.CommandText = "EVALUATE 'Sales'"

# Filtered with CALCULATETABLE
$cmd.CommandText = "EVALUATE CALCULATETABLE('Sales', 'Sales'[Region] = ""West"")"

# Aggregation
$cmd.CommandText = "EVALUATE SUMMARIZECOLUMNS('Date'[Year], ""@Total"", SUM('Sales'[Amount]))"

# Scalar via ROW
$cmd.CommandText = "EVALUATE ROW(""Result"", COUNTROWS('Sales'))"

# DMV queries (model metadata via SQL-like syntax)
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_TABLES"
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_MEASURES"
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_COLUMNS"
$cmd.CommandText = "SELECT * FROM `$SYSTEM.TMSCHEMA_RELATIONSHIPS"
接続を閉じる
$conn.Close()

6. セマンティックモデルの変更

すべての変更には TOM 接続が必要です(セクション 3)。変更の各バッチの後に $model.SaveChanges() を呼び出してください。

A. オブジェクト型ごとの CRUD

すべてのオブジェクト型の CRUD の完全な例については、tom-object-types.md を参照してください。

一般的なオブジェクト型と TOM コレクション(すべてを網羅しているわけではありません。名前空間全体については、Microsoft TOM API docs を参照してください):

オブジェクトコレクション作成読み取り更新削除
テーブル$model.TablesNew-Object ...Table$model.Tables["Name"]プロパティを設定.Remove($obj)
列$table.ColumnsNew-Object ...DataColumn$table.Columns["Name"]プロパティを設定.Remove($obj)
メジャー$table.MeasuresNew-Object ...Measure$table.Measures["Name"]プロパティを設定.Remove($obj)
計算列$table.ColumnsNew-Object ...CalculatedColumn型でフィルター.Expression を設定.Remove($obj)
計算テーブル$model.Tablesテーブルと計算パーティションパーティションの型を確認パーティションの式を設定.Remove($obj)
リレーションシップ$model.RelationshipsNew-Object ...SingleColumnRelationshipインデックスまたはフィルタープロパティを設定.Remove($obj)
階層$table.HierarchiesNew-Object ...Hierarchy$table.Hierarchies["Name"]レベルを変更.Remove($obj)
ロール$model.RolesNew-Object ...ModelRole$model.Roles["Name"]権限を設定.Remove($obj)
パースペクティブ$model.PerspectivesNew-Object ...Perspective$model.Perspectives["Name"]メンバーシップを切り替え.Remove($obj)
カルチャ$model.CulturesNew-Object ...Culture$model.Cultures["en-US"]翻訳を設定.Remove($obj)
パーティション$table.PartitionsNew-Object ...Partition$table.Partitions["Name"]ソース/式を設定.Remove($obj)
注釈任意のオブジェクト$obj.Annotations.Add(...)$obj.Annotations["Key"].Value を設定.Remove($obj)
式$model.ExpressionsNew-Object ...NamedExpression$model.Expressions["Name"].Expression を設定.Remove($obj)
データソース$model.DataSourcesNew-Object ...StructuredDataSource$model.DataSources["Name"]接続を設定.Remove($obj)
計算グループ$model.TablesCalculationGroup を含むテーブル型でフィルター項目を追加/削除.Remove($obj)

簡単な例(インライン):

# Add a measure
$m = New-Object Microsoft.AnalysisServices.Tabular.Measure
$m.Name = "Total Revenue"
$m.Expression = "SUM(Sales[Amount])"
$m.FormatString = "`$#,0"
$m.Description = "Sum of all sales amounts"
$model.Tables["Sales"].Measures.Add($m)

# Add a relationship
$rel = New-Object Microsoft.AnalysisServices.Tabular.SingleColumnRelationship
$rel.Name = "Sales_to_Date"
$rel.FromColumn = $model.Tables["Sales"].Columns["DateKey"]
$rel.ToColumn = $model.Tables["Date"].Columns["DateKey"]
$rel.FromCardinality = [Microsoft.AnalysisServices.Tabular.RelationshipEndCardinality]::Many
$rel.ToCardinality = [Microsoft.AnalysisServices.Tabular.RelationshipEndCardinality]::One
$model.Relationships.Add($rel)

# Rename a column
$model.Tables["Sales"].Columns["amt"].Name = "Amount"

# Hide a table
$model.Tables["Bridge"].IsHidden = $true

# Delete a measure
$m = $model.Tables["Sales"].Measures["Old Measure"]
$model.Tables["Sales"].Measures.Remove($m)

# Add a role with RLS
$role = New-Object Microsoft.AnalysisServices.Tabular.ModelRole
$role.Name = "Region Filter"
$role.ModelPermission = [Microsoft.AnalysisServices.Tabular.ModelPermission]::Read
$model.Roles.Add($role)
$tp = New-Object Microsoft.AnalysisServices.Tabular.TablePermission
$tp.Table = $model.Tables["Sales"]
$tp.FilterExpression = "[Region] = USERNAME()"
$role.TablePermissions.Add($tp)

$model.SaveChanges()
B. オブジェクト型、プロパティの調査と値の設定

TOM オブジェクト型の完全な一覧、プロパティや列挙値を調べる PowerShell リフレクションのパターン、プロパティの読み取り/設定例については、references/tom-object-types.md を参照してください。

7. DAX 式の検証

メジャー/列の式を保存する前に、ライブモデルに対してテスト実行して検証してください。これにより、不正なメタデータを保存せずに、構文エラー、列参照の欠落、循環依存を検出できます。

# Validate a DAX expression before adding it as a measure
$testExpr = "SUM('Sales'[Amount]) / COUNTROWS('Sales')"
$cmd = $conn.CreateCommand()
$cmd.CommandText = "EVALUATE ROW(`"@Test`", $testExpr)"
try {
    $reader = $cmd.ExecuteReader()
    $reader.Close()
    Write-Output "VALID"
} catch {
    Write-Output "INVALID: $($_.Exception.Message)"
}

計算テーブルの式の場合は、COUNTROWS でラップします。

$tableExpr = "CALENDAR(DATE(2020,1,1), DATE(2030,12,31))"
$cmd.CommandText = "EVALUATE ROW(`"@Count`", COUNTROWS($tableExpr))"

フィルター式(RLS)の場合は、CALCULATETABLE でテストします。

$filterExpr = "'Sales'[Region] = `"West`""
$cmd.CommandText = "EVALUATE CALCULATETABLE(ROW(`"@OK`", 1), $filterExpr)"

8. トランザクションとロールバック

SaveChanges() は、保留中の変更をすべて単一の暗黙的なトランザクションで適用します。いずれかのオブジェクトの検証に失敗すると、バッチ全体が自動的にロールバックされます。

コミット前に確認やロールバックが必要な複数ステップのワークフローでは、次のようにします。

try {
    # Make changes (not yet persisted)
    $model.Tables["Sales"].Measures["Revenue"].Name = "Total Revenue"
    $model.Tables["Sales"].Measures["Cost"].Name = "Total Cost"

    # Inspect before committing (changes are local to this connection)
    foreach ($m in $model.Tables["Sales"].Measures) {
        Write-Output "  [$($m.Name)]"
    }

    # Commit all changes atomically
    $model.SaveChanges()
    Write-Output "Committed"
} catch {
    # Discard all uncommitted changes
    $model.UndoLocalChanges()
    Write-Output "Rolled back: $($_.Exception.Message)"
}

UndoLocalChanges() は、最後の SaveChanges() 以降に行われたすべての変更を破棄します。これは PBI Desktop におけるロールバックの仕組みです。ローカルの Analysis Services インスタンスには、明示的なトランザクションの開始/コミット API はありません。

9. モデルの検証

保存前の検証

TOM API には、公開された Validate() メソッドがありません。検証は SaveChanges() 中に暗黙的に行われます(失敗すると、バッチ全体がロールバックされます)。保存前に検証するには、オブジェクトを手動で確認してください。

# Check measures have valid expressions (non-empty)
foreach ($m in ($model.Tables | ForEach-Object { $_.Measures }) ) {
    if ([string]::IsNullOrWhiteSpace($m.Expression)) {
        Write-Output "WARNING: Measure [$($m.Name)] in [$($m.Table.Name)] has no expression"
    }
}

# Check relationships reference valid columns
foreach ($rel in $model.Relationships) {
    $sr = [Microsoft.AnalysisServices.Tabular.SingleColumnRelationship]$rel
    if ($sr.FromColumn -eq $null -or $sr.ToColumn -eq $null) {
        Write-Output "WARNING: Relationship [$($sr.Name)] has null column references"
    }
}

# Check for duplicate measure names across tables
$names = @{}
foreach ($m in ($model.Tables | ForEach-Object { $_.Measures })) {
    if ($names.ContainsKey($m.Name)) {
        Write-Output "WARNING: Duplicate measure name [$($m.Name)] in [$($m.Table.Name)] and [$($names[$m.Name])]"
    }
    $names[$m.Name] = $m.Table.Name
}

10. ファイルパスの確認とメタデータファイルの編集

開いているファイルのパスを確認する

TOM では .pbix/.pbip ファイルのパスを直接取得できません。

主な方法 — Desktop ブリッジ: pbir desktop list を実行すると、実行中の各インスタンスで開いているファイルの正確なパスが表示されます(pbir CLI と「external tool access」プレビュー機能が必要です。セクション 2a を参照してください)。この方法を利用できない場合にのみ、以下の方法を使用してください。

代替方法 — User.zip の FileHistory(Store 版と非 Store 版の両方で使用可能):

# Read the most recently opened file from PBI Desktop's settings
$userZip = "$env:USERPROFILE\Microsoft\Power BI Desktop Store App\User.zip"
# For non-Store installs: "$env:LOCALAPPDATA\Microsoft\Power BI Desktop\User.zip"

Add-Type -Assembly System.IO.Compression.FileSystem
$z = [System.IO.Compression.ZipFile]::OpenRead($userZip)
$entry = $z.Entries | Where-Object { $_.Name -eq 'Settings.xml' }
$reader = New-Object System.IO.StreamReader($entry.Open())
$content = $reader.ReadToEnd()
$reader.Close()
$z.Dispose()

# Extract FileHistory entries (ordered by lastAccessedDate, most recent first)
$history = ($content -split '(?=<Entry)' | Where-Object { $_ -match 'FileHistory' })[0]
$json = [regex]::Match($history, 'Value="s\[(.*?)\]"').Groups[1].Value -replace '&quot;', '"'
$files = $json | ConvertFrom-Json
$files | Select-Object filePath, lastAccessedDate | Format-Table -AutoSize

最初のエントリが、最後に開いたファイルです。Mac 上のファイル(Parallels 経由)は、\\Mac\Home\... パスとして表示されます。

制限事項: この方法は完全ではありません。現在開いているファイルではなく、最近使用したファイルの履歴を読み取ります。PBI Desktop のインスタンスが複数開いている場合や、履歴で最後にアクセスしたファイルが現在開いているファイルと異なる場合、結果が正しくない可能性があります。少しでも不明点がある場合は、ユーザーに確認してください。

代替方法 — ウィンドウタイトル(非 Store 版 PBI Desktop のみ):

Get-Process PBIDesktop -ErrorAction SilentlyContinue | Select-Object Id, MainWindowTitle

注: Store 版 PBI Desktop(Microsoft Store/WindowsApps から入手したもの)では、ウィンドウタイトルにファイルパスが表示されません。代わりに、上記の User.zip の方法を使用してください。

代替方法 — msmdsrv コマンドライン(ファイルパスではなくワークスペースのパスを取得):

# Useful for finding the port; does NOT reveal the source file path
(Get-WmiObject Win32_Process -Filter "Name='msmdsrv.exe'").CommandLine
PBIP メタデータファイル(接続、レポート、モデル)の編集

.pbip プロジェクトでは、メタデータファイルはディスク上に人が読める JSON/TMDL 形式で保存されているため、直接読み取って変更できます。

一般的な対象:

ファイル用途スキル
<Name>.Report/definition.pbirレポートとモデルの接続(byPath または byConnection)pbip
<Name>.Report/definition/report.jsonレポートレベルの設定、テーマ、フィルターpbir-format
<Name>.SemanticModel/definition/*.tmdlモデルのメタデータ(テーブル、メジャー、リレーションシップ)tmdl
<Name>.SemanticModel/definition/expressions.tmdlM/Power Query の共有式とパラメーターtmdl

これらのファイルの構文、構造、編集パターンについては、pbip plugin から該当するスキルを読み込んでください。

  • pbip -- プロジェクト構造、ファイルの種類、.pbir 接続、フォーク
  • pbir-format -- report.json、visual.json、テーマ、フィルター、PBIR JSON スキーマ
  • tmdl -- TMDL 構文、メジャー、列、ロール、リレーションシップ
外部ファイルの編集内容を再読み込みする

Power BI Desktop は、外部ファイルの変更を監視しません。レポートを開いたままディスク上で行った編集は、黙って無視されるか、次回の Desktop の保存時に上書きされます。変更を適用するには、以下の優先順で行ってください。

  1. TOM の変更($model.SaveChanges())は、実行中のインスタンスに直ちに反映されます。モデルのメタデータには、この方法を優先してください。
  2. PBIR レポート定義の編集(ページ、ビジュアル)は、pbir desktop refresh "Report.Report" を実行すると、開いているキャンバスにホットリロードされます(PBIP/PBIR のみ対象で、.pbix は対象外です。プレビュー機能が必要です)。StaticResources 内のテーマ JSON の編集はホットリロードされないため、いったん閉じてから開き直してください。インスタンスに未保存の変更がある場合、Desktop は先に保存するため、ディスク上の編集が上書きされる可能性があります。
  3. その他すべて(ディスク上の TMDL 編集、テーマファイル、.pbix):Power BI Desktop を閉じてから編集し、開き直してください。

レポート(PBIR)ファイルについては、Desktop ブリッジを使うと、開き直すことなくディスク上の編集内容を開いているキャンバスに再読み込みできます(file.reload/v1 パイプメソッドを使用し、代替手段として powerbi-desktop npm CLI も使用できます)。セクション 13 を参照してください。モデル(TMDL)のディスク上の編集を反映するには、引き続き閉じてから開き直す必要があります。または、上記のようにライブ TOM の SaveChanges() を使用してください。

Microsoft Documentation
トピックURL
TOM API Referencelearn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.tabular
TOM Overviewlearn.microsoft.com/en-us/analysis-services/tom/introduction-to-the-tabular-object-model-tom-in-analysis-services-amo
ADOMD.NET Referencelearn.microsoft.com/en-us/dotnet/api/microsoft.analysisservices.adomdclient
Client Librarieslearn.microsoft.com/en-us/analysis-services/client-libraries
DMV Referencelearn.microsoft.com/en-us/analysis-services/instances/use-dynamic-management-views-dmvs-to-monitor-analysis-services
DAX Referencedax.guide
Compatibility Levelslearn.microsoft.com/en-us/analysis-services/tabular-models/compatibility-level-for-tabular-models-in-analysis-services

最新のTOM/ADOMD.NETリファレンスドキュメントを取得するには、https://learn.microsoft.com を検索し、該当するページを取得してください。ユーザーのリクエストに基づいて検索し、十分なコンテキストを得てから作業を進められるよう、必要に応じて複数回検索してください。

11. EVALUATEANDLOG による DAX のデバッグ

EVALUATEANDLOG(<Value>, [Label], [MaxRows]) は任意の DAX 式をラップし、そのまま返すとともに、トレースイベントを介して中間結果を JSON として出力します。PBI Desktop のみで動作します。

プログラムによるキャプチャでは、TOM Trace API を使用することで、外部ツール(DAX Debug Output、SQL Server Profiler、DAX Studio)が不要になります。DAXEvaluationLog トレースイベント(enum ID 135)をサブスクライブし、Register-ObjectEvent を使って同期化された ArrayList にイベントをキャプチャし、$Event.SourceEventArgs.TextData から JSON を解析します。

重要な実装上の詳細: Register-ObjectEvent -Action は別の PowerShell runspace で実行されます。アクションブロック内の $global: 変数はスコープを共有しません。-MessageData を使って同期化されたコレクションを渡してください。

$evalEvents = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
$job = Register-ObjectEvent -InputObject $trace -EventName "OnEvent" -MessageData $evalEvents -Action {
    $Event.MessageData.Add($Event.SourceEventArgs) | Out-Null
}

トレースの配信は非同期です: DAXEvaluationLog イベントは通常、クエリの返却から 2~3.5 秒後に届くため、短い固定時間のスリープでは見逃します。結果を読み取る前に、キャプチャしたイベント数をポーリングしてください(500 ミリ秒間隔で最大約 10 秒)。ウォームキャッシュでの実行でもイベントは出力されます。イベントを発生させるためにキャッシュのクリアに頼らないでください。VertiPaq キャッシュは、コールドキャッシュでのタイミング計測が必要な場合にのみクリアしてください。

$server.Execute('{ "clearCache": { "object": { "database": "' + $db.Name + '" } } }') | Out-Null

よくあるデバッグパターン:

パターン方法
メジャーチェーンの分解各中間ステップをラップします:EVALUATEANDLOG([Step1], "Label1")
フィルターコンテキストの調査ALL/REMOVEFILTERS の有無による CALCULATE をトレースします
BLANK とゼロの判定比較前の値をトレースします。DAX では BLANK = 0 は TRUE です
変数コンテキストの落とし穴VAR の値と CALCULATE の結果を一緒にトレースし、VAR が再評価されないことを確認します
総計の診断行と合計の粒度で分子と分母をトレースします
テーブル式の調査CALCULATETABLE の結果をラップします。トレースには、集計に実際に使われる行が表示されます

セットアップ全体、JSON ペイロードの構造、イベントのバッチ処理動作、およびすべてのデバッグパターンについては、evaluateandlog-debugging.md を参照してください。

12. パフォーマンスプロファイリング

DAX Studio のサーバータイミングに相当する処理をプログラムで行います。QueryEnd、VertiPaqSEQueryEnd、VertiPaqSEQueryCacheMatch のトレースイベントをサブスクライブして、クエリごとのフォーミュラエンジン(FE)とストレージエンジン(SE)の時間を測定します。

主な計算式: FE 時間 = 合計時間 - SE スキャン時間の合計

重要: VertiPaqSEQueryCacheMatch は Duration 列や CpuTime 列をサポートしていません。追加すると $trace.Update() が例外をスローします。キャッシュ一致イベントには TextData と EventClass のみを追加してください。

ワークフロー:

  1. パフォーマンスイベントを含むトレースを作成します(列の互換性についてはリファレンスを参照してください)
  2. キャッシュをクリアして(TMSL の clearCache)、コールド時のタイミングを測定します
  3. ADOMD.NET 経由で DAX を実行します
  4. トレースイベントを解析します。合計時間には QueryEnd、スキャンごとの SE 時間には VertiPaqSEQueryEnd を使用します
  5. コールドキャッシュとウォームキャッシュを比較して、キャッシュの効果を測定します

統計的サンプリング: 単一の測定値にはばらつきがあります。変更の前後で必ず 6~12 回サンプリングし、平均値ではなく中央値を比較してください。範囲が大きく重なっている場合、その差はノイズである可能性が高いです。最初のコールドキャッシュでの実行はウォームアップとして破棄してください。Measure-QueryMedian ヘルパーについてはリファレンスを参照してください。

ビジュアルクエリのプロファイリング: PBIR の visual.json 定義から SUMMARIZECOLUMNS クエリを構築します。列の射影はグループ化列に、メジャーの射影はメジャー参照になり、Aggregation.Function は SUM(0)、MIN(1)、MAX(2)、COUNT(3)、AVERAGE(4)にマッピングされます。

設定全般、タイミングの解釈、サンプリングパターン、PBIR から DAX への変換については、performance-profiling.md を参照してください。

13. レポートキャンバスの操作(Desktop Bridge)

上記の TOM 接続で操作できるのはモデル(テーブル、メジャー、リレーションシップ、ロール、更新)です。レポートキャンバス(ページとビジュアル)にはアクセスできません。Power BI Desktop はそのための別個のローカル API、つまり Windows の名前付きパイプ \\.\pipe\pbi-desktop-bridge-<PID> 上で動作するプロセスごとの JSON-RPC サーバーである Desktop Bridge を公開しています。両方を組み合わせることで、モデルを変更した直後にレポートが再描画されることを確認できます。

pbir CLI がインストールされている場合は、同じパイプをラップするため、パイプを直接操作するよりもこちらを優先してください。

pbir desktop list                                                             # PID + open file per instance
pbir model "Report.Report" -q 'EVALUATE ROW("Check", [New Measure])'           # engine-level check
pbir desktop refresh "Report.Report"                                          # reload on-disk PBIR into the canvas
pbir desktop screenshot "Report.Report/Page Name.Page" -o verify.png          # inspect rendering

PowerShell の引数をシングルクォートで囲むことで、シェル固有の解析停止トークンを使わずに、埋め込まれた DAX の引用符を保持できます。

pbir がない場合は、このスキルが TOM/ADOMD を操作するのと同じ方法で、PowerShell からパイプを直接操作します。これには Desktop Bridge のプレビュー設定を有効にする必要があります(File > Options and settings > Options > Preview features で設定してから再起動してください)。パイプのディレクトリを列挙して PID を自動検出し、JSON-RPC 経由で操作します。application.state.get/v1 は開いているファイルのパス(currentFilePath。これを使って Bridge がディスク上の PBIP を特定できます)を返し、file.reload/v1 はディスク上の PBIR をキャンバスに再読み込みし、report.snapshot.capture/v1 はページの PNG を返します。

モデルとレポートを組み合わせた操作手順:TOM でモデルを編集して $model.SaveChanges() を実行します(変更はすぐに反映されます)。次にレポートを reload し、スクリーンショットを撮って、ビジュアルに変更が反映されたことを確認します(メジャー名の変更、新しい書式文字列、リレーションシップの修復など)。ディスク上のレポート(PBIR)の変更は reload で反映されます。一方、ディスク上のモデル(TMDL)の変更と StaticResources 内のテーマファイルを反映するには、引き続き再度開く必要があるため、モデルの変更にはライブ TOM を優先してください。Bridge は Windows アプリを操作するため、macOS では Parallels VM 内で実行してください(parallels-macos.md を参照)。

コマンド一式、PID の選択、JSON-RPC メソッド(bridge.manifest、application.state.get/v1、file.reload/v1、report.snapshot.capture/v1)、および Analysis Services のローカル API との連携について詳しくは、desktop-bridge.md を参照してください。ビジュアル、ページ、書式設定、フィルター、ブックマークを変更する場合は、pbir-cli スキル(reports プラグイン)を使用してください。ここで扱う Desktop Bridge は再読み込みとスクリーンショットの取得のみを行い、レポートの編集は行いません。

代替方法(パイプを直接操作した際にフレーミング、エンコーディング、またはパラメーターの形式が変更されたビルドで問題が発生した場合のみ):pbir desktop コマンド(reports プラグインの pbir-cli スキル)を使用します。これらのコマンドは同じメソッドをラップしています。desktop-bridge.md を参照してください。

参考資料

スキルの参考資料:

  • TOM オブジェクト型の CRUD - UDF、Direct Lake、KPI メモなど、すべてのオブジェクト型を対象とした CRUD の完全な例
  • 注釈と拡張プロパティ - 標準的な PBI 注釈、Tabular Editor のテーブルグループ、自動日付/時刻、フィールドパラメーター、クエリグループ、カスタム注釈
  • カレンダー列グループ - TOM によるグレゴリオ暦、会計暦、ISO 週ベースのカレンダー定義。時間単位、プライマリ列/関連列
  • DAX 式の配置場所 - モデル内で DAX が使われる場所:メジャー、計算列/テーブル、計算項目、書式文字列、詳細行、RLS、UDF
  • DAX の落とし穴 - 非推奨/非推奨の関数、エージェントが SQL/Python/M から誤って想像する存在しない関数、よくある構文ミス、BLANK と NULL の違い
  • EVALUATEANDLOG のデバッグ - TOM Trace API によるプログラムでの DAX デバッグ。中間結果の取得、キャッシュのクリア、一般的な DAX の問題に対する 6 つのデバッグパターン
  • パフォーマンスプロファイリング - Trace API による DAX Server Timings。FE/SE の時間内訳、コールド/ウォームキャッシュの比較、PBIR ビジュアルから DAX への変換、トレースイベント列の互換性
  • クエリリスナー - DMV ポーリングによる、実行中のビジュアル DAX クエリの取得。クエリ構造、タイミング、フィルターパターンの解釈
  • モデルのエクスポート - Tabular Editor CLI、fab CLI、または TOM シリアライザーによる BIM/TMDL へのエクスポート
  • TMDL/BIM ファイルの読み込み - ローカルの TMDL フォルダーまたは BIM ファイルを TOM にオフラインで読み込み、調査、変更、再シリアライズし、fab CLI でデプロイ
  • VertiPaq 統計 - 列カーディナリティ、ディクショナリ/データサイズ、テーブル別メモリ使用量、DMV によるサーバータイミング
  • モデルの更新 - すべての更新方法(TMSL、TOM RequestRefresh、ADOMD.NET)
  • macOS + Parallels ガイド - PBI Desktop が Parallels VM で実行されている場合に、macOS から接続する方法
  • DAX ライブラリパッケージ - daxlib.org から再利用可能な DAX UDF パッケージをインストール。DaxLib.SVG、PowerofBI.IBCS、パッケージ構造、注釈
  • デスクトップブリッジ(レポートキャンバス) - 生の名前付きパイプ JSON-RPC API(PowerShell、または pbir desktop コマンド)を介して、開いているレポートキャンバスを再読み込みしてスクリーンショットを撮影。モデル(TOM)の編集とレポートの検証を組み合わせる

スキルのルートにある CLI ツール:

  • daxlib -- daxlib.org から DAX ライブラリパッケージを参照、ダウンロード、インストールする CLI。スクリプトは daxlib.sh(bash + jq が必要)です。モデル操作(追加/更新/削除)は dotnet run を介して scripts/daxlib-tom/ を呼び出します(.NET 8 SDK が必要)。macOS では、モデル操作は自動的に Parallels 経由で実行されます。コマンドの全リファレンスについては、daxlib.md を参照してください。

エージェント:

  • query-listener -- 実行中のビジュアル DAX クエリをリアルタイムで取得するようディスパッチします。DISCOVER_SESSIONS をポーリングし、クエリテキストとタイミングを報告します

scripts/ 内のサンプルスクリプト:

  • connect-and-enumerate.ps1 - PBI Desktop に接続し、すべてのテーブル、列、メジャー、リレーションシップを一覧表示します
  • explore-model.ps1 - 階層的にメタデータを列挙します(テーブル、列、メジャー、階層、パーティション、リレーションシップ、ロール、パースペクティブ、カルチャ、式、データソース)
  • query-dax.ps1 - ADOMD.NET 経由で DAX クエリを実行し、整形された結果を出力します
  • refresh-table.ps1 - 設定可能な更新タイプを使用し、TMSL 経由でテーブルまたはモデル全体を更新します
  • modify-tom-objects.ps1 - テーブルの作成、メジャーの名前変更、フォルダーや書式の設定、列の非表示、リレーションシップの作成(元に戻す機能付き)を行います
  • create-field-parameter.ps1 - メジャーのリストから、必要なメタデータをすべて含むフィールドパラメーター テーブルを作成します
  • debug-dax.ps1 - EVALUATEANDLOG のトレース取得とパフォーマンス計測で DAX をデバッグします。ポートを自動検出し、モデルのメジャーを列挙し、Invoke-DebugQuery ヘルパーを提供します
  • load-tmdl.ps1 - ローカルの TMDL フォルダーまたは BIM ファイルを TOM にオフラインで読み込みます(実行中のエンジンは不要)。モデルを列挙し、必要に応じてメジャーを追加して保存できます
  • connect-from-mac.sh - prlctl exec 経由で Parallels VM 内の PowerShell スクリプトを実行する macOS 用ラッパーです

外部の参考資料:

コメント