středa 8. července 2015

Automatická záloha / restore / replikace MS SQL Express

Automatická záloha / restore / replikace MS SQL Express pomoci powershellu.
Problém je že tato verze neobsahuje agenta který v určitý čas provede automatickou zálohu nebo restore.Představte si že máte primární server z důležitou databází(samozřejmě verze sql serveru je express).Dále máte backup server který má sloužit jako záložní.Pokud padne primární prostě se plynule přejde na sekundární.
Úkol tedy zní:zálohovat v určitém intervalu primární server.Zálohu přesunout někam do bezpečí(jiný stroj,jiná lokace).Posléze provést restore na záložním serveru.
V podstatě se jedná o automatickou replikaci MS SQL Express pomocí powershellu.Přidaná hodnota je že můžeme replikovat na více strojů plus zálohu odkládat jako backup.Veškeré úkony provedené/neprovedené jsou zaznamenány v rozsáhlem logu který nás informuje jak job proběhl.

Skript dáme samozřejmě spouštět ve windows(plánovač úloh). Interval zálohy je daný  nastavením scheduleru.   


################################  Vytvoril:Petr Skrivan #########################
#musime naimportovat modul pro praci s sql serverem
import-module -Name sqlps
#import modulu bohuzel rozhazi pocatecni UNC cesty takze se musime..
#.. prepnout do rootu jinak neprojde nic kde se pouzivaji UNC cesty
cd c:
#o provedene/neprovedene zaloze odesilam zpravu a prilozim podrobny log
#..v podobe txt souboru
function sendemail ($subject,$body)
{
$smtpKlient = new-object system.net.mail.smtpClient
$zprava = New-Object system.net.mail.mailmessage
$prihlaseni = New-Object system.net.NetworkCredential
$prihlaseni.username = "vasmejl@organizace.cz"
$smtpKlient.Host = "192.168.150.1"
$smtpKlient.Credentials = $prihlaseni
$zprava.From = "error@organizace.cz"
$zprava.To.Add("error@organizace.cz")
$zprava.Subject = $subject
$zprava.Body = $body
$cesta = "c:/zalohadbwebserver.txt"
$priloha = New-Object system.net.mail.attachment($cesta)
$zprava.Attachments.Add($priloha)
$smtpKlient.Send($zprava)
$priloha.Dispose()
sleep 2
exit
}
#naformatuji dnesni datum do podoby napr 24.8.2014 
$dnes = Get-Date -format d.M.yyyy
$dnes > c:/zalohadbwebserver.txt #zapis do logu

########### kontrola zda jsou na serveru databaze ktere chceme zalohovat  #######
#nuluji pomocnou promennou
$polenazvudb = " "
#seznam kontrolovanych a nasledne zalohovanych db
$kontrolovanedb = "databaze1","jinadatabaze","dalsidatabaze"
#nactu si nazvy vsech databazi na serveru pomoci filtru select rikam ze chci jen jmena
$mamedatabaze =  dir sqlserver:\sql\webserver\default\databases | select Name
#nuluji pomocnou promennou
$nenalezena = ""
#ze seznamu databazi setavim jeden dlouhy string
$mamedatabaze | foreach {$polenazvudb += $_.name}
#seznam kontrolovanych databazi poslu do kolecka a otestuji zda existuji na serveru
$kontrolovanedb | foreach{
#pokud je jmeno databaze nalezeno v dlouhem stringu zapiu to do logu
if ($polenazvudb -match $_)
{"Databaze $_ na zdroji nalezena" >> c:/zalohadbwebserver.txt }

#else se provede jen kdyz neni jmeno databaze nalezeno v dlouhem stringu
else
#do pomocne promenne se prida jmeno databaze jenz nebyla nalezena
{$nenalezena += "$_, "
"Databaze $_ na zdroji nenalezena" >> c:/zalohadbwebserver.txt} 
}#konec kolecka foreach
#pokud promenna $nenalezena neco obsahuje pak nejaka databaze nebyla nalezena odeslu email s
#..infem o tom ktera databaze nebyla nalezena
if ($nenalezena){sendemail "zaloha webserver" "tyto database nebyly zálohovany $nenalezena "}
" " >> c:/zalohadbwebserver.txt


#!!!!!!!!!!   Zacatek velkeho kolecka foreach   !!!!!!!!!!!!!!!!!!!!!!!!

#nyni mame v promenne $kontrolovanedb jen existujici nazvy validnich datbazi
foreach($dbvroure in $kontrolovanedb)
{

###################   kontrola statusu databaze na zdrojovem serveru  ##########
#import modulu nam zajistil ze z sql serverem muzu pacovat s pomoci dir
$kontrolazdroj = dir sqlserver:\sql\dbserver\default\databases | Where-Object {$_.name -match $dbvroure}
if ($kontrolazdroj -notmatch "normal")# pokud je status normalni
{(Get-Date -format "H:mm:ss")+": Status databaze $dbvroure na zdrojovem serveru : OK" >> c:/zalohadbwebserver.txt }
else
{"kontrola statusu db: $dbvroure na zdrojovem serveru selhala" >> c:/zalohadbwebserver.txt
sendemail "zaloha webserver" "kontrola statusu db: $dbvroure na zdrojovem serveru selhala"}

#################### mazani moznych duplicit #################################
#podivame se do adresare ktery je momentalne v kolecku na soubor ktery ma nazev
#..jako dnesni datum
$duplicitnidb = dir \\192.168.150.25\c$\Backup\MSSQL\$dbvroure\$dnes.bak
#pokud najdeme soubor ktery je ve spravnem adresari a ma nazev jako $dnes
#..podminka projde a soubor bude smazan
if($duplicitnidb)
{
Remove-Item $duplicitnidb
}

############################## zaloha databazi ##################################
#zalohuj vybranou databazi na vybranem serveru nazev zalohy bude dnesni datum
backup-sqldatabase -ServerInstance dbserver -Database $dbvroure -BackupFile "C:\Backup\MSSQL\$dbvroure\$dnes.bak"
(Get-Date -format "H:mm:ss")+": Na zdrojovem serveru zalohuji databazi: $dbvroure" >> c:/zalohadbwebserver.txt
sleep 5

################ konekt do adresare se zalohami na zdroji ######################
#do promenne si nactu kompletni obsah adresare
$zdrojzalohadb = dir \\192.168.100.25\c$\Backup\MSSQL\$dbvroure
(Get-Date -format "H:mm:ss")+": Pripojuji adresar se zalohami na zdrojovem serveru databaze: $dbvroure" >> c:/zalohadbwebserver.txt
#otestuji zda mam spojeni na server a zda dana slozka obsahuje nejake zalohy
IF(!$zdrojzalohadb)
{
#volam funkci sendemail a predavam ji parametry
"nemohu se pripojit do adresare $dbvroure se zalohami na zdrojovem serveru pro databazi: $dbvroure" >> c:/zalohadbserver.txt
sendemail "zaloha dbserver" "nebylo se mozne spojit se serverem,nebo slozka neobsahuje zadne zalohy pro db $dbvroure"
}

######################  hledani dnesni databaze na zdroji #######################
#obsah adresare poslu do roury a najdu slozku jejiz datum vytvoreni odpovida
#.. dnesnimu datumu

$dneszdrojzalohadb = $zdrojzalohadb | where-object {$_.CreationTime.GetDateTimeFormats()[0] -match $dnes}
#pokud zadane misto obsahuje dva a vice souboru s dnesnim datumem neco je spatne
#..takze posilam emajl a ukoncuji program
if ($dneszdrojzalohadb.Count -gt 1)
{
#volam funkci sendemail a predavam ji parametry
"Adresar se zalohami na zdrojovem serveru obsahuje vicero souboru s dnesnim datem nelze urcit vhodny soubor zalohy pro db: $dbvroure" >> c:/zalohadbwebserver.txt
sendemail "zaloha dbserver" "adresar se zalohami na zdrojovem serveru obsahuje vicero souboru s dnesnim datumem nelze urcit spravny soubor jedna se o: $dbvroure"
}


#otestuji zda byla nalezena dnesni zaloha testuji zda je promenna prazdna
#..pokud ano odeslu email
IF(!$dneszdrojzalohadb)
{
#volam funkci sendemail a predavam ji parametry
"Na zdrojovem serveru v adresari zaloh nebyl nalezen soubor z dnesnim datem vytvoreni pro db: $dbvroure" >> c:/zalohadbwebserver.txt
sendemail "zaloha dbserver" "Na zdrojovem serveru v adresari zaloh nebyl nalezen soubor z dnesnim datem vytvoreni pro db: $dbvroure"
}

#zbyva jen soubor s dnesnim datumem takze si jeho jmeno ulozim do promenne
$jmenodneszdrojzalohadb = $dneszdrojzalohadb.name
(Get-Date -format "H:mm:ss")+": Na zdrojovem serveru nalezen soubor $jmenodneszdrojzalohadb kopiruji na zalozni server do adresare $dbvroure" >> c:/zalohadbwebserver.txt
#################### kopirovani backupu na zalozni server #######################
#zkopiruj ze zadaneho adresare zadany file do zadane destinace
copy-item \\192.168.150.25\c$\Backup\MSSQL\$dbvroure\$jmenodneszdrojzalohadb -Destination \\192.168.150.16\c$\backup\$dbvroure\
#sleep 5
(Get-Date -format "H:mm:ss")+": Testuji zda zaloznim serveru existuje databaze: $dbvroure" >> c:/zalohadbwebserver.txt
$existujedatabaze = dir sqlserver:\sql\vm-webbackup\Default\databases | Where-Object {$_.name -match "$dbvroure"}
#otestujeme zda na serveru existuje databaze delame restore takze databaze uz
#..musi na serveru byt jinak dojde k selhani
IF(!$existujedatabaze)
{
#volam funkci sendemail a predavam ji parametry
"na zaloznim serveru nebyla nalezena db: $dbvroure" >> c:/zalohadbwebserver.txt
sendemail "zaloha dbserver" "na zaloznim serveru nebyla nalezena db: $dbvroure"
}


##################### restore databaze na zaloznim serveru ####################
sleep 5
#restor databazi na vm-webbackup pomoci serverinstance skript je spoustem primo
#...na stroji vm-webbackup takze nepouzivam unc cesty ale vse provadim lokalne
(Get-Date -format "H:mm:ss")+": Provadim restore databaze $dbvroure na zaloznim serveru " >> c:/zalohadbwebserver.txt
try{
Restore-SqlDatabase -ServerInstance vm-webbackup -Database $dbvroure -BackupFile C:\backup\$dbvroure\$jmenodneszdrojzalohadb -RestoreAction Database -ReplaceDatabase
} #konec bloku try
Catch
{
(Get-Date -format "H:mm:ss")+": Restore databaze $dbvroure na zaloznim serveru se nepodarila" >> c:/zalohadbwebserver.txt
sendemail "zaloha dbserver" "Restore databaze $dbvroure na zaloznim serveru se nepodarila"
} #konec bloku catch


###############   kontrola statusu databaze na zaloznim serveru  ###############
$kontrolazdroj = dir sqlserver:\sql\vm-webbackup\default\databases | Where-Object {$_.name -match $dbvroure}
if ($kontrolazdroj.Status -notmatch "normal")
{"kontrola statusu db: $dbvroure na zaloznim serveru selhala" >> c:/zalohadbwebserver.txt
sendemail "zaloha dbserver" "kontrola statusu db: $dbvroure na zaloznim serveru selhala"}
else
{(Get-Date -format "H:mm:ss")+": Status databaze $dbvroure na zaloznim serveru: OK" >> c:/zalohadbwebserver.txt }



#################  odstranim stare soubory na zaloznim serveru ##################
cd \\192.168.150.16\c$\backup\$dbvroure
sleep 2
$smaz = dir | Where-Object {$_.name -notmatch $dnes}
(Get-Date -format "H:mm:ss")+": Mazu stare zalohy na zaloznim serveru delete: $dbvroure" >> c:/zalohadbwebserver.txt
Remove-Item $smaz
" " >> c:/zalohadbwebserver.txt

} #     >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   KONEC HLAVNIHO KOLECKA FOREACH

#toto je nutne jinak se rozhazi UNC cesty u ostatnich skriptu
Remove-Module -Name sqlps



ukázka logu: