CI/CD Integration
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](TBD: ADD LINK TO EULA)
The Tabular Editor CLI is designed for unattended execution in continuous integration and delivery pipelines. A single binary, structured output, non-interactive mode, native CI annotations for GitHub Actions and Azure DevOps, and VSTEST-compatible test results make it a natural replacement for ad-hoc TE2 invocations.
Warning
During preview, we recommend against using the CLI in production pipelines. Commands, flags, and outputs may change before GA. Build and evaluate in non-production pipelines first; share feedback so the GA shape matches your needs.
What makes the CLI CI-friendly
- Single self-contained binary. No runtime install, no
TabularEditor.exe, nostart /wait. --non-interactiveglobal flag. Disables every prompt; fails fast with actionable errors.--forceon mutating commands (te deploy,te refresh) skips confirmation prompts.--ci vsts/--ci github. Emit native pipeline annotations to stderr.--trx <file>. Produce VSTEST results consumable by Azure DevOps test publishing.- Structured errors.
--output jsonemits{"error": "...", "hint": "..."}to stderr so pipeline steps can fail with a useful message.
GitHub Actions
A complete deploy + test workflow. The example assumes a service principal stored in repository secrets (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID).
name: Deploy semantic model
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
steps:
- uses: actions/checkout@v4
- name: Download Tabular Editor CLI
run: |
# TBD: replace with the official download URL once confirmed.
curl -L -o te.zip "<download-url-linux-x64>"
unzip te.zip -d $HOME/.local/bin
chmod +x $HOME/.local/bin/te
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Validate
run: te validate ./model --ci github --trx validate.trx
- name: Best Practice Analyzer (gate)
run: te bpa run ./model --fail-on error --ci github --trx bpa.trx
- name: Deploy
run: |
te deploy ./model \
-s "${{ vars.WORKSPACE }}" \
-d "${{ vars.MODEL }}" \
--auth env \
--non-interactive \
--force \
--ci github
- name: Regression tests
run: |
te test run \
-s "${{ vars.WORKSPACE }}" \
-d "${{ vars.MODEL }}" \
--auth env --non-interactive \
--ci github --trx tests.trx
- name: Publish test results
if: always()
uses: actions/upload-artifact@v4
with:
name: trx-results
path: '*.trx'
Azure DevOps Pipelines
Equivalent YAML for Azure DevOps. --ci vsts emits ##vso[...] commands that the pipeline interprets as errors, warnings, and task-status updates.
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
- group: 'te-cli-secrets' # Contains AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID
steps:
- checkout: self
- task: PowerShell@2
displayName: 'Install Tabular Editor CLI'
inputs:
targetType: 'inline'
script: |
# TBD: replace with the official download URL once confirmed.
Invoke-WebRequest -Uri "<download-url-win-x64>" -OutFile te.zip
Expand-Archive te.zip -DestinationPath $(Agent.ToolsDirectory)\te
Write-Host "##vso[task.prependpath]$(Agent.ToolsDirectory)\te"
- script: te validate ./model --ci vsts --trx validate.trx
displayName: 'Validate'
- script: te bpa run ./model --fail-on error --ci vsts --trx bpa.trx
displayName: 'BPA gate'
- script: |
te deploy ./model ^
-s "$(WORKSPACE)" -d "$(MODEL)" ^
--auth env --non-interactive --force --ci vsts
displayName: 'Deploy'
env:
AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
AZURE_TENANT_ID: $(AZURE_TENANT_ID)
- script: te test run -s "$(WORKSPACE)" -d "$(MODEL)" --auth env --non-interactive --ci vsts --trx tests.trx
displayName: 'Regression tests'
env:
AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
AZURE_TENANT_ID: $(AZURE_TENANT_ID)
- task: PublishTestResults@2
condition: always()
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '*.trx'
BPA gate patterns
te deploy and te save run the Best Practice Analyzer as a pre-flight gate by default. Three behaviors are worth picking up front:
- Enforce — the default. Pipeline fails if BPA finds violations at severity ≥ error. Pair with
--fail-on warningon a standalonete bpa runstep if you want warnings to fail too. - Auto-fix —
--fix-bpaappliesfixExpressions in memory for the deployed artifact. Source files are not modified. Useful when the source of truth lives in the model and you want deploys to normalize style without developer intervention. - Bypass —
--skip-bpadisables the gate for a single command. Useful for emergency hotfixes; not recommended as a default.
# Treat warnings as failures in PR validation
te bpa run ./model --fail-on warning --ci github --trx bpa.trx
# Auto-fix during deploy (source unchanged)
te deploy ./model -s my-ws -d my-model --fix-bpa --force --ci github
# Emergency bypass
te deploy ./model -s my-ws -d my-model --skip-bpa --force --ci github
See Custom Configuration for controlling the BPA gate globally via bpaOnDeploy / bpaOnSave config keys.
Refresh patterns
Refresh in pipelines is typically a follow-up step after deploy. Use --non-interactive and pick a deterministic --type:
# Full refresh of the whole model after deploy
te refresh -s my-ws -d my-model --type full --non-interactive --ci github
# Refresh a single fact table (e.g., daily incremental pipeline)
te refresh -s my-ws -d my-model --table Sales --type full --non-interactive
# Recalculate only (useful after calculation-group changes)
te refresh -s my-ws -d my-model --type calculate --non-interactive
For incremental refresh workflows, combine --apply-refresh-policy, --effective-date <yyyy-MM-dd>, and --partition <Table.Partition> flags. See Command Reference for details.
Artifact patterns
Emit TMSL or XMLA as an artifact without deploying, so DBAs or a later job can review or apply it:
# Produce the XMLA/TMSL script that would deploy — do not deploy
te deploy ./model -s my-ws -d my-model --xmla deploy.tmsl --force
# Produce the TMSL refresh command — do not execute
te refresh -s my-ws -d my-model --type full --dry-run > refresh.tmsl
Commit these artifacts to git, upload them to the pipeline's artifact storage, or pass them between jobs. They're plain text and diff cleanly in pull requests.
Secret handling
| Approach | When to use | Notes |
|---|---|---|
Service principal via env vars (AZURE_CLIENT_ID / AZURE_CLIENT_SECRET / AZURE_TENANT_ID, --auth env) |
General CI/CD | Map pipeline secrets to environment variables at the step or job level. Never pass secrets in command arguments. |
Service principal via te auth login once per job (echo $SECRET \| te auth login -u $ID -p - -t $TENANT) |
Multi-step jobs | The login is cached, so subsequent te commands acquire tokens silently — no need to set AZURE_CLIENT_* for every step or re-pass -u/-p/-t. Pipe the secret via stdin rather than interpolating it. |
Managed identity (--auth managed-identity) |
Azure VMs, Container Apps, Azure Functions | No secrets to manage. Preferred in Azure-hosted environments. |
Certificate (--certificate <path>) |
Enterprise scenarios with cert rotation | Mount the certificate as a secure file step; pass --certificate-password via env. |
Warning
Do not echo secrets or the output of te auth status to pipeline logs. The CLI writes warnings to stderr when secrets are passed on the command line — respect those warnings in CI.
Related pages
- Authentication and Connections — authentication methods in detail.
- Custom Configuration — configuration and profile overrides.
- Automation and Scripting — general scripting patterns.
- Migrating from the TE2 Command Line — migrating an existing TE2-based pipeline.