﻿/* SQL Server Migration Inventory, SQL Server 2016+ */
SET NOCOUNT ON; SET XACT_ABORT ON;
DECLARE @InventoryDB sysname=N'DBA_MigrationInventory', @IncludeSensitiveLoginScripts bit=0, @sql nvarchar(max);
IF DB_ID(@InventoryDB) IS NULL EXEC(N'CREATE DATABASE '+QUOTENAME(@InventoryDB));
SET @sql=N'USE '+QUOTENAME(@InventoryDB)+N'; IF SCHEMA_ID(N''inv'') IS NULL EXEC(N''CREATE SCHEMA inv AUTHORIZATION dbo'');
DECLARE @t sysname; DECLARE c CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM sys.tables WHERE schema_id=SCHEMA_ID(N''inv''); OPEN c; FETCH NEXT FROM c INTO @t;
WHILE @@FETCH_STATUS=0 BEGIN EXEC(N''DROP TABLE inv.''+QUOTENAME(@t)); FETCH NEXT FROM c INTO @t; END; CLOSE c; DEALLOCATE c;'; EXEC(@sql);
SET @sql=N'USE '+QUOTENAME(@InventoryDB)+N';
SELECT SYSDATETIMEOFFSET() CollectedAt,CAST(SERVERPROPERTY(''ServerName'') AS nvarchar(256)) SourceServer,ORIGINAL_LOGIN() CollectedBy INTO inv.CollectionInfo;
SELECT CAST(SERVERPROPERTY(''ServerName'') AS nvarchar(256)) ServerName,CAST(SERVERPROPERTY(''MachineName'') AS nvarchar(256)) MachineName,CAST(SERVERPROPERTY(''InstanceName'') AS nvarchar(256)) InstanceName,CAST(SERVERPROPERTY(''ComputerNamePhysicalNetBIOS'') AS nvarchar(256)) PhysicalHost,CAST(SERVERPROPERTY(''Edition'') AS nvarchar(256)) Edition,CAST(SERVERPROPERTY(''ProductVersion'') AS nvarchar(128)) ProductVersion,CAST(SERVERPROPERTY(''ProductLevel'') AS nvarchar(128)) ProductLevel,CAST(SERVERPROPERTY(''Collation'') AS nvarchar(256)) Collation,CAST(SERVERPROPERTY(''IsClustered'') AS int) IsClustered,CAST(SERVERPROPERTY(''IsHadrEnabled'') AS int) IsHadrEnabled,CAST(SERVERPROPERTY(''IsIntegratedSecurityOnly'') AS int) WindowsAuthOnly,CAST(@@VERSION AS nvarchar(max)) FullVersion INTO inv.InstanceInfo;
SELECT cpu_count,scheduler_count,hyperthread_ratio,socket_count,cores_per_socket,physical_memory_kb/1024 PhysicalMemoryMB,committed_kb/1024 SqlCommittedMB,committed_target_kb/1024 SqlTargetMB,sqlserver_start_time,virtual_machine_type_desc INTO inv.MachineInfo FROM sys.dm_os_sys_info;
SELECT DISTINCT volume_mount_point,logical_volume_name,file_system_type,CAST(total_bytes/1073741824.0 AS decimal(19,2)) TotalGB,CAST(available_bytes/1073741824.0 AS decimal(19,2)) FreeGB,CAST(100.0*available_bytes/NULLIF(total_bytes,0) AS decimal(9,2)) FreePct,supports_compression,is_compressed INTO inv.Volumes FROM sys.master_files f CROSS APPLY sys.dm_os_volume_stats(f.database_id,f.file_id);
SELECT d.database_id,d.name,d.state_desc,d.recovery_model_desc,d.compatibility_level,d.collation_name,d.user_access_desc,d.is_read_only,d.page_verify_option_desc,d.is_broker_enabled,d.is_trustworthy_on,d.containment_desc,d.log_reuse_wait_desc,d.create_date,SUSER_SNAME(d.owner_sid) OwnerName,CAST(SUM(CASE WHEN f.type=0 THEN f.size ELSE 0 END)*8.0/1024 AS decimal(19,2)) DataMB,CAST(SUM(CASE WHEN f.type=1 THEN f.size ELSE 0 END)*8.0/1024 AS decimal(19,2)) LogMB INTO inv.Databases FROM sys.databases d LEFT JOIN sys.master_files f ON d.database_id=f.database_id GROUP BY d.database_id,d.name,d.state_desc,d.recovery_model_desc,d.compatibility_level,d.collation_name,d.user_access_desc,d.is_read_only,d.page_verify_option_desc,d.is_broker_enabled,d.is_trustworthy_on,d.containment_desc,d.log_reuse_wait_desc,d.create_date,d.owner_sid;
SELECT DB_NAME(database_id) DatabaseName,file_id,name LogicalName,type_desc,physical_name,CAST(size*8.0/1024 AS decimal(19,2)) SizeMB,max_size,growth,is_percent_growth INTO inv.DatabaseFiles FROM sys.master_files;
SELECT name,value,value_in_use,minimum,maximum,is_dynamic,is_advanced INTO inv.Configuration FROM sys.configurations;
SELECT servicename,startup_type_desc,status_desc,service_account,filename,is_clustered,cluster_nodename,instant_file_initialization_enabled,last_startup_time INTO inv.Services FROM sys.dm_server_services;
SELECT principal_id,name,type_desc,default_database_name,default_language_name,is_disabled,create_date,modify_date INTO inv.ServerPrincipals FROM sys.server_principals WHERE name NOT LIKE N''##%'';
SELECT r.name ServerRole,m.name MemberName INTO inv.ServerRoleMembers FROM sys.server_role_members x JOIN sys.server_principals r ON r.principal_id=x.role_principal_id JOIN sys.server_principals m ON m.principal_id=x.member_principal_id;
SELECT credential_id,name,credential_identity,create_date,modify_date INTO inv.Credentials FROM sys.credentials;
SELECT server_id,name,product,provider,data_source,location,provider_string,catalog,connect_timeout,query_timeout,is_linked,is_rpc_out_enabled,is_data_access_enabled,collation_name INTO inv.LinkedServers FROM sys.servers;
SELECT s.name LinkedServer,p.name LocalPrincipal,l.uses_self_credential,l.remote_name INTO inv.LinkedServerMappings FROM sys.linked_logins l JOIN sys.servers s ON s.server_id=l.server_id LEFT JOIN sys.server_principals p ON p.principal_id=l.local_principal_id;
SELECT job_id,name,enabled,description,start_step_id,SUSER_SNAME(owner_sid) OwnerName,date_created,date_modified INTO inv.AgentJobs FROM msdb.dbo.sysjobs;
SELECT j.name JobName,s.step_id,s.step_name,s.subsystem,s.command,s.database_name,s.database_user_name,s.retry_attempts,s.retry_interval,s.output_file_name,s.proxy_id INTO inv.AgentJobSteps FROM msdb.dbo.sysjobsteps s JOIN msdb.dbo.sysjobs j ON j.job_id=s.job_id;
SELECT j.name JobName,s.name ScheduleName,s.enabled,s.freq_type,s.freq_interval,s.freq_subday_type,s.freq_subday_interval,s.freq_recurrence_factor,s.active_start_date,s.active_end_date,s.active_start_time,s.active_end_time INTO inv.AgentSchedules FROM msdb.dbo.sysjobs j JOIN msdb.dbo.sysjobschedules x ON x.job_id=j.job_id JOIN msdb.dbo.sysschedules s ON s.schedule_id=x.schedule_id;
SELECT id,name,enabled,email_address,pager_address INTO inv.Operators FROM msdb.dbo.sysoperators;
SELECT id,name,message_id,severity,enabled,database_name,event_description_keyword,job_id INTO inv.Alerts FROM msdb.dbo.sysalerts;
SELECT profile_id,name,description,last_mod_datetime,last_mod_user INTO inv.DatabaseMailProfiles FROM msdb.dbo.sysmail_profile;
SELECT account_id,name,description,email_address,display_name,replyto_address,last_mod_datetime,last_mod_user INTO inv.DatabaseMailAccounts FROM msdb.dbo.sysmail_account;
SELECT endpoint_id,name,protocol_desc,type_desc,state_desc,is_admin_endpoint INTO inv.Endpoints FROM sys.endpoints;
SELECT name,is_disabled,create_date,modify_date,OBJECT_DEFINITION(object_id) Definition INTO inv.ServerTriggers FROM sys.server_triggers;
SELECT audit_id,name,audit_guid,create_date,modify_date,type_desc,on_failure_desc,is_state_enabled,queue_delay,predicate INTO inv.Audits FROM sys.server_audits;
SELECT server_specification_id,name,create_date,modify_date,is_state_enabled,audit_guid INTO inv.AuditSpecifications FROM sys.server_audit_specifications;
SELECT d.name DatabaseName,MAX(CASE WHEN b.type=''D'' THEN b.backup_finish_date END) LastFull,MAX(CASE WHEN b.type=''I'' THEN b.backup_finish_date END) LastDiff,MAX(CASE WHEN b.type=''L'' THEN b.backup_finish_date END) LastLog INTO inv.BackupStatus FROM sys.databases d LEFT JOIN msdb.dbo.backupset b ON b.database_name=d.name GROUP BY d.name;
SELECT DB_NAME(database_id) DatabaseName,encryption_state,percent_complete,key_algorithm,key_length,encryptor_thumbprint INTO inv.TDE FROM sys.dm_database_encryption_keys;
SELECT name,is_published,is_subscribed,is_merge_published,is_distributor,is_cdc_enabled,is_encrypted,temporal_history_retention_enabled INTO inv.DatabaseFeatures FROM sys.databases;
CREATE TABLE inv.UserDatabaseObjects(DatabaseName sysname,FeatureName nvarchar(128),ItemCount bigint);
CREATE TABLE inv.ExternalDependencies(DatabaseName sysname,ReferencingObject nvarchar(776),ReferencedServer sysname,ReferencedDatabase sysname,ReferencedSchema sysname,ReferencedEntity sysname);
CREATE TABLE inv.LoginMigrationScripts(LoginName sysname,IsSensitive bit,CreateScript nvarchar(max));
CREATE TABLE inv.MigrationChecklist(Category nvarchar(100),Item nvarchar(200),DetectedCount bigint,RequiredAction nvarchar(1000),Status nvarchar(30));'; EXEC(@sql);
BEGIN TRY SET @sql=N'USE '+QUOTENAME(@InventoryDB)+N'; SELECT group_id,name,automated_backup_preference_desc,failure_condition_level,health_check_timeout INTO inv.AvailabilityGroups FROM sys.availability_groups; SELECT replica_id,group_id,replica_server_name,endpoint_url,availability_mode_desc,failover_mode_desc,seeding_mode_desc INTO inv.AvailabilityReplicas FROM sys.availability_replicas; SELECT g.name AvailabilityGroup,d.database_name INTO inv.AGDatabases FROM sys.availability_databases_cluster d JOIN sys.availability_groups g ON g.group_id=d.group_id;'; EXEC(@sql); END TRY BEGIN CATCH PRINT ERROR_MESSAGE(); END CATCH;
DECLARE @db sysname; DECLARE c CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM sys.databases WHERE database_id>4 AND state_desc=N'ONLINE' AND source_database_id IS NULL; OPEN c; FETCH NEXT FROM c INTO @db;
WHILE @@FETCH_STATUS=0 BEGIN BEGIN TRY SET @sql=N'USE '+QUOTENAME(@InventoryDB)+N'; INSERT inv.UserDatabaseObjects SELECT N'''+REPLACE(@db,'''','''''')+N''',N''Users'',COUNT_BIG(*) FROM '+QUOTENAME(@db)+N'.sys.database_principals WHERE principal_id>4 AND type IN(''S'',''U'',''G'',''E'',''X''); INSERT inv.UserDatabaseObjects SELECT N'''+REPLACE(@db,'''','''''')+N''',N''CLR assemblies'',COUNT_BIG(*) FROM '+QUOTENAME(@db)+N'.sys.assemblies WHERE is_user_defined=1; INSERT inv.UserDatabaseObjects SELECT N'''+REPLACE(@db,'''','''''')+N''',N''Synonyms'',COUNT_BIG(*) FROM '+QUOTENAME(@db)+N'.sys.synonyms; INSERT inv.UserDatabaseObjects SELECT N'''+REPLACE(@db,'''','''''')+N''',N''Certificates'',COUNT_BIG(*) FROM '+QUOTENAME(@db)+N'.sys.certificates WHERE name NOT LIKE ''##%''; INSERT inv.ExternalDependencies SELECT DISTINCT N'''+REPLACE(@db,'''','''''')+N''',QUOTENAME(OBJECT_SCHEMA_NAME(referencing_id,DB_ID(N'''+REPLACE(@db,'''','''''')+N''')))+N''.''+QUOTENAME(OBJECT_NAME(referencing_id,DB_ID(N'''+REPLACE(@db,'''','''''')+N'''))),referenced_server_name,referenced_database_name,referenced_schema_name,referenced_entity_name FROM '+QUOTENAME(@db)+N'.sys.sql_expression_dependencies WHERE referenced_server_name IS NOT NULL OR referenced_database_name IS NOT NULL;'; EXEC(@sql); END TRY BEGIN CATCH PRINT N'Skipped '+QUOTENAME(@db)+N': '+ERROR_MESSAGE(); END CATCH; FETCH NEXT FROM c INTO @db; END; CLOSE c; DEALLOCATE c;
IF @IncludeSensitiveLoginScripts=1 BEGIN SET @sql=N'USE '+QUOTENAME(@InventoryDB)+N'; INSERT inv.LoginMigrationScripts SELECT name,1,N''CREATE LOGIN ''+QUOTENAME(name)+N'' WITH PASSWORD = ''+CONVERT(nvarchar(max),password_hash,1)+N'' HASHED, SID = ''+CONVERT(nvarchar(max),sid,1)+N'', DEFAULT_DATABASE = ''+QUOTENAME(COALESCE(default_database_name,N''master''))+N'';'' FROM sys.sql_logins WHERE name NOT LIKE N''##%'';'; EXEC(@sql); END;
SET @sql=N'USE '+QUOTENAME(@InventoryDB)+N'; INSERT inv.MigrationChecklist SELECT N''Databases'',N''User databases'',COUNT_BIG(*),N''Backup/restore; preserve owner, compatibility, files and recovery model'',N''Review'' FROM inv.Databases WHERE database_id>4 UNION ALL SELECT N''Security'',N''Server principals'',COUNT_BIG(*),N''Migrate required logins with SID and server permissions'',N''Review'' FROM inv.ServerPrincipals UNION ALL SELECT N''Integration'',N''Linked servers'',COUNT_BIG(*),N''Recreate providers/options/mappings; re-enter secrets securely'',N''Review'' FROM inv.LinkedServers WHERE server_id<>0 UNION ALL SELECT N''Automation'',N''SQL Agent jobs'',COUNT_BIG(*),N''Script jobs, owners, proxies, steps and schedules'',N''Review'' FROM inv.AgentJobs UNION ALL SELECT N''Security'',N''TDE databases'',COUNT_BIG(*),N''Back up certificate and private key before restore'',N''Critical'' FROM inv.TDE WHERE encryption_state IN(2,3,4,5,6) UNION ALL SELECT N''Dependencies'',N''External references'',COUNT_BIG(*),N''Validate servers, databases, DNS, paths and connectivity'',N''Review'' FROM inv.ExternalDependencies UNION ALL SELECT N''Operations'',N''Audits'',COUNT_BIG(*),N''Recreate audit targets/specifications and validate paths'',N''Review'' FROM inv.Audits; SELECT * FROM inv.MigrationChecklist ORDER BY Category,Item;'; EXEC(@sql);
