8. Juni 2011

Powershell Script zum Backup eines Lync Servers

Wie bereits letzte Woche angekündigt habe ich nun endlich mein Backup Script für Lync in der Version 1.0 fertiggestellt.

Es sichert alle wichtigen Einstellungen und Dateien um im Katastrophenfall auch einen Lync Server wiederherstellen zu können.

Das Script steht hier zum Download bereit.

Damit das Script auf dem Lync Server laufen kann, muss evtl. die Execution Policy for Scripte geändert werden:

Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Code des Lync Backup Scriptes.


###################################################################################
## Script Version: 1.0
###################################################################################
## This Scripts collects important data from your Lync environment and
## stores it in files to a Backup path. With this data you should be able
## to recover from a desaster issue in your lync environment.
## Currently this script should be run on at every Frontend Server.
## I do not warrant any functionalty or completeness on this script. 
## You run it on your own risk and it does not replace proper backups 
## and system management!
## I do not collect data for the following roles:
## Group Chat, Monitoring, Archiving, XMPP, EDGE
## 
## Credits to Adrian, Dough, Kevin, Nick, Scott, Vincent
## for helping me and inspiring me on this script
##
## ChangeLog: -
##
## Open Topics:
## - powershell remoting to other Lync servers
## - Edge Server Backup (Network, Routes, Host file, Certificates, RootCertificates)
####################################################################################
 
#Please specify your backup folder here
$BackupPath = "C:\Backup"
 
#Specify if you want to export Certificates with private Keys, 
#needs Admin Rights and may be a security risk!
$ExportCert = $true
# Passphrase Secret for exported Certificate PFX
$SecretForPFX = "geheim"
 
#-----------------------------------------------------------------------------
 
# Funktion to test if Modules are abvailable
Function Get-MyModule { 
    Param([string]$name) 
    if(-not(Get-Module -name $name)) 
    { if(Get-Module -ListAvailable | Where-Object { $_.name -eq $name }) 
    { Import-Module -Name $name 
    Write-Host "`nModule for $name was loaded!" } 
    else { Write-Host -BackgroundColor DarkRed "`nModule for $name was not loaded!" }
    } # end if not module 
    else {Write-Host "`nModule for $name was already loaded!" } 
    }
Get-Mymodule -name "Lync"
 
#Build up Unique Foldername and create required path
$filepath = $BackupPath + "\{0:yyyyMMdd-HHmm}\" -f (Get-Date)
If(!(Test-Path -Path $BackupPath)) {New-Item $BackupPath -Type directory |Out-Null}
if(!(Test-Path -Path $filepath)) {New-Item $filepath -Type directory|Out-Null}
 
#Get basic topology data
Write-Host Exporting Basic Topology Data
Export-CSconfiguration -Force:$True -AsBytes | Set-Content -Encoding Byte ($filepath + "CSConfiguration.zip")
Export-CsLisConfiguration -AsBytes | Set-Content -Encoding Byte ($filepath + "CSLISConfiguration.zip")
Get-CsService |Export-Csv ($filepath + "Get-CsService.csv")
Get-CSTopology -asxml | out-file ($filepath + "CurrentTopology.tbxml")
(Get-CSSimpleUrlConfiguration).SimpleUrl | out-file ($filepath + "SimpleURLs.txt")
 
#Export User Data
Write-Host Exporting Users
Get-CsUser |Export-Csv ($filepath + "user.csv")
 
#Backup Filestores in Oganization including encrypted Meeting Data
$FileStores = Get-CsService -Filestore
foreach ($FileStore in $FileStores){
    Write-host Copying File from $FileStore.UncPath 
    #icacls $($FileStore.UncPath)\* /save ($filepath + "FileStore-ACLs.txt") /T
    robocopy $($FileStore.UncPath) $($filepath + $FileStore.ServiceId + "\") /NFL /NDL /NP /E /SEC /DCOPY:T /XF Meeting.Active
}
 
#Export Certificates w/ private Keys if exportable and requested above
get-cscertificate | Out-File ($filepath + "InstalledCertificates.txt")
If ($ExportCert) {
    dir cert:\LocalMachine\my |Where-Object { $_.hasPrivateKey } |Foreach-Object { [system.IO.file]::WriteAllBytes(    "$filepath\$($_.FriendlyName)-$($_.ThumbPrint).pfx",($_.Export('PFX', $SecretForPFX)))}
    Write-Host -BackgroundColor DarkGreen Certificates with PrivateKey were exported!
}
 
#Export ResponseGroup Configuration
$ApplicationServers = Get-CsService -ApplicationServer
If (Test-Path "C:\Program Files\Microsoft Lync Server 2010\ResKit\RgsImportExport.ps1") {
    Import-Module "C:\Program Files\Microsoft Lync Server 2010\ResKit\RgsImportExport.ps1"
    foreach ($ApplicationServer in $ApplicationServers){
    Write-Host Exporting ResponseGroup Configuration Data from $($ApplicationServer.Identity)
    Export-CsRgsConfiguration -Source $($ApplicationServer.Identity) -FileName ($filepath + $ApplicationServer.PoolFqdn +"-CsRgsConfiguration.zip") 
    }
}
 
#Export Conference Directories and Users Contacts and premissions
$ApplicationDatabases = Get-CsService -ApplicationDatabase
foreach ($ApplicationDatabase in $ApplicationDatabases){
Write-Host "Exporting User Data (Contacts, Conference) from" + $($ApplicationDatabase.Identity)
& 'C:\Program Files\Common Files\Microsoft Lync Server 2010\Support\DBImpExp.exe' /hrxmlfile:c:\file.xml /sqlserver:$($ApplicationDatabase.PoolFqdn + "\" +  $ApplicationDatabase.SqlInstanceName)
}
 
#Backup IIS Settings
Write-Host Running IIS Backup:
Invoke-Expression "C:\Windows\System32\inetsrv\appcmd.exe add backup"
$IISBackupPath = $($filepath + "IIS-Backup\")
New-Item $IISBackupPath -Type directory |Out-Null
foreach ($folder in Get-ChildItem C:\Windows\system32\inetsrv\backup) {
    #copy Backup from within the last 10 minutes
    if ($folder.CreationTime -gt ($(Get-Date).AddMinutes(-10))){
        Copy-Item $folder.FullName -destination $IISBackupPath -Recurse -ErrorAction SilentlyContinue
    }
}
 
#Backup some Network Configuration data from local server 
Write-Host "Backup some Network Configuration data from local server (hosts, Network Config)"
&ipconfig /all > $($filepath + "network-Settings.txt")
&route print >> $($filepath + "network-Settings.txt")
copy-item C:\Windows\System32\drivers\etc\hosts -destination ($filepath + "etc-hosts-file.txt") -ErrorAction SilentlyContinue
 
#SQL Backups on XDS,LIS,RTC,RTCAB,RGCCONFIG from RTC Instance
$SQLBackupFile = $($filepath + "Backup-LYNC-DBS.sql")
$SQLBackupPath = $($filepath + "SQL-Backup\")
New-Item $SQLBackupPath -Type directory |Out-Null
#First, build a Backup Script File
$DBs = "XDS", "LIS", "RTC", "RTCAB", "RGSCONFIG"
foreach ($DB in $DBs){
    $sqlstatement = "BACKUP DATABASE " + $DB + " TO DISK = '" + $SQLBackupPath + $DB + ".bak';"
    Add-Content -Path $sqlbackupfile  "$sqlstatement`n"
}
#Run SQL Backup with SQLCMD Tool
$CMSDatabase = Get-CsService -CentralManagementDatabase
Write-Host Backing SQL from $($CMSDatabase.PoolFqdn + "\" +  $CMSDatabase.SqlInstanceName)
& 'C:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE' -S $($CMSDatabase.PoolFqdn + "\" +  $CMSDatabase.SqlInstanceName) -i $sqlbackupfile
Remove-Item -Path $sqlbackupfile
Write-Host "`nBackup run has finished! "
Write-Host "You may backup your files from $filepath to tape!"

Kommentare:

  1. Kannst Du mal erläutern wies Du die RTCAB1 nicht mitsicherst?

    AntwortenLöschen
  2. Hi Nini und Grüße in den Norden,
    also die RTCAB und die RTCAB1 arbeiten im Lync zusammen. HIer werden die Adressbuchdaten gespeichert. Die Datenbanken wechseln sich alle 24 Stunden ab.
    Wenn ein neues Adressbuch erstellt wird, erfolgt die Speicherung erst in der aktuell nicht genutzen Datenbank. Ist die Adressdatenbank aktualisiert wird auf diese intern umgeschwenkt. So ist zu jeder Zeit eine aktuelle Adressdatenbank gefüllt und kann für die Address Book Web Query (ABWQ) genutzt werden.
    Ob man überhaupt eine RTCab(1) Datenbank sichert kommt ein wenig auf die Größe der Lync-Installation an. Eventuell kann man sie auch einfach weg lassen und im Desaster-Recoveryfall einfach nach dem Recovery schnell neu erstellen lassen:
    Ein "Update-CsUserDatabase" liest das komplette Active Directory mit den Benutzern neu ein.

    Ein "Update-CsAddressBook" erstellt das Adress buch dann auf Basis der eingelesenen AD-Daten neu.

    Das kann natürlich in großen Umgebungen dauern, und da vervorzugt man dann einen Restore.
    Ich sichere in meinen Script nur die RTCab und habe so maximal eine Datensicherung vom Vortag, oder eine aktuelle.

    Übrigens, das Adressbuch wird normalerweise nachts um 01:30 Uhr lokaler Serverzeit erstellt. Man kann dies auf eine andere Zeit umstellen:
    Set-CsAddressBookConfiguration -identity site:Frankfurt -RunTimeOfDay 23:00

    Es bleibt aber bei einmal in 24h. Wenn dies öfter aktualisert werden soll, muss man zu obigen Update-CsAddressbook greifen und dieses z.B. mit dem Task Scheduler öfter auführen. Die Clients laden es dennoch nur einmal am Tag herunter.
    Wer ein aktuelles Adressbuch am Client haben will muss auf die Address Book Web Query (ABWQ) Dienste auf dem Lync Frontend umstellen. Dann fragt der Client nicht seine eigene, lokales Adressbuchdatei ab, sondern fragt immer beim ABWQ über das Netz nach. Hier werden alle 5 Minuten Updates aus dem AD übernommen.
    Die Konfiguration für die ABWQ erfolgt über eine Client Policy:
    Set-CsClientPolicy -Identity ClientPolicyName -AddressBookAvailability

    AntwortenLöschen
  3. Hi,

    super Script, erfüllt alles, was es sollte, danke!

    Bei dem Export der Zertifikate sollte man darauf achten, dass der Private Key auch exportierbar ist, sonst rennt das Script in eine Exception, außerdem habe ich den FriendlyName weggelassen beim Dateinamen, falls keiner vergeben wurde:
    dir cert:\LocalMachine\my | Where-Object { $_.hasPrivateKey -and $_.PrivateKey.CspKeyContainerInfo.Exportable } | Foreach-Object { [system.IO.file]::WriteAllBytes($filepath + $_.ThumbPrint + ".pfx",($_.Export('PFX', $SecretForPFX)))}

    Das hrxmlfile, das von DmbImpEx erstellt wird, wird nicht im Backup-Verzeichnis abgelegt, sondern unter C:\file.xml. Hier sollte der Pfad noch geändert werden, z.B.:
    /hrxmlfile:$($filepath + "user-contacts.xml")

    AntwortenLöschen