Fifteen years ago, migrating fifty logins between servers meant either a script somebody wrote and half-trusted, or an afternoon of right-click-Generate-Script-As in SSMS, one object at a time. dbatools replaced that entire category of manual work with roughly 700 PowerShell commands covering nearly everything a DBA does by hand: migrations, health checks, backups, permissions, configuration comparisons. It's become the default assumption for SQL Server automation tooling for a reason.
What It Actually Replaces
| Manual SSMS task | dbatools equivalent |
|---|---|
| Generate Scripts wizard, one object at a time | Get-DbaAgentJob / Get-DbaDbTable / etc. piped into Export-DbaScript, one command per object type instead of one click per object |
| Scripting an entire database's schema for source control or redeploy | Export-DbaDacPackage, one command, one .dacpac (schema) or .bacpac (schema + data) |
| Copy logins between servers by hand, matching SIDs manually | Copy-DbaLogin, SID-matched automatically |
| Clicking through every database's properties to compare configuration | Get-DbaDbState, Test-DbaDiskAllocation, dozens of comparable Test-Dba* checks |
| Manually running and eyeballing backup history per database | Get-DbaLastBackup across every database on every instance in one call |
| Ad-hoc query against a handful of servers, one connection at a time | Invoke-DbaQuery against a list of instances in a single pipeline |
The pattern across all of it: what used to be "open SSMS, connect, click through a wizard" becomes one command that can run against one server or two hundred, identically, and can be checked into source control so the exact steps of a migration are reviewable before anyone runs them.
Why It Became a Foundation for In-House Tooling
Building custom SQL Server automation on top of raw ADO.NET or Invoke-Sqlcmd means reinventing connection handling, error propagation, and object output for every new script. dbatools already solved those problems consistently across its entire command surface, which is why it's a common foundation layer for organization-specific tools rather than something teams route around: writing a wrapper function that calls Invoke-DbaQuery under the hood gets you consistent behavior for free that would otherwise be its own maintenance burden.
sqmSQLTool is built exactly this way: its backup, integrity-check, and inventory functions are wrappers around dbatools commands, not reimplementations of what dbatools already does well. The value it adds on top isn't a different way of talking to SQL Server, it's the opinionated defaults, consistent logging, and result-object shape that turn a library of general-purpose commands into a toolset built around one team's actual workflow.
Gotchas That Trip Up T-SQL Habits
Invoke-DbaQuery fails silently without -EnableException. By default, dbatools follows a "warn, don't throw" philosophy consistent across the module, a failed query can return nothing and continue script execution rather than stopping it. In a PowerShell script or module built for automation (not interactive troubleshooting), always pass -EnableException so failures actually surface as terminating errors your try/catch can handle, instead of silently producing an empty result set that looks like "no rows" rather than "the query failed."
# Silent on failure, easy to misread as "zero rows returned"
$result = Invoke-DbaQuery -SqlInstance $srv -Query $badSql
# Fails loudly, exactly what an automated script needs
$result = Invoke-DbaQuery -SqlInstance $srv -Query $badSql -EnableException
- Generic table results need
-As PSObject.Invoke-DbaQuery's default output type doesn't always play well with generic downstream processing (exporting to CSV, feeding a WinForms grid, arbitrary column sets that vary by query);-As PSObjectgives you a plain object per row that behaves predictably regardless of what columns the query happens to return. - Command names describe the object, not the underlying T-SQL.
Copy-DbaDatabaseorchestrates a chain of backup/restore/configuration steps behind one verb-noun name; reading the source (dbatools is open source) is often faster than guessing at behavior from the name alone when something needs non-default handling. - Connections aren't always what you'd assume. Some commands accept a live SMO server object, others a connection string, others just an instance name they resolve internally; passing the wrong shape produces a confusing type error rather than a clear "wrong parameter" message.
- Module version drift between servers. A command's parameters and defaults do change across dbatools releases; a script written against one installed version can behave differently on another machine with an older or newer install, pin or verify the module version for anything running unattended.
Where It Fits Next to Purpose-Built Tools
dbatools is a general-purpose toolbox, not a replacement for tools built around a specific workflow with its own UI and validation. It's common, and often the right call, to build a purpose-specific tool that calls dbatools commands underneath for the actual SQL Server interaction, getting dbatools' tested connection and object handling without reinventing it, while the tool itself adds the workflow, validation, and guardrails specific to the task at hand. SQLSetupTool's WinForms installation wizard and sqmSQLTool's health-check and inventory functions both follow this split: dbatools handles talking to SQL Server correctly, the tool on top handles the specific workflow and guardrails around it.
The Bottom Line
dbatools didn't just script what DBAs already did by hand, it made cross-server, repeatable operations the default instead of the exception. The module's own philosophy (warn rather than throw, by default) is exactly the thing to override explicitly in anything unattended: add -EnableException, check your object output shape, and the module handles the parts of SQL Server automation that used to be its own maintenance burden.