<#
.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) <= 86400000]]]</Select>
</Query>
<Query Id="1" Path="System">
<Select Path="System">*[System[Provider[@Name='Volsnap'] and TimeCreated[timediff(@SystemTime) <= 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