Skip to content

Report (SSRS) Deployment

How eSHARS report definitions (.rdl), shared datasets, and data sources are deployed to the SSRS report server during a release, and how to recover a failed publish.

This is the release-time report deploy path. For provisioning the report-server VM itself see Report Server (infrastructure). For local report authoring in Visual Studio see the dev-site Report Configuration guide.


Go-forward — GitHub Actions CD Deploy (HISD repo, PR #6478)

Source of truth: .github/workflows/cd-deploy.yml and .github/ops-scripts/deploy-reports.ps1 in the HISD repo, branch ci/tier-aware-deploys. Reports are not a separate workflow — they are the deploy-reports job within the unified tier-aware CD Deploy pipeline (alongside webui, eublob, jobscheduler, dbupgrader, tmhp, reportviewer).

How a deploy is triggered

TriggerResult
CI Build succeeds on developAuto-deploys to dev (nonprod)
Manual workflow_dispatchPromote to any slot: dev/qa/regression (nonprod tier) or uat/training/prod (prod tier)

CD Deploy consumes the artifacts from a CI Build run (latest successful on develop, or a specified ci_run_id). The resolve job derives the tier from the slot and resolves all tier-specific infra names centrally (rebuilt CAF names), then reads the run’s build-manifest.json so each component deploys only if it changed in that build (changed_reports). If no manifest is present (older runs), it deploys all components.

How reports are published

The deploy-reports job runs on the self-hosted runner on the per-tier ops VM, then executes deploy-reports.ps1 on the SSRS VM itself via a managed Run Command (ops-vm-run action) — so publishing happens locally against http://localhost/ReportServer, never cross-VM HTTP. Flow:

  1. Stage to blob — the reports artifact is uploaded to the tier staging account (saeshars{tier}cus) → deploy-artifacts container → reports/<ci_run_id>/, guarded by a .staged sentinel so re-runs skip re-upload.
  2. Run on the SSRS VM — the VM pulls the staged blobs with its own managed identity (Connect-AzAccount -Identity) and runs deploy-reports.ps1 with ReportingServicesTools.

What the script does (this is the important behaviour for HM-6163):

  • Publishes the project manifest, not a glob. The file list comes from Eshars.Report.rptproj (ItemGroup/Report + ItemGroup/DataSet), so the * - Backup.rdl working files are never published. (PR #6478: 69 reports + 1 dataset.)
  • Catalog layout (per-environment, from the REPORT_FOLDER Environment variable):
    • Reports → REPORT_FOLDER
    • Datasets → <REPORT_FOLDER>/../Datasets (sibling of the reports folder)
    • Data sources → REPORT_DATASOURCE_FOLDER (documented default <REPORT_FOLDER>/Data Sources)
    • Missing report/dataset folders are created (New-RsFolder).
  • Data sources are CI-upserted from Key VaultHisdDbDataSource, HisdDbReportDataSource, and HistReportDbDataSource. On every deploy (whenever KeyVaultName is set, which the job always sets), the script New-RsDataSource -Overwrites all three with CredentialRetrieval Store and the SQL password pulled from the env’s Key Vault (SqlAdminPassword). CI then re-points each report’s data source reference at them (Set-RsDataSourceReference). They are not admin-managed — CI owns them. So the prerequisite per env is a correct SqlAdminPassword in the KV plus correct DB_SERVER / SQL_USER / CORE_DB_NAME vars, not hand-created data sources.
  • Per report: Write-RsCatalogItem -Overwrite, then re-wire every DataSource reference to <DataSourceFolder>/<refName>.

Resolved infra (rebuilt CAF names)

resolve maps slot → tier → infra. The report/SSRS VM (component ssrs):

TierSlotsSSRS VMStaging account
nonproddev, qa, regressionvm-eshars-ssrs-nonprod-cus (10.22.1.6)saesharsnonprodcus
produat, training, prodvm-eshars-ssrs-prod-cus (pending provision)saesharsprodcus

All tier VMs live in rg-eshars-compute-{tier}-cus. Per-env core DB is per-environment SQL (sql-eshars-{env}-cus).

Per-environment configuration contract

GitHub Environment variables/secrets per slot (from PR #6478):

NameKindPurpose
REPORT_FOLDERvarSSRS catalog folder for this env’s reports
REPORT_DATASOURCE_FOLDERvar (recommended)Data-source catalog folder. Defaults to <REPORT_FOLDER>/../Data Sources; set explicitly to be unambiguous.
SECURE_SETTINGS_KEYVAULTvarPer-env Key Vault (kv-eshars-{env}); supplies the SQL password
DB_SERVER, SQL_USER, CORE_DB_NAMEvarData-source connection target
SqlAdminPasswordKV secretRead from SECURE_SETTINGS_KEYVAULT at deploy time

Before running deploys for real

Outstanding items from PR #6478 (must be done before the GitHub path can deploy a given env):

  • Confirm prod-tier resource names still marked # TODO confirm in resolve (SSRS VM / report server URL are *-001 placeholders).
  • Create GitHub Environments for uat/training/prod with REPORT_FOLDER set and required-reviewer protection on prod.
  • Set SECURE_SETTINGS_KEYVAULT on every environment, and ensure each env’s KV holds a correct SqlAdminPassword (CI stores it on the data sources).
  • Set REPORT_DATASOURCE_FOLDER explicitly per env (see the folder-default caution above).
  • Confirm DB_SERVER / SQL_USER / CORE_DB_NAME per env — CI builds the data-source connection from these. (Data sources are CI-upserted, not hand-created.)
  • Provision the prod SSRS VM (vm-eshars-ssrs-prod-cus) and prod App Gateway ingress.

Legacy — Azure DevOps (runs until PR #6478 merges, then retired)

Step 10 of the hisd-deploy-stage ADO template (in this eshars-ops repo, pipelines/templates/hisd-deploy-stage.yml) runs E:\Build_Scripts\eSHARS\Build\Push-ReportDefinitions.ps1 on the self-hosted eSHARS-Agents pool. It enumerates every *.rdl in eSHARS Build\eSHARS Reports and calls CreateCatalogItem("Report", <name>, "/Reports", overwrite=$true, …) via ReportService2010.asmx.

Legacy targets (from pipelines/environments/*.yml):

EnvReport serverWeb service URLreportServerEnvironment
Dev/QA/Reg/Stg/UAT40.113.238.4 (vm-sql16-np)…/{Dev,QA,Reg,Stg,UAT}ReportServerDevel/QA/REGRESSION/STG/UAT
Training40.122.54.1 (vm-sql16-1-np)…/ReportsTRAIN
Production23.99.180.223 (vm-ssrs16-prod)…/ReportServerPROD

Manual legacy publish (recovery only)

Terminal window
.\Push-ReportDefinitionsV2.ps1 `
-ReportServerUri "http://40.113.238.4/UATReportServer/ReportService2010.asmx?wsdl" `
-ReportServerEnvironment "UAT" `
-ReportSourceFolder "C:\path\to\eSHARS Build\eSHARS Reports" `
-ReportAdminUser "<ssrs-admin-user>" -ReportAdminPass "<ssrs-admin-password>"

Credentials come from the ADO variable group / approved secret store — never hardcoded.


Report permissions (production)

After a production report deploy, ensure the report SQL login has EXECUTE on all report stored procedures (a new report backed by a new proc fails to render otherwise).

Troubleshooting

SymptomLikely cause / action
deploy-reports job: no staged blobs under reports/<run_id>/The reports artifact didn’t stage — check the CI build produced a reports artifact and the manifest had reports=true.
Run Command on SSRS VM fails to auth to blob/KVThe SSRS VM managed identity lacks Storage Blob Data Reader on the staging account or KV access — fix in Terraform, not by hand.
Report publishes but renders blank / data errorData source pointing at the wrong DB, or references resolving to the wrong folder. CI upserts the data sources from KV — check DB_SERVER/SQL_USER/CORE_DB_NAME and SqlAdminPassword for the env. If references resolve to an empty folder, suspect the REPORT_DATASOURCE_FOLDER default mismatch (DataSources vs Data Sources).
Report renders but errors on a stored procMissing EXECUTE grant — see Report permissions.
REPORT_FOLDER is not setThe slot’s GitHub Environment is missing the REPORT_FOLDER variable.
(Legacy) ADO step green but reports not updatedPipeline ran the agent-local script, which may have drifted. Verify E:\Build_Scripts\eSHARS\Build\.

For pipeline-level failures see Pipeline Failure.