powershelldba.de · Uwe Janke

dbatools vs. sqmSQLTool: The Decisive Difference

The question comes up in every review: if dbatools already does all of this, why is there a second module? The honest answer is not "ours is better." It is that the two solve different problems, and the second one only exists because the first one deliberately refuses to.

The short version

dbatools ships verbs. sqmSQLTool ships procedures. That single sentence covers most of it, and everything below is just the consequences.

A verb is one operation: back up a database, add a database to an availability group, read the wait statistics. It has to work on SQL Server 2012 and on 2025, on a laptop and in a bank, for a DBA who has a documented standard and for one who is troubleshooting at two in the morning. To manage that, it cannot assume anything about your environment. Being unopinionated is not a gap in dbatools, it is the design requirement.

A procedure is the sequence of verbs your organization has agreed on, with the preconditions, the ordering, the cleanup, and the evidence that it ran. That is inherently opinionated: it is only correct for one house standard. Nobody can ship it to you, because it encodes decisions only you have made.

So the two are not competitors. sqmSQLTool declares dbatools as a RequiredModules entry in its manifest. Without dbatools installed, the module does not load at all. It is a consumer, not a replacement.

What the layer on top actually contains

The clearest example is adding databases to an Availability Group. In dbatools that is Add-DbaAgDatabase, and it does its job well. In sqmSQLTool the same task is Add-sqmDatabaseToAG, which calls seven different dbatools cmdlets on the way through:

Get-DbaAvailabilityGroup      # does the AG exist, are we on the primary
Get-DbaAgDatabase             # is the database already a member (idempotency)
Get-DbaDatabase               # does it exist, what state is it in
Set-DbaDbRecoveryModel        # SIMPLE will not join, so switch it to FULL
Get-DbaAgReplica              # enumerate the secondaries
Remove-DbaDatabase            # drop stale leftover copies on the secondaries
Add-DbaAgDatabase             # the actual operation

Six of those seven lines are not the operation. They are the decisions around it: what counts as a safe precondition, what happens to a database left behind by a previous failed attempt, whether it is acceptable to silently change a recovery model. And with -All, the databases are seeded one after another instead of in parallel, because automatic seeding of twenty databases at once produces a load spike on the primary that a shared production instance will not forgive.

dbatools cannot make any of those calls for you. Dropping a database on a secondary is exactly the kind of destructive assumption a general-purpose module must never bake into a cmdlet. In a house module, where the standard says "the AG is the source of truth and stale secondary copies are garbage," it is the correct default.

The value of a house module is not the code it adds. It is the decisions it removes from the person running it at 2 a.m.

The second difference: what a function returns

dbatools returns objects. That is right for a library: objects compose, pipe, filter, and export however the caller wants. The caller decides what to do with them.

An in-house module often has a different deliverable. When a report function in sqmSQLTool runs, the objects still go to the pipeline, but by default it also writes TXT, CSV, and HTML into a per-function subfolder under a configured output path, plus a timestamped line into a per-function, per-day log file. Nobody has to remember to add -OutputPath or to pipe into Export-Csv. That was a real bug for a while: several functions produced nothing on disk unless you passed the parameter explicitly, which is fine for a library and wrong for a module whose output is meant to end up in a change record.

This is the part people underestimate. In a regulated environment the artifact is the work product. A wait-statistics analysis that only ever existed in somebody's console did not happen, as far as the audit is concerned.

The third difference: site configuration

dbatools has no idea what your maintenance jobs are called, where your log share lives, or which monitoring account is allowed to be a sysadmin. It should not. sqmSQLTool holds those as module configuration, read at import and overridable per site:

Get-sqmConfig -Key 'OutputPath'
Get-sqmConfig -Key 'LogPath'
Get-sqmConfig -Key 'OlaJobNameFull'      # the house name of the full-backup job
Get-sqmConfig -Key 'CheckTempDbMaxFiles' # the house health-check threshold

The point is not that config files are clever. It is that the house standard lives in exactly one place instead of being retyped as a parameter in forty scripts, each of which will be updated at a different time, or not at all.

The same pattern, one layer further out New-sqmAgentCommandJob takes any sqmSQLTool function plus its parameters, serializes the parameter set, and builds the SQL Agent job and step around it. dbatools gives you New-DbaAgentJob; the house layer gives you "run our procedure, unattended, on schedule, with the log ending up where the log belongs."

The fourth difference: it is allowed to be wrong on purpose

A house module can work around problems a community module cannot touch. One concrete case: a Policy-Based Management policy set to On-Change:Prevent blocks ALTER SERVER ROLE, which means a perfectly legitimate temporary sysadmin grant fails with a policy violation. dbatools will never special-case that, and should not. The house function knows that this policy exists in this estate, handles it, and documents why.

That freedom is also the risk. Every workaround is a bet that the condition it works around is real and will stay real. A community module is disciplined by thousands of users across thousands of environments. A house module is disciplined only by whoever reviews it, which is the argument for reviewing it harder, not more loosely.

Side by side

 dbatoolssqmSQLTool
Unit of workOne operationOne runbook
AudienceEvery SQL Server estateOne estate, one standard
OpinionsAvoided by designThe entire point
Primary outputObjects for the pipelineObjects plus a report and a log on disk
Environment knowledgeNone assumedJob names, paths, thresholds in config
DependencyStandaloneRequires dbatools

When you should not build one

None of that means you should go and build one. Two cases where a house module is the wrong answer:

For anything ad hoc, exploratory, cross-organization, or one-off, dbatools alone is the right tool and the second layer is pure overhead. The house module earns its keep in the narrow band where the same procedure runs repeatedly, has to be identical every time, and has to leave evidence behind.

The one-line test

Before writing a new function on top of dbatools, ask: could this be contributed to dbatools as it stands?

If yes, contribute it there. If no, ask why not. The answer will name a specific assumption about your environment, your naming, your policy, or your audit requirements. That assumption is the actual reason your module exists, and it belongs in the function's help text. If you cannot name it, you are writing a wrapper rather than a procedure, and a wrapper is just one more thing that breaks when the layer underneath moves.

Questions people actually ask

Q: Is sqmSQLTool a replacement for dbatools? No. dbatools is a required module in the manifest, so it has to be installed for sqmSQLTool to load at all. Individual functions call dbatools cmdlets underneath and add the procedure layer around them: preconditions, ordering, cleanup, logging, and a report artifact.
Q: What is the actual difference between dbatools and an in-house module? dbatools ships verbs, which must stay unopinionated to work in every environment. An in-house module ships procedures, which are only correct for one organization's standard. The house module makes the decisions dbatools deliberately refuses to make for you.
Q: When should you not build your own module on top of dbatools? When you have no written standard to encode, or when the function is general enough to belong upstream in dbatools instead. A house module only earns its place where the same procedure runs repeatedly, has to be identical every time, and has to leave evidence behind.
Related reading For what dbatools replaces from point-and-click work in the first place, see dbatools: Why PowerShell Replaced Point-and-Click DBA Work. For the same argument applied to one narrow task, see AlwaysOnSetup vs. Enable-SqlAlwaysOn and New-SqlAvailabilityGroup and Why We Built sqmDataTransfer. The full function reference is at sqmSQLTool command reference.