In addition to changing the backup plans so that we are doing t-log backups every hour and adding alerts, operators, and notifications tot he server I started kicking around teh idea of using transaction log shipping to add an extra layer of protection to the system. But the issue was the production database server is SQL Server 2000 Standard, which does not include log shipping.
I decided to write a PowerShell script that would use the backups I was alreeady creating to simulate log shipping to a warm standby server. Here is what I have so far.
############################################# ## SQL Server Related variables. ## ############################################# $ErrorActionPreference = "SilentlyContinue" $DebugPreference = "Continue" #SQL Server to which the database is being restored. $SQLServer = "sqlserver" #Name of the database being restored. $database = "database" #The connection string used to access teh SQL Server. Do not change. $ConnectionString = "Server = $SQLServer; Database = master; Integrated Security = True" #Time in seconds the script should waite for the non-query to finish executing. $TimeOut = 1800 #1800 = 30 minutes. This is more than enough time in my environment. ############################################# # Email Configuration Variables ## ############################################# $EmailSub = "PSLogShipping Status Report" $MsgText = "Included is the list of backups/logs applied and the event log." $EmailServer = "exchange" $EmailTo = @(email@email.com) $emailFrom = "email@email.com" ############################################## ## General Configuration variables. ## ############################################## #Path to the list used to keep track of logs already done. $ListPath = "C:\script\AppliedLogs.csv" $header = "Name, LastWriteTime, TimeApplied" if(!(Test-Path $ListPath)){ $header | Out-File $ListPath } $EventLog = "C:\script\EventLog.txt" if(!(Test-Path $EventLog)){ $null | Out-File $EventLog } #An array used by the email finction to attach the files. $emailLogs = @($ListPath, $EventLog) #Path to the logs. $path = "\\server\share\" #Test tobe sure tha path above is valid. if(!(Test-Path $path)){ Write-Host "The path to the backup files/logs cannot be found." } #The hour at which your list should be cleared. $ClearList = 15 ############################################### ## Do not change anything under this section ## ## unless you know what you are doing. ## ############################################### function Send-Email() { param( [string]$smtpServer, [string]$from, [string[]]$recipient, [string]$subject, [string]$body, [string[]]$attachment, [bool]$isHTML ) $msg = new-object Net.Mail.MailMessage $msg.From = $from foreach($person in $recipient) { $msg.To.Add($person) } $msg.Subject = $subject $msg.Body = $body $msg.IsBodyHTML = $isHTML foreach($item in $attachment) { $msg.Attachments.Add($item) } $smtp = new-object Net.Mail.SmtpClient($smtpServer) $smtp.Send($msg) } #Append to log. $today = Get-Date $today.ToString() + " Starting...." | Out-File $EventLog -Append if($today.Hour -lt $ClearList) { $BackupDone = $false ##Get the primary backup. $files = get-childitem $path | where { $_.Name -match ".bak" } $backup = $files | sort -desc LastWriteTime #Set the date we need to work with to determine which t-logs we will look at. #We only look at ones after the most recent backup. if($backup.Length -gt 1) { $BackupDate = $backup[0].LastWriteTime $BackupFile = $backup[0].Name } else { $BackupDate = $backup.LastWriteTime $BackupFile = $backup.Name } ##Get the list of t-logs. Only logs with a date greater than that of the backup. $logs = Get-ChildItem $path | where { ($_.Name -match ".trn") -and ($_.LastWriteTime -gt $BackupDate) } #Read the list of log files already applied. $list = Import-Csv $ListPath #Remove any logs/backup that have already been applied from our active list. foreach($item in $list) { #Has the backup been applied yet? if($BackupFile -eq $item.Name) { $logs = @($logs | Where-Object {$_.Name -ne $item.Name}) $BackupDone = $true } $logs = @($logs | Where-Object {$_.Name -ne $item.Name }) #Append to log. $time = Get-Date -displayhint time $time.toString() + " Done Getting backup and transaction logs for list." | Out-File $EventLog -Append } ##Connect to SQL Server $SQLConnection = New-Object System.Data.SqlClient.SqlConnection $SQLConnection.ConnectionString = $ConnectionString $SQLCmd = New-Object System.Data.SQLClient.SQLCommand $SQLCmd.Connection = $SQLConnection $SQLCmd.CommandTimeout = $TimeOut Write-Debug "Attempting to open connection." #Append to log. $time = Get-Date -displayhint time $time.toString() + " Attempting to open connection." | Out-File $EventLog -Append $SQLConnection.Open() $time = Get-Date -displayhint time $time.toString() + " Connection Open." | Out-File $EventLog -Append if($BackupDone -eq $true) { #Apply the logs foreach($log in $logs) { Write-Debug "Attempting to execute Non-Query." $SQLcmd.CommandText ="RESTORE DATABASE $database FROM DISK ='$path\$log' WITH NORECOVERY" $SQLcmd.ExecuteNonQuery() #Append to log. $time = Get-Date -displayhint time $time.toString() + " Non-Query executed.`n RESTORE DATABASE $database FROM DISK ='$path\$log' WITH NORECOVERY" | Out-File $EventLog -Append #Append the log info to the list. $CurrentDate = Get-Date $text = "`n"+$log.Name+", "+$log.LastWriteTime+", "+$CurrentDate $text | Out-File $ListPath -Append $text | Out-File $EventLog -Append } } else { #Apply the backup Write-Debug "RESTORE DATABASE $database FROM DISK ='$path\$BackupFile' WITH NORECOVERY, REPLACE" $SQLcmd.CommandText = "RESTORE DATABASE $database FROM DISK ='$path\$BackupFile' WITH NORECOVERY, REPLACE" $SQLcmd.ExecuteNonQuery() #Append to log. $time = Get-Date -displayhint time $time.toString() + " Non-Query executed.`n RESTORE DATABASE $database FROM DISK ='$path\$BackupFile' WITH NORECOVERY, REPLACE" | Out-File $EventLog -Append #Append the backup file to the list. $CurrentDate = Get-Date $text = "`n"+$BackupFile+", "+$BackupDate+", "+$CurrentDate $text | Out-File $ListPath -Append $text | Out-File $EventLog -Append } #Close connection to server Write-Debug "Closing Connection." $SqlConnection.Close() #Append to log. $time = Get-Date -displayhint time $time.toString() + " Connection Closed." | Out-File $EventLog -Append "------------------------------------" | Out-File $EventLog -Append } else { ##Connect to SQL Server $SQLConnection = New-Object System.Data.SqlClient.SqlConnection $SQLConnection.ConnectionString = "Server = $SQLServer; Database = master; Integrated Security = True" $SQLCmd = New-Object System.Data.SQLClient.SQLCommand $SQLCmd.Connection = $SQLConnection $SQLCmd.CommandTimeout = $TimeOut $SQLConnection.Open() #Recover the database from the backup process. Write-Debug "RESTORE DATABASE $database WITH RECOVERY" $SQLcmd.CommandText = "RESTORE DATABASE $database WITH RECOVERY" $SQLcmd.ExecuteNonQuery() #Append to log. $time = Get-Date -displayhint time $time.toString() + " Non-Query executed.`n RESTORE DATABASE $database WITH RECOVERY" | Out-File $EventLog -Append #Close connection to server $SqlConnection.Close() #Append to log. $time = Get-Date -displayhint time $time.toString() + " Connection Closed." | Out-File $EventLog -Append "------------------------------------" | Out-File $EventLog -Append #Send the event log and the list of applied DBs. Send-Email -smtpServer $emailServer -from $emailFrom -recipient $emailTo -subject $EmailSub -body $MyText -attachment $emailLogs -isHTML $true #Sleep 15 seconds so the email can be sent. Start-Sleep -s 15 #Clear the list. Write-Host "Purging list.......`n" $header = "Name, LastWriteTime, TimeApplied" $header | Out-File $ListPath Write-Host "Done!" }
0 comments:
Post a Comment