CheckBackups.psm1
|
<#
.SYNOPSIS Checks if backups are not missing, their sizes is growing correctly and if there are any issues, then it is sending notification by email. .PARAMETER PSScriptRoot Default working location for script, containing .ini file etc. .PARAMETER ParameterSplitSymbol Symbol that is used as a splitter in .ini file to split arguments. #> function Check-Backups { [CmdletBinding()] Param( [String]$PSScriptRoot = "C:\Work", [String]$ParameterSplitSymbol = "&", [String]$IniFileName = "CheckBackups.ini", [String]$IniFileLocation = (Join-Path $PSScriptRoot $IniFileName), [String]$LogFileName = "CheckBackups.log", [String]$LogFilePath = (Join-Path $PSScriptRoot $LogFileName), [String]$emailFrom = "powiadomienia.madler@gmail.com", [String]$emailTo = "patryk.milewski@gmail.com", [String]$emailPassword = "powiadomienia123", [String]$emailSubject = "Check-Backups Powershell script problem found.", [String]$emailSmtp = "smtp.gmail.com", [String]$emailPortsmtp = "587", [Switch]$SendEmailNotifications # by default true ) BEGIN { Write-Verbose "Starting Check-Backups function." Write-Verbose "Starting BEGIN block of Check-Backup function." function Print-Parameters([String]$parameters) { Write-Verbose ("Printing parameters for line: " + $parameters) $counter = 1 $line = "" $splittedParameters = $parameters.Split("{" + $ParameterSplitSymbol + "}") foreach ($parameter in $splittedParameters) { $line += " $counter" + ": $parameter" $counter++ } Write-Verbose ("Printing parameters returning final ine: " + $line) return $line } function Get-Size([String]$path) { ((Get-ChildItem -path $path -recurse | Measure-Object -property length -sum).sum) } } PROCESS { Write-Verbose "Starting PROCESS block of Check-Backup function." if (Test-Path $IniFileLocation) { Write-Verbose ("Reading input from ini file in path: " + $IniFileLocation) $iniFile = [System.IO.File]::OpenText($IniFileLocation) $iniFile = Get-Content $IniFileLocation $logger = New-Object Logger($LogFileName, $LogFilePath) [EmailAutoresponder]$emailAutoresponder = New-Object EmailAutoresponder($emailFrom, $emailTo, $emailPassword, $emailSubject, $emailSmtp, $emailPortsmtp) foreach ($line in $iniFile) { if ($line[0] -eq "#" -or $line.Length -eq 0) { Write-Verbose ("Skipping empty or comment line: " + $line + " .") continue } Write-Verbose ("Executing line with parameters:" + (Print-Parameters $line)) $splittedLine = $line.Split("{" + $ParameterSplitSymbol + "}") $backupPath = $splittedLine[0] $hotBackupWeekly = ($splittedLine[1] -like "true") $sendEmailNotification = ($splittedLine[2] -like "true") $expectedColdSizeChange = [Int]$splittedLine[3] $emailAutoresponder.sendMail = $sendEmailNotification [Backup]$backup = New-Object Backup($backupPath, $expectedColdSizeChange, $logger, $emailAutoresponder) if ($hotBackupWeekly -eq $True) { $backup.ValidateStandardBackups() } else { $backup.ValidateNotStandardBackups() } } } else { Write-Error ("Ini file not found, cannot proceed program execution. Provided path: " + $IniFileLocation) return 1 } } END { Write-Verbose "Starting END block of Check-Backup function." if ($emailAutoresponder -ne $null -and $SendEmailNotifications -eq $True) { Write-Verbose "Trying to send email notification." if ($emailAutoresponder.SendMail() -eq $True) { $logger.AddLog("Successfuly sent email notification to: " + $emailTo) } } } } Class Logger { [String]$logFileName [String]$logFileLocation Logger([String]$logFIleName, [String]$logFileLocation) { Write-Verbose ("Constructing Logger Class instance with parameters 1: " + $logFileName + " 2: " + $logFileLocation) $this.logFileName = $logFileName $this.logFileLocation = $logFileLocation $this.Initialize() $this.AddLog("Log file created.") } Initialize() { Write-Verbose "Initializing Logger instance" if (!(Test-Path $this.logFileLocation)) { Write-Verbose "Log file not found, creating a new one" New-item -ItemType File $this.logFileLocation -Force } } AddLog([String]$newLog) { Write-Verbose ("Adding a new log: " + $newLog) "[" + (Get-Date) + ":] " + $newLog | Add-Content $this.logFileLocation } } Class Backup { [String]$backupPath [Int]$expectedColdSizeChange [String[]]$daysOfTheWeek = @("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday") [Logger]$logger [EmailAutoresponder]$emailAutoresponder Backup($backupPath, $expectedColdSizeChange, [ref]$logger, [ref]$emailAutoresponder) { $this.backupPath = $backupPath $this.expectedColdSizeChange = $expectedColdSizeChange $this.logger = $logger.Value $this.emailAutoresponder = $emailAutoresponder.Value } ValidateStandardBackups() { Write-Verbose ("Listing directories in backup path: " + $this.backupPath) if (!(Test-Path $this.backupPath)) { $this.AddLogAndAppendToBody("Provided backup path doesn't exists: " + $this.backupPath) return } $foldersListInPath = Get-ChildItem -Directory -Name -Path $this.backupPath $coldBackups = @() Write-Verbose ("Looking for cold backups in backup path.") foreach ($folderName in $foldersListInPath) { if ($folderName -match "^[\d\.]+$" -and $folderName.Length -eq 8) { $coldBackups += @([Int]$folderName) } } Write-Verbose ("Cold backups found: " + $coldBackups) $coldBackups = [Array]::Sort($coldBackups) $this.logger.AddLog("Found " + $coldBackups.Length + " cold backups in path: " + $this.backupPath) $firstPath = $True $previousSize = 0 foreach ($path in $coldBackups) { if ($firstPath -eq $True) { $previousSize = Get-Size (Join-Path $this.backupPath $path) $firstPath = $False continue } $fullPath = Join-Path $this.backupPath $path $backupSize = (Get-Size $fullPath) $sizeDiff = $backupSize - $previousSize CheckBackupSize $backupSize $sizeDiff $this.expectedColdSizeChange $fullPath } foreach ($day in $this.daysOfTheWeek) { if (!(Test-Path (Join-Path $this.backupPath $day))) { $this.AddLogAndAppendToBody("Missing " + $day + " backup in a path: " + $this.backupPath) continue } $backupSize = Get-Size (Join-Path $this.backupPath $day) if ($backupSize -eq 0) { $this.AddLogAndAppendToBody("Backup in path " + (Join-Path $this.backupPath $day) + " is empty.") } } } ValidateNotStandardBackups() { $this.AddLogAndAppendToBody("Unimplemented method call - ValidateNotStandardBackups in class Backup. Check module ini file.") } hidden CheckBackupSize($backupSize, $sizeDiff, $expectedChange, $fullPath) { if (($expectedChange -gt 0 -and $sizeDiff -lt $expectedChange) -or ($expectedChange -lt 0 -and $sizeDiff -gt $expectedChange)) { $this.AddLogAndAppendToBody("Size difference of a " + $fullPath + " is not inside expected range: " + $expectedChange) } if ($backupSize -eq 0) { $this.AddLogAndAppendToBody("Size of a " + $fullPath + " is 0. Probably missing backup.") } } hidden AddLogAndAppendToBody($log) { $this.logger.AddLog($log) $this.emailAutoresponder.AppendToBody($log) } } Class EmailAutoresponder { [String]$from [String]$to [pscredential]$credential [String]$subject [String]$smtp [String]$port [String]$body = "" [Switch]$sendMail = $False [Switch]$isEmpty = $True EmailAutoresponder([String]$from, [String]$to, [String]$password, [String]$subject, [String]$smtp, [String]$port) { Write-Verbose ("Constructing EmailAutoresponder instance with parameters 1: " + $from + " 2: " + $to + " 3: " + $password + " 4: " + $subject + " 5: " + $smtp + " 6: " + $port) $this.from = $from $this.to = $to $secureString = ConvertTo-SecureString $password -AsPlainText -Force $this.credential = New-Object System.Management.Automation.PSCredential ($this.from, $secureString) $this.subject = $subject $this.smtp = $smtp $this.port = $port } [bool]SendMail() { if ($this.isEmpty -eq $True) { Write-Verbose "Email autoresponder body is empty, not sending an email." return $False } try { Write-Verbose ("Sending email notification with body: " + $this.body) Send-MailMessage -From $this.from -To $this.to -Credential $this.credential -Subject $this.subject -Body $this.body -SmtpServer $this.smtp -Port $this.port -UseSsl -Encoding UTF8 -ErrorAction Stop Write-Verbose "Successfully sent notification" return $True } catch { Write-Verbose "Failed to send notification" return $False } } AppendToBody($body) { if ($this.sendMail -eq $True) { Write-Verbose ("Appending to email body new text: " + $body) $this.body += $body $this.isEmpty = $False } else { Write-Verbose "Not appending to email body a new text, because of sendMail switch set to false." } } ResetResponder() { Write-Verbose "Reseting email autoresponder to default stance." $this.body = "" $this.isEmpty = $True } } Export-ModuleMember -Function Check-Backups |