Thank you for taking the time to read through this post and help me out. I have been reading Learn PowerShell 3 In a Month of Lunches (3 times) and finishing the last couple of chapters of Learn PowerShell scripting in a month of Lunches. I’m looking forward to Learn PowerShell Toolmaking in a Month of Lunches. These books have really helped me get a foundation in PowerShell and what I can do with that, including how I think about using PowerShell.
With that said, a recent issue at work has given me an opportunity to built a set of tools and a controller script to monitor some backup files. As it is only my second set of tools I have developed, I admittedly am still refining some rough edges, but I have it working pretty smooth now except for one function, and that is the conversion of the object to HTML and then sending it to the last tool for email delivery.
This is the controller script:
$Backup = Get-CheckBackup -Path "\\Server\d$\Folder\" $day = Get-Date $Failed = $Backup | Where-Object {$_.Status -eq "Failed"} $Successful = $Backup | Where-Object {$_.Status -eq "Successful"} If ($Failed -ne $Null) { $Failed | ConvertTo-Html | Out-File -FilePath "\\Server\D$\Folder\Logs\FailedBackup.txt" $FailedBack = Get-Content -Path "\\Server\D$\Folder\Logs\FailedBackup.txt" Send-BackupFileFail -To "NetworkMonitoring@Company.org" -Files $FailedBack } #If IF ($Successful -ne $Null) { $day | Select-Object -Property Date | ConvertTo-Html | Out-File -FilePath "\\Server\D$\Folder\Logs\SuccessfulBackup.txt" -Append $Successful | ConvertTo-HTML | Out-File -FilePath "\\Server\D$\Folder\Logs\SuccessfulBackup.txt" -Append If ($day.DayOfWeek -eq "Sunday") { $success = Get-Content -Path "\\Server\d$\Folder\logs\SuccessfulBackup.txt" Send-IHCRCBackupFileSuccess -To "NetworkMonitoring@Company.org" -Files $success Remove-Item -Path "\\Server\d$\Folder\logs\SuccessfulBackup.txt" } #If } #IF
The script starts by calling my first tool, which collects the files in the patch, checks their LastWriteDate to a date specified in the function call, in this case, the default of 0 or today, and determines if the backup was successful or failed, and then creates and object which is then placed in an array. This is all saved into a variable in the controller script.
Get-CheckBackup function
Function Get-CheckBackup { Get-CheckBackup Status FullName LastWriteTIme ------ -------- ------------- Failed C:\Demo\HelloWorld.ps1 2/12/2018 2:30:38 PM Successful C:\Demo\NewFile.txt 6/21/2018 10:05:28 AM Failed C:\Demo\TestFailedFile1.txt 6/20/2018 3:50:36 PM In this example, the function is run using all the defaults, dasy of 0, current path and no recurse. Files with a lastWriteTime that does not match today's date have a status of "Failed." .EXAMPLE PS C:\> Get-CheckBackup -days 0 -Paths "c:\Demo\NewFile.txt" Status FullName LastWriteTIme ------ -------- ------------- Successful C:\Demo\NewFile.txt 6/21/2018 10:05:28 AM In this example, the days were set to zero, and a path to a specific file is set. The function returns the status of the file based on the LastWriteTime .EXAMPLE PS C:\> Get-CheckBackup -days 1 -Paths "c:\Demo" -Recurse Status FullName LastWriteTIme ------ -------- ------------- Failed C:\Demo\HelloWorld.ps1 2/12/2018 2:30:38 PM Successful C:\Demo\NewFile.txt 6/21/2018 10:05:28 AM Successful C:\Demo\TestFailedFile1.txt 6/20/2018 3:50:36 PM Failed C:\Demo\Demo Plus Folder\Eventlogapplication.htm 3/9/2018 11:40:57 AM Successful C:\Demo\Demo Plus Folder\New File Also.txt 6/21/2018 10:06:10 AM In this example, the days to check against was 1, and a path of "C:\Demo" was set. This displays all the files in the current and all sub folders, and sets a status of "successful" on files that have a lastwritetime of 1 day or less. .EXAMPLE PS C:\> $Days = 2 PS C:\> $Backups = "C:\test","C:\Demo" PS C:\> Get-CheckBackup -Days $Days -Paths $Backups -Recurse Status FullName LastWriteTIme ------ -------- ------------- Successful C:\test\Demo Test File.txt 6/21/2018 11:08:12 AM Failed C:\test\New Hire Procedure.docx 6/6/2017 1:01:15 PM Failed C:\test\Termination Procedure.docx 6/7/2017 3:44:52 PM Failed C:\test\Workstation Retire Procedure.docx 5/24/2017 9:59:57 AM Failed C:\test\PowerShellTest\30days.csv 4/24/2018 4:20:36 PM Failed C:\test\PowerShellTest\Get-FolderSize.ps1 11/8/2017 11:09:46 AM Successful C:\test\PowerShellTest\I Updated Today.txt 6/21/2018 11:08:28 AM Failed C:\test\PowerShellTest\Install-WMF5.1.ps1 3/22/2017 1:44:48 PM Failed C:\Demo\HelloWorld.ps1 2/12/2018 2:30:38 PM Successful C:\Demo\NewFile.txt 6/21/2018 10:05:28 AM Successful C:\Demo\TestFailedFile1.txt 6/20/2018 3:50:36 PM Failed C:\Demo\Demo Plus Folder\Eventlogapplication.htm 3/9/2018 11:40:57 AM Successful C:\Demo\Demo Plus Folder\New File Also.txt 6/21/2018 10:06:10 AM In this example, The days and the path is set by the variables $Days and $Backups. This lists all the files in the folders and sub folders, marking fiels with a lastWritetime of 2 days or less as "successful," and sets the remaining files status as failed. #> [CmdletBinding(SupportsShouldProcess)] param ( [String] $Days = 0, [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String[]] $Paths = (Get-Location), [switch] $recurse ) BEGIN { Write-Verbose "Initializing some variables that are used through function" $day = (Get-Date).AddDays(-$days).ToString('MM/dd/yyyy') $Log = @() } #Begin PROCESS { ForEach ($path in $paths) { Write-Verbose "Collecting the files to verify the backup status" If ($recurse -eq $True) { $Files = Get-ChildItem -Path $path -File -Recurse } #If else { $files = Get-ChildItem -Path $path -File } #Else ForEach ($File in $Files) { Wite-Verbose "Collecting the files that failed their backup" $succeed = $file.LastWriteTime -lt $day Write-Verbose "Collecting properties about the failed backup files" If ($succeed -eq $true) { $props = @{'FullName'=$File.FullName 'LastWriteTIme'=$file.LastWriteTime 'Status'='Failed'} $obj = New-Object -TypeName PSObject -Property $props } #If Write-Verbose "Collecting files which had a successful backup" elseif ($succeed -eq $false) { Write-Verbose "Collecting properties about the successful backup files" $props = @{'FullName'=$File.FullName 'LastWriteTIme'=$file.LastWriteTime 'Status'='Successful'} $obj = New-Object -TypeName PSObject -Property $props } #Elseif Write-Verbose "Adding each record to an array" $log += $obj } #ForEach Comapre } #ForEach Get Files } #Process END { Write-Verbose "Dispalying the results of data collection" Write-Output $log } #End } #Function
The controller script then separates the objects into the variable $Failed for files that did not backup up properly, and $Successful for successful backups. This is where I run into the problem. If I pipe the variable to ConvertTo-HTML | Send-Email tool I built, or even just try and pipe the object, it works, but it creates a separate email for each and every object, even blank lines.
The only solution I have found is to convert the objects in the variable using ConvertTo-HTML then pipe it to OutFile and save the file. After that is done, I have to use Get-Content to populate another variable, and I can then use that Variable to populate the backup file path in the email body to show what file/s either missed their backup or were successful.
Send-BackupFileFail function
Function Send-BackupFileFail { [CmdletBinding(SupportsShouldProcess)] param ( [parameter(Mandatory=$true, HelpMessage = "Enter the email address or addresses to send message to", ValueFromPipelineByPropertyName=$True)] [validatenotnullorempty()] [Alias('Send')] [String[]] $To, [parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [validatenotnullorempty()] [String[]] $Files ) BEGIN {} PROCESS { Write-Verbose "Sending the email notfication with backup files that failed" Send-MailMessage -To $To ` -Subject "Failed Bakup Files - $(Get-Date -DisplayHint Date)" ` -SmtpServer "Email.Company.org" ` -from "Monitoring@Company.org" ` -BodyAsHtml ` -Body "The following files have not completed a successful backukp since the corresponding date and time. $Files Perform any necessary troubleshooting to bring the backups up to date. The Company IT Team Company Ext: 1234 Email: ALL_IT_STAFF@Company.org www.Company.org" } #Process } #Function
Send-BackupFileSuccess function
Function Send-BackupFileSuccess { [CmdletBinding(SupportsShouldProcess)] param ( [parameter(Mandatory=$true, HelpMessage = "Enter the email address or addresses to send message to", ValueFromPipelineByPropertyName=$True)] [validatenotnullorempty()] [Alias('Send')] [String[]] $To, [parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)] [validatenotnullorempty()] [String[]] $Files ) BEGIN {} PROCESS { Write-Verbose "Sending the email notification of successful backup files" Send-MailMessage -To $To ` -Subject "Successful Backup Files Report" ` -SmtpServer "email.company.org" ` -from "Monitoring@company.org" ` -BodyAsHtml ` -Body "The following files have Successfully completed the backukp process for the the dates and times listed. $Files The Company IT Team Companyr Ext: 1234 Email: ALL_IT_STAFF@Company.org www.Company.org" } #Process } #Function
Is there something a missed? Is there a better way to insert these objects into the email body?
Thank you in advance for taking the time to look over the code and helping me to refine this process.
Daniel Olson