Sample report
The one thing to know
An enabled nightly job, OrphanTest_NightlyLoad, silently fails half its work every night, and depends on an external server that no catalog tool can even see it using.
01
SQLHOST01 hosts one live database, OrphanTestDB, with six objects and two SQL Agent jobs. Around it sit six loose files, a batch script, a PowerShell script, a T-SQL script, an Excel workbook, an SSIS package, and an SSRS report, each of which reaches into the database through a hardcoded connection string. Nothing on this box is documented; the map below was reconstructed entirely from what the server and the files disclose.
One thing is already broken. The nightly job OrphanTest_NightlyLoad is enabled and runs every night, but its second step fails every time, and has on both of its two most recent runs. The first step succeeds, so the job looks like it is working to anyone glancing at a job list. It is not: the half that pulls legacy data never completes.
The failing step depends on a server outside this box, ARCHIVE-SQL, through a reference that a normal catalog inventory cannot see, because it is assembled as dynamic SQL at runtime. A second, independent path to that same external server exists inside the SSIS package. Both are single points of failure that no one currently owns: the jobs were last invoked by SQLHOST01\olddev, a login that appears nowhere else.
02
| Count | Type | Detail |
|---|---|---|
| 1 | SQL Server | SQLHOST01, SQL Server 2022 Developer Edition (16.0.1000.6) |
| 2 | Databases | OrphanTestDB (local, online) · LegacyDB (referenced on ARCHIVE-SQL) |
| 6 | SQL objects | 2 tables, 1 view, 1 scalar function, 2 stored procedures |
| 1 | Linked server | ARCHIVE-SQL (SQLNCLI) |
| 2 | Agent jobs | both enabled · 6 history records read |
| 3 | Job steps | 2 T-SQL, 1 CmdExec |
| 6 | Files | .bat · .ps1 · .sql · .xlsx · .rdl · .dtsx |
| 2 | Server names in files | localhost, ARCHIVE-SQL |
03
The full graph holds 23 nodes and 41 edges. Below is the part that carries the risk: the two jobs, what they run, and the two independent paths that leave this box for an external server. Amber marks what is failing or unconfirmed; green marks what runs and works.
04
Ranked highest severity first. Severity is computed from signals in the graph, not written by hand, so it is repeatable. Every finding carries its evidence.
OrphanTest_NightlyLoad is enabled and scheduled. Step 1 (RunSummary) succeeds; step 2 (PullLegacy) fails with error 2812 on every run in the history. Because step 1 reports success, the job's failure is invisible from a normal job-status glance. The legacy data pull has not completed on any recent run.
Evidence
msdb.dbo.sysjobhistory · run 20260917, step 2 (PullLegacy): "Could not find stored procedure 'dbo.usp_PullLegacy'. [SQLSTATE 42000] (Error 2812). The step failed."
msdb.dbo.sysjobhistory · run 20260917, job outcome: "The job failed. The last step to run was step 2 (PullLegacy)."
Do first. Decide whether step 2 still needs to run. If it does, fix the ARCHIVE-SQL dependency below and add failure alerting to the job so the next silent failure surfaces. If it does not, disable the step so the job stops reporting failure every night.
effort: lowdbo.usp_PullLegacy reads from ARCHIVE-SQL.LegacyDB.dbo.ExternalCustomers, an external server, but it builds that query as dynamic SQL at runtime. sys.sql_expression_dependencies records nothing for it, so a catalog-only inventory shows this procedure depending on nothing external. orphanmap caught it by scanning the module text and cites the exact line. This is also the procedure the failing job step above calls.
Evidence
module line · OrphanTestDB.dbo.usp_PullLegacy, line 6: DECLARE @sql NVARCHAR(400) = N'SELECT * FROM [ARCHIVE-SQL].[LegacyDB].[dbo].[ExternalCustomers]';
Confirm ARCHIVE-SQL exists and is reachable from this server, then decide: repoint the linked server, or retire the procedure. Because the reference is dynamic SQL, a schema-comparison or catalog dependency tool will not flag it, document it manually once fixed.
effort: mediumBeyond the procedure, the SSIS package LoadCustomers.dtsx also connects to ARCHIVE-SQL / LegacyDB, through its own connection string. If ARCHIVE-SQL is renamed or retired, both the nightly procedure and the SSIS load break, and one of them is already the failing step. Neither dependency is documented anywhere on the box.
Evidence
file, XML · LoadCustomers.dtsx, line 9: DTS:ConnectionString="Data Source=ARCHIVE-SQL;Initial Catalog=LegacyDB;Provider=SQLNCLI11.1;Integrated Security=SSPI;..."
module line · usp_PullLegacy, line 6 (see above)
Treat ARCHIVE-SQL as a shared single point of failure. Both callers need updating together in any migration or rename. Add both to your runbook for that server so a future retirement does not take out the nightly load and the SSIS package by surprise.
effort: lowSix files outside the database, a batch script, a PowerShell script, a T-SQL script, an Excel workbook, an SSRS report, and the SSIS package, each carry a connection string naming localhost / OrphanTestDB. None of them are visible from inside SQL Server. Move, rename, or migrate the box and every one of them fails silently, with no error surfaced to the database.
Evidence (one line per file)
nightly_load.bat, line 6: sqlcmd -S localhost -d OrphanTestDB -E -i "...\refresh_customers.sql"
maint.ps1, line 3: $connStr = "Server=localhost;Database=OrphanTestDB;Integrated Security=True;..."
SalesReport.xlsx · xl/connections.xml: Provider=SQLOLEDB.1;Data Source=localhost;Initial Catalog=OrphanTestDB;...
CustomerReport.rdl, line 7: <ConnectString>Data Source=localhost;Initial Catalog=OrphanTestDB</ConnectString>
Before any server rename or migration, inventory these six files. Each hardcodes localhost, so they only work while everything stays on one box. Replace with an alias or a shared config, or at minimum list them as migration blockers.
effort: mediumThe Agent job history records both jobs as last invoked by SQLHOST01\olddev. That account does not own any other object the inventory found, the fingerprint of a departed developer. Whoever inherits this box inherits jobs no current login is responsible for.
Evidence
msdb.dbo.sysjobhistory: "The Job was invoked by User SQLHOST01\olddev."
Reassign ownership of both jobs to a current, monitored account and confirm the olddev login can be disabled without breaking a schedule. This is low urgency but it is exactly the loose end that turns into a 2am incident later.
effort: low05
What is actually running today versus what is referenced but not confirmed reachable. A read-only inventory of one box can prove a dependency exists; it cannot cross a linked server to prove the far end is alive, so external targets are marked accordingly rather than guessed.
06
The critical chains in plain English. Each sentence maps to a cited edge above.
OrphanTest_NightlyLoad (Agent job)
Runs on a schedule. Step 1 executes usp_CustomerSummary inside OrphanTestDB and succeeds. Step 2 executes usp_PullLegacy, which tries to read ExternalCustomers from the ARCHIVE-SQL linked server and fails. If step 2 is meant to feed the summary, the summary is running on stale data every night.
OrphanTest_FileLoad (Agent job)
A CmdExec step runs nightly_load.bat from a file share. The batch file calls sqlcmd against localhost/OrphanTestDB and runs refresh_customers.sql, which in turn executes usp_CustomerSummary. So the same procedure is driven two ways, once from Agent T-SQL and once from a batch file on a share, and only one of those paths is visible from inside SQL Server.
07
What the collector saw directly, and what it could not, so you know which findings to lean on before you act. These caveats are stamped from the tool's own coverage table, not softened.
SQL catalog & Agent jobs. Databases, objects, dependencies, linked servers, job steps, and job history read live from system views. High confidence.
File connection strings. .bat, .ps1, .sql, .rdl, .xlsx, .dtsx opened read-only and parsed; each reference cites a line or part. High confidence in this sample.
Access (.accdb/.mdb). Extraction is exercised on synthetic bytes in the test estate, not yet against a broad set of real files. Treat Access findings as leads to verify.
The far side of a link. A read-only scan of one box cannot cross a linked server or UNC path to confirm the target is alive. External targets are reported as referenced, never as confirmed.
08
For the skeptic. A sample of the graph edges behind the findings above, exactly as the tool emitted them, so any dependency can be verified by hand.
// the linked-server reference the catalog misses { "src": "obj:OrphanTestDB.dbo.usp_PullLegacy", "dst": "lnk:ARCHIVE-SQL", "type": "REFERENCES_LINKED_SERVER", "source": { "type": "module_line", "line": 6, "text": "...SELECT * FROM [ARCHIVE-SQL].[LegacyDB].[dbo].[ExternalCustomers]..." } } // the CmdExec step that invokes a batch file on a share { "src": "step:OrphanTest_FileLoad#1", "dst": "file:\\FILESERVER\\Legacy\\legacyshare\\nightly_load.bat", "type": "INVOKES", "source": { "type": "jobstep", "object": "msdb.dbo.sysjobsteps", "key": "job_id=7601F29E..., step_id=1" } } // SSIS package connection string, second path to the external server { "src": "file:...\\loadcustomers.dtsx", "dst": "db:LegacyDB", "type": "CONNECTS_TO", "source": { "type": "file_xml", "line": 9, "text": "Data Source=ARCHIVE-SQL;Initial Catalog=LegacyDB;..." } }
The report is in the works
The free tool draws the graph today. This report, ranked risk, dead vs live, a citation on every line, is what's coming next. Leave your email and I'll tell you the moment it's ready.
One upload, one PDF, no call and no consultant. $225 a report, when the upload launches.
One email when the report launches, and that is the only reason I keep your address. I will never sell it, rent it, or share it with anyone, and I will not use it for anything I have not spelled out to you here in writing. No list-swapping, no spam, and every email carries a one-click unsubscribe.
On your data. The free tool runs entirely on your own machine. The only connection it makes is to the SQL Server you point it at: no telemetry, no phone-home, and passwords are never written to its output. When the report launches, an uploaded graph contains hostnames, paths, and object names, never passwords, the tool strips those before anything leaves your box. It will be encrypted, deleted within 7 days, and never used to train anything.