6Findings
22 findings: 3 critical, 9 high, 8 medium, 2 low. Each shows the evidence from the read-only scripts (fictional output), why it matters, and what to do.
| ID | Severity | Area | Finding | Server(s) |
| F-01 | Critical | Supportability | The billing server runs SQL Server 2016, which reached end of support on July 14, 2026 | SMB-SQL01 |
| F-02 | Critical | Backup & recovery | The document-intake database has no point-in-time recovery, and its nightly dump has failed for three nights | SMB-PG01 |
| F-03 | Critical | Backup & recovery | No restore has ever been tested, and backups sit on the same server or storage as the data | All four |
| F-04 | High | Backup & recovery | SQL Server log backups are missing for two FULL-recovery databases; the log drive is 93% full | SMB-SQL01 |
| F-05 | High | User access | Too many accounts have full administrator rights — on every platform | SMB-SQL01, SMB-SQL02, SMB-PG01, SMB-ORA01 |
| F-06 | High | User access | Shared and built-in accounts are in daily use | SMB-SQL01, SMB-PG01, SMB-ORA01 |
| F-07 | High | User access | Orphaned and unused accounts remain, and password rules are weak | SMB-SQL01, SMB-PG01, SMB-ORA01 |
| F-08 | High | Security | Billing and patient data is not encrypted at rest, in backups, or on the network | SMB-SQL01, SMB-SQL02, SMB-PG01, SMB-ORA01 |
| F-09 | High | Operations | SQL Server integrity checks have been off for 11 months, and index maintenance fails every week | SMB-SQL01 |
| F-10 | High | Supportability | PostgreSQL 14 stops receiving fixes on November 12, 2026, and this server is 15 updates behind | SMB-PG01 |
| F-11 | High | Capacity | A forgotten replication slot is holding 38 GB of logs and filling the PostgreSQL volume | SMB-PG01 |
| F-12 | High | Supportability | The billing server’s Windows version reaches end of support on January 12, 2027 | SMB-SQL01 |
| F-13 | Medium | Performance | Memory, parallelism and tempdb are at default settings on the billing server | SMB-SQL01 |
| F-14 | Medium | Performance | Index health: heavy fragmentation, unused indexes and one very large heap | SMB-SQL01 |
| F-15 | Medium | Capacity | SQL Server data drive has about 10 months of room; autogrowth settings are poor | SMB-SQL01 |
| F-16 | Medium | Supportability | Supported servers are behind on updates | SMB-SQL02, SMB-ORA01 |
| F-17 | Medium | Security | xp_cmdshell is enabled on the billing server and used by three EDI procedures | SMB-SQL01 |
| F-18 | Medium | Operations | Nobody is notified when a job or backup fails | All four |
| F-19 | Medium | Modernization readiness | Items to resolve before moving the billing server | SMB-SQL01 |
| F-20 | Medium | High availability | No standby for the billing or ledger databases | SMB-SQL01, SMB-ORA01 |
| F-21 | Low | Performance | PostgreSQL vacuum is falling behind on the main documents table | SMB-PG01 |
| F-22 | Low | Modernization readiness | Deprecated SQL Server features are in use (warnings, not blockers) | SMB-SQL01 |
F-01CriticalSupportability
The billing server runs SQL Server 2016, which reached end of support on July 14, 2026
Server(s): SMB-SQL01
Your main billing database server no longer receives regular security fixes from Microsoft, and it is not even on the last service pack.
Evidence (sample script output)
SMB-SQL01 product_version 13.0.5026.0 product_level SP2 update_level NULL
edition Standard Edition (64-bit) engine_edition 2
Why it matters
Microsoft ended extended support for SQL Server 2016 on July 14, 2026[1][2]. Paid Extended Security Updates (ESU) deliver critical security updates for up to three years (Year 3 ends July 17, 2029)[2] and are bought per server through the Azure portal, with on-premises servers connected through Azure Arc[3]. Standard edition is eligible[3]. Service Pack 3 is the final service pack for SQL Server 2016[2].
Recommendation
Plan a move off SQL Server 2016 (see the recommended path). Bring SMB-SQL01 to SP3 and its final update as a first step. Decide by an agreed date whether ESU is needed as a short bridge; get current ESU pricing directly from Microsoft or your licensing partner — this report does not quote it.
Source: sqlserver/01_instance_inventory.sql
F-02CriticalBackup & recovery
The document-intake database has no point-in-time recovery, and its nightly dump has failed for three nights
Server(s): SMB-PG01
If the intake server failed today, you would lose everything scanned since Thursday night — four days of documents — and nobody has been told the backup is failing.
Evidence (sample script output)
archive_mode off · wal_level replica · pg_stat_archiver.archived_count 0
docintake size 64 GB
Last successful pg_dump: 2026-10-09 23:30 (cron log on server; Oct 10–12 runs exited with
"No space left on device" — dump target /backup is on the same 500 GB volume as the data)
Why it matters
Without WAL archiving, PostgreSQL can only be restored to the moment of the last dump, so the recovery point is at best one day. The dumps are now failing because the shared volume is almost full (see F-11), and the failure is only visible in a log file nobody reads.
Recommendation
Free space safely (F-11), confirm a good dump today and copy it off the server. Then set up WAL archiving with a proper PostgreSQL backup tool to separate storage so you can restore to any point in time, and alert your IT contact when a backup fails.
Source: postgresql/01_inventory_settings.sql · postgresql/02_databases_backup_wal.sql
F-03CriticalBackup & recovery
No restore has ever been tested, and backups sit on the same server or storage as the data
Server(s): All four
There is no evidence that any backup on any platform can actually be restored, or how long it would take. Most backup files live on the same machine or storage array as the databases they protect.
Evidence (sample script output)
SQL Server restorehistory (12 months): 0 rows on SMB-SQL01 and SMB-SQL02
SMB-SQL01 sample_destination E:\SQLBackups\BillingCore\BillingCore_FULL_20261013_010001.bak
SMB-SQL02 sample_destination D:\Backup\PracticePortal\PracticePortal_LOG_20261013_144500.trn
SMB-ORA01 v$rman_status RESTORE/VALIDATE (12 months): 0 rows
backup pieces: DISK · Local/NFS path /u03/fra/LEDGER/... (same SAN as data, per interview)
SMB-PG01 pg_dump files in /backup on the data volume
SQL Server backups: encryption_algorithm NULL · all_have_checksums 0
Why it matters
Your stated targets are a 1-hour recovery point (RPO) and a 4-hour recovery time (RTO) for billing. On SMB-SQL01 a nightly copy to the NAS runs at 03:00 (per interview), so losing the VM or its storage could lose up to about a day of billing work, not one hour. With no timed restore on any platform, every RTO is unproven.
Recommendation
Copy backups off-host as they are taken (not once a night), keep at least one copy that ransomware on the server cannot reach, turn on SQL Server backup checksums, and run a timed restore of BillingCore, docintake and LEDGERPDB to a test server now and quarterly after that.
Source: sqlserver/03_backup_history.sql · oracle/02_backup_recovery.sql · postgresql/02_databases_backup_wal.sql
F-04HighBackup & recovery
SQL Server log backups are missing for two FULL-recovery databases; the log drive is 93% full
Server(s): SMB-SQL01
Two databases keep a full history of changes that is never backed up, so it keeps growing. When the log drive fills, those databases stop accepting work — including EDI claim imports.
Evidence (sample script output)
EDI_Staging recovery FULL last_log NULL log_gb 71.4 log_reuse_wait_desc LOG_BACKUP
ReportServer recovery FULL last_log NULL log_gb 2.2 log_reuse_wait_desc LOG_BACKUP
L:\ total_gb 120.0 free_gb 8.4 free_pct 7.0
Why it matters
In FULL recovery, log space is only reused after a log backup. Without one, the log grows until the disk is full and the database refuses writes.
Recommendation
Decide the needed recovery point for each database. For EDI_Staging (re-loadable staging data) SIMPLE recovery may be appropriate; for ReportServer add log backups. Then reclaim log space once, in a maintenance window, and size the log properly.
Source: sqlserver/02_databases.sql · sqlserver/03_backup_history.sql · sqlserver/09_disk_file_growth.sql
F-05HighUser access
Too many accounts have full administrator rights — on every platform
Server(s): SMB-SQL01, SMB-SQL02, SMB-PG01, SMB-ORA01
Application accounts, a vendor support account and a former contractor can all do anything on the database servers. Only a handful of named people should.
Evidence (sample script output)
SMB-SQL01 sysadmin (9): sa · NT SERVICE\MSSQLSERVER · NT SERVICE\SQLSERVERAGENT · SMB\Domain Admins ·
SMB\IT-Support (group) · SMB\jmorales (enabled) · billing_app · edi_import · svc_ssrs
SMB-SQL02 sysadmin (4): NT SERVICE\MSSQLSERVER · NT SERVICE\SQLSERVERAGENT · SMB\DBA-Admins · portal_app
SMB-PG01 rolsuper = true (4): postgres · docintake_app · kbrown · replicator
SMB-ORA01 granted_role DBA (6): LEDGER_ADMIN · LEDGER_APP · LEDGER_VENDOR · BACKUP_USER · DPATEL · MLOPEZ
Why it matters
If any application is compromised — for example through the internet-facing payment portal — administrator rights let an attacker read every database, create new accounts and, on SMB-SQL01 where xp_cmdshell is on, run commands on the server. The PostgreSQL replicator role only needs the REPLICATION attribute, not superuser.
Recommendation
Agree a short list of named administrators per platform. Replace admin rights on application accounts with only the rights each app needs (database roles / object grants). Give the vendor account a time-limited, logged access route. Remove SMB\jmorales after confirming with HR that the contract ended.
Source: sqlserver/05_security_logins.sql · postgresql/03_roles_access_review.sql · oracle/03_users_access_review.sql
F-06HighUser access
Shared and built-in accounts are in daily use
Server(s): SMB-SQL01, SMB-PG01, SMB-ORA01
Several people log in with the same account, so nobody can tell who did what. Built-in master accounts are switched on and in use.
Evidence (sample script output)
SMB-SQL01 sa_is_disabled 0 sa_policy_checked 0 sa_password_last_set 2019-03-04 (Mixed mode)
sessions: login_name sa program_name SQLCMD host_name SMB-EDI01
SMB-PG01 usename postgres distinct_client_addresses 3 (three staff workstations, per interview)
SMB-ORA01 LEDGER_ADMIN distinct_machines 7 distinct_os_users 6 sessions 11
dba_users_with_defpwd: DBSNMP account_status OPEN
Why it matters
Shared accounts make audits and investigations impossible and mean a password can never really be changed. The sa and postgres accounts are the first ones attackers try; an Oracle account still using a published default password is effectively unlocked.
Recommendation
Give each person their own named account and each application its own login. Lock DBSNMP (or change its password if a monitoring tool needs it). Change the EDI script to its own login, then disable sa. Keep the postgres role for local maintenance only.
Source: sqlserver/05_security_logins.sql · sqlserver/12_user_access_review.sql · postgresql/03_roles_access_review.sql · oracle/03_users_access_review.sql
F-07HighUser access
Orphaned and unused accounts remain, and password rules are weak
Server(s): SMB-SQL01, SMB-PG01, SMB-ORA01
Accounts for people and systems that are no longer around are still open, and several platforms let passwords live forever.
Evidence (sample script output)
SMB-SQL01 BillingCore orphaned users (no matching login): 7
SQL logins with is_policy_checked = 0: 4 (sa, billing_app, edi_import, svc_ssrs)
SMB-ORA01 OPEN accounts with last_login > 90 days or never: 11 (incl. 3 named for former staff)
profile DEFAULT PASSWORD_LIFE_TIME UNLIMITED · LEDGER_APP_PROFILE FAILED_LOGIN_ATTEMPTS UNLIMITED
SMB-PG01 password_method md5 (legacy): 9 of 11 login roles · rolvaliduntil NULL on all
log_connections off → unused roles cannot be identified from the database
Why it matters
Every open account is a door. Old accounts are rarely watched, so they are a favorite way in. PostgreSQL does not record last-login dates, so connection logging has to be turned on before unused roles can be found.
Recommendation
Confirm each account with its owner (HR for people, IT for systems), then lock, monitor for complaints for two weeks, and drop. Apply sensible password and lockout rules. In PostgreSQL move to SCRAM-SHA-256 and turn on connection logging.
Source: sqlserver/12_user_access_review.sql · oracle/03_users_access_review.sql · postgresql/01_inventory_settings.sql · postgresql/03_roles_access_review.sql
F-08HighSecurity
Billing and patient data is not encrypted at rest, in backups, or on the network
Server(s): SMB-SQL01, SMB-SQL02, SMB-PG01, SMB-ORA01
If a database or backup file were copied off a server, it could be read as-is. Connections from the applications are not encrypted either.
Evidence (sample script output)
SQL Server sys.dm_database_encryption_keys: 0 rows (both servers)
dm_exec_connections encrypt_option FALSE: 214 (SMB-SQL01) · 96 (SMB-SQL02)
PostgreSQL ssl off · pg_stat_ssl.ssl false for all 38 client sessions
Oracle v$rman_configuration: no ENCRYPTION setting (backups unencrypted on disk)
Why it matters
On SQL Server 2016, Transparent Data Encryption (TDE) is an Enterprise-edition feature, so SMB-SQL01 (Standard) cannot use it today; Standard edition gained TDE in SQL Server 2019[12], so SMB-SQL02 could enable it now. Oracle encryption options depend on edition and license — confirm with Oracle before planning. Because these systems hold health and billing information, review the encryption decisions with your compliance advisor — this report is not legal or compliance advice.
Recommendation
Enable TDE on PracticePortal with the certificate backed up separately; turn on SQL Server backup encryption; enable SSL in PostgreSQL and require it in pg_hba for network connections; install trusted certificates and force encrypted SQL Server connections after testing each app. Include encryption in the SMB-SQL01 target design.
Source: sqlserver/02_databases.sql · sqlserver/05_security_logins.sql · postgresql/03_roles_access_review.sql · oracle/02_backup_recovery.sql
F-09HighOperations
SQL Server integrity checks have been off for 11 months, and index maintenance fails every week
Server(s): SMB-SQL01
Nobody would know if the billing database had quiet corruption, and the weekly tune-up job has failed every run since August.
Evidence (sample script output)
job_name enabled failures_30d successes_30d last_run
Maint - Weekly Index Rebuild 1 4 0 2026-10-11 02:00
DBA - CHECKDB Weekly 0 0 0 2025-11-09 03:00
EDI - Import 837 Files 1 2 28 2026-10-13 06:15
last failure (Index Rebuild, step 1): Msg 1712 — Online index operations can only be
performed in Enterprise edition of SQL Server.
Why it matters
Corruption found late is often unrecoverable because the backups contain it too. The index job was changed in August to use ONLINE = ON, which Standard edition does not support, so it fails immediately and does nothing — which is why fragmentation has built up (F-14).
Recommendation
Re-enable a weekly DBCC CHECKDB (against a restored copy if the window is tight). Fix the index job for Standard edition and rebuild only what needs it.
Source: sqlserver/04_agent_job_failures.sql
F-10HighSupportability
PostgreSQL 14 stops receiving fixes on November 12, 2026, and this server is 15 updates behind
Server(s): SMB-PG01
The intake database’s version is about to lose all security and bug fixes from the PostgreSQL project, and it has not had the fixes released in the last three years either.
Evidence (sample script output)
server_version 14.9 · server_version_num 140009 · uptime 211 days
Why it matters
The PostgreSQL project states that PostgreSQL 14 stops receiving fixes on November 12, 2026; the final 14.x minor release listed is 14.24[13]. Supported major versions include 17 (to November 2029) and 18 (to November 2030)[14].
Recommendation
Short term: apply 14.24 (a minor update — no data conversion) in the next window. Then upgrade to PostgreSQL 17 or 18 after confirming the intake application supports it; rehearse with pg_upgrade on a copy first.
Source: postgresql/01_inventory_settings.sql
F-11HighCapacity
A forgotten replication slot is holding 38 GB of logs and filling the PostgreSQL volume
Server(s): SMB-PG01
An old, disconnected copy of the database is still being ‘waited for’, so PostgreSQL keeps every change log for it. The disk is 91% full, which is what broke the nightly backups.
Evidence (sample script output)
slot_name old_replica_dr slot_type physical active false wal_status extended retained_wal 38 GB
pg_ls_waldir: 2,661 files · 41.6 GB
/var/lib/postgresql (df -h, from IT): 500 GB · 91% used
Why it matters
A replication slot tells PostgreSQL to keep logs until a replica has received them. The DR replica it belonged to was switched off in the spring (per interview), so the logs will grow until the disk is full and PostgreSQL stops.
Recommendation
Confirm the old replica is truly retired, then drop the slot in a change window and watch the WAL directory shrink. Move backups to separate storage. Re-introduce a standby later only with monitoring of its slot by your IT team.
Source: postgresql/02_databases_backup_wal.sql · postgresql/06_capacity.sql
F-12HighSupportability
The billing server’s Windows version reaches end of support on January 12, 2027
Server(s): SMB-SQL01
Even after a SQL Server fix, the operating system underneath loses support in three months.
Evidence (sample script output)
SMB-SQL01 windows_release 10.0 (Windows Server 2016 Standard, per inventory)
Why it matters
Windows Server 2016 extended support ends January 12, 2027[10]. An in-place SQL Server upgrade on this host would leave the operating-system problem unsolved.
Recommendation
Use a side-by-side migration to a new server on a currently supported Windows Server rather than an in-place upgrade.
Source: sqlserver/01_instance_inventory.sql
F-13MediumPerformance
Memory, parallelism and tempdb are at default settings on the billing server
Server(s): SMB-SQL01
The billing server is set up the way it came out of the box, which makes it slower than it needs to be and can starve Windows of memory.
Evidence (sample script output)
max server memory (MB) 2147483647 (physical_memory_mb 32,767 → Not capped)
max degree of parallelism 0
cost threshold for parallelism 5
Page life expectancy 212 s
tempdb: 1 data file (24,576 MB, growth 10 %)
Top waits (38 days): CXPACKET 34.1 % · PAGEIOLATCH_SH 22.7 % · WRITELOG 9.8 % · LCK_M_U 6.2 % ·
SOS_SCHEDULER_YIELD 5.9 % · ASYNC_NETWORK_IO 4.4 % · PAGELATCH_UP 3.1 %
Why it matters
High CXPACKET with MAXDOP 0 and a cost threshold of 5 points to small queries going parallel unnecessarily. PAGEIOLATCH waits and a low page life expectancy show data being re-read from disk. PAGELATCH_UP on a single tempdb file suggests allocation contention. SMB-SQL02, by contrast, is configured sensibly.
Recommendation
Cap max server memory (around 27–28 GB to start), set MAXDOP to 4–8 and cost threshold to 50 as a starting point, add equal-size tempdb data files. Change one thing at a time and compare waits before and after.
Source: sqlserver/06_configuration.sql · sqlserver/07_wait_stats.sql
F-14MediumPerformance
Index health: heavy fragmentation, unused indexes and one very large heap
Server(s): SMB-SQL01
Several of the busiest billing tables are stored inefficiently, and the optimizer keeps asking for an index that does not exist.
Evidence (sample script output)
BillingCore: 41 indexes ≥ 30 % fragmented and ≥ 1,000 pages
dbo.ClaimLine IX_ClaimLine_ClaimID page_count 1,912,404 fragmentation_pct 87.3
BillingCore: 17 nonclustered indexes with 0 reads since restart
Top missing-index hint: dbo.ClaimLine equality (PayerID) inequality (ServiceDate)
include (ChargeAmount, Status) user_seeks 48,210 avg_user_impact_pct 91.4
EDI_Staging heap: dbo.EDI_RawSegment row_count 212,480,115
Why it matters
Unused indexes slow every insert and update. Missing-index hints are suggestions, not instructions, and must be checked against existing indexes.
Recommendation
Fix the maintenance job first (F-09). Test the missing-index hint. Drop unused indexes only after a full business cycle including month-end. Add a clustered index or purge routine to EDI_RawSegment.
Source: sqlserver/08_index_health.sql
F-15MediumCapacity
SQL Server data drive has about 10 months of room; autogrowth settings are poor
Server(s): SMB-SQL01
The main data drive is filling at roughly 11 GB a month, partly because backup files are kept on it.
Evidence (sample script output)
E:\ total_gb 1000.0 free_gb 118.3 free_pct 11.8 (holds data files and E:\SQLBackups)
BillingCore_Data growth 1 MB → Very small fixed growth - review
ClaimsArchive_Data growth 10 % → Percent growth - review
Growth (180 days): BillingCore 151.2 → 182.4 GB · ClaimsArchive 382.0 → 418.2 GB
Why it matters
About 67 GB of growth in six months is roughly 11 GB a month, so E: fills in about 10 months — sooner if backup retention grows.
Recommendation
Move backups off E: (also helps F-03). Set fixed growth increments. Size the new server’s storage from this trend plus headroom.
Source: sqlserver/09_disk_file_growth.sql
F-16MediumSupportability
Supported servers are behind on updates
Server(s): SMB-SQL02, SMB-ORA01
The portal and ledger databases are on supported versions, but they have not been kept up to date with released fixes.
Evidence (sample script output)
SMB-SQL02 15.0.4261.1 update_level CU18
SMB-ORA01 dba_registry_sqlpatch latest: 19.18.0.0.230117 Release Update (applied 2023-03-04)
Why it matters
SQL Server 2019 is in extended support until January 9, 2030[4]; check the current build against Microsoft’s list on the day of patching[11]. Oracle 19c is a long-term release; Oracle has announced Premier Support to December 31, 2029[15] (confirm in Oracle’s Lifetime Support Policy). Oracle ships Release Updates quarterly, so 19.18 is several updates behind.
Recommendation
Patch SMB-SQL02 after testing on a copy. Apply the current 19c Release Update to SMB-ORA01 once the ledger vendor confirms it is certified, rehearsing on a clone first.
Source: sqlserver/01_instance_inventory.sql · oracle/01_inventory.sql
F-17MediumSecurity
xp_cmdshell is enabled on the billing server and used by three EDI procedures
Server(s): SMB-SQL01
SQL Server is allowed to run Windows commands, and three claim-import procedures rely on it to move files.
Evidence (sample script output)
xp_cmdshell configured_value 1 running_value 1
EDI_Staging modules flagged 'xp_cmdshell call': dbo.usp_MoveInboundFiles,
dbo.usp_ArchiveProcessedFiles, dbo.usp_PurgeOutbound
Why it matters
Combined with sysadmin application logins (F-05), a compromised application could run commands on the server. It is also not available in Azure SQL Database or Managed Instance.
Recommendation
Move the file handling to an SSIS package, a PowerShell job step or the application, then disable xp_cmdshell.
Source: sqlserver/06_configuration.sql · sqlserver/11_upgrade_blockers.sql
F-18MediumOperations
Nobody is notified when a job or backup fails
Server(s): All four
Failures are recorded but no one is told, which is how the backup, integrity and disk problems went unnoticed.
Evidence (sample script output)
SQL Server sysoperators 0 rows · notify_level_email 0 on all jobs · sysmail_profile 0 rows
PostgreSQL pg_dump cron job writes to a local log only
Oracle RMAN scripts run from cron; no failure e-mail configured (per interview)
Why it matters
Silent failure is the common thread behind several findings.
Recommendation
Configure failure e-mails on every backup and maintenance job to a mailbox your IT contact actually reads, plus a simple daily ‘backup age’ report. (GracePath does not provide round-the-clock monitoring; these alerts go to your own team.)
Source: sqlserver/04_agent_job_failures.sql · sqlserver/11_upgrade_blockers.sql
F-19MediumModernization readiness
Items to resolve before moving the billing server
Server(s): SMB-SQL01
None of these is a problem today, but each affects which platform you can move to and how much testing is needed.
Evidence (sample script output)
BillingCore assembly PdfStamp permission_set_desc UNSAFE_ACCESS
BillingCore 12 modules reference ClaimsArchive / EDI_Staging (three-part names)
ReportServer / ReportServerTempDB present → SSRS 2016 catalog
ClaimsArchive compatibility_level 110 · dbo.ClaimNote.NoteText ntext · dbo.ClaimImage.Img image
Why it matters
From SQL Server 2017 onward, ‘clr strict security’ requires UNSAFE assemblies to be signed or trusted. Azure SQL Database does not support CLR or cross-database queries, which rules it out for BillingCore as built. There will be no SSRS release after 2022; from SQL Server 2025 on-premises reporting moves to Power BI Report Server, available to SQL Server 2025 Standard and Enterprise customers, and SSRS 2022 is supported until January 11, 2033[9]. Compatibility level 110 is still accepted by SQL Server 2022 and 2025[7], but should be raised deliberately after testing.
Recommendation
Sign or replace the PdfStamp assembly; choose SSRS 2022 or Power BI Report Server with the report owners; keep the three billing databases together; test ClaimsArchive at the target level on a copy; run Microsoft’s migration assessment tooling alongside this review.
Source: sqlserver/11_upgrade_blockers.sql · sqlserver/02_databases.sql
F-20MediumHigh availability
No standby for the billing or ledger databases
Server(s): SMB-SQL01, SMB-ORA01
If either server fails, recovery means rebuilding and restoring from backup, which has never been timed (F-03).
Evidence (sample script output)
SMB-SQL01 IsClustered 0 · IsHadrEnabled 0 · no log shipping configured
SMB-ORA01 database_role PRIMARY · no standby destinations (v$archive_dest) · Standard Edition 2
Why it matters
With a 4-hour RTO for billing, a restore-only plan may be enough if a timed test proves it. If not, a standby is needed. High-availability options and their licensing differ by product and edition.
Recommendation
Decide after the timed restore tests. If restores meet the targets, document and drill them; if not, add a standby (for SQL Server, log shipping or a Basic Availability Group; for Oracle, an option that matches your edition and license) to the new design.
Source: sqlserver/01_instance_inventory.sql · oracle/01_inventory.sql
F-21LowPerformance
PostgreSQL vacuum is falling behind on the main documents table
Server(s): SMB-PG01
Deleted and updated rows are not being cleaned up quickly, so the main table is bigger and slower than it should be.
Evidence (sample script output)
public.document_page n_live_tup 18,204,551 n_dead_tup 8,177,032 dead_pct 31.0
last_autovacuum 2026-09-24 03:12
pid 48211 usename docintake_app state idle in transaction transaction_age 06:12:44
Why it matters
A session left ‘idle in transaction’ by the intake app stops vacuum from removing old rows anywhere in the database.
Recommendation
Ask the app vendor about the idle transaction; set idle_in_transaction_session_timeout after testing; tune autovacuum for the large table.
Source: postgresql/04_performance_maintenance.sql
F-22LowModernization readiness
Deprecated SQL Server features are in use (warnings, not blockers)
Server(s): SMB-SQL01
Some older coding patterns are still used. They work today but should be cleaned up during migration testing.
Evidence (sample script output)
Deprecated Features counters (38 days): sysdatabases 18,402 · Data types: text ntext or image 9,877 ·
sysobjects 4,215 · More than two-part column name 1,309 · Table hint without WITH 642 · SET ROWCOUNT 88
Why it matters
Deprecated means scheduled for eventual removal, not removed. None of these is on Microsoft’s list of features removed in SQL Server 2022 or 2025 (for example Data Quality Services and Master Data Services are removed in 2025 and are not installed here)[8].
Recommendation
Log the calling code with an Extended Events session during testing and fix it in the procedures that are touched for the migration anyway.
Source: sqlserver/10_deprecated_features.sql · sqlserver/11_upgrade_blockers.sql