SvcProc.psm1
<# #> ######## # Global settings $ErrorActionPreference = "Stop" $InformationPreference = "Continue" Set-StrictMode -Version 2 <# #> Function Format-RecordAsString { [CmdletBinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline)] $Input, [Parameter(Mandatory=$false)] [switch]$DisplaySummary = $false, [Parameter(Mandatory=$true)] [bool]$StopOnError ) begin { $errors = 0 $warnings = 0 } process { $timestamp = [DateTime]::Now.ToString("yyyyMMdd HH:mm") if ([System.Management.Automation.InformationRecord].IsAssignableFrom($_.GetType())) { ("{0} (INFO): {1}" -f $timestamp, $_.ToString()) } elseif ([System.Management.Automation.VerboseRecord].IsAssignableFrom($_.GetType())) { ("{0} (VERBOSE): {1}" -f $timestamp, $_.ToString()) } elseif ([System.Management.Automation.ErrorRecord].IsAssignableFrom($_.GetType())) { $errors++ ("{0} (ERROR): {1}" -f $timestamp, $_.ToString()) $Input | Out-String -Stream | ForEach-Object { ("{0} (ERROR): {1}" -f $timestamp, $_.ToString()) } if ($StopOnError) { Write-Error "Error encountered and StopOnError is true. Stopping." } } elseif ([System.Management.Automation.DebugRecord].IsAssignableFrom($_.GetType())) { ("{0} (DEBUG): {1}" -f $timestamp, $_.ToString()) } elseif ([System.Management.Automation.WarningRecord].IsAssignableFrom($_.GetType())) { $warnings++ ("{0} (WARNING): {1}" -f $timestamp, $_.ToString()) } elseif ([string].IsAssignableFrom($_.GetType())) { ("{0} (INFO): {1}" -f $timestamp, $_.ToString()) } else { # Don't do ToString() here as this breaks things like Format-Table that # don't convert to string properly. Out-String (below) will handle this for us. $Input } } end { # Summarise the number of errors and warnings, if required if ($DisplaySummary) { $timestamp = [DateTime]::Now.ToString("yyyyMMdd HH:mm") ("{0} (INFO): Warnings: {1}" -f $timestamp, $warnings) ("{0} (INFO): Errors: {1}" -f $timestamp, $errors) } } } <# #> Function Reset-LogFileState { [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$LogPath, [Parameter(Mandatory=$false)] [int]$PreserveCount = 5, [Parameter(Mandatory=$false)] [int]$RotateSizeKB = 0 ) process { # Check if the target is a directory if (Test-Path -PathType Container $LogPath) { Write-Error "Target is a directory" } # Create the log file, if it doesn't exist if (!(Test-Path $LogPath)) { Write-Verbose "Log Path doesn't exist. Attempting to create." if ($PSCmdlet.ShouldProcess($LogPath, "Create Log")) { New-Item -Type File $LogPath -EA SilentlyContinue | Out-Null } else { return } } # Get the attributes of the target log file $logInfo = Get-Item $LogPath $logSize = ($logInfo.Length/1024) Write-Verbose "Current log file size: $logSize KB" # Check the size of the log file and rotate if greater than # the desired maximum if ($logSize -gt $RotateSizeKB) { Write-Verbose "Rotation required due to log size" Write-Verbose "PreserveCount: $PreserveCount" # Shuffle all of the logs along [int]$count = $PreserveCount while ($count -gt 0) { # If count is 1, we're working on the active log if ($count -le 1) { $source = $LogPath } else { $source = ("{0}.{1}" -f $LogPath, ($count-1)) } $destination = ("{0}.{1}" -f $LogPath, $count) # Check if there is an actual log to move and rename if (Test-Path -Path $source) { Write-Verbose "Need to rotate $source" if ($PSCmdlet.ShouldProcess($source, "Rotate")) { Move-Item -Path $source -Destination $destination -Force } } $count-- } # Create the log path, if it doesn't exist (i.e. was renamed/rotated) if (!(Test-Path $LogPath)) { if ($PSCmdlet.ShouldProcess($LogPath, "Create Log")) { New-Item -Type File $LogPath -EA SilentlyContinue | Out-Null } else { return } } # Clear the content of the log path (only applies if no rotation was done # due to 0 PreserveCount, but the log is over the RotateSizeKB maximum) if ($PSCmdlet.ShouldProcess($LogPath, "Truncate")) { Clear-Content -Path $LogPath -Force } } } } <# #> Function Invoke-ServiceRun { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [ValidateNotNull()] [ScriptBlock]$ScriptBlock, [Parameter(Mandatory=$false)] [ValidateNotNull()] [int]$Iterations = -1, [Parameter(Mandatory=$false)] [ValidateSet("Start", "Finish")] [string]$WaitFrom = "Start", [Parameter(Mandatory=$false)] [ValidateNotNull()] [int]$WaitSeconds = 0, [Parameter(Mandatory=$false)] [ValidateNotNull()] [string]$LogPath = "", [Parameter(Mandatory=$false)] [int]$RotateSizeKB = 0, [Parameter(Mandatory=$false)] [int]$PreserveCount = 5, [Parameter(Mandatory=$false)] [bool]$StopOnError = $false ) process { # Are we running indefinitely? [int]$count = $Iterations $infinite = $false if ($count -lt 0) { $infinite = $true } # Set up log rotation arguments $rotateArgs = $null if (![string]::IsNullOrEmpty($LogPath)) { $rotateArgs = @{ LogPath = $LogPath PreserveCount = $PreserveCount RotateSizeKB = $RotateSizeKB } } while ($infinite -or $count -gt 0) { # Capture start of script run $start = [DateTime]::Now Write-Verbose ("Start Time: " + $start.ToString("yyyyMMdd HH:mm:ss")) # Rotate log if ($null -ne $rotateArgs) { Reset-LogFileState @rotateArgs } # Run script block and redirect output as string Write-Verbose "Running script block" if ([string]::IsNullOrEmpty($LogPath)) { & { try { & $ScriptBlock *>&1 } catch { $_ } } | Format-RecordAsString -DisplaySummary -StopOnError $StopOnError | Out-String -Stream if (!$?) { Write-Information "Script returned error" } } else { & { try { & $ScriptBlock *>&1 } catch { $_ } } | Format-RecordAsString -DisplaySummary -StopOnError $StopOnError | Out-String -Stream | Tee-Object -Append -FilePath $LogPath if (!$?) { Write-Information "Script returned error" } } # Capture finish of script run $finish = [DateTime]::Now Write-Verbose ("Finish Time: " + $finish.ToString("yyyyMMdd HH:mm:ss")) if ($count -gt 0) { $count-- } # Sleep for next iteration if we have iterations remaining # or are infinite (-1). if ($infinite -or $count -gt 0) { # Calculate the wait time $relative = $finish if ($WaitFrom -eq "Start") { $relative = $start } # Determine the wait time in seconds $wait = ($relative.AddSeconds($WaitSeconds) - [DateTime]::Now).TotalSeconds Write-Verbose "Next iteration in $wait seconds" if ($wait -gt 0) { # Wait until we should run again Write-Verbose "Starting sleep" Start-Sleep -Seconds $wait } } } } } |