In grösseren, geschäftskritischen print2forms-Installationen ist es meist nicht gewünscht, dass die Implementierung und der Test neuer Abläufe oder Formulare im produktiven System erfolgt.
Solche Kunden verfügen über eine zusätzliche print2forms-Testinstallation, in der sie gefahrlos entwickeln und testen können. Ist ein neuer Ablauf oder ein neues Formular fertiggestellt, muss es in die Produktivinstallation übertragen werden.
Dies ist keine Aufgabe, die durch die Übertragung einzelner Dateien realisiert werden kann, weil print2forms alle seine XML-Dateien durch Signaturen schützt und auch die Inhaltsverzeichnisse von Containern eine Signatur haben. Deshalb müssen in der Regel immer ganze Container übertragen werden.
Beim Kopieren der Container lauern diverse Fallstricke. Zum einen ist es natürlich so, dass Container, die Daten wie IP-Adressen enthalten nicht kopiert werden sollten. Das betrifft den Drucker-, den Gateway-, den Proxy- und den Router-Container. Diese Container dürfen eh nicht kopiert werden, weil die Testinstallationen aus Kostengründen sowieso nur die Komponenten enthalten, die für die Entwicklung und Test notwendig sind.
Es wäre daher wünschenswert, wenn man diesen Kopierprozess automatisieren könnte.
Im folgenden wird ein PowerShell-Skript vorgestellt, das die Synchronisation zwischen einer Produktiv- und einer Testinstallation von print2forms durchführt.
Die Grundidee bei der Entwicklung des Skripts ist die, dass das p2fRoot-Verzeichnis der Testinstallation zunächst in einem bestimmten Verzeichnis (Variable $source) auf dem Rechner mit der Produktivversion abgelegt wird.
Dann wird entweder manuell oder irgendwann über die Aufgabenplanung zu einem festzulegenden Zeitpunkt (im Wartungsfenster) das Skript aufgerufen.
Als erstes prüft das Skript, ob überhaupt ein p2fRoot-Verzeichnis der Testinstallation gefunden werden kann. Ist dies nicht der Fall, hat das Skript nichts zu tun und es terminiert.
Wird ein p2fRoot-Verzeichnis der Testinstallation vorgefunden, werden als nächstes die print2forms-Dienste angehalten (Variable $services), weil ein Austausch im laufenden Betrieb prinzipiell nicht möglich ist.
Zur Sicherheit wird vom p2fRoot-Verzeichnis der Produktivinstallation (Variable $root) in einem Backup-Verzeichnis (Variable $backup) eine mit einem Zeitstempel versehene Kopie erstellt, mit der im Fehlerfall die Produktivversion wiederhergestellt werden kann.
Anschliessend werden ausgewählte Container (Variable $containers) aus dem p2fRoot-Verzeichnis der Testinstallation in das p2fRoot-Verzeichnis der Produktivinstallation kopiert.
War das erfolgreich, werden die zuvor angehaltenen print2forms-Dienste wieder gestartet. Damit das Skript beim nächsten Aufruf nicht wieder auslöst, wird das p2fRoot-Verzeichnis der Testinstallation gelöscht.
Damit stehen die in der Testinstallation vorgenommenen Änderungen oder Erweiterungen auch in der Produktivinstallation zur Verfügung.
# # p2fCopyRoot.ps1 # <# .SYNOPSIS This script needs to be executed with administrator privilegs. The script exits if no source p2fRoot or no destination p2fRoot are available. The script must be executed on the machine on which p2fServer is running. For event logging the p2fServer is the message source and the id is 5000. - Stop services. - Backup production p2fRoot. - Copy specified containers from source p2fRoot to destination p2fRoot. - Start services. - Remove source root. #> ################################################################################ ## ## Specifiy Parameters ## ################################################################################ # Path to the source root $source = "C:\tmp\p2f\Quelle\p2fRoot" # Path to the destination root $root = 'C:\tmp\p2f\Ziel\p2fRoot' # Path to backup directory $backup="C:\tmp\p2f\Backup" # list of containers to be replaced $containers = "PRC", "OVL", "RES", "ATM", "DAT" # list of services to stop/start # Beware of the order $services = "p2fService", "p2fRouter", "p2fServer" ##################### NO USER MODIFICATIONS BEYOND THIS POINT ################## Set-StrictMode -Version 3 # Administrator credentials required, else abort with error message If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` [Security.Principal.WindowsBuiltInRole] "Administrator")) { # Use p2fServer as message source Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "The script p2fCopyRoot must run as an Administrator" Exit 1 } # # If no source root is available abort without message # If (-NOT (Test-Path $source)) { Exit 2 } # # If no destination root is available abort with message # If (-NOT (Test-Path $root)) { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType success -EventId 5000 -Message "The destination root is not available" Exit 3 } $p2fError = $false # Remember stopped services $stopservices=@() foreach($service in $services) { Try { $a = Get-Service -ErrorAction SilentlyContinue -Name $service if ($a) { if($a.Status -eq "running") { Stop-Service -ErrorAction Stop -InputObject $a $stopservices += @($service) } } else { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "Service \"" + $service + "\" not installed" } } Catch { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "Error on stoping service " + $service } } # Backup If (Test-Path $root) { Try { $date = Get-Date -UFormat "%y-%m-%dT%H-%M" $bpath = $backup + "\" + "p2fRoot-" + $date Copy-Item -Recurse -ErrorAction Stop $root $bpath } Catch { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "Error on backup root" $p2fError = $true } } # Copy container if (! $p2fError) { foreach ($container in $containers) { $target = $root + "\" + $container $src = $source + "\" + $container Try { Remove-Item -Recurse -ErrorAction stop $target Copy-Item -Recurse -ErrorAction stop $src $target } Catch [system.exception] { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "Error on container " + $container } } } # start services [array]::Reverse($stopservices) foreach($service in $stopservices) { Try { $a = Get-Service -ErrorAction Stop -Name $service if($a.Status -ne "running") { Start-Service -ErrorAction Stop -InputObject $a } } Catch { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "Error on starting service " + $service } } # remove source root If (! $p2fError) { Try { Remove-Item -Recurse -ErrorAction stop $source } Catch { Write-EventLog -LogName Application -Source p2fServer -Category 0 -EntryType error -EventId 5000 -Message "Error on removing " + $source } } exit 0
Die nachfolgend abgebildete XML-Datei kann für die Konfiguration der Aufgabenplanung verwendet werden. Es installiert eine Aufgabe die täglich zu einer bestimmten Zeit aufgerufen wird.
Die eigentliche Aufgabe, den Aufruf des Skripts, muss wegen des Skriptpfades an die lokalen Gegebenheiten angepasst werden. Genauso muss auch das Konto für die Ausführung angepasst werden.
<?xml version="1.0" encoding="UTF-16"?> <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <RegistrationInfo> <Date>2016-08-23T11:02:23.1218926</Date> <Author>Thor\rw</Author> <Description>Wenn im Quellverzeichnis eine neue p2fRoot vorhanden ist, wird sie in die p2fRoot des p2fServers kopiert.</Description> </RegistrationInfo> <Triggers> <CalendarTrigger> <StartBoundary>2016-08-23T11:15:00</StartBoundary> <Enabled>true</Enabled> <ScheduleByDay> <DaysInterval>1</DaysInterval> </ScheduleByDay> </CalendarTrigger> </Triggers> <Principals> <Principal id="Author"> <UserId>THOR\RW</UserId> <LogonType>S4U</LogonType> <RunLevel>LeastPrivilege</RunLevel> </Principal> </Principals> <Settings> <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries> <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries> <AllowHardTerminate>true</AllowHardTerminate> <StartWhenAvailable>false</StartWhenAvailable> <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> <IdleSettings> <StopOnIdleEnd>true</StopOnIdleEnd> <RestartOnIdle>false</RestartOnIdle> </IdleSettings> <AllowStartOnDemand>true</AllowStartOnDemand> <Enabled>true</Enabled> <Hidden>false</Hidden> <RunOnlyIfIdle>false</RunOnlyIfIdle> <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession> <UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine> <WakeToRun>false</WakeToRun> <ExecutionTimeLimit>P3D</ExecutionTimeLimit> <Priority>7</Priority> </Settings> <Actions Context="Author"> <Exec> <Command>powershell</Command> <Arguments>-F c:\tmp\p2f\p2fCopyRoot.ps1</Arguments> <WorkingDirectory>c:\tmp\p2f</WorkingDirectory> </Exec> </Actions> </Task>