Operator runbook
Deploying and operating the app through DABs
This is the supported install path for the Well-Architected assessment. It owns validation, install, upgrade, backup, recovery, application rollback and uninstall. The lifecycle commands are deliberately wrapped around Databricks Declarative Automation Bundles: the app never creates its own grants or infrastructure.
The commands below are safe to copy from a clean clone. They require an explicit profile and target,
print the resolved actor, workspace and resource plan, and change nothing until --apply is supplied.
Who does what
| Actor | Action |
|---|---|
| Workspace installer | Selects the workspace profile, binds the warehouse and Lakebase database, deploys the DAB and approves destructive lifecycle actions |
| Assessment facilitator | Defines the assessment and runs or reviews it through the signed-in user's OBO permissions |
| Scheduled service principal | Starts only scheduled assessments and reads only the estate grants the installer gives it |
| App service principal | Runs the server and writes the app's records to the bound Lakebase database |
The interactive user and scheduled principal are intentionally different identities. The app reports missing access and setup preflight actions; it never grants access to itself or to either caller.
1. Prepare a clean clone and an explicit profile
Install Node.js 22 or later and Databricks CLI 0.292.0 or later, then:
git clone https://github.com/databricks-solutions/databricks-waf.git
cd databricks-waf
git checkout <release-tag-or-candidate-commit>
cd app
npm ci
databricks --version
databricks auth profiles
databricks current-user me --profile <profile>
Choose the profile deliberately. Do not rely on DEFAULT, and clear DATABRICKS_HOST,
DATABRICKS_TOKEN and DATABRICKS_WAREHOUSE_ID if they are set: environment credentials override
--profile and can send a valid token to a different workspace.
The customer installation uses target customer, which contains no workspace ids. Put that
installation's bindings in the ignored file:
app/.databricks/bundle/customer/variable-overrides.json
{
"sql_warehouse_id": "<warehouse-id>",
"postgres_branch": "projects/<project>/branches/<branch>",
"postgres_database": "projects/<project>/branches/<branch>/databases/<database-resource-id>",
"assessor_group": "<direct-membership-group>"
}
The warehouse and Lakebase database are customer resources. The DAB binds them with CAN_USE and
CAN_CONNECT_AND_CREATE; it does not create or own them. The database resource id is not necessarily
the PostgreSQL database name.
The App is stateful, so Lakebase is required. A missing branch or database makes validation fail rather than permitting an in-memory installation that loses reviews on restart.
2. Validate and inspect the DAB plan
npm run lifecycle -- validate --profile <profile> --target customer
This runs databricks bundle validate and databricks bundle plan, reports the resolved actor,
workspace, App, job and bound data resources, and checks the scheduled identity without changing it.
Resolve every missing required variable or authentication error before continuing.
The scheduled job is installed paused. Leaving its variables empty is a supported interactive-only installation, not a half-configured schedule.
3. Install
Preview first:
npm run lifecycle -- install --profile <profile> --target customer
Then apply the same reviewed plan:
npm run lifecycle -- install --profile <profile> --target customer --apply
The applied command performs the complete Apps/DAB sequence:
- validate the selected target;
- produce the DAB resource plan;
- deploy with active-run protection;
- run the DAB App resource so configuration is applied and the App restarts;
- read the deployed App back from the platform;
- verify
RUNNING, a successful active deployment, both bindings and every declared effective OBO scope; - run a second DAB plan and require every managed resource to be unchanged.
The deployment id and App URL printed at the end are the install record. Open Diagnostics in the App and run the assessment setup preflight before collecting evidence.
4. Optional scheduled assessment identity
Create a dedicated service principal and its secret as described in
scheduled-scans.md. Add only the non-secret references to the same ignored
override file:
{
"app_url": "https://<app-host>.aws.databricksapps.com",
"schedule_client_id": "<application-id>",
"schedule_secret_scope": "waf-schedule",
"schedule_secret_key": "oauth-secret"
}
Never put the OAuth secret in the bundle file. The secret value stays inside the workspace secret scope.
With a client id configured, the lifecycle command invokes the repository's derived grant tool. Preview the base grant set with the normal validation command. To include metadata visibility for every customer catalog and the four sharing-configuration grants, use:
npm run lifecycle -- upgrade --profile <profile> --target customer \
--schedule-catalogs all --schedule-sharing
Apply only after reviewing the exact grant report:
npm run lifecycle -- upgrade --profile <profile> --target customer \
--schedule-catalogs all --schedule-sharing --apply
The grant set is derived from the statements the app runs. A second applied run issues no new grants. After a healthy manual job run, unpause the schedule deliberately; deployment never opts a customer into unattended collection.
5. Upgrade
Check out the exact candidate or release being installed and record the current deployment id from
databricks apps get databricks-waf-assessment --profile <profile> -o json. Then:
npm ci
npm run verify
npm run lifecycle -- upgrade --profile <profile> --target customer
npm run lifecycle -- upgrade --profile <profile> --target customer --apply
install refuses when the App already exists; upgrade refuses when it does not. After bundle run app,
the lifecycle waits for the exact new deployment created by that command to become active and refuses a
failed or concurrently replaced deployment. Only then does the post-deploy read guard against an Apps
update silently dropping OBO scopes or changing either binding.
Application rollback and data recovery are separate procedures. Do not use application rollback as a substitute for a backup.
6. Application rollback
Use a clean checkout of the previously approved tag or commit. The rollback command verifies that the
current HEAD is exactly the ref named by --to and that the app working tree has no tracked or
unignored untracked changes; it never changes the checkout for you.
First preview it:
npm run lifecycle -- rollback --profile <profile> --target customer --to <previous-tag-or-commit>
Then guard the replacement with the deployment id currently serving:
npm run lifecycle -- rollback --profile <profile> --target customer \
--to <previous-tag-or-commit> \
--from-deployment <current-deployment-id> \
--apply
The same running-state, binding, scope and no-change-plan checks run after rollback. If the active deployment changed between preview and apply, rollback refuses instead of replacing somebody else's deployment.
7. Back up and recover Lakebase records
The recovery archive covers the App-owned waf schema in the Lakebase Autoscaling database resolved
from the same explicit DAB target. All current durable customer records are in that schema; the versioned
manifest records an explicit empty durableUnityCatalogArtifacts list. Add any future durable UC
artefact to this contract before relying on the command for that release.
Use a private directory outside the repository. Backup refuses an existing archive, a relative or in-repository path, and a directory accessible to group or other users. Prefer GPG encryption:
mkdir -m 700 <private-backup-directory>
npm run recovery -- backup --profile <profile> --target customer \
--archive <absolute-path>.dump.gpg \
--retain-until <yyyy-mm-dd> \
--gpg-recipient <recipient> \
--expect-result <final-assessment-id> \
--expect-review <assessment-review-id> \
--expect-action <improvement-action-id>
Review the resolved workspace, database, endpoint, App role, record counts and digest, then add --apply.
--plaintext-ok may replace --gpg-recipient only when the operator explicitly accepts a private
mode-0600 plaintext archive. pg_dump uses one serializable, deferrable snapshot of the waf schema;
the manifest records before/after fingerprints and whether the source stayed stable across the archive.
Those three named records are mandatory evidence for the complete fresh-install rehearsal. After the
installation has produced a real closed-month publication, --expect-publication <month-publication-id>
adds it to a later rehearsal. Do not fabricate or backdate a publication to satisfy recovery evidence.
A backup without the three fresh-install records is still operationally valid, but reports that the
customer-journey evidence set is incomplete.
Restore never writes over the bound database. Supply a new Lakebase database resource id, preview, then apply:
npm run recovery -- restore --profile <profile> --target customer \
--archive <absolute-path>.dump.gpg \
--database-id <new-recovery-database-id>
npm run recovery -- restore --profile <profile> --target customer \
--archive <absolute-path>.dump.gpg \
--database-id <new-recovery-database-id> \
--apply
The applied command creates an empty database through the Lakebase Autoscaling API. A deterministic
no-login role owns the restored objects and is granted only to the operator and App PostgreSQL role; the
database is transferred to that owner through the Lakebase control plane before pg_restore runs in one
transaction. Verification requires the original schema/table/constraint fingerprint, every stored
application-record digest, App membership in the owner role, and every named final assessment, review,
action and any optional publication in the manifest. Failed targets and their owner role are retained for inspection.
The private restore receipt records both resource names, archive digest and the full inspection.
For schema-upgrade recovery, keep the serving App and original database unchanged while restoring the
pre-upgrade archive. Check out the previously approved App commit, change only the ignored customer
target's postgres_database override to the verified recovery resource, then preview and apply the
application rollback against the active deployment id. The DAB path therefore
changes the App binding and code together while the prior application/data pair remains readable.
Recovery cleanup is separately guarded. Preview prints the recovery database, its exact no-login role, the retained source and archive, and a confirmation token bound to that inventory:
npm run recovery -- cleanup --profile <profile> --target customer \
--archive <absolute-path>.dump.gpg \
--database-id <new-recovery-database-id>
Apply only the printed token. Cleanup deletes the named recovery database first and then its dedicated owner role; if role deletion is interrupted, the same preview/apply flow safely finishes the orphaned role. Cleanup never deletes the bound source or archive. Keep the archive and manifest through their recorded retention date, then dispose of them under the customer's backup and legal-hold policy.
8. Uninstall
Uninstall is two-step and preserves customer data. Preview it first:
npm run lifecycle -- uninstall --profile <profile> --target customer
The output lists the exact App, Lakeflow Job and bundle workspace path that will be removed. It separately lists the warehouse, Lakebase branch, Lakebase database and customer records that will remain, then runs the scheduled-principal revocation as a dry run and prints every direct grant or membership it would remove. It also prints a confirmation token bound to the bundle, profile, target, workspace host, active deployment, job id and complete removal/retention/revocation inventory.
Only then apply the exact token:
npm run lifecycle -- uninstall --profile <profile> --target customer \
--confirm '<confirmation-token-printed-by-preview>' \
--apply
Where a scheduled principal is configured, its directly revocable Unity Catalog grants and assessor-group
membership are removed before bundle destroy. App and warehouse permissions are not rewritten by that
tool because Databricks permission updates replace the full access list; after the App has been removed,
review the warehouse ACL and remove the scheduled principal only if no other workload uses that entry.
Delete the Lakebase database, branch, warehouse, service principal or secret scope only under a separate customer retention decision. The lifecycle command intentionally cannot turn removing the application into deleting its retained governance record.