/*********************************PLEASE READ THIS BEFORE YOU PROCEED*****************************************/ /***************DO NOT RUN THIS SCRIPT ON PRIMARY, THIS SCRIPT ONLY TO BE RUN ON SECONDARY********************/ USE [master] GO /****** Object: Database [dbmig] Script Date: 1/1/2022 8:51:38 PM ******/ CREATE DATABASE [dbmig] GO USE [dbmig] /****** Object: Table [dbo].[tblLSTracking] Script Date: 1/1/2022 8:51:38 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[tblLSTracking]( [Server] [char](100) NULL, [database_name] [nvarchar](128) NULL, [backup_start_date] [datetime] NULL, [backup_finish_date] [datetime] NULL, [backup_type] [varchar](8) NULL, [backup_size] [numeric](20, 0) NULL, [physical_device_name] [nvarchar](260) NULL, [file_name] [nvarchar](260) NULL, [backupset_name] [nvarchar](128) NULL, [processing_status] [varchar](30) NULL, [backup_set_id] [int] NOT NULL, [checkpoint_lsn] [numeric](25, 0) NULL, [database_backup_lsn] [numeric](25, 0) NULL ) ON [PRIMARY] GO /****** Object: StoredProcedure [dbo].[uspManageSecondaryCutover] Script Date: 1/1/2022 8:51:38 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[uspManageSecondaryCutover] ( @ListofDBs NVARCHAR(MAX) -- Enter your database name as comma separated ) /***********Author - Rajib Sadhu**************/ /* EXEC dbo.uspManageSecondaryCutover 'AdventureWorks2019,TEST_1' */ AS BEGIN SET NOCOUNT ON DECLARE @cmd NVARCHAR(4000) DECLARE @LiCounter INT = 1 DECLARE @LiMaxCount INT DECLARE @LsDatabaseName SYSNAME DECLARE @JobName NVARCHAR(1000) /*****************Parse the comma separated database names****************/ CREATE TABLE #DBList ( DatabaseId INT IDENTITY(1,1), DatabaseName SYSNAME ) DECLARE @DbListXML XML = CAST(''+ Replace(@ListofDBs, ',', '')+ '' AS XML) INSERT INTO #DBList (DatabaseName) SELECT f.x.value('.', 'SYSNAME') AS user_id FROM @DbListXML.nodes('/root/U') f(x) /****************Check if the database specified exists***********************************/ IF EXISTS (SELECT DatabaseName from #DBList WHERE DatabaseName NOT IN (SELECT name FROM master.sys.databases)) BEGIN RAISERROR('One of more databases is not present. Cleanup operation is cancelled.', 16, 1) RETURN 1 END /*******************************Drop Tran Log Restore Jobs***************************/ IF EXISTS(SELECT 1 FROM [dbo].[tblLSTracking] WHERE [processing_status] <> 'Processed' AND [database_name] IN (SELECT DatabaseName from #DBList)) BEGIN RAISERROR('Latest Transaction Logs are not applied for one or more databases in the list. Cutover operation is aborted.', 16, 1) RETURN 1 END SET @LiCounter = 1 WHILE(1=1) BEGIN SELECT @LiMaxCount = MAX(DatabaseId) FROM #DBList IF(@LiCounter <= @LiMaxCount) BEGIN SELECT @LsDatabaseName = DatabaseName FROM #DBList WHERE DatabaseId = @LiCounter SET @JobName = 'LSRestore_' + @LsDatabaseName SET @cmd = 'EXEC msdb..sp_delete_job @job_name = ' + '''' + @JobName + '''' EXEC (@cmd) SET @LiCounter = @LiCounter +1 END ElSE BEGIN BREAK END END --WHILE(1=1) /******************************Finish Restore***********************************************/ SET @LiCounter = 1 WHILE(1=1) BEGIN SELECT @LiMaxCount = MAX(DatabaseId) FROM #DBList IF(@LiCounter <= @LiMaxCount) BEGIN SELECT @LsDatabaseName = DatabaseName FROM #DBList WHERE DatabaseId = @LiCounter SET @cmd = 'EXECUTE msdb.dbo.rds_finish_restore ' + '''' + @LsDatabaseName + '''' EXEC (@cmd) SET @LiCounter = @LiCounter +1 END ElSE BEGIN BREAK END END --WHILE(1=1) SET NOCOUNT OFF END GO /****** Object: StoredProcedure [dbo].[uspManageSecondaryLSTracking] Script Date: 1/1/2022 8:51:38 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[uspManageSecondaryLSTracking] ( @ListofDBs NVARCHAR(MAX) -- Enter your database name as comma separated ) /***********Author - Rajib Sadhu**************/ /* EXEC dbo.uspManageSecondaryLSTracking 'AdventureWorks2019,AdventureWorksDW2019,pubs2,TEST_1' */ AS BEGIN SET NOCOUNT ON DECLARE @cmd NVARCHAR(1000) DECLARE @LiCounter INT = 1 DECLARE @LiMaxCount INT DECLARE @LsDatabaseName SYSNAME DECLARE @FullBackupCheckpointLSN NUMERIC(25, 0) DECLARE @BackupSetId INT /*****************Parse the comma separated database names****************/ CREATE TABLE #DBList ( DatabaseId INT IDENTITY(1,1), DatabaseName SYSNAME ) DECLARE @DbListXML XML = CAST(''+ Replace(@ListofDBs, ',', '')+ '' AS XML) INSERT INTO #DBList (DatabaseName) SELECT f.x.value('.', 'SYSNAME') AS user_id FROM @DbListXML.nodes('/root/U') f(x) /************Populate Full Backup and Tran Log information*****************/ CREATE TABLE #TblTaskStatus ( task_id int, task_type varchar(250), [database_name] varchar(500), [complete] numeric(5,2), [duration_mins] int, lifecycle varchar(250), task_info varchar(max), last_updated datetime, created_at datetime, S3_object_arn varchar(8000), overwrite_S3_backup_file varchar(50), KMS_master_key_arn varchar(500), filepath varchar(500), overwrite_file varchar(500) ) SET @LiCounter = 1 WHILE(1=1) BEGIN SELECT @LiMaxCount = MAX(DatabaseId) FROM #DBList IF(@LiCounter <= @LiMaxCount) BEGIN SELECT @LsDatabaseName = DatabaseName FROM #DBList WHERE DatabaseId = @LiCounter ---Populate tran log restore status INSERT INTO #TblTaskStatus EXEC msdb.dbo.rds_task_status @LsDatabaseName UPDATE LST SET LST.processing_status = 'Processed' FROM [dbo].[tblLSTracking] LST INNER JOIN #TblTaskStatus TS ON TS.database_name = LST.database_name AND TS.task_type = 'RESTORE_DB_LOG_NORECOVERY' AND LST.database_name = @LsDatabaseName AND LST.backup_type = 'Log' AND TS.lifecycle = 'SUCCESS' AND LST.file_name = SUBSTRING(TS.S3_object_arn, CHARINDEX(TS.database_name + '_', TS.S3_object_arn), LEN(TS.S3_object_arn)) AND LST.processing_status = 'in-progress' DELETE FROM #TblTaskStatus SET @LiCounter = @LiCounter +1 END ElSE BEGIN BREAK END END --WHILE(1=1) DROP TABLE #DBList DROP TABLE #TblTaskStatus SET NOCOUNT OFF END GO /****** Object: StoredProcedure [dbo].[uspManageSecondaryRestoreLogs] Script Date: 1/1/2022 8:51:38 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[uspManageSecondaryRestoreLogs] ( @DatabaseName NVARCHAR(128) -- Enter your database name as comma separated ,@PrimaryServerName NVARCHAR(500) -- Enter the Primary Server Name in the log shipping, can be found using SELECT SERVERPROPERTY('MachineName') ,@S3BucketARN NVARCHAR(500) -- Pass the S3 Bucket ARN ) /***********Author - Rajib Sadhu**************/ /* EXEC dbo.uspManageSecondaryRestoreLogs 'AdventureWorks2019', 'ec2amaz-g5rhpdl' , 'arn:aws:s3:::rds-sql-backup-restore-demo' */ AS BEGIN SET NOCOUNT ON DECLARE @LiCounter INT = 1 DECLARE @LiMaxCount INT DECLARE @cmd NVARCHAR(4000) DECLARE @LsFileName NVARCHAR(1000) DECLARE @BackupDir NVARCHAR(4000) CREATE TABLE #tblLSTracking( [tracking_id] [int] identity(1,1), [Server] [char](100) NULL, [database_name] [nvarchar](128) NULL, [backup_start_date] [datetime] NULL, [backup_finish_date] [datetime] NULL, [backup_type] [varchar](8) NULL, [backup_size] [numeric](20, 0) NULL, [physical_device_name] [nvarchar](260) NULL, [file_name] [nvarchar](260) NULL, [backupset_name] [nvarchar](128) NULL, [processing_status] [varchar](30) NULL ) /********************Pull all the tran log records not processed***************/ INSERT INTO #tblLSTracking ( [Server] ,[database_name] ,[backup_start_date] ,[backup_finish_date] ,[backup_type] ,[backup_size] ,[physical_device_name] ,[file_name] ,[backupset_name] ,[processing_status] ) SELECT [Server] ,[database_name] ,[backup_start_date] ,[backup_finish_date] ,[backup_type] ,[backup_size] ,[physical_device_name] ,[file_name] ,[backupset_name] ,[processing_status] FROM [dbo].[tblLSTracking] WHERE [database_name] = @DatabaseName AND [backup_type] = 'Log' AND [processing_status] IS NULL ORDER BY [backup_finish_date] ASC /******************************Restore Tran Log Backup*******************************/ SET @LiCounter = 1 WHILE(1=1) BEGIN SELECT @LiMaxCount = MAX([tracking_id]) FROM #tblLSTracking IF(@LiCounter <= @LiMaxCount) BEGIN SELECT @LsFileName = [file_name] FROM #tblLSTracking WHERE [tracking_id] = @LiCounter SET @BackupDir = '''' + @S3BucketARN + '/' + LOWER(@PrimaryServerName) + '/' + LOWER(@DatabaseName) + '/' + @LsFileName + '''' SET @cmd = 'exec msdb.dbo.rds_restore_log @restore_db_name=' + '''' + @DatabaseName + '''' + ', @s3_arn_to_restore_from=' + @BackupDir + ', @with_norecovery=1;' EXEC (@cmd) --print @cmd UPDATE [dbo].[tblLSTracking] SET [processing_status] = 'in-progress' WHERE [file_name] = @LsFileName SET @LiCounter = @LiCounter +1 END ElSE BEGIN BREAK END END --WHILE(1=1) DROP TABLE #tblLSTracking SET NOCOUNT OFF END GO /****** Object: StoredProcedure [dbo].[uspManageSecondarySetSecondary] Script Date: 1/1/2022 8:51:38 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[uspManageSecondarySetSecondary] ( @ListofDBs NVARCHAR(MAX) -- Enter your database name as comma separated ,@S3BucketARN NVARCHAR(500) -- Pass the S3 Bucket ARN ,@PrimaryServerName NVARCHAR(500) -- Enter the Primary Server Name in the log shipping, can be found using SELECT SERVERPROPERTY('MachineName') ,@RDSAdminUser NVARCHAR(100) -- Enter RDS Admin user name ,@LogRestoreFrequency SMALLINT --Enter how frequently you want to restore tran logs ) /***********Author - Rajib Sadhu**************/ /* EXEC dbo.uspManageSecondarySetSecondary 'AdventureWorks2019,AdventureWorksDW2019,pubs2,TEST_1', 'arn:aws:s3:::rds-sql-backup-restore-demo', 'ec2amaz-g5rhpdl' , 'admin', 15 */ AS BEGIN SET NOCOUNT ON DECLARE @LvMachineName NVARCHAR(500) DECLARE @cmd NVARCHAR(4000) DECLARE @LiCounter INT = 1 DECLARE @LiMaxCount INT DECLARE @LsDatabaseName SYSNAME DECLARE @jobId BINARY(16) DECLARE @JobName NVARCHAR(1000) DECLARE @BackupDir NVARCHAR(4000) DECLARE @Backupshare NVARCHAR(4000) DECLARE @LSBackUpScheduleUID UNIQUEIDENTIFIER DECLARE @LSBackUpScheduleID INT /*****************Parse the comma separated database names****************/ CREATE TABLE #DBList ( DatabaseId INT IDENTITY(1,1), DatabaseName SYSNAME ) DECLARE @DbListXML XML = CAST(''+ Replace(@ListofDBs, ',', '')+ '' AS XML) INSERT INTO #DBList (DatabaseName) SELECT f.x.value('.', 'SYSNAME') AS user_id FROM @DbListXML.nodes('/root/U') f(x) /***************Make sure DB List does not have any system databases***********************/ IF EXISTS(SELECT 1 FROM #DBList WHERE DatabaseName IN ('master','model','msdb','tempdb','rdsadmin','ssisdb')) BEGIN RAISERROR('Please remove system database/s from the list to proceed.', 16, 1) RETURN 1 END /****************Check if the database specified exists***********************************/ IF EXISTS (SELECT DatabaseName from #DBList WHERE DatabaseName IN (SELECT name FROM master.sys.databases)) BEGIN RAISERROR('One of more databases in the list already present. Restore operation is cancelled.', 16, 1) RETURN 1 END /******************************Restore Full Backup*******************************/ SET @LiCounter = 1 WHILE(1=1) BEGIN SELECT @LiMaxCount = MAX(DatabaseId) FROM #DBList IF(@LiCounter <= @LiMaxCount) BEGIN SELECT @LsDatabaseName = DatabaseName FROM #DBList WHERE DatabaseId = @LiCounter SET @BackupDir = '''' + @S3BucketARN + '/' + LOWER(@PrimaryServerName) + '/' + LOWER(@LsDatabaseName) + '/' + @LsDatabaseName + '_fullbackup.bak' + '''' SET @cmd = 'exec msdb.dbo.rds_restore_database @restore_db_name=' + '''' + @LsDatabaseName + '''' + ', @s3_arn_to_restore_from=' + @BackupDir + ', @type=''FULL'', @with_norecovery=1;' EXEC (@cmd) UPDATE [dbo].[tblLSTracking] SET [processing_status] = 'Processed' WHERE [database_name] = @LsDatabaseName AND [backup_type] = 'Database' AND [processing_status] IS NULL SET @LiCounter = @LiCounter +1 END ElSE BEGIN BREAK END END --WHILE(1=1) /*******************************Tran Log Restore Job Creation******************************/ SET @LiCounter = 1 WHILE(1=1) BEGIN SELECT @LiMaxCount = MAX(DatabaseId) FROM #DBList IF(@LiCounter <= @LiMaxCount) BEGIN SELECT @LsDatabaseName = DatabaseName FROM #DBList WHERE DatabaseId = @LiCounter SET @JobName = 'LSRestore_' + @LsDatabaseName SET @cmd = 'EXEC [dbmig].[dbo].[uspManageSecondaryRestoreLogs] ' + '''' + @LsDatabaseName + '''' + ', ' + '''' + @PrimaryServerName + '''' + ', ' + '''' + @S3BucketARN + '''' SET @jobId = NULL SET @LSBackUpScheduleUID = NULL SET @LSBackUpScheduleID = NULL EXEC msdb.dbo.sp_add_job @job_name=@JobName, @enabled=1, @notify_level_eventlog=0, @notify_level_email=2, @notify_level_page=2, @delete_level=0, @category_name=N'[Uncategorized (Local)]', @owner_login_name=@RDSAdminUser, @job_id = @jobId OUTPUT EXEC msdb.dbo.sp_add_jobserver @job_name=@JobName, @server_name = @@SERVERNAME EXEC msdb.dbo.sp_add_jobstep @job_name=@JobName, @step_name=N'tran log restore', @step_id=1, @cmdexec_success_code=0, @on_success_action=1, @on_fail_action=2, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=@cmd, @database_name=N'master', @flags=0 EXEC msdb.dbo.sp_add_schedule @schedule_name =N'LSRestoreSchedule' ,@enabled = 1 ,@freq_type = 4 ,@freq_interval = 1 ,@freq_subday_type = 4 ,@freq_subday_interval = @LogRestoreFrequency ,@freq_recurrence_factor = 0 ,@active_start_date = 20100101 ,@active_end_date = 99991231 ,@active_start_time = 0 ,@active_end_time = 235900 ,@schedule_uid = @LSBackUpScheduleUID OUTPUT ,@schedule_id = @LSBackUpScheduleID OUTPUT EXEC msdb.dbo.sp_attach_schedule @job_id = @jobId ,@schedule_id = @LSBackUpScheduleID SET @LiCounter = @LiCounter +1 END ElSE BEGIN BREAK END END --WHILE(1=1) /*******************************Create LSTracking-Secondary Job******************************/ SET @JobName = 'LSTracking-Secondary' SET @cmd = 'EXEC [dbmig].[dbo].[uspManageSecondaryLSTracking] ' + '''' + CAST(@ListofDBs AS NVARCHAR(4000)) + '''' SET @jobId = NULL SET @LSBackUpScheduleUID = NULL SET @LSBackUpScheduleID = NULL EXEC msdb.dbo.sp_add_job @job_name=@JobName, @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'No description available.', @category_name=N'[Uncategorized (Local)]', @owner_login_name=@RDSAdminUser, @job_id = @jobId OUTPUT EXEC msdb.dbo.sp_add_jobserver @job_name=@JobName, @server_name = @@SERVERNAME EXEC msdb.dbo.sp_add_jobstep @job_name=@JobName, @step_name=N'Track secondary LS Restore', @step_id=1, @cmdexec_success_code=0, @on_success_action=1, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=@cmd, @database_name=N'master', @flags=0 EXEC msdb.dbo.sp_add_schedule @schedule_name =N'run every 5 minutes' ,@enabled = 1 ,@freq_type = 4 ,@freq_interval = 1 ,@freq_subday_type = 4 ,@freq_subday_interval = 5 ,@freq_relative_interval=0 ,@freq_recurrence_factor = 0 ,@active_start_date = 20100101 ,@active_end_date = 99991231 ,@active_start_time = 0 ,@active_end_time = 235900 ,@schedule_uid = @LSBackUpScheduleUID OUTPUT ,@schedule_id = @LSBackUpScheduleID OUTPUT EXEC msdb.dbo.sp_attach_schedule @job_id = @jobId ,@schedule_id = @LSBackUpScheduleID DROP TABLE #DBList SET NOCOUNT OFF END GO