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:
 



čtvrtek 4. července 2013

Watchdog programu


Musíme ohlídat zda nám na serveru běží konkrétní software:
Představte si firmu kde je několik video serverů.Přesněji servery pro nahrávání videa z kamerového IP systému.Na každém serveru běží program Nuoo který nahrává záznam,ale dokáže si i ohlídat IP kamery a v případě ztráty signálu poslat email.Jenže pokud Nuoo neběží,pak žijeme v blahé nevědomosti že se záznamy vesele tvoří. Z tohoto blaženého pocitu vás vytrhne až žádost policie o kamerový záznam ,který nemáte a vy vše musíte vysvětlovat nadřízenému,policii atd.
Co se stalo? mám nástroje které nám včas nahlásí že nějaký server není na síti.Jenže server byl na síti a byl funkční jen neběžel program pro záznam videa.
Jinými slovy je tu důvod sáhnout po powershellu.
Cíl:zkontrolovat zda na serveru běží konkrétní proces a pokud ne odeslat email s výstražnou zprávou.
V zájmu čistoty kodu jsem ještě přidal test na dostupnost serveru na síti,plus dostupnost disku c který je pro běh programu nezbytný.
Skript nejdříve otestuje zda je server na síti a zda má dostupný disk c.Pokud je toto splněno tak teprve poté je server přidán do seznamu serveru na kterých se provede test na běžící procesy.
Jistě dalo by se to ošetřit podmínkou a k závěrečnému testování připustit jen ty servery které jsou živé,ale tento způsob jsem použil už v předešlých ukázkách a tak nabízím další alternativu.



################################  Vytvoril:Petr Skrivan ####################################

#pozor tato funkce se provadi az nakonec i kdyz je na zacatku skriptu tak je ignorovana..
#..a spusti se az po zavolani(funkce se vola pomoci jmena)


#pojmenovani funkce:tato se stara o odeslání emailu s přílohou pochazi z knihy...
#..Jak vyzrát na powershell 2.0 (vrele doporucuji)od Patrika Maliny
function sendemail
{
#testujeme zda promenna $odesleemail ma hodnotu 1 pokud ano odesle email..
#..vzdy kdyz se neco pokazi(neni ping atd)tak promenne $odesleemail priradime hodnotu 1
#..zde tedy testneme zda se v prubehu skriptu neco pokazilo
if($odesleemail -eq 1)
{
#zde je casova pauza ktera pocka 5 sekund aby skript mel dostatek casu na ulozeni text-logu
Start-Sleep -Seconds 5
$smtpKlient = new-object system.net.mail.smtpClient
$zprava = New-Object system.net.mail.mailmessage
$prihlaseni = New-Object system.net.NetworkCredential
#zde uvedete vase prihlasovaci udaje na postovnim serveru
$prihlaseni.username = "skritek@neco.cz"
$prihlaseni.Password = "heslo"
#adresa postovniho serveru
$smtpKlient.Host = "192.168.100.100"
#prihlasovaci procedura
$smtpKlient.Credentials = $prihlaseni
#od koho byla zprava odeslana
$zprava.From = "error@neco.cz"
#komu odesilame zpravu
$zprava.To.Add("skritek@neco.cz")
#predmet zpravy
$zprava.Subject = "VAROVANI-Software Nuoo"
#text zpravy
$zprava.Body = "Byla zaznamenana chyba v kamerovem systemu"
#zde si do promenne ulozime cestu k vytvorenemu logu
$cesta = "c:\kamery.txt"
#novy objekt priradime promenne $priloha
$priloha = New-Object system.net.mail.attachment($cesta)
#zde prilohu pridame do zpravy
$zprava.Attachments.Add($priloha)
#odesleme zpravu
$smtpKlient.Send($zprava)
#uvolni prilohu(treba pro dalsi zpracovani)
$priloha.Dispose()
}
#ac to tak nevypada tak toto je posledni prikaz skriptu protoze je uzavren ve funkci..
#..ktera se provadi az na zavolani funkce jmenem v nasem pripade sendmail na konci skriptu
exit
}
#zde si deklarujeme globalni promennou a priradime ji hodnotu 0
[int] $global:odesleemail = 0
$datumacas=Get-Date
#zalozime textovy log pojmenujeme a zaroven zapiseme text v uvozovkach,plus..
#..pridame aktualni datum a cas jeden znak > znamena zaloz txt soubor,pokud..
#..existuje pak ho prepis,timto si osetrime ze se nam logy nebudou hromadit..
#..protoze novy log prepise automaticky ten stary
"Zacatek testu $datumacas" > c:\kamery.txt
#seznam serveru ulozime do promenne
$comp="kamery","kamery2","kamery3","kamery4","kamery5","kamery6","kamery7"
#deklarujeme promennou typu pole
$overenecompy = @()
#seznam serveru posleme do roury vzdy jeden ze seznamu,promena $_ je jmeno serveru ktery..
#..je momentalne v roure je to vlastne cyklus ktery se zopakuje tolikrat kolik serveru..
#..do roury posleme
$comp | foreach {
#otestujeme zda je server na siti,rovnou zde testuji zda je dostupny disk c,ktery je pro..
#..beh softwaru nezbytny...POZOR skript musi spoustet PC s admin pravy na domenu
$zijeserver=Test-Path "\\$_\c$"
#pokud byla cesta v siti nalezena
if ($zijeserver -eq "true")
{
echo "Server $_ je online"
#do logu zapiseme ze server je online a bezi,dve znamenka >> znaci ze text se ma pridat..
#..do stavajiciho logu,promenna $_ reprezentuje jmeno serveru ktery momentalne testujeme
"Server $_ je online" >> c:\kamery.txt
#server je v poradku je na siti,takze ho pridame do seznamu pro testovani zda na nem bezi..
#..i nas hledany software
$overenecompy += $_
}
#pokud neni server na siti nebo nema pristupny disk c provede se else
else
{
echo "Server $_ ,nebo disk C: neni dostupny"
#zapis do logu..zvyraznime pomoci grafiky(povystrčíme upozornění)
          "Server $_ ,nebo disk C: neni dostupny.. !!!!! POZOR !!!" >> c:\nuoo.txt
#neco je spatne potrebujeme odeslat email takze nasi promenne priradime hodnotu 1
$global:odesleemail = 1
#a protoze server nebyl nalezen,neni pridan do promenne $overenecompy,timto je vyrazen..
#..z dalsiho testovani protoze nadale budeme pracovat se seznamem $overenecompy
}
}
#graficka uprava logu
"------------------------------------------------------" >> c:\kamery.txt
" " >> c:\kamery.txt
#opet roura stejne jako vyse zde si jen objekt proudici v roure pojmenujeme..
#..takze nepracujeme s promennou $_ ale $finalcomp,ktera postupne nabude vsechny..
#..hodnoty(jmeno serveru) z naseho seznamu overenych serveru
##
foreach ($finalcomp in $overenecompy)
{
#do promenne $proces si nacteme jmena vsech bezicich procesu a vyhledame ten ktery nas zajima
#pokud hledany proces najdeme je jasne ze software bezi a promenna $proces nabude hodnotu..
$proces=Get-Process -ComputerName $finalcomp|where-object{$_.processname -like “mainconsole”}
#zajimava podminka ktera testuje zda ma promena jakoukoliv hodnotu,pokud neni null pak ..
#..podminka projde
if ($proces)
{
#vse je v poradku neni treba odesilat email zapiseme uspech do logu
"Software Nuoo na serveru $finalcomp je zcela funkcni" >> c:\kamery.txt
echo "Nuoo na serveru $finalcomp ok"
}
#promenna $proces ma hodnotu null jinymi slovy proces nebyl,nalezen uplne proste...
#..program na serveru nebezi,provedeme tedy else
else
{
#musime odeslat email o coz se postara promenna odesleemail
$global:odesleemail = 1
#
echo "Na serveru $finalcomp nebezi Nuoo "
#vysledek testu(neuspech) zapiseme do logu
"Na serveru $finalcomp doslo k potizim se softwarem Nuoo...!!! POZOR !!!" >> c:\kamery.txt
}
}
#zavolame funkci
sendemail