<# .SYNOPSIS Emails a report of existing Volume Shadow Copies, and VSS-related events logged in the last 24 hours .DESCRIPTION This script uses WMI to retrieve a list of local disk volumes, and a list of all the existing volume shadow copies (shadows). It then correlates them using the VolumeID of the disk volume. It retrieves VSS-related events logged to the Application and System event logs in last 24 hours. It then generates a text email report, comprising of a a summary of the volumes and shadow counts, VSS events, and then a listing of all shadows, grouped by volume. I have configured a Scheduled Task to run this script once a day. .NOTES Author: Geoffrey.Duke@uvm.edu License: Creative Commons Attribution-ShareAlike 3.0 License-URL: http://creativecommons.org/licenses/by-sa/3.0/ v1.2 - 2012-03-23 Expanded VSS event collection v1.1 - 2012-01-12 Added VSS event collection v1.0 - 2012-01-05 Hey, it works! #> # email settings: $email_to = "saa-ad@uvm.edu" $email_server = "smtp.uvm.edu" $hostinfo = get-wmiobject Win32_ComputerSystem $email_from = $( $hostinfo.DNSHostname.ToLower() + '@' + $hostinfo.domain ) $email_subject = $( $hostinfo.DNSHostname + " Volume Shadow Copy Report" ) function main { # Init $line = '-' * 66 $tempfile = [System.IO.Path]::GetTempFileName() # Some custom formats $ft_snaptime = @{ Name="Creation Time"; Width=25; Alignment="left"; Expression={Convert-InstallDate($_.InstallDate)} } $ft_snapid = @{ Name="Shadow Copy ID"; Width=40; Alignment="right"; Expression={$_.ID} } # Collect Volume and VSS data # DriveType 3 is "Local Disk" $volumes = gwmi Win32_Volume -filter "DriveType = '3'" ` -Property DeviceID,DriveLetter,Label | sort {$_.DriveLetter} $snapshots = gwmi Win32_ShadowCopy -Property ID,InstallDate,VolumeName $maxheadroom = 0 $name_for_vol = @{} # Work through each volume, generating both summary info # as well as per-volume shadow lists (saved to temp file # so we don't have to process the whole thing twice) foreach ( $volume in $volumes ) { # Create a meaningful drive heading $heading = "" if ( $volume.DriveLetter ) { $heading = $volume.DriveLetter if ( $volume.Label ) { $heading += $( ' "' + $volume.Label + '"' ) } } else { $heading = '-- ' if ( $volume.Label ) { $heading += $( '"' + $volume.Label + '"' ) } else { $heading += $volume.DeviceID.substring(10,38) } } $volume | add-member noteproperty NiceName $heading $name_for_vol.Add( $volume.DeviceID , $heading ) if ( $heading.length -gt $maxheadroom) { $maxheadroom = $heading.length} # Collect the ShadowCopies for this volume, if any $drivesnaps = $snapshots | ? { $_.VolumeName -eq $volume.DeviceID } | sort InstallDate -descending if ( $drivesnaps -is [array] ) { $snapcount = [string] $drivesnaps.count $volume | add-member noteproperty SnapCount $drivesnaps.count } else { $snapcount = 'No' $volume | add-member noteproperty SnapCount 0 } # Heading is padded nicely in case a VolumeID shows up $summary = $heading.padright(50) + $($snapcount + " Snapshots" ).padleft(16) # If the volume has ShadowCopies, save details to tempfile if ($drivesnaps -is [array]) { # Write the heading to the tempfile add-content $tempfile $summary add-content $tempfile $( '-' * 66 ) foreach ($snap in $drivesnaps) { add-content $tempfile $( ' ' + $(Convert-InstallDate($snap.InstallDate)) + ' ' + $snap.ID) } add-content $tempfile "`n" } } # end foreach volume # Collect VSS Events from App log # Filter: AppLog, Source=VSS, exclude specific Info event, in last 24 hrs. [xml] $vssfilter = @" <QueryList> <Query Id="0" Path="Application"> <Select Path="Application">*[System[Provider[@Name='VSS'] and (EventID!=8224) and TimeCreated[timediff(@SystemTime) &lt;= 86400000]]]</Select> </Query> <Query Id="1" Path="System"> <Select Path="System">*[System[Provider[@Name='Volsnap'] and TimeCreated[timediff(@SystemTime) &lt;= 86400000]]]</Select> </Query> </QueryList> "@ # Regex for extracting volid from event body $re_volid = [Regex] "(?<volid>\\\\\?\\Volume{.+?})" $events = @(Get-WinEvent -FilterXml $vssfilter) # Build email message $body = "Volume Shadow Copy Report - " + $(get-date -f F) + "`n`n" $body += $( "Volume".padright($maxheadroom) + ' ' + "ShadowCopies`n") $body += $( $("-" * $maxheadroom) + ' ' + "------------`n") foreach ( $volume in $volumes ) { $body += $( $volume.NiceName.padright($maxheadroom) + ' ' + ($volume.SnapCount.toString()).padleft(12) ) + "`n" } $body += "`n`nVSS events in last 24 hours:`n" $body += $line + "`n" if ( $events.count -eq 0 ) { body += "No VSS events to report`n" } else { foreach ( $event in $events ) { # replace volume GUID with something useful if( $event.message -match $re_volid) { $volid = $Matches.volid + '\' if($name_for_vol.ContainsKey($volid)) { $volname = $Name_for_Vol[$volid] } else { $volname = '[unknown volume id]' } } else { $volname = "[no volume id in event]" } $body += $( "{0:g} {1} {2} ID:{3} Vol: {4}`n" -f $event.TimeCreated, # 0 $event.Logname.substring(0,3).ToUpper(), # 1 $event.ProviderName, # 2 $event.ID, # 3 $volname # 4 ) $message = $event.Message.replace('Volume Shadow Copy Service','[VSS]') $body += $(' ' + $message.Substring(0,60) +"...`n`n") } $body += "`n" } $body += "`nShadow Copy Details:`n`n" $body += [System.IO.File]::ReadAllText($tempfile) send-mailmessage -to $email_to ` -from $email_from ` -subject $email_subject ` -smtpServer $email_server ` -body $body write-host "Report saved to $tempfile" } # end MAIN function Convert-InstallDate { param( [string] $installdate ) #eg 20111203070045.860152-300 # 012345678901234567890 <-index # 11111111112 $year = $installdate.substring( 0,4) $month = $installdate.substring( 4,2) $day = $installdate.substring( 6,2) $hour = $installdate.substring( 8,2) $minute = $installdate.substring(10,2) $second = $installdate.substring(12,2) return get-date -year $year -month $month -day $day ` -hour $hour -minute $minute -second $second ` -format "yyyy-MM-dd hh:mm.ss tt" } main