Automation and Scripting
Important
The Tabular Editor CLI is in Limited Public Preview. It is offered for evaluation with a Tabular Editor account; no license is required during preview. Commands, flags, and outputs may change before general availability. The preview build stops functioning after 2026-09-30. We recommend against using the CLI in production CI/CD pipelines during preview. Please refer to our license agreement.
The Tabular Editor CLI is composable; every command supports structured output, disables interactive prompts on demand, and returns predictable exit codes. The same primitives work equally well for shell pipelines, Python scripts, PowerShell automation, and agent-driven workflows.
Structured output
Use --output-format to switch any command between text (human-readable) and machine-readable formats:
| Format | Use for | Notes |
|---|---|---|
text (default) |
Human-readable use | Plain text on stdout regardless of whether the stream is a TTY or piped. |
json |
Machine-readable use | Always valid JSON to stdout. Use --error-format json if you also want machine-readable errors on stderr. |
csv |
Tabular results (query, bpa run, bpa rules, vertipaq, validate, test, refresh, profile list, session list, find, replace, get, ls) |
RFC 4180 escaping. |
tmsl (alias bim) |
Whole-object TMSL/BIM serialization | Accepted by te get and te ls. |
tmdl |
Whole-object TMDL serialization | Accepted by te get only (single object). |
te ls --output-format json
te query -q "EVALUATE VALUES('Date'[Year])" --output-format csv
te bpa run --output-format json
Note
--output-format and --error-format are independent. Setting --output-format json does not switch stderr to JSON; pass --error-format json for that. There is no automatic format switching when stdout is redirected - the default is always text unless you ask otherwise.
Non-interactive mode
Add --non-interactive to any command to disable confirmation prompts, credential picklists, and guided wizards. If the command needs input it cannot resolve from flags, environment, or config, it exits non-zero with an actionable error instead of hanging.
te deploy ./model --non-interactive --force --ci github
Exit codes
Every te command exits with a predictable status code so callers can branch on success or failure without parsing stdout.
| Exit | Meaning |
|---|---|
0 |
Success. |
1 |
Generic failure - invalid arguments, command failed, validation errors, auth failure, BPA gate failed at severity ≥ error. |
2 |
Used by te diff to indicate models differ (distinct from 0 identical and non-zero errors). |
Combine exit codes with --ci <vsts\|github> annotations and --trx <file> to surface rich failure information in CI - see CI/CD Integration.
Errors on stderr
Errors, warnings, and the preview banner are written to stderr; structured data is written to stdout. This means you can pipe JSON safely without it being contaminated by progress indicators or diagnostic messages:
te ls --output-format json | jq '.[] | .name'
te vertipaq --output-format json > stats.json
Python
Python is a natural host for orchestrating CLI calls from data pipelines, notebooks, or test harnesses. Invoke te with subprocess.run, request JSON, and parse stdout:
import json
import subprocess
def query(server: str, database: str, dax: str) -> list[dict]:
result = subprocess.run(
["te", "query",
"-s", server,
"-d", database,
"-q", dax,
"--output-format", "json",
"--non-interactive"],
check=True,
capture_output=True,
text=True,
)
return json.loads(result.stdout)
rows = query("Finance", "Revenue Model", "EVALUATE TOPN(10, 'Sales')")
for row in rows:
print(row)
To capture structured errors from stderr:
import json
import subprocess
result = subprocess.run(
["te", "deploy", "./model",
"-s", "Finance", "-d", "Revenue",
"--output-format", "json", "--non-interactive", "--force"],
capture_output=True, text=True,
)
if result.returncode != 0:
try:
err = json.loads(result.stderr.strip().splitlines()[-1])
print("Deploy failed:", err.get("error"), "- hint:", err.get("hint"))
except json.JSONDecodeError:
print("Deploy failed:\n", result.stderr)
PowerShell
PowerShell handles JSON natively. te is a regular console binary that works directly in PowerShell pipelines (see Migrating from the TE2 Command Line if you're porting from the older TabularEditor.exe CLI):
$rows = te query -s Finance -d Revenue -q "EVALUATE TOPN(10, 'Sales')" --output-format json --non-interactive
| ConvertFrom-Json
$rows | Format-Table
# Check exit code after the pipeline
if ($LASTEXITCODE -ne 0) {
Write-Error "Query failed with exit $LASTEXITCODE"
exit $LASTEXITCODE
}
Read secrets from the environment rather than passing them as plaintext:
$env:AZURE_CLIENT_ID = "your-app-id"
$env:AZURE_CLIENT_SECRET = "your-client-secret"
$env:AZURE_TENANT_ID = "your-tenant-id"
te deploy ./model `
-s my-workspace -d my-model `
--auth env --non-interactive --force --ci vsts
Bash
Compose commands with pipes and jq. The CLI's text output is colorized for humans, but switching to --output-format json gives you a clean shape to work with:
# Count measures per table
te ls --type measure --output-format json \
| jq -r '.[] | .table' \
| sort | uniq -c | sort -rn
# Fail the shell script if BPA finds any errors
te bpa run --fail-on error --output-format json > bpa.json \
|| { echo "BPA gate failed"; jq '.violations' bpa.json; exit 1; }
Composability example
Generating a refresh TMSL script and version-controlling it is three commands:
te connect MyWorkspace MyModel
te refresh --type full --dry-run > refresh.tmsl
cat refresh.tmsl
The resulting TMSL can be reviewed in a pull request, committed, executed by the CLI (te refresh --type full), handed to a DBA, or applied by any XMLA-compatible tool. The CLI becomes a building block rather than a black box.
Useful patterns
A handful of small idioms that come up often when composing te commands in scripts or pipelines:
- Idempotent creates and removes.
te add Sales/Marker -t Measure -i "0" --if-not-exists --saveandte rm Sales/OldMeasure --if-exists --saveboth exit0whether or not the object existed - safe to re-run in CI. - Dry-run diffs.
te replaceis dry-run by default; add--saveonly when you're satisfied with the preview. - Emit TMSL for review.
te deploy ./model --xmla deploy.tmslproduces the deployment script without touching the server - useful for DBA review or manual apply. - Path-only output.
te ls --paths-onlyandte find --paths-onlyemit one object path per line, ideal for piping toxargs,te get, orte set. The model-level containers (te ls Measures,te ls Columns) compose well with this for whole-model sweeps. - Benchmarking queries.
te query --trace --cold --runs 5runs a DAX query with cold cache, five iterations, and captures FE/SE trace events. - Step timings in CI logs. Long-running commands (
te deploy,te refresh,te script,te validate) include adurationMsfield in JSON output - useful for surfacing per-step timings in pipeline summaries.
Related pages
- CI/CD Integration - pipeline-specific patterns and YAML examples.
- Command Reference - full command reference.
- Interactive Mode - when interactive mode fits better than scripting.