Newer
Older
# Script to be run by the task scheduler for automatic updates of the AutoTx
# service binaries and / or configuration file.
# Testing has been done on PowerShell 5.1 only, so we set this as a requirement:
#requires -version 5.1
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)][string] $UpdaterSettings
)
try {
. $UpdaterSettings
}
catch {
$ex = $_.Exception.Message
Write-Host "Error reading settings file: '$($UpdaterSettings)' [$($ex)]"
Exit
}
$Me = $MyInvocation.MyCommand -replace '.ps1'
### function definitions #####################################################
function Ensure-ServiceRunning([string]$ServiceName) {
$Continue = $True
try {
$Service = Get-Service $ServiceName -ErrorAction Stop
if ($Service.Status -ne "Running") {
Write-Host "Service $($ServiceName) is not running."
$Continue = $False
}
}
catch {
Write-Host $_.Exception.Message
$Continue = $False
}
if ($Continue) {
Return
}
Log-Error "ERROR: Service '$($ServiceName)' must be installed and running."
Exit
}
function Exit-IfDirMissing([string]$DirName, [string]$Desc) {
if (Test-Path -PathType Container $DirName) {
Return
}
$msg = "ERROR: can't find / access $($Desc) path: $($DirName)"
Send-MailReport -Subject "path or permission error" -Body $msg
Exit
}
function Get-LastLogLines([string]$Path, [int]$Count) {
try {
$msg = Get-Content -Path $Path -Tail $Count -ErrorAction Stop
}
catch {
$ex = $_.Exception.Message
$errxx = "XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX"
$msg = "`n$errxx`n`n$ex `n`n$errxx"
}
# Out-String is required as otherwise all newlines from the log disappear:
Return $msg | Out-String
}
function Stop-MyService([string]$Message) {
if ((Get-Service $ServiceName).Status -eq "Stopped") {
Log-Info "$($Message) (Service already in state 'Stopped')"
Return
}
try {
Log-Info "$($Message) Attempting service $($ServiceName) shutdown..."
Stop-Service "$($ServiceName)" -ErrorAction Stop
}
catch {
$ex = $_.Exception.Message
Send-MailReport -Subject "Shutdown of service $($ServiceName) failed!" `
-Body "Trying to stop the service results in this error:`n$($ex)"
Exit
}
}
function Start-MyService {
if ((Get-Service $ServiceName).Status -eq "Running") {
Return
}
try {
Start-Service "$($ServiceName)" -ErrorAction Stop
}
catch {
$ex = $_.Exception.Message
$msg = "Trying to start the service results in this error:`n$($ex)`n`n"
$msg += " -------------------- last 50 log lines --------------------`n"
$msg += Get-LastLogLines "$($LogPath)\service.log" 50
$msg += " -------------------- ----------------- --------------------`n"
Send-MailReport -Subject "Startup of service $($ServiceName) failed!" `
}
}
Niko Ehrenfeuchter
committed
function Get-WriteTime([string]$FileName) {
try {
$TimeStamp = (Get-Item "$FileName").LastWriteTime
}
catch {
$ex = $_.Exception.Message
Log-Error "Error determining file age of '$($FileName)'!`n$($ex)"
Exit
}
Niko Ehrenfeuchter
committed
Return $TimeStamp
}
Niko Ehrenfeuchter
committed
function File-IsUpToDate([string]$ExistingFile, [string]$UpdateCandidate) {
# Compare write-timestamps, return $False if $UpdateCandidate is newer.
Write-Verbose "Comparing $($UpdateCandidate) vs. $($ExistingFile)..."
$CandidateTime = Get-WriteTime -FileName $UpdateCandidate
$ExistingTime = Get-WriteTime -FileName $ExistingFile
if ($CandidateTime -le $ExistingTime) {
Write-Verbose "File $($ExistingFile) is up-to-date."
Return $True
}
Niko Ehrenfeuchter
committed
Return $False
}
Niko Ehrenfeuchter
committed
function Create-Backup {
Param (
[Parameter(Mandatory=$True)]
[ValidateScript({Test-Path -PathType Leaf $_})]
[String]$FileName
)
$FileWithoutSuffix = [io.path]::GetFileNameWithoutExtension($FileName)
$FileSuffix = [io.path]::GetExtension($FileName)
$BaseDir = Split-Path -Parent $FileName
# assemble a timestamp string like "2017-12-04T16.41.35"
$BakTimeStamp = Get-Date -Format s | foreach {$_ -replace ":", "."}
Niko Ehrenfeuchter
committed
$BakName = "$($FileWithoutSuffix)_pre-$($BakTimeStamp)$($FileSuffix)"
Log-Info "Creating backup of '$($FileName)' as '$($BaseDir)\$($BakName)'."
Niko Ehrenfeuchter
committed
Rename-Item "$FileName" "$BaseDir\$BakName" -ErrorAction Stop
}
catch {
$ex = $_.Exception.Message
Niko Ehrenfeuchter
committed
Log-Error "Backing up '$($FileName)' as '$($BakName) FAILED!`n$($ex)"
Niko Ehrenfeuchter
committed
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
}
function Update-File {
# Check the given $SrcFile if a file with the same name is existing in
# $DstPath. If $SrcFile is newer, stop the service, create a backup of the
# file in $DstPath and finally copy the file from $SrcFile to $DstPath.
#
# Return $True if the file was updated, $False otherwise.
#
# WARNING: the function TERMINATES the script on any error!
#
Param (
[Parameter(Mandatory=$True)]
[ValidateScript({[IO.Path]::IsPathRooted($_)})]
[String]$SrcFile,
[Parameter(Mandatory=$True)]
[ValidateScript({(Get-Item $_).PSIsContainer})]
[String]$DstPath
)
$DstFile = "$($DstPath)\$(Split-Path -Leaf $SrcFile)"
if (-Not (Test-Path "$DstFile")) {
Log-Info "File not existing in destination, NOT UPDATING: $($DstFile)"
Return $False
}
if (File-IsUpToDate -ExistingFile $DstFile -UpdateCandidate $SrcFile) {
Return $False
}
Stop-MyService "Found newer file at $($SrcFile), updating..."
Niko Ehrenfeuchter
committed
try {
Niko Ehrenfeuchter
committed
Create-Backup -FileName $DstFile
}
catch {
Niko Ehrenfeuchter
committed
Log-Error "Backing up $($DstFile) FAILED!`n$($_.Exception.Message)"
Exit
}
try {
Copy-Item -Path $SrcFile -Destination $DstPath -ErrorAction Stop
Log-Info "Updated config file '$($DstFile)'."
}
catch {
Log-Error "Copying $($SrcFile) FAILED!`n$($_.Exception.Message)"
Exit
}
Niko Ehrenfeuchter
committed
Return $True
}
$NewConfig = "$($UpdateConfigPath)\configuration.xml"
if (Test-Path -PathType Leaf $NewConfig) {
Niko Ehrenfeuchter
committed
$ret = Update-File $NewConfig $InstallationPath
} else {
$ret = $False
Write-Verbose "No configuration file found at '$($NewConfig)'."
}
Niko Ehrenfeuchter
committed
Return $ret
}
function Copy-ServiceFiles {
try {
Write-Verbose "Looking for source package using pattern: $($Pattern)"
$PkgDir = Get-ChildItem -Path $UpdateBinariesPath -Directory -Name |
Where-Object {$_ -match $Pattern} |
Sort-Object |
Select-Object -Last 1
Stop-MyService "Trying to update service using package [$($PkgDir)]."
Write-Verbose "Update source package: $($PkgDir)"
Copy-Item -Recurse -Force -ErrorAction Stop `
-Path "$($UpdateBinariesPath)\$($PkgDir)\$($ServiceName)\*" `
-Destination "$InstallationPath"
}
catch {
Log-Error "Updating service binaries FAILED!`n$($_.Exception.Message)"
Exit
}
Log-Info "Updated service binaries!"
}
function Update-ServiceBinaries {
$MarkerFile = "$($UpdateMarkerPath)\$($env:COMPUTERNAME)"
if (Test-Path "$MarkerFile" -Type Leaf) {
Write-Verbose "Found marker [$($MarkerFile)], not updating service."
}
Copy-ServiceFiles
try {
New-Item -Type File "$MarkerFile" -ErrorAction Stop | Out-Null
Write-Verbose "Created marker file [$($MarkerFile)]."
Log-Error "Creating [$($MarkerFile)] FAILED!`n$($_.Exception.Message)"
Log-Info "Service binaries were updated successfully."
}
function Upload-LogFiles {
try {
Copy-Item -Force -ErrorAction Stop `
-Path "$($LogPath)\service.log" `
-Destination $LogfileUpload
Write-Verbose "Uploaded logfile to [$($LogfileUpload)]."
}
catch {
Log-Error "Uploading logfile FAILED!`n$($_.Exception.Message)"
# do not exit here, error on log file uploading is non-critical!
}
}
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
function Send-MailReport([string]$Subject, [string]$Body) {
$Subject = "[$($Me)] $($env:COMPUTERNAME) - $($Subject)"
$msg = "------------------------------`n"
$msg += "From: $($EmailFrom)`n"
$msg += "To: $($EmailTo)`n"
$msg += "Subject: $($Subject)`n`n"
$msg += "Body: $($Body)"
$msg += "`n------------------------------`n"
try {
Send-MailMessage `
-SmtpServer $EmailSMTP `
-From $EmailFrom `
-To $EmailTo `
-Body $Body `
-Subject $Subject `
-ErrorAction Stop
Log-Info -Message "Sent Mail Message:`n$($msg)"
}
catch {
$ex = $_.Exception.Message
$msg = "Error sending email!`n`n$($msg)"
$msg += "Exception message: $($ex)"
Log-Error -Message $msg
}
}
function Log-Message([string]$Type, [string]$Message, [int]$Id){
$msg = "[$($Me)] "
try {
Write-EventLog `
-LogName Application `
-Source "AutoTx" `
-Category 0 `
-EventId $Id `
-EntryType $Type `
-Message "[$($Me)]: $($Message)" `
-ErrorAction Stop
$msg += "Logged message (Id=$($Id), Type=$($Type)).`n"
$msg += "--- Log Message ---`n$($Message)`n--- Log Message ---`n"
}
catch {
$ex = $_.Exception.Message
$msg += "Error logging message (Id=$($Id), Type=$($Type))!`n"
$msg += "--- Log Message ---`n$($Message)`n--- Log Message ---`n"
$msg += "--- Exception ---`n$($ex)`n--- Exception ---"
}
Niko Ehrenfeuchter
committed
Write-Verbose $msg
}
function Log-Error([string]$Message){
Log-Message -Type Error -Message $Message -Id 1
}
function Log-Info([string]$Message) {
Log-Message -Type Information -Message $Message -Id 1
}
################################################################################
# first check if the service is installed and running at all
$UpdateConfigPath = "$($UpdateSourcePath)\Configs\$($env:COMPUTERNAME)"
$UpdateMarkerPath = "$($UpdateSourcePath)\Service\UpdateMarkers"
$UpdateBinariesPath = "$($UpdateSourcePath)\Service\Binaries"
$LogfileUpload = "$($UpdateSourcePath)\Logs\$($env:COMPUTERNAME)"
Exit-IfDirMissing $InstallationPath "installation"
Exit-IfDirMissing $UpdateSourcePath "update source"
Exit-IfDirMissing $UpdateConfigPath "configuration update"
Exit-IfDirMissing $UpdateMarkerPath "update marker"
Exit-IfDirMissing $UpdateBinariesPath "service binaries update"
Exit-IfDirMissing $LogfileUpload "log file target"
# NOTE: Upload-LogFiles is called before AND after the main tasks to make sure
# the logfiles are uploaded no matter if one of the other tasks fails and
# terminates the entire script:
Upload-LogFiles
Niko Ehrenfeuchter
committed
$ConfigUpdated = Update-Configuration
$ServiceUpdated = Update-ServiceBinaries
$msg = ""
if ($ConfigUpdated) {
$msg += "The configuration files were updated.`n"
}
if ($ServiceUpdated) {
$msg += "The service binaries were updated.`n"
}
if ($msg -ne "") {
Start-MyService
Send-MailReport -Subject "Config and / or service has been updated!" `
-Body $msg
}