Moving a SQL Server workload to a new instance means recreating five things by hand, unless you script them: logins (with the same SIDs, or every restored database ends up with orphaned users), SQL Agent jobs, database user and role mappings, linked servers, and the actual RESTORE ... WITH MOVE statements when the target’s drive layout doesn’t match the source. Doing this by hand across more than a handful of databases is exactly the kind of repetitive, easy-to-miss-a-step work that migrations don’t forgive.
Six generator scripts, each covered on its own page below, each read the source instance’s own system catalogs and produce plain DDL text, reviewed in your own SSMS window, then run against the target once you’re satisfied it’s correct. Nothing touches the target automatically, that review step is the whole design. These generators carried two genuine bugs and one silent-corruption risk between them, all fixed and documented on the page for the script they affect.
Start Here
The Six Generators
🔑 Logins
Generate-LoginScript
SIDs and hashed passwords preserved, so restored databases don’t end up with orphaned users.
⏰ Agent Jobs
Generate-AgentJobScript
Job, steps, start step, and schedule, in the order needed to recreate each job correctly.
👤 User Mappings
Generate-UserMappingScript
Database owner, roles, users, and role memberships, per database, across the whole instance.
🔗 Linked Servers
Generate-LinkedServerScript
Linked servers and login mappings. Stored remote credentials can’t be scripted, those come out as a placeholder.
💽 Restore with Move
Generate-RestoreWithMoveScriptRESTORE ... WITH MOVE for every database, when source and target drive layouts differ.
🔧 Fix Orphaned Users
Fix-OrphanedUsers
The cleanup pass: generates the ALTER USER remap for anything the login script didn’t line up cleanly.
Why This Matters
- Manual migration steps are where consistency dies. Recreating twenty logins by hand across a maintenance window is exactly when one gets missed, or a permission gets applied differently than the one before it
- SID-preserving login recreation avoids the classic orphaned-user problem. A
CREATE USERmapped to a login whose SID doesn’t match the original database user’s SID silently fails to map, and you find out when an application can’t connect - Generated DDL means you review the exact statement before it runs, not a black-box migration tool doing it for you. Every
sp_add_job,CREATE USER, andsp_addlinkedservercall is right there to read - These are generators, not a full migration platform. They don’t move data, don’t orchestrate cutover timing, and don’t replace a proper migration runbook. They solve the specific, tedious sub-problem of “recreate this metadata correctly on the target”
How They Fit Together
Run these in the natural order:
- Generate Login Script first, later steps need logins to already exist on the target
- Restore the databases with Generate Restore With Move Script if drive layouts differ, or a plain restore if they match
- Generate User Mapping Script next, user mapping needs both the databases and the logins to exist
- Generate Agent Job Script and Generate Linked Server Script last, in either order, both depend on logins existing but not on each other
- Fix Orphaned Users as a final check, catches anything the SID-preserving login script didn’t line up cleanly
Beyond the Scripts: The Topology You’re Migrating Into Changes Everything
These five scripts solve one part of a migration: recreating logins, jobs, user mappings, linked servers, and file paths correctly on the target. They don’t solve the bigger question underneath all of it, how the source and target relate to each other during cutover, and that answer depends entirely on what you’re migrating from and to.
Standalone to standalone. The simplest case, and what these scripts are written for. Full backup, restore on the new server (Generate Restore With Move Script handles the path translation), run the other four scripts, then cut over. The one thing worth doing properly here even in the simple case: point applications at a DNS name, not a hardcoded server name, from the start. A CNAME means cutover is repointing one DNS record instead of touching every connection string in every application, and the next migration is just as easy.
Windows Server Failover Clustering, Always On Availability Groups, or SAN-based storage. This is a different problem, and these five scripts are a small part of it, not the whole plan. The target needs the same HA topology as the source, or a deliberate decision to change it, not just the databases and metadata recreated on a lone server. Enterprise Edition features (Always On availability groups, readable secondaries, Resource Governor) need licensing and edition parity on the target before anything else matters. Check which ones actually apply to your version rather than assuming: partitioning moved to Standard in SQL Server 2016 SP1 and TDE in 2019. SAN-backed storage means the storage team is part of the migration, not just the DBA. None of that shows up in a RESTORE ... WITH MOVE script, and treating it as if it does is how a migration plan looks complete on paper and falls over on cutover night.
Migrating to Azure. A different migration again, not a variation on the same theme. Azure SQL Database, Azure SQL Managed Instance, and SQL Server on an Azure VM are three different targets with three different migration paths (the Database Migration Service, native backup-to-URL restore, or a straightforward lift-and-shift, depending which one), and the right choice depends on compatibility requirements these scripts don’t check for at all.
The honest scope of this pillar: it handles the metadata recreation problem well, for a standalone-to-standalone migration. Anything involving HA, SAN, or cloud needs that same rigor applied to a much bigger set of questions, that’s a genuinely separate piece of work, not a checklist item to add to this one.
Best Practices Across the Series
- Always review the generated DDL before running it on the target. Nothing here executes automatically, that review step is the entire point of generating text instead of just running a migration tool.
- Map
owner_login_namevalues in the Agent Job script’s output to real logins that exist on the target before running it. - Replace every
ENTER_PASSWORD_HEREplaceholder in the Linked Server script’s output; stored remote credentials genuinely cannot be scripted, there’s no way around re-entering them by hand. - Before trusting a
RESTORE ... WITH MOVEscript’s paths, check for the-- WARNING: source path does not start with @OldDataRootcomment. If it’s there, the computedMOVEpath is not reliable and needs to be corrected by hand. - After restoring, run Fix Orphaned Users to catch anything the SID-preserving login script didn’t line up cleanly.
Common Questions
Why do logins break after a migration even though I recreated them?
Because a SQL login’s SID has to match the database user it maps to. Recreating a login by name gives it a new SID, so the user inside the database is orphaned and the login appears to have no access. Scripting logins with their original SIDs avoids the whole problem.
What gets forgotten most often in a migration?
The things that are not the database: Agent jobs, linked servers, credentials and proxies, database mail, server-level triggers, and non-default configuration. The data moves because moving it is the obvious task; the surroundings move because someone made a list.
Can I run generated scripts without reading them?
No, and that is the point of generating rather than automating. They are built from the source server’s real state, which may include things you deliberately do not want to carry forward, such as a login that should have been removed years ago.
How do I check compatibility before moving to a newer version?
Look at deprecated feature usage and compatibility level before the move, not after. A database restored to a newer instance keeps its old compatibility level, which delays the problem to the day someone changes it. Compatibility Levels: What Actually Changes, in Related Scripts below, covers that in full.
The Migration Runbooks
The generators handle the metadata; these runbooks are the cutover itself, step by step, with the network pre-flight, verification gates and rollback points:
- Standalone Instance Migration Runbook, the base backup-and-restore cutover
- Availability Groups and Failover Clusters Migration Runbook, the HA variant with endpoint and listener steps
- Version Upgrade Runbook, side-by-side and in-place
- Edition Change Runbook, upgrade and downgrade paths
- Windows Server OS Upgrade Runbook for SQL Server, the one everyone forgets until the SPNs break
See Also
This pillar is part of DBA Scripts: The Complete Guide, the map across the whole series organized by the question you’re actually asking.
Summary
Reading generated DDL and running generated DDL are two different claims. Two real bugs came out of actually running these five generators: a linked-server query against columns that don’t exist on sys.linked_logins, and a user-mapping generator that produced a guaranteed syntax error the moment any database had an unmapped user, which on a real instance is not a rare case. Both are fixed and reverified. A third finding, silent path corruption in the restore-with-move script when the configured path prefix doesn’t match reality, is fixed with an explicit warning rather than a silent wrong answer.
None of these five scripts replace a proper migration runbook or a dedicated migration tool. What they do is turn five tedious, error-prone manual recreation steps into DDL you can actually read before you run it. Run them in order, logins first, then restore, then user mappings, then jobs and linked servers, and run Get Migration Risk Assessment before and after to confirm the migration actually landed the way it was supposed to.
Leave a Reply