Security at CSP Continuity
Your cost data never leaves your tenant. We built the entire product around that sentence — and this page exists so your security team can verify it.
CSP Continuity is an Azure Managed Application published by Savoy Systems. It deploys from the Azure Marketplace into a resource group in your subscription, reads your cost and reservation data with read-only permissions, and stores every byte in resources you own. There is no Savoy-hosted service, no SaaS backend, no data pipeline to us. This page covers the architecture, the controls, the trade-offs we made deliberately, and the questions enterprise reviewers ask most.
PDF, October 2026. Publisher-conducted assessment — not a third-party penetration test, and the report says so plainly.
At a glance
| Deployment model | Azure Managed Application — runs entirely in your tenant, in your subscription |
|---|---|
| Data location | Your Azure SQL database, your storage account, your Application Insights — all in a resource group you can see |
| Data egress | None of your cost data. The only recurring outbound business event is a marketplace billing meter carrying a subscription-count tier — no cost figures, no resource names |
| Permissions | Read-only: Cost Management Reader + Reservations Reader. The app cannot modify, move, or delete anything in your environment |
| Authentication | Your Microsoft Entra ID, enforced by App Service Authentication; fail-closed from the moment of deployment |
| Authorization | Access restricted to users you explicitly assign in Entra ("Users and groups") |
| Encryption | TLS 1.2 minimum everywhere in transit; platform encryption + SQL TDE at rest |
| Publisher access | Savoy has no standing access to your data. The managed-resource-group deny assignment is enforced by Azure, not by us |
| Update integrity | Every artifact is SHA-256 pinned in an unbroken chain from the marketplace template to the running code |
Architecture: where it runs and what it touches
One deployment creates, inside a managed resource group in your subscription: a Linux Function App (Python), an Azure SQL database, a storage account, a Log Analytics workspace with Application Insights, and an Azure Monitor Workbook (the dashboard). A system-assigned managed identity is the application's only identity — there are no deployment-specific service accounts in our tenant, and nothing phones home.
The collection pipeline runs on a timer inside your tenant: it reads cost and reservation data from Microsoft's Cost Management APIs for the subscriptions you grant, computes amortization and discounts locally, writes results to your SQL database and storage account, and emits dashboard metrics to your own Application Insights resource. The dashboard is an Azure Monitor Workbook querying that Application Insights data — rendered by the Azure portal itself.
Every outbound connection, and what it carries
The application's network egress is limited to Microsoft first-party endpoints. In full:
| Destination | Purpose | What is sent |
|---|---|---|
management.azure.com | Reading your cost/reservation data; daily self-check of the app's own deployment state | Authenticated read requests. Data flows in, not out |
graph.microsoft.com | Displaying who has access to the app (its own assignment list) | Read request for the app's own role assignments |
Azure Monitor ingestion (*.monitor.azure.com / *.applicationinsights.azure.com) | Dashboard metrics to your own Application Insights resource | Aggregated daily totals and subscription health — transits Microsoft's ingestion front door, lands in your tenant |
marketplaceapi.microsoft.com | Marketplace metered billing | A tier dimension and a quantity of 1, once per month. No cost figures, no subscription names, no resource data |
*.blob.core.windows.net | Downloading your own Microsoft-issued cost report files; reading the application package from your own storage account | Your data, from Microsoft or your own tenant, inbound |
That marketplace billing event is the single recurring fact that leaves your tenant, and it exists only so Azure can bill you on the plan you chose. The in-product pages load zero external resources — fonts are self-hosted, there are no CDN scripts, no trackers, no beacons.
Identity and access control
Sign-in is your Entra ID. Every dashboard, management page, and API requires authentication through your own Microsoft Entra tenant via App Service Authentication. The app registration is single-tenant: accounts outside your directory cannot sign in at all.
Fail-closed from minute zero. The deployment template ships with authentication configured to reject every request (HTTP 401) before onboarding ever runs. There is no window between deployment and configuration in which data is reachable anonymously. The only endpoints exempt from sign-in are a health probe, the onboarding status/instructions pages (which contain no data), and the Azure lifecycle webhook — each deliberately minimal.
Access is assignment-based. Authentication alone is not authorization: onboarding enables Entra's "assignment required" enforcement, so only users or groups you explicitly assign (Entra ID → Enterprise applications → CSP Continuity → Users and groups) can sign in — not every account in your directory. The administrator who runs onboarding is assigned automatically so there is no lockout; everyone else is added by your admins, on your terms. Unassigned users are rejected by Microsoft's sign-in page before ever reaching the application. Two facts your reviewers will want stated plainly: per Microsoft's documented behavior, Global Administrators bypass the assignment requirement, and removing a user takes effect at their next token issuance rather than instantly terminating a live session.
Defense in depth in code. Independently of platform authentication, application code re-verifies the authenticated principal on every endpoint, so data stays protected even if platform authentication were ever misconfigured out-of-band.
Read-only resource permissions. The managed identity holds exactly two Azure roles against your environment — Cost Management Reader and Reservations Reader — plus the platform-granted ability to read its own managed-app resource. You choose the scope at onboarding, as an explicit choice between management-group-wide or per-subscription. The identity cannot write to, modify, or delete anything in your environment. Its one write-capable permission is scoped to itself: it can update its own application's configuration, which powers zero-touch update self-healing (details under Updates).
What Savoy can and cannot do. Azure's managed-application deny assignment governs the managed resource group: it is enforced by the Azure platform, not by us, and it restricts the publisher's authorization to the narrow operations declared in the marketplace offer. Savoy has no standing credentials to your deployment, no remote access channel into it, and no copy of your data — there is nowhere for a copy to exist, because no pipeline to us exists. You retain full read visibility into every resource the app creates, including its audit logs.
Encryption
- In transit: HTTPS-only on the application; TLS 1.2 minimum on the app, SCM endpoint, SQL server, and storage account; SQL connections use
Encrypt=yeswith certificate validation (TrustServerCertificate=no). These settings were verified on a live marketplace deployment, not just in the template. - At rest: Transparent Data Encryption on the SQL database; Azure Storage service-side encryption; configuration values stored as platform-encrypted application settings.
- Storage is private: public blob access disabled on the account and on every container.
Secrets management
- No secrets ship in the package. Every credential a deployment uses is generated inside your tenant at deploy or onboarding time: the database password you choose at install (never defaulted, never derived, never logged), platform-generated storage keys, and a randomly generated read-only reporting credential. No two installations share any secret.
- Sign-in is secretless. App Service Authentication uses a managed-identity federated credential instead of a client secret — there is no authentication secret to store, rotate, or leak, and nothing that expires.
- Secrets live as platform-encrypted application settings inside the managed resource group, protected by the deny assignment. We deliberately do not use Key Vault here: inside a marketplace managed application, the platform deny assignment blocks the role-based data-plane access Key Vault requires. Rather than punch holes in the deny assignment — the control that protects you from unauthorized changes, including by us — we rely on the platform's application-setting encryption and keep the deny assignment intact. (More in Design decisions, below.)
- The reporting credential is read-only and per-deployment, scoped to the reporting views only, and is displayed only to authenticated administrators, with caching disabled on the response.
Network posture
- Nothing listens on insecure channels. FTP/FTPS publishing disabled; basic-auth deployment credentials disabled on both publishing endpoints; the Kudu/SCM surface is dead to password auth.
- The database is reachable for your BI tools — deliberately, and narrowly. Direct Power BI connectivity to your own cost database is a product feature, so the SQL endpoint accepts TLS-encrypted connections gated by server firewall rules. The in-app firewall helper lets an assigned administrator open individual
/32addresses only — single IPs, never ranges, never rules it didn't create. Database authentication events and queries are audited to storage in your tenant with 90-day retention. - The application has no CORS surface — no cross-origin access is configured anywhere, which closes the common web attack-amplification path for cookie-authenticated apps.
Supply-chain and update integrity
Every artifact you run is covered by an unbroken SHA-256 chain, anchored in the marketplace template that Microsoft certified:
-
The deployment template carries the SHA-256 of the onboarding script, and scripts are published only at versioned URLs — a published script can never silently change out from under its hash. Every surface that shows you the download command also shows the one-line
Get-FileHashverification against the template-pinned value. - The onboarding script carries the SHA-256 of the application package and refuses to install a package that does not match — verification is fail-closed, not advisory.
- The running application re-verifies the same pin before it will relocate or update its own package.
Updates are yours to apply. Publisher updates arrive through the Azure Marketplace as versioned offers — we cannot push code into your tenant. When you apply an update, the application's self-healing re-establishes its configuration and re-verifies the package pin without manual steps. The application package is hosted in your own storage account, so day-to-day operation never depends on publisher infrastructure: if cspcontinuity.com vanished tomorrow, your deployment would keep running and keep collecting.
Dependencies are pinned and bundled into the deployment package — no install-time fetches at runtime — and are security-audited against known-vulnerability databases as part of the release process. The in-tenant package carries no secrets and no customer data.
Application security
- Parameterized database access throughout. No SQL is built from user input; dynamic identifiers come only from fixed allowlists. This claim has been adversarially verified — by static analysis and by manual review of every query path.
- Strict input allowlisting on every configuration write: the API cannot be used to modify deployment state, billing state, or credentials, because those keys are simply not writable through it.
- Output encoding on every page; database-sourced values (resource names, tags) are treated as untrusted and escaped in all display contexts.
- Generic errors by design. Error responses carry a correlation ID and a generic message; stack traces, driver errors, and connection details never reach a client. Diagnostic detail stays in your tenant's logs — and those logs are themselves kept free of secrets (no connection strings, tokens, or passwords are ever logged).
- Outbound destination allowlisting: application credentials are only ever presented to Microsoft endpoints; export destinations are validated against Azure Storage domains (public, Government, and China clouds) before any connection is attempted.
Logging, monitoring, and audit
- Structured logging with correlation IDs across the entire collection pipeline — every pipeline run is traceable end-to-end in your own Application Insights (90-day retention, in your resource group).
- SQL auditing enabled to your storage account, 90-day retention: authentication events and queries.
- The managed application's activity is visible in your Azure Activity Log like any other resource; you can route any of it to your SIEM — it's your workspace.
- A coarse health/billing status endpoint lets your operations team verify the deployment is collecting and reporting correctly; failures surface loudly in the app and workbook rather than silently.
Lifecycle: suspend, cancel, uninstall
- Suspend/cancel: collection stops when the managed application is suspended or cancelled; already-collected data remains in your tenant under your control, and read access to it stays available.
- Uninstall: deleting the managed application deletes the managed resource group — database, storage, telemetry, and every byte of collected data are destroyed with it. Nothing persists on our side because nothing ever existed on our side. Export anything you want to keep before deleting — the Power BI connection or a storage export makes this a five-minute task.
- Complete offboarding: onboarding created two artifacts outside the managed resource group — the Entra app registration used for sign-in, and the read-only role assignments. The uninstall documentation walks through removing both; an automated offboarding script is first on our published hardening backlog.
Design decisions we want you to see
Enterprise reviewers find these faster than any scanner — so here they are, with the reasoning. Each is a deliberate trade-off inside the managed-application model, documented rather than discovered.
- Key-based auth is used inside your tenant where the deny assignment blocks role-based auth. The marketplace deny assignment — the control that prevents anyone, including us, from tampering with the deployment — also blocks Azure RBAC data-plane access within the managed resource group. The app therefore uses platform-generated keys (storage, telemetry ingestion) within your tenant boundary, stored as platform-encrypted application settings. We chose an intact deny assignment plus key-based auth over a weakened deny assignment plus RBAC. The keys never leave your tenant.
- No Key Vault, for the same reason (deny assignment blocks its data plane). Application settings are platform-encrypted at rest and readable only by identities that can already administer the app.
- The SQL endpoint is network-reachable so your analysts can connect Power BI Desktop directly to your own data — access is gated by TLS 1.2, a customer-chosen strong password, firewall rules, and full auditing. A private-endpoint-only design would remove the product's self-service BI value; we chose auditability and narrow firewall self-service instead.
- A long-lived read-only access signature keeps the application package mounted from your own storage across Azure's routine platform maintenance, so your deployment never depends on publisher uptime. It is read-only, scoped to the package blob, and the package contains only the already-published application code.
- The dashboard's telemetry ingestion accepts key-based writes (again: deny assignment). The key only permits writing metrics into your own Application Insights resource — worst case is dashboard graffiti in your own tenant, with no read or confidentiality impact.
Development practices
- Changes ship with a test suite of 1,100+ automated tests; releases follow a one-change-per-release discipline with post-deploy verification.
- Secret scanning is enforced on the repository; dependency and template security scanning run as release gates.
- A full security assessment (architecture, permissions, complete code review, secrets/dependency/static/infrastructure scanning, plus a live-deployment audit) was completed in October 2026 using an enterprise security team's methodology; the three High findings and highest-impact Mediums were remediated and released before publication. It states plainly that it is publisher-conducted, not a third-party engagement.
- Development is a single-maintainer model — stated rather than disguised. The compensating controls are the ones above: the test gate, secret scanning, the artifact hash chain, and marketplace-mediated releases that customers apply on their own schedule.
Enterprise security FAQ
Does any of our cost data leave our tenant?
Can Savoy Systems access our data or our environment?
What permissions does it need, and why?
Who in our company can see the cost data?
Does it support SSO and MFA?
Where are credentials stored? Why not Key Vault?
How do we know the software we're running is what you published?
How are updates delivered? Can the publisher push code to us?
What happens when we uninstall?
What data retention applies while it's running?
Is the database exposed to the internet?
Will this trip Microsoft Defender for Cloud recommendations in our tenant?
Who is Savoy Systems? Why should we trust a small vendor?
Do you have SOC 2 / ISO 27001?
Has it been penetration tested?
What is your incident response process? How would we be notified of a security issue affecting the product?
What about business continuity — what if Savoy disappears?
Does the product use AI / send data to AI services?
Which clouds are supported?
Last reviewed: October 2026, against a full security review of the shipping architecture and a live marketplace deployment.